From 44ced23a29afc8a9e8ef743fb8a8f84790740094 Mon Sep 17 00:00:00 2001 From: Robin Becker Date: Tue, 29 Jun 2021 20:54:47 +0100 Subject: [PATCH] [watchmedo] Fix usage of os.setsid() and os.killpg() Unix-only functions (#810) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #809. Co-authored-by: Mickaël Schoentgen --- .github/workflows/tests.yml | 2 +- changelog.rst | 3 ++- src/watchdog/tricks/__init__.py | 14 +++++++++++--- tests/test_watchmedo.py | 23 +++++++++++++++++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 24ba6af9..9b0c18f9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,7 +6,7 @@ on: - ".github/workflows/tests.yml" - "requirements-tests.txt" - "setup.*" - - "src/*" + - "src/**/*" - "tests/*.py" jobs: diff --git a/changelog.rst b/changelog.rst index bac59df1..21b8e57b 100644 --- a/changelog.rst +++ b/changelog.rst @@ -8,7 +8,8 @@ Changelog 2021-xx-x • `full history `__ -- Thanks to our beloved contributors: @ +- [watchmedo] Fix usage of ``os.setsid()`` and ``os.killpg()`` Unix-only functions. (`#809 `_) +- Thanks to our beloved contributors: @replabrobin, @BoboTiG 2.1.3 ~~~~~ diff --git a/src/watchdog/tricks/__init__.py b/src/watchdog/tricks/__init__.py index 5c3f2ab8..300d3597 100644 --- a/src/watchdog/tricks/__init__.py +++ b/src/watchdog/tricks/__init__.py @@ -170,13 +170,21 @@ def __init__(self, command, patterns=None, ignore_patterns=None, self.process = None def start(self): - self.process = subprocess.Popen(self.command, preexec_fn=os.setsid) + # windows doesn't have setsid + self.process = subprocess.Popen(self.command, preexec_fn=getattr(os, 'setsid', None)) def stop(self): if self.process is None: return + + def kill_process(stop_signal): + if hasattr(os, 'getpgid') and hasattr(os, 'killpg'): + os.killpg(os.getpgid(self.process.pid), stop_signal) + else: + os.kill(self.process.pid, self.stop_signal) + try: - os.killpg(os.getpgid(self.process.pid), self.stop_signal) + kill_process(self.stop_signal) except OSError: # Process is already gone pass @@ -188,7 +196,7 @@ def stop(self): time.sleep(0.25) else: try: - os.killpg(os.getpgid(self.process.pid), 9) + kill_process(9) except OSError: # Process is already gone pass diff --git a/tests/test_watchmedo.py b/tests/test_watchmedo.py index 4b883732..c90dec89 100644 --- a/tests/test_watchmedo.py +++ b/tests/test_watchmedo.py @@ -48,3 +48,26 @@ def test_load_config_invalid(tmpdir): watchmedo.load_config(yaml_file) assert not os.path.exists(critical_dir) + + +def make_dummy_script(tmpdir, n=10): + script = os.path.join(tmpdir, 'auto-test-%d.py' % n) + with open(script, 'w') as f: + f.write('import time\nfor i in range(%d):\n\tprint("+++++ %%d" %% i, flush=True)\n\ttime.sleep(1)\n' % n) + return script + + +def test_kill_auto_restart(tmpdir, capfd): + from watchdog.tricks import AutoRestartTrick + import sys + import time + script = make_dummy_script(tmpdir) + a = AutoRestartTrick([sys.executable, script]) + a.start() + time.sleep(5) + a.stop() + cap = capfd.readouterr() + assert '+++++ 0' in cap.out + assert '+++++ 9' not in cap.out # we killed the subprocess before the end + # in windows we seem to lose the subprocess stderr + # assert 'KeyboardInterrupt' in cap.err