mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 17:13:08 +00:00 
			
		
		
		
	Add a pid file to avoid running honcho multiple times
This commit is contained in:
		| @@ -13,6 +13,8 @@ | ||||
| # OR WITHIN THE LOCAL FILE "LICENSE" | ||||
| # | ||||
| # | ||||
| import atexit | ||||
| import logging | ||||
| import os | ||||
| import sys | ||||
|  | ||||
| @@ -22,13 +24,14 @@ from sith.composer import start_composer, stop_composer | ||||
| from sith.settings import PROCFILE_RUNSERVER | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     logging.basicConfig(encoding="utf-8", level=logging.INFO) | ||||
|  | ||||
|     os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sith.settings") | ||||
|  | ||||
|     from django.core.management import execute_from_command_line | ||||
|  | ||||
|     if os.environ.get(DJANGO_AUTORELOAD_ENV) is None and PROCFILE_RUNSERVER is not None: | ||||
|         start_composer(PROCFILE_RUNSERVER) | ||||
|         _ = atexit.register(stop_composer) | ||||
|  | ||||
|     execute_from_command_line(sys.argv) | ||||
|  | ||||
|     stop_composer() | ||||
|   | ||||
| @@ -1,26 +1,68 @@ | ||||
| import os | ||||
| import logging | ||||
| import signal | ||||
| import subprocess | ||||
| import sys | ||||
|  | ||||
| import psutil | ||||
|  | ||||
| COMPOSER_PID = "COMPOSER_PID" | ||||
| from sith import settings | ||||
|  | ||||
|  | ||||
| def get_pid() -> int | None: | ||||
|     """Read the PID file to get the currently running composer if it exists""" | ||||
|     if not settings.COMPOSER_PID_PATH.exists(): | ||||
|         return None | ||||
|     with open(settings.COMPOSER_PID_PATH, "r", encoding="utf8") as f: | ||||
|         return int(f.read()) | ||||
|  | ||||
|  | ||||
| def write_pid(pid: int): | ||||
|     """Write currently running composer pid in PID file""" | ||||
|     if not settings.COMPOSER_PID_PATH.exists(): | ||||
|         settings.COMPOSER_PID_PATH.parent.mkdir(parents=True, exist_ok=True) | ||||
|     with open(settings.COMPOSER_PID_PATH, "w", encoding="utf8") as f: | ||||
|         _ = f.write(str(pid)) | ||||
|  | ||||
|  | ||||
| def delete_pid(): | ||||
|     """Delete PID file for cleanup""" | ||||
|     settings.COMPOSER_PID_PATH.unlink(missing_ok=True) | ||||
|  | ||||
|  | ||||
| def is_composer_running() -> bool: | ||||
|     """Check if the process in the PID file is running""" | ||||
|     pid = get_pid() | ||||
|     if pid is None: | ||||
|         return False | ||||
|     try: | ||||
|         return psutil.Process(pid).is_running() | ||||
|     except psutil.NoSuchProcess: | ||||
|         return False | ||||
|  | ||||
|  | ||||
| def start_composer(procfile: str): | ||||
|     """Starts the composer and stores the PID as an environment variable | ||||
|     This allows for running smoothly with the django reloader | ||||
|     """ | ||||
|     if is_composer_running(): | ||||
|         logging.info(f"Composer is already running with pid {get_pid()}") | ||||
|         logging.info( | ||||
|             f"If this is a mistake, please delete {settings.COMPOSER_PID_PATH} and restart the process" | ||||
|         ) | ||||
|         return | ||||
|     process = subprocess.Popen( | ||||
|         [sys.executable, "-m", "honcho", "-f", procfile, "start"], | ||||
|     ) | ||||
|     os.environ[COMPOSER_PID] = str(process.pid) | ||||
|     write_pid(process.pid) | ||||
|  | ||||
|  | ||||
| def stop_composer(): | ||||
|     """Stops the composer if it was started before""" | ||||
|     if (pid := os.environ.get(COMPOSER_PID, None)) is not None: | ||||
|         process = psutil.Process(int(pid)) | ||||
|     if is_composer_running(): | ||||
|         process = psutil.Process(get_pid()) | ||||
|         if process.parent() != psutil.Process(): | ||||
|             logging.info("Currently running composer is controlled by another process") | ||||
|             return | ||||
|         process.send_signal(signal.SIGTERM) | ||||
|         process.wait() | ||||
|         _ = process.wait() | ||||
|         delete_pid() | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import atexit | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| from .composer import start_composer, stop_composer | ||||
| @@ -15,7 +17,4 @@ def pytest_load_initial_conftests(early_config, parser, args): | ||||
|     """Hook that loads the composer before the pytest-django plugin""" | ||||
|     if PROCFILE_PYTEST is not None: | ||||
|         start_composer(PROCFILE_PYTEST) | ||||
|  | ||||
|  | ||||
| def pytest_unconfigure(config): | ||||
|     stop_composer() | ||||
|         _ = atexit.register(stop_composer) | ||||
|   | ||||
| @@ -56,6 +56,9 @@ BASE_DIR = Path(__file__).parent.parent.resolve() | ||||
| PROCFILE_RUNSERVER = env.str("PROCFILE_RUNSERVER", None) | ||||
| PROCFILE_PYTEST = env.str("PROCFILE_PYTEST", None) | ||||
|  | ||||
| ## File path used to avoid running the composer multiple times at the same time | ||||
| COMPOSER_PID_PATH = env.path("COMPOSER_PID_PATH", BASE_DIR / "composer.pid") | ||||
|  | ||||
| # Quick-start development settings - unsuitable for production | ||||
| # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user