make_app() now uses the debugger param in DebugMiddleware()

Setting app.debugger in setup.py now takes proper effect.
A warning is issued if the entry point is not callable.

Change-Id: I58a860b8ef0e1cb956b4554071275c024a1949d2
Closes-Bug: #1449573
This commit is contained in:
Joe D'Andrea
2015-04-28 11:33:49 -04:00
parent 87b8d00a2e
commit ec7bdc432d
5 changed files with 76 additions and 12 deletions

View File

@@ -29,11 +29,24 @@ browser for easy debugging:
To further aid in debugging, the middleware includes the ability to repeat the To further aid in debugging, the middleware includes the ability to repeat the
offending request, automatically inserting a breakpoint, and dropping your offending request, automatically inserting a breakpoint, and dropping your
console into the Python debugger, ``pdb``: console into the Python debugger, ``pdb.post_mortem``:
.. figure:: debug-middleware-2.png .. figure:: debug-middleware-2.png
:alt: Pecan debug middleware request debugger. :alt: Pecan debug middleware request debugger.
You can also use any debugger with a suitable ``post_mortem`` entry point.
For example, to use the `PuDB Debugger <http://pypi.python.org/pypi/pudb>`_,
set ``debugger`` like so::
import pudb
app = {
...
'debug': True,
'debugger': pudb.post_mortem,
...
}
.. seealso:: .. seealso::
Refer to the `pdb documentation Refer to the `pdb documentation

View File

@@ -4,19 +4,16 @@ from .core import (
) )
from .decorators import expose from .decorators import expose
from .hooks import RequestViewerHook from .hooks import RequestViewerHook
from .middleware.debug import DebugMiddleware
from .middleware.errordocument import ErrorDocumentMiddleware
from .middleware.recursive import RecursiveMiddleware
from .middleware.static import StaticFileMiddleware
from .configuration import set_config, Config from .configuration import set_config, Config
from .configuration import _runtime_conf as conf from .configuration import _runtime_conf as conf
from . import middleware
try: try:
from logging.config import dictConfig as load_logging_config from logging.config import dictConfig as load_logging_config
except ImportError: except ImportError:
from logutils.dictconfig import dictConfig as load_logging_config # noqa from logutils.dictconfig import dictConfig as load_logging_config # noqa
import six
import warnings import warnings
@@ -40,6 +37,8 @@ def make_app(root, **kw):
debug mode is set. debug mode is set.
:param debug: A flag to enable debug mode. This enables the debug :param debug: A flag to enable debug mode. This enables the debug
middleware and serving static files. middleware and serving static files.
:param debugger: A callable to start debugging, defaulting to the Python
debugger entry point ``pdb.post_mortem``.
:param wrap_app: A function or middleware class to wrap the Pecan app. :param wrap_app: A function or middleware class to wrap the Pecan app.
This must either be a wsgi middleware class or a This must either be a wsgi middleware class or a
function that returns a wsgi application. This wrapper function that returns a wsgi application. This wrapper
@@ -90,19 +89,28 @@ def make_app(root, **kw):
# Configuration for serving custom error messages # Configuration for serving custom error messages
errors = kw.get('errors', getattr(conf.app, 'errors', {})) errors = kw.get('errors', getattr(conf.app, 'errors', {}))
if errors: if errors:
app = ErrorDocumentMiddleware(app, errors) app = middleware.errordocument.ErrorDocumentMiddleware(app, errors)
# Included for internal redirect support # Included for internal redirect support
app = RecursiveMiddleware(app) app = middleware.recursive.RecursiveMiddleware(app)
# When in debug mode, load our exception dumping middleware # When in debug mode, load our exception dumping middleware
static_root = kw.get('static_root', None) static_root = kw.get('static_root', None)
if debug: if debug:
app = DebugMiddleware(app) debugger = kw.get('debugger', None)
debugger_kwargs = {}
if six.callable(debugger):
debugger_kwargs['debugger'] = debugger
elif debugger:
warnings.warn(
"`app.debugger` is not callable, ignoring",
RuntimeWarning
)
app = middleware.debug.DebugMiddleware(app, **debugger_kwargs)
# Support for serving static files (for development convenience) # Support for serving static files (for development convenience)
if static_root: if static_root:
app = StaticFileMiddleware(app, static_root) app = middleware.static.StaticFileMiddleware(app, static_root)
elif static_root: elif static_root:
warnings.warn( warnings.warn(

View File

@@ -0,0 +1,4 @@
from . import debug
from . import errordocument
from . import recursive
from . import static

View File

@@ -253,7 +253,10 @@ class DebugMiddleware(object):
To further aid in debugging, the middleware includes the ability to repeat To further aid in debugging, the middleware includes the ability to repeat
the offending request, automatically inserting a breakpoint, and dropping the offending request, automatically inserting a breakpoint, and dropping
your console into the Python debugger, ``pdb``. your console into the Python debugger, ``pdb.post_mortem``.
You can also use any debugger with a suitable ``post_mortem`` entry point
such as the `PuDB Debugger <http://pypi.python.org/pypi/pudb>`_,
For more information, refer to the `documentation for pdb For more information, refer to the `documentation for pdb
<http://docs.python.org/library/pdb.html>`_ available on the Python <http://docs.python.org/library/pdb.html>`_ available on the Python
@@ -261,7 +264,7 @@ class DebugMiddleware(object):
:param app: the application to wrap. :param app: the application to wrap.
:param debugger: a callable to start debugging, defaulting to the Python :param debugger: a callable to start debugging, defaulting to the Python
debugger, ``pdb``. debugger entry point ``pdb.post_mortem``.
""" """
def __init__(self, app, debugger=pdb.post_mortem): def __init__(self, app, debugger=pdb.post_mortem):

View File

@@ -5,6 +5,7 @@ import warnings
import webob import webob
from webob.exc import HTTPNotFound from webob.exc import HTTPNotFound
import mock
from webtest import TestApp from webtest import TestApp
import six import six
from six import b as b_ from six import b as b_
@@ -1616,6 +1617,41 @@ class TestNonCanonical(PecanTestCase):
assert len(wrapped_apps) == 1 assert len(wrapped_apps) == 1
class TestDebugging(PecanTestCase):
def test_debugger_setup(self):
class RootController(object):
pass
def debugger():
pass
app_conf = dict(
debug=True,
debugger=debugger
)
with mock.patch('pecan.middleware.debug.DebugMiddleware') \
as patched_debug_middleware:
app = make_app(RootController(), **app_conf)
args, kwargs = patched_debug_middleware.call_args
assert kwargs.get('debugger') == debugger
def test_invalid_debugger_setup(self):
class RootController(object):
pass
debugger = 'not_a_valid_entry_point'
app_conf = dict(
debug=True,
debugger=debugger
)
with mock.patch('pecan.middleware.debug.DebugMiddleware') \
as patched_debug_middleware:
app = make_app(RootController(), **app_conf)
args, kwargs = patched_debug_middleware.call_args
assert kwargs.get('debugger') is None
class TestLogging(PecanTestCase): class TestLogging(PecanTestCase):
def test_logging_setup(self): def test_logging_setup(self):