]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
fix autoreload argv perservation
authorSteve <steve.liushihao@gmail.com>
Tue, 19 Dec 2017 14:27:57 +0000 (22:27 +0800)
committerBen Darnell <ben@bendarnell.com>
Sat, 19 May 2018 23:53:57 +0000 (19:53 -0400)
tornado/autoreload.py
tornado/test/autoreload_test.py

index 2f91127031480b734bac3a03164911606af7ce8d..38b3f8470f1b3c1e890f3e5f768f1b52f1ff27ee 100644 (file)
@@ -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()
+
index 6a9729dbbebaf8ef23c5703404a361c680197af9..44cb31397e020e2e701557669d07abc4976f8aa2 100644 (file)
@@ -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)