multi-process mode is used.
"""
+from __future__ import with_statement
+
import functools
import logging
import os
scheduler = ioloop.PeriodicCallback(callback, check_time, io_loop=io_loop)
scheduler.start()
+def wait():
+ """Wait for a watched file to change, then restart the process.
+
+ Intended to be used at the end of scripts like unit test runners,
+ to run the tests again after any source file changes (but see also
+ the command-line interface in `main`)
+ """
+ io_loop = ioloop.IOLoop()
+ start(io_loop)
+ io_loop.start()
+
+_watched_files = set()
+
+def watch(filename):
+ """Add a file to the watch list.
+
+ All imported modules are watched by default.
+ """
+ _watched_files.add(filename)
_reload_attempted = False
if not path: continue
if path.endswith(".pyc") or path.endswith(".pyo"):
path = path[:-1]
+ _check_file(io_loop, modify_times, path)
+ for path in _watched_files:
+ _check_file(io_loop, modify_times, path)
+
+def _check_file(io_loop, modify_times, path):
try:
modified = os.stat(path).st_mtime
except:
- continue
+ return
if path not in modify_times:
modify_times[path] = modified
- continue
+ return
if modify_times[path] != modified:
logging.info("%s modified; restarting server", path)
_reload_attempted = True
os.spawnv(os.P_NOWAIT, sys.executable,
[sys.executable] + sys.argv)
sys.exit(0)
+
+_USAGE = """\
+Usage:
+ python -m tornado.autoreload -m module.to.run [args...]
+ python -m tornado.autoreload path/to/script.py [args...]
+"""
+def main():
+ """Command-line wrapper to re-run a script whenever its source changes.
+
+ Scripts may be specified by filename or module name::
+
+ python -m tornado.autoreload -m tornado.test.runtests
+ python -m tornado.autoreload tornado/test/runtests.py
+
+ Running a script with this wrapper is similar to calling
+ `tornado.autoreload.wait` at the end of the script, but this wrapper
+ can catch import-time problems like syntax errors that would otherwise
+ prevent the script from reaching its call to `wait`.
+ """
+ original_argv = sys.argv
+ sys.argv = sys.argv[:]
+ if len(sys.argv) >= 3 and sys.argv[1] == "-m":
+ mode = "module"
+ module = sys.argv[2]
+ del sys.argv[1:3]
+ elif len(sys.argv) >= 2:
+ mode = "script"
+ script = sys.argv[1]
+ sys.argv = sys.argv[1:]
+ else:
+ print >>sys.stderr, _USAGE
+ sys.exit(1)
+
+ try:
+ if mode == "module":
+ import runpy
+ runpy.run_module(module, run_name="__main__", alter_sys=True)
+ elif mode == "script":
+ with open(script) as f:
+ global __file__
+ __file__ = script
+ # Use globals as our "locals" dictionary so that
+ # something that tries to import __main__ (e.g. the unittest
+ # module) will see the right things.
+ exec f.read() in globals(), globals()
+ print "exec done"
+ except SystemExit, e:
+ logging.info("Script exited with status %s", e.code)
+ except Exception, e:
+ logging.warning("Script exited with uncaught exception", exc_info=True)
+ if isinstance(e, SyntaxError):
+ watch(e.filename)
+ sys.argv = original_argv
+ wait()
+
+
+if __name__ == "__main__":
+ main()
handler.stream = old_stream
def main():
- """A simple test runner with autoreload support.
+ """A simple test runner.
+
+ This test runner is essentially equivalent to `unittest.main` from
+ the standard library, but adds support for tornado-style option
+ parsing and log formatting.
The easiest way to run a test is via the command line::
- python -m tornado.testing --autoreload tornado.test.stack_context_test
+ python -m tornado.testing tornado.test.stack_context_test
See the standard library unittest module for ways in which tests can
be specified.
be overridden by naming a single test on the command line::
# Runs all tests
- tornado/test/runtests.py --autoreload
+ tornado/test/runtests.py
# Runs one test
- tornado/test/runtests.py --autoreload tornado.test.stack_context_test
+ tornado/test/runtests.py tornado.test.stack_context_test
- If --autoreload is specified, the process will continue running
- after the tests finish, and when any source file changes the tests
- will be rerun. Without --autoreload, the process will exit
- once the tests finish (with an exit status of 0 for success and
- non-zero for failures).
"""
from tornado.options import define, options, parse_command_line
- define('autoreload', type=bool, default=False)
+ define('autoreload', type=bool, default=False,
+ help="DEPRECATED: use tornado.autoreload.main instead")
define('httpclient', type=str, default=None)
argv = [sys.argv[0]] + parse_command_line(sys.argv)
raise
if options.autoreload:
import tornado.autoreload
- import tornado.ioloop
- ioloop = tornado.ioloop.IOLoop()
- tornado.autoreload.start(ioloop)
- ioloop.start()
+ tornado.autoreload.wait()
if __name__ == '__main__':
main()