-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix reloader on OSX py38 and Windows (#1827)
* Fix watchdog reload worker repeatedly if there are multiple changed files * Simplify autoreloader, don't need multiprocessing.Process. Now works on OSX py38. * Allow autoreloader with multiple workers and run it earlier. * This works OK on Windows too. * I don't see how cwd could be different here. * app.run and app.create_server argument fixup. * Add test for auto_reload (coverage not working unfortunately). * Reloader cleanup, don't use external kill commands and exit normally. * Strip newlines on test output (Windows-compat). * Report failures in test_auto_reload to avoid timeouts. * Use different test server ports to avoid binding problems on Windows. * Fix previous commit * Listen on same port after reload. * Show Goin' Fast banner on reloads. * More robust testing, also -m sanic. * Add a timeout to terminate process * Try a workaround for tmpdir deletion on Windows. * Join process also on error (context manager doesn't). * Cleaner autoreloader termination on Windows. * Remove unused code. * Rename test. * Longer timeout on test exit. Co-authored-by: Hùng X. Lê <[email protected]> Co-authored-by: L. Kärkkäinen <[email protected]> Co-authored-by: Adam Hopkins <[email protected]>
- Loading branch information
1 parent
4658e0f
commit 230941f
Showing
3 changed files
with
142 additions
and
127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import os | ||
import secrets | ||
import sys | ||
|
||
from subprocess import PIPE, Popen | ||
from tempfile import TemporaryDirectory | ||
from textwrap import dedent | ||
from threading import Timer | ||
from time import sleep | ||
|
||
import pytest | ||
|
||
|
||
# We need to interrupt the autoreloader without killing it, so that the server gets terminated | ||
# https://stefan.sofa-rockers.org/2013/08/15/handling-sub-process-hierarchies-python-linux-os-x/ | ||
|
||
try: | ||
from signal import CTRL_BREAK_EVENT | ||
from subprocess import CREATE_NEW_PROCESS_GROUP | ||
|
||
flags = CREATE_NEW_PROCESS_GROUP | ||
except ImportError: | ||
flags = 0 | ||
|
||
def terminate(proc): | ||
if flags: | ||
proc.send_signal(CTRL_BREAK_EVENT) | ||
else: | ||
proc.terminate() | ||
|
||
def write_app(filename, **runargs): | ||
text = secrets.token_urlsafe() | ||
with open(filename, "w") as f: | ||
f.write(dedent(f"""\ | ||
import os | ||
from sanic import Sanic | ||
app = Sanic(__name__) | ||
@app.listener("after_server_start") | ||
def complete(*args): | ||
print("complete", os.getpid(), {text!r}) | ||
if __name__ == "__main__": | ||
app.run(**{runargs!r}) | ||
""" | ||
)) | ||
return text | ||
|
||
def scanner(proc): | ||
for line in proc.stdout: | ||
line = line.decode().strip() | ||
print(">", line) | ||
if line.startswith("complete"): | ||
yield line | ||
|
||
|
||
argv = dict( | ||
script=[sys.executable, "reloader.py"], | ||
module=[sys.executable, "-m", "reloader"], | ||
sanic=[sys.executable, "-m", "sanic", "--port", "42104", "--debug", "reloader.app"], | ||
) | ||
|
||
@pytest.mark.parametrize("runargs, mode", [ | ||
(dict(port=42102, auto_reload=True), "script"), | ||
(dict(port=42103, debug=True), "module"), | ||
(dict(), "sanic"), | ||
]) | ||
async def test_reloader_live(runargs, mode): | ||
with TemporaryDirectory() as tmpdir: | ||
filename = os.path.join(tmpdir, "reloader.py") | ||
text = write_app(filename, **runargs) | ||
proc = Popen(argv[mode], cwd=tmpdir, stdout=PIPE, creationflags=flags) | ||
try: | ||
timeout = Timer(5, terminate, [proc]) | ||
timeout.start() | ||
# Python apparently keeps using the old source sometimes if | ||
# we don't sleep before rewrite (pycache timestamp problem?) | ||
sleep(1) | ||
line = scanner(proc) | ||
assert text in next(line) | ||
# Edit source code and try again | ||
text = write_app(filename, **runargs) | ||
assert text in next(line) | ||
finally: | ||
timeout.cancel() | ||
terminate(proc) | ||
proc.wait(timeout=3) |