提交 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:
sys.argv = [sys.argv[0]] + rest
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)
else:
assert path is not None
with open(path) as f:
# 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())
runpy.run_path(path, run_name="__main__")
except SystemExit as e:
gen_log.info("Script exited with status %s", e.code)
except Exception as e:
......
......@@ -11,6 +11,9 @@ import unittest
class AutoreloadTest(unittest.TestCase):
def setUp(self):
# When these tests fail the output sometimes exceeds the default maxDiff.
self.maxDiff = 1024
self.path = mkdtemp()
# 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())
}
)
with self.subTest(mode="module"):
# 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([sys.executable, "-m", "testapp"])
self.assertEqual(
out,
(
"import testapp succeeded\n"
+ "Starting __name__='__main__', __spec__.name=testapp.__main__\n"
)
* 2,
)
with self.subTest(mode="file"):
# When the __main__.py file is run directly, there is no qualified module spec and we
# cannot import testapp.
out = self.run_subprocess([sys.executable, "testapp/__main__.py"])
self.assertEqual(
out,
"import testapp failed\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([sys.executable, "testapp"])
self.assertEqual(
out,
"import testapp failed\nStarting __name__='__main__', __spec__.name=__main__\n"
* 2,
)
# The autoreload wrapper should support all the same modes as the python interpreter.
# The wrapper itself should have no effect on this test so we try all modes with and
# without it.
for wrapper in [False, True]:
with self.subTest(wrapper=wrapper):
with self.subTest(mode="module"):
if wrapper:
base_args = [sys.executable, "-m", "tornado.autoreload"]
else:
base_args = [sys.executable]
# 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"])
self.assertEqual(
out,
(
"import testapp succeeded\n"
+ "Starting __name__='__main__', __spec__.name=testapp.__main__\n"
)
* 2,
)
with self.subTest(mode="file"):
out = self.run_subprocess(base_args + ["testapp/__main__.py"])
# In file mode, we do not expect the path to be set so we can import testapp,
# but when the wrapper is used the -m argument to the python interpreter
# does this for us.
expect_import = (
"import testapp succeeded"
if wrapper
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):
# This test verifies that when `python -m tornado.autoreload`
......@@ -190,6 +214,7 @@ exec(open("run_twice_magic.py").read())
def test_reload_wrapper_args(self):
main = """\
import os
import sys
print(os.path.basename(sys.argv[0]))
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册