Skip to content

Commit

Permalink
[watchmedo] Fix usage of os.setsid() and os.killpg() Unix-only functi…
Browse files Browse the repository at this point in the history
…ons (#810)

Fixes #809.

Co-authored-by: Mickaël Schoentgen <contact@tiger-222.fr>
  • Loading branch information
replabrobin and BoboTiG committed Jun 29, 2021
1 parent 31801ac commit 44ced23
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Expand Up @@ -6,7 +6,7 @@ on:
- ".github/workflows/tests.yml"
- "requirements-tests.txt"
- "setup.*"
- "src/*"
- "src/**/*"
- "tests/*.py"

jobs:
Expand Down
3 changes: 2 additions & 1 deletion changelog.rst
Expand Up @@ -8,7 +8,8 @@ Changelog

2021-xx-x • `full history <https://github.com/gorakhargosh/watchdog/compare/v2.1.3...master>`__

- Thanks to our beloved contributors: @
- [watchmedo] Fix usage of ``os.setsid()`` and ``os.killpg()`` Unix-only functions. (`#809 <https://github.com/gorakhargosh/watchdog/pull/809>`_)
- Thanks to our beloved contributors: @replabrobin, @BoboTiG

2.1.3
~~~~~
Expand Down
14 changes: 11 additions & 3 deletions src/watchdog/tricks/__init__.py
Expand Up @@ -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
Expand All @@ -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
Expand Down
23 changes: 23 additions & 0 deletions tests/test_watchmedo.py
Expand Up @@ -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

0 comments on commit 44ced23

Please sign in to comment.