From: Steve Date: Tue, 19 Dec 2017 14:27:57 +0000 (+0800) Subject: fix autoreload argv perservation X-Git-Tag: v5.1.0b1~11^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=831ae392ce377e49fae1ce7359c47345971926ea;p=thirdparty%2Ftornado.git fix autoreload argv perservation --- diff --git a/tornado/autoreload.py b/tornado/autoreload.py index 2f9112703..38b3f8470 100644 --- a/tornado/autoreload.py +++ b/tornado/autoreload.py @@ -107,6 +107,8 @@ _watched_files = set() _reload_hooks = [] _reload_attempted = False _io_loops = weakref.WeakKeyDictionary() # type: ignore +_autoreload_is_main = False +_original_argv = None def start(check_time=500): @@ -218,7 +220,7 @@ def _reload(): if spec: argv = ['-m', spec.name] + sys.argv[1:] else: - argv = sys.argv + argv = _original_argv if _autoreload_is_main else sys.argv path_prefix = '.' + os.pathsep if (sys.path[0] == '' and not os.environ.get("PYTHONPATH", "").startswith(path_prefix)): @@ -269,7 +271,15 @@ def main(): can catch import-time problems like syntax errors that would otherwise prevent the script from reaching its call to `wait`. """ + # Remember that we were launched with autoreload as main. + # The main module can be tricky; set the variables both in our globals + # (which may be __main__) and the real importable version. + import tornado.autoreload + global _autoreload_is_main + global _original_argv + tornado.autoreload._autoreload_is_main = _autoreload_is_main = True original_argv = sys.argv + tornado.autoreload._original_argv = _original_argv = original_argv sys.argv = sys.argv[:] if len(sys.argv) >= 3 and sys.argv[1] == "-m": mode = "module" @@ -336,4 +346,8 @@ def main(): if __name__ == "__main__": # See also the other __main__ block at the top of the file, which modifies # sys.path before our imports - main() + # Ensure that any global variables accessed by main() are in the module namespace + # instead of the __main__ namespace + import tornado.autoreload + tornado.autoreload.main() + diff --git a/tornado/test/autoreload_test.py b/tornado/test/autoreload_test.py index 6a9729dbb..44cb31397 100644 --- a/tornado/test/autoreload_test.py +++ b/tornado/test/autoreload_test.py @@ -6,9 +6,13 @@ import sys from tempfile import mkdtemp from tornado.test.util import unittest +import tornado.autoreload -MAIN = """\ +class AutoreloadTest(unittest.TestCase): + + def test_reload_module(self): + main = """\ import os import sys @@ -24,15 +28,12 @@ if 'TESTAPP_STARTED' not in os.environ: autoreload._reload() """ - -class AutoreloadTest(unittest.TestCase): - def test_reload_module(self): # Create temporary test application path = mkdtemp() os.mkdir(os.path.join(path, 'testapp')) open(os.path.join(path, 'testapp/__init__.py'), 'w').close() with open(os.path.join(path, 'testapp/__main__.py'), 'w') as f: - f.write(MAIN) + f.write(main) # Make sure the tornado module under test is available to the test # application @@ -46,3 +47,71 @@ class AutoreloadTest(unittest.TestCase): universal_newlines=True) out = p.communicate()[0] self.assertEqual(out, 'Starting\nStarting\n') + + def test_reload_module_with_argv_preservation(self): + main = """\ +import os +import sys +from tornado import autoreload + +# This import will fail if path is not set up correctly +import testapp + +print(autoreload._original_argv) +sys.stdout.flush() +if 'TESTAPP_STARTED' not in os.environ: + os.environ['TESTAPP_STARTED'] = '1' +else: + # Force the autoreload to exit + autoreload.add_reload_hook(lambda: os._exit(0)) + autoreload._reload() +""" + + touch = """\ +import os +import time +import sys +import stat +for i in range(50): + time.sleep(0.1) + + # Update the access time and modification time of file + st = os.stat(sys.argv[1]) + os.utime(sys.argv[1], (st[stat.ST_ATIME] + i, st[stat.ST_MTIME] + i)) + """ + + # Create temporary test application + path = mkdtemp() + os.mkdir(os.path.join(path, 'testapp')) + open(os.path.join(path, 'testapp/__init__.py'), 'w').close() + with open(os.path.join(path, 'testapp/__main__.py'), 'w') as f: + f.write(main) + with open(os.path.join(path, 'testapp/touch.py'), 'w') as f: + f.write(touch) + + # Make sure the tornado module under test is available to the test + # application + pythonpath = os.getcwd() + if 'PYTHONPATH' in os.environ: + pythonpath += os.pathsep + os.environ['PYTHONPATH'] + + autoreload_proc = Popen( + [sys.executable, '-m', 'tornado.autoreload', '-m', 'testapp'], + stdout=subprocess.PIPE, cwd=path, + env=dict(os.environ, PYTHONPATH=pythonpath), + universal_newlines=True) + touch_proc = Popen( + [sys.executable, os.path.join(path, 'testapp/touch.py'), + os.path.join(path, 'testapp/__init__.py')] + , stdout=subprocess.PIPE, cwd=path, + env=dict(os.environ, PYTHONPATH=pythonpath), + universal_newlines=True) + + # Once the autoreload process is done, we kill the touching process + autoreload_proc.wait() + touch_proc.kill() + + out = autoreload_proc.communicate()[0] + autoreload_module = os.path.join(os.path.dirname(os.path.abspath( + tornado.autoreload.__file__)), 'autoreload.py') + self.assertEqual(out, (str([autoreload_module, '-m', 'testapp']) + '\n') * 2)