From e17a61e0ca5b530764aa5005e3d6a07ecdb4e7be Mon Sep 17 00:00:00 2001 From: John Sirois Date: Sun, 13 Oct 2024 18:11:43 -0700 Subject: [PATCH 1/2] Handle devpi-server pid rollover. --- testing/devpi.py | 25 +++++++++++++++---------- tox.ini | 1 + 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/testing/devpi.py b/testing/devpi.py index b52a8e181..c61755842 100644 --- a/testing/devpi.py +++ b/testing/devpi.py @@ -12,6 +12,8 @@ import subprocess import time +import psutil # type: ignore[import] + from pex.atomic_directory import atomic_directory from pex.common import safe_open, safe_rmtree from pex.interpreter import PythonInterpreter @@ -44,7 +46,7 @@ def load(cls): try: with open(cls._PATH) as fp: data = json.load(fp) - return cls(url=data["url"], pid=data["pid"]) + return cls(url=data["url"], pid=data["pid"], create_time=data.get("create_time")) except (OSError, IOError, ValueError, KeyError) as e: logger.warning( "Failed to load devpi-server pid file from {path}: {err}".format( @@ -81,24 +83,27 @@ def record( if not base_url: return None + try: + create_time = psutil.Process(pid).create_time() + except psutil.Error: + return None + url = "{base_url}/root/pypi/+simple/".format(base_url=base_url) with safe_open(cls._PATH, "w") as fp: - json.dump(dict(url=url, pid=pid), fp, indent=2, sort_keys=True) - return cls(url=url, pid=pid) + json.dump(dict(url=url, pid=pid, create_time=create_time), fp, indent=2, sort_keys=True) + return cls(url=url, pid=pid, create_time=create_time) url = attr.ib() # type: str pid = attr.ib() # type: int + create_time = attr.ib() # type: Optional[float] def alive(self): # type: () -> bool - # TODO(John Sirois): Handle pid rollover try: - os.kill(self.pid, 0) - return True - except OSError as e: - if e.errno == errno.ESRCH: # No such process. - return False - raise + process = psutil.Process(self.pid) + return self.create_time is None or self.create_time == process.create_time() + except psutil.NoSuchProcess: + return False def kill(self): # type: () -> None diff --git a/tox.ini b/tox.ini index 22b3e404c..5ef90fe0a 100644 --- a/tox.ini +++ b/tox.ini @@ -43,6 +43,7 @@ deps = # Python < 3.6 in newer releases so we force low here. more-itertools<=8.10.0; python_version < "3.6" pexpect==4.9.0 + psutil pytest==4.6.11; python_version < "3.6" pytest==6.2.5; python_version == "3.6" pytest==7.4.4; python_version == "3.7" From c4409661ded279fe39a27590970d92fb445b6bbb Mon Sep 17 00:00:00 2001 From: John Sirois Date: Mon, 14 Oct 2024 11:40:03 -0700 Subject: [PATCH 2/2] Require process creation time. --- testing/devpi.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/testing/devpi.py b/testing/devpi.py index c61755842..f98e7269c 100644 --- a/testing/devpi.py +++ b/testing/devpi.py @@ -18,7 +18,7 @@ from pex.common import safe_open, safe_rmtree from pex.interpreter import PythonInterpreter from pex.interpreter_constraints import InterpreterConstraint -from pex.typing import TYPE_CHECKING +from pex.typing import TYPE_CHECKING, cast from pex.venv.virtualenv import InvalidVirtualenvError, Virtualenv from testing import PEX_TEST_DEV_ROOT @@ -46,7 +46,7 @@ def load(cls): try: with open(cls._PATH) as fp: data = json.load(fp) - return cls(url=data["url"], pid=data["pid"], create_time=data.get("create_time")) + return cls(url=data["url"], pid=data["pid"], create_time=data["create_time"]) except (OSError, IOError, ValueError, KeyError) as e: logger.warning( "Failed to load devpi-server pid file from {path}: {err}".format( @@ -71,6 +71,14 @@ def _read_base_url( return match.group("url") return None + @staticmethod + def _get_process_creation_time(pid): + # type: (int) -> Optional[float] + try: + return cast(float, psutil.Process(pid).create_time()) + except psutil.Error: + return None + @classmethod def record( cls, @@ -83,9 +91,8 @@ def record( if not base_url: return None - try: - create_time = psutil.Process(pid).create_time() - except psutil.Error: + create_time = cls._get_process_creation_time(pid) + if create_time is None: return None url = "{base_url}/root/pypi/+simple/".format(base_url=base_url) @@ -95,15 +102,11 @@ def record( url = attr.ib() # type: str pid = attr.ib() # type: int - create_time = attr.ib() # type: Optional[float] + create_time = attr.ib() # type: float def alive(self): # type: () -> bool - try: - process = psutil.Process(self.pid) - return self.create_time is None or self.create_time == process.create_time() - except psutil.NoSuchProcess: - return False + return self.create_time == self._get_process_creation_time(self.pid) def kill(self): # type: () -> None