From: Ben Darnell Date: Thu, 27 Jul 2023 00:15:12 +0000 (-0400) Subject: autoreload: Add --until-success flag X-Git-Tag: v6.4.0b1~20^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8f71301ee5a6b2cad1fef5eabe890b992f08b3ce;p=thirdparty%2Ftornado.git autoreload: Add --until-success flag This flag terminates the autoreload loop after the first successful run. This makes it possible to cleanly shut down a process that is using "python -m tornado.autoreload" without printing a traceback. Fixes #2398 --- diff --git a/tornado/autoreload.py b/tornado/autoreload.py index 376b346e3..3277982d7 100644 --- a/tornado/autoreload.py +++ b/tornado/autoreload.py @@ -313,6 +313,11 @@ def main() -> None: ) parser.disable_interspersed_args() parser.add_option("-m", dest="module", metavar="module", help="module to run") + parser.add_option( + "--until-success", + action="store_true", + help="stop reloading after the program exist successfully (status code 0)", + ) opts, rest = parser.parse_args() if opts.module is None: if not rest: @@ -324,6 +329,7 @@ def main() -> None: path = None sys.argv = [sys.argv[0]] + rest + exit_status = 1 try: import runpy @@ -333,6 +339,7 @@ def main() -> None: assert path is not None runpy.run_path(path, run_name="__main__") except SystemExit as e: + exit_status = e.code gen_log.info("Script exited with status %s", e.code) except Exception as e: gen_log.warning("Script exited with uncaught exception", exc_info=True) @@ -349,6 +356,7 @@ def main() -> None: if e.filename is not None: watch(e.filename) else: + exit_status = 0 gen_log.info("Script exited normally") # restore sys.argv so subsequent executions will include autoreload sys.argv = original_argv @@ -361,6 +369,8 @@ def main() -> None: if loader is not None: watch(loader.get_filename()) # type: ignore + if opts.until_success and exit_status == 0: + return wait() diff --git a/tornado/test/autoreload_test.py b/tornado/test/autoreload_test.py index 025b6ed5d..5675faa2a 100644 --- a/tornado/test/autoreload_test.py +++ b/tornado/test/autoreload_test.py @@ -16,12 +16,12 @@ class AutoreloadTest(unittest.TestCase): self.path = mkdtemp() - # Each test app runs itself twice via autoreload. The first time it manually triggers + # Most test apps run themselves twice via autoreload. The first time it manually triggers # a reload (could also do this by touching a file but this is faster since filesystem # timestamps are not necessarily high resolution). The second time it exits directly # so that the autoreload wrapper (if it is used) doesn't catch it. # - # The last line of each test's "main" program should be + # The last line of each such test's "main" program should be # exec(open("run_twice_magic.py").read()) self.write_files( { @@ -240,3 +240,25 @@ exec(open("run_twice_magic.py").read()) ) self.assertEqual(out, "main.py\nargv=['arg1', '--arg2', '-m', 'arg3']\n" * 2) + + def test_reload_wrapper_until_success(self): + main = """\ +import os +import sys + +if "TESTAPP_STARTED" in os.environ: + print("exiting cleanly") + sys.exit(0) +else: + print("reloading") + exec(open("run_twice_magic.py").read()) +""" + + # Create temporary test application + self.write_files({"main.py": main}) + + out = self.run_subprocess( + [sys.executable, "-m", "tornado.autoreload", "--until-success", "main.py"] + ) + + self.assertEqual(out, "reloading\nexiting cleanly\n")