Issue #518 resolved

Autoreload seems to be broken in rev 1092

Anonymous created an issue

Test yourself: turn autoreload on/off.

07/May/2006:03:36:11 HTTP INFO Port 8080 not free on 'localhost' Traceback (most recent call last): File "testing.py", line 38, in ? cherrypy.server.start() File "/home/ari/lib/python2.4/site-packages/cherrypy/_cpserver.py", line 32, in start wait_for_free_port(host, port) File "/home/ari/lib/python2.4/site-packages/cherrypy/_cpserver.py", line 157, in wait_for_free_port raise cherrypy.NotReady("Port not free.") cherrypy._cperror.NotReady: Port not free.

07/May/2006:03:36:11 ENGINE INFO SystemExit raised: shutting down autoreloader

07/May/2006:03:36:11 HTTP INFO HTTP Server shut down

07/May/2006:03:36:11 ENGINE INFO CherryPy shut down

Reported by ari@viivakoodi.org

Comments (5)

  1. Robert Brewer

    Okay, this is getting silly. Autoreload should be the first thing called, not buried halfway into the startup process. Most CP apps follow the pattern: 1) declare app, 2) mount, 3) httpserver.start, 4) engine.start. Maybe 3 and 4 are reversed, but autoreload isn't called until halfway through engine.start. It should instead be 1) autoreload, then in the child process only: declare app, mount, server.start, engine.start, right? We should just move autoreload out of _cpengine.start and into cherrypy.quickstart; stop making it a config entry. Then the whole test framework could stop caring what environment we're in.

  2. Robert Brewer

    Fixed in [1181] in a new "reexec" branch. The new mechanism replaces the old lib/autoreload module, inlining autoreload functionality into cherrypy.engine. The config API is unchanged.

    There's a new Engine.autoreload method, which iterates over Engine.reload_files. If any have changed, it calls another new method, Engine._reexec(), which calls os.execv(). Note in particular that the old autoreload mechanism would always spawn a subprocess on engine startup; the new mechanism uses os.execv instead to replace the current process with a new copy (only after the engine and server are ''fully'' stopped). Note that os.execv has the additional benefit of being callable from any thread, not just the main thread. This was a source of considerable hackery in the old autoreload module (which used os.spawnve and callbacks).

    I decided to call Engine.autoreload from Engine.block instead of Engine.start. My reasoning was that, if you're not calling block(), you're most likely using mod_python (or some other controlling process) that already blocks for you, and therefore autoreload could do Very Bad Things if called. In the rare cases where the user is implementing their own blocking, the autoreload method is available to them just like it is to Engine.block.

    os.execv has a known drawback: on Windows, when running a Python application in a console, the execv call creates a new process, not a child of the caller. Although the new process is attached to the same console (and uses it for stderr, so log_to_screen works), the calling process exits, the console obtains the return code, and then displays a DOS prompt because the calling process has terminated. This could confuse some users into believing that the autoreload failed when it has not. Hmm. Maybe I should've used "print" instead of "cherrypy.log" inside _reexec to ameliorate this. Anyway, I felt this was an infinitely better gotcha than the two-simultaneous-copies-of-my-entire-app gotcha.

    I'm going to leave this in a branch until we have some discussion about the tradeoffs.

  3. Log in to comment