"""DebugAppServer
`DebugAppServer` executes all requests within the main thread, allowing
servlets to be easily debugged using any Python debugger. The drawback is
that only one request can be processed at a time using this approach.
Also, exceptions are not caught and gracefully handled with an HTML error
page. Instead, the exceptions rise uncaught which allows the debugger to
trap them so you can inspect the program.
To use, simply run "python Launch.py DebugAppServer" using whatever debugging
environment you prefer.
Caveats:
This app server supports the AutoReload feature but not for Python's standard
modules or WebKit or WebKit's dependencies (MiscUtils, WebUtils and TaskKit).
Even with that limitation, auto-reload is still useful because if you modify
any module in your site, the auto-reload will happen. Note that the app server
*will* restart when the aformentioned modules are modified, but the modules
won't actually be reloaded.
Currently the session sweeper is still run within a separate thread, and
a "close thread" is started up by the AppServer base class, but neither
of these two extra threads should pose any problems debugging servlets.
Tested on:
- WingIDE on Windows, http://wingware.com/
- PythonWin
- JEdit with the JPyDbg plugin, on Windows
"""
import sys
from Common import *
import ThreadedAppServer, Profiler
OriginalThreadedAppServer = ThreadedAppServer.ThreadedAppServer
ThreadedAppServer.doesRunHandleExceptions = False
class DebugAppServer(OriginalThreadedAppServer):
"""Single-threaded AppServer for debugging purposes.
We are piggybacking on 99% of the code in ThreadedAppServer. Our
trick is to replace the request queue with a dummy object that
executes requests immediately instead of pushing them onto a queue
to be handled by other threads.
"""
_excludePrefixes = 'WebKit MiscUtils WebUtils TaskKit'.split()
def __init__(self, path=None):
"""Initialize DebugAppServer."""
OriginalThreadedAppServer.__init__(self, path)
self._requestQueue = DummyRequestQueue()
print 'You are running the debugging app server.'
def config(self):
"""Return default configuration."""
if self._config is None:
OriginalThreadedAppServer.config(self)
self.setSetting('StartServerThreads', 0)
self.setSetting('MaxServerThreads', 0)
self.setSetting('MinServerThreads', 0)
return self._config
def mainloop(self):
"""Main loop for Windows.
This is needed for COM support on Windows, because special thread
initialization is required on any thread that runs servlets, in
this case the main thread itself.
"""
self.initThread()
try:
OriginalThreadedAppServer.mainloop(self)
finally:
self.delThread()
def createApplication(self):
"""Create and return an application object. Invoked by __init__."""
return DebugApplication(server=self)
def restart(self):
Profiler.reset()
self.initiateShutdown()
self._closeThread.join()
sys.stdout.flush()
sys.stderr.flush()
self._imp.delModules(includePythonModules=False,
excludePrefixes=self._excludePrefixes)
raise ThreadedAppServer.RestartAppServerError
from Application import Application
class DebugApplication(Application):
"""This is a modified Application class for debugging."""
def handleException(self):
"""Handle exception.
This should only be used in cases where there is no transaction object,
for example if an exception occurs when attempting to save a session
to disk.
"""
raise
def handleExceptionInTransaction(self, excInfo, transaction):
"""Handle exception with info.
Raises exception `excInfo` (as returned by ``sys.exc_info()``)
that was generated by `transaction`.
"""
raise
class DummyRequestQueue:
"""This is a dummy replacement for the request queue.
It merely executes handlers as soon as they are "pushed".
"""
def put(self, handler):
handler.handleRequest()
handler.close()
ThreadedAppServer.ThreadedAppServer = DebugAppServer
main = ThreadedAppServer.main
def runMainLoopInThread():
return 0
ThreadedAppServer.runMainLoopInThread = runMainLoopInThread