提交 bc68b8ad 编写于 作者: B Ben Darnell

autoreload: Support directories in CLI wrapper

A previous commit added support for using autoreload within programs
that were started as directories; this commit supports them when
run with the -m tornado.autoreload wrapper.

This change may have side effects for file mode since we now use
runpy.run_path instead of executing the file by hand (I don't think
the run_path function existed when this code was originally written).
上级 04f832ba
...@@ -325,24 +325,13 @@ def main() -> None: ...@@ -325,24 +325,13 @@ def main() -> None:
sys.argv = [sys.argv[0]] + rest sys.argv = [sys.argv[0]] + rest
try: try:
if opts.module is not None: import runpy
import runpy
if opts.module is not None:
runpy.run_module(opts.module, run_name="__main__", alter_sys=True) runpy.run_module(opts.module, run_name="__main__", alter_sys=True)
else: else:
assert path is not None assert path is not None
with open(path) as f: runpy.run_path(path, run_name="__main__")
# Execute the script in our namespace instead of creating
# a new one so that something that tries to import __main__
# (e.g. the unittest module) will see names defined in the
# script instead of just those defined in this module.
global __file__
__file__ = path
# If __package__ is defined, imports may be incorrectly
# interpreted as relative to this module.
global __package__
del __package__
exec_in(f.read(), globals(), globals())
except SystemExit as e: except SystemExit as e:
gen_log.info("Script exited with status %s", e.code) gen_log.info("Script exited with status %s", e.code)
except Exception as e: except Exception as e:
......
...@@ -11,6 +11,9 @@ import unittest ...@@ -11,6 +11,9 @@ import unittest
class AutoreloadTest(unittest.TestCase): class AutoreloadTest(unittest.TestCase):
def setUp(self): def setUp(self):
# When these tests fail the output sometimes exceeds the default maxDiff.
self.maxDiff = 1024
self.path = mkdtemp() self.path = mkdtemp()
# Each test app runs itself twice via autoreload. The first time it manually triggers # Each test app runs itself twice via autoreload. The first time it manually triggers
...@@ -124,38 +127,59 @@ exec(open("run_twice_magic.py").read()) ...@@ -124,38 +127,59 @@ exec(open("run_twice_magic.py").read())
} }
) )
with self.subTest(mode="module"): # The autoreload wrapper should support all the same modes as the python interpreter.
# In module mode, the path is set to the parent directory and we can import testapp. # The wrapper itself should have no effect on this test so we try all modes with and
# Also, the __spec__.name is set to the fully qualified module name. # without it.
out = self.run_subprocess([sys.executable, "-m", "testapp"]) for wrapper in [False, True]:
self.assertEqual( with self.subTest(wrapper=wrapper):
out, with self.subTest(mode="module"):
( if wrapper:
"import testapp succeeded\n" base_args = [sys.executable, "-m", "tornado.autoreload"]
+ "Starting __name__='__main__', __spec__.name=testapp.__main__\n" else:
) base_args = [sys.executable]
* 2, # In module mode, the path is set to the parent directory and we can import
) # testapp. Also, the __spec__.name is set to the fully qualified module name.
out = self.run_subprocess(base_args + ["-m", "testapp"])
with self.subTest(mode="file"): self.assertEqual(
# When the __main__.py file is run directly, there is no qualified module spec and we out,
# cannot import testapp. (
out = self.run_subprocess([sys.executable, "testapp/__main__.py"]) "import testapp succeeded\n"
self.assertEqual( + "Starting __name__='__main__', __spec__.name=testapp.__main__\n"
out, )
"import testapp failed\nStarting __name__='__main__', __spec__.name=None\n" * 2,
* 2, )
)
with self.subTest(mode="file"):
with self.subTest(mode="directory"): out = self.run_subprocess(base_args + ["testapp/__main__.py"])
# Running as a directory finds __main__.py like a module. It does not manipulate # In file mode, we do not expect the path to be set so we can import testapp,
# sys.path but it does set a spec with a name of exactly __main__. # but when the wrapper is used the -m argument to the python interpreter
out = self.run_subprocess([sys.executable, "testapp"]) # does this for us.
self.assertEqual( expect_import = (
out, "import testapp succeeded"
"import testapp failed\nStarting __name__='__main__', __spec__.name=__main__\n" if wrapper
* 2, else "import testapp failed"
) )
# In file mode there is no qualified module spec.
self.assertEqual(
out,
f"{expect_import}\nStarting __name__='__main__', __spec__.name=None\n"
* 2,
)
with self.subTest(mode="directory"):
# Running as a directory finds __main__.py like a module. It does not manipulate
# sys.path but it does set a spec with a name of exactly __main__.
out = self.run_subprocess(base_args + ["testapp"])
expect_import = (
"import testapp succeeded"
if wrapper
else "import testapp failed"
)
self.assertEqual(
out,
f"{expect_import}\nStarting __name__='__main__', __spec__.name=__main__\n"
* 2,
)
def test_reload_wrapper_preservation(self): def test_reload_wrapper_preservation(self):
# This test verifies that when `python -m tornado.autoreload` # This test verifies that when `python -m tornado.autoreload`
...@@ -190,6 +214,7 @@ exec(open("run_twice_magic.py").read()) ...@@ -190,6 +214,7 @@ exec(open("run_twice_magic.py").read())
def test_reload_wrapper_args(self): def test_reload_wrapper_args(self):
main = """\ main = """\
import os
import sys import sys
print(os.path.basename(sys.argv[0])) print(os.path.basename(sys.argv[0]))
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册