Skip to content

Commit

Permalink
Merge pull request #253 from Pylons/fix/separate-toolbar-app-from-hos…
Browse files Browse the repository at this point in the history
…t-app

refactor the toolbar out of the parent app
  • Loading branch information
mmerickel committed Apr 23, 2016
2 parents ca28c00 + 88896ed commit 2e4aa25
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 145 deletions.
33 changes: 19 additions & 14 deletions pyramid_debugtoolbar/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from pyramid.config import Configurator
from pyramid.security import NO_PERMISSION_REQUIRED
from pyramid.settings import asbool
from pyramid.wsgi import wsgiapp2
from pyramid_debugtoolbar.utils import (
as_cr_separated_list,
as_display_debug_or_false,
Expand All @@ -14,9 +12,13 @@
SETTINGS_PREFIX,
STATIC_PATH,
)
from pyramid_debugtoolbar.toolbar import (IRequestAuthorization,
toolbar_tween_factory) # API
toolbar_tween_factory = toolbar_tween_factory # pyflakes
from pyramid_debugtoolbar.toolbar import (
IRequestAuthorization,
IToolbarWSGIApp,
toolbar_tween_factory,
)

toolbar_tween_factory = toolbar_tween_factory # API

default_panel_names = (
'pyramid_debugtoolbar.panels.headers.HeaderDebugPanel',
Expand Down Expand Up @@ -124,8 +126,6 @@ def includeme(config):
# Update the current registry with the new settings
config.registry.settings.update(settings)

config.include('pyramid_mako')
config.add_mako_renderer('.dbtmako', settings_prefix='dbtmako.')
config.add_tween('pyramid_debugtoolbar.toolbar_tween_factory')
config.add_subscriber(
'pyramid_debugtoolbar.toolbar.beforerender_subscriber',
Expand All @@ -138,12 +138,13 @@ def includeme(config):

# Create the new application using the updated settings
application = make_application(settings, config.registry)
config.add_route('debugtoolbar', '/_debug_toolbar/*subpath')
config.add_view(wsgiapp2(application), route_name='debugtoolbar',
permission=NO_PERMISSION_REQUIRED)
config.registry.registerUtility(application, IToolbarWSGIApp)

# register routes and views that can be used within the tween
config.add_route('debugtoolbar', '/_debug_toolbar/*subpath', static=True)
config.add_static_view('/_debug_toolbar/static', STATIC_PATH, static=True)
config.introspection = introspection

config.introspection = introspection

def make_application(settings, parent_registry):
""" WSGI application for rendering the debug toolbar."""
Expand All @@ -157,11 +158,15 @@ def make_application(settings, parent_registry):
config.add_route('debugtoolbar.source', '/source')
config.add_route('debugtoolbar.execute', '/execute')
config.add_route('debugtoolbar.console', '/console')
config.add_route('debugtoolbar.redirect', '/redirect')
config.add_route(EXC_ROUTE_NAME, '/exception')
config.add_route('debugtoolbar.sql_select', '/{request_id}/sqlalchemy/select/{query_index}')
config.add_route('debugtoolbar.sql_explain', '/{request_id}/sqlalchemy/explain/{query_index}')
config.add_route(
'debugtoolbar.sql_select',
'/{request_id}/sqlalchemy/select/{query_index}')
config.add_route(
'debugtoolbar.sql_explain',
'/{request_id}/sqlalchemy/explain/{query_index}')
config.add_route('debugtoolbar.request', '/{request_id}')
config.add_route('debugtoolbar.main', '/')
config.scan('pyramid_debugtoolbar.views')

return config.make_wsgi_app()
24 changes: 12 additions & 12 deletions pyramid_debugtoolbar/panels/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,17 @@ class TracebackPanel(DebugPanel):

def __init__(self, request):
self.request = request
self.traceback = None
self.exc_history = request.exc_history

@property
def has_content(self):
if hasattr(self.request, 'pdbt_tb'):
return True
else:
return False
return self.traceback is not None

def process_response(self, response):
if self.has_content:
traceback = self.request.pdbt_tb

self.traceback = traceback = getattr(self.request, 'pdbt_tb', None)
if self.traceback is not None:
exc = escape(traceback.exception)
summary = Traceback.render_summary(traceback, include_title=False, request=self.request)
token = self.request.registry.pdtb_token
url = '' # self.request.route_url(EXC_ROUTE_NAME, _query=qs)
evalex = self.exc_history.eval_exc
Expand All @@ -42,19 +38,23 @@ def process_response(self, response):
'title': exc,
'exception': exc,
'exception_type': escape(traceback.exception_type),
'summary': summary,
'plaintext': traceback.plaintext,
'plaintext_cs': re.sub('-{2,}', '-', traceback.plaintext),
'traceback_id': traceback.id,
'token': token,
'url': url,
}

def render_content(self, request):
return super(TracebackPanel, self).render_content(request)
# stop hanging onto the request after the response is processed
del self.request

def render_vars(self, request):
return {
'static_path': request.static_url(STATIC_PATH),
'root_path': request.route_url(ROOT_ROUTE_NAME)
'root_path': request.route_url(ROOT_ROUTE_NAME),

# render the summary using the toolbar's request object, not
# the original request that generated the traceback!
'summary': self.traceback.render_summary(
include_title=False, request=request),
}
92 changes: 50 additions & 42 deletions pyramid_debugtoolbar/toolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from pyramid.exceptions import URLDecodeError
from pyramid.httpexceptions import WSGIHTTPException
from pyramid.interfaces import Interface
from pyramid.renderers import render
from pyramid.threadlocal import get_current_request
from pyramid_debugtoolbar.compat import bytes_
from pyramid_debugtoolbar.compat import url_unquote
Expand All @@ -15,20 +14,27 @@
from pyramid_debugtoolbar.utils import hexlify
from pyramid_debugtoolbar.utils import last_proxy
from pyramid_debugtoolbar.utils import logger
from pyramid_debugtoolbar.utils import make_subrequest
from pyramid_debugtoolbar.utils import replace_insensitive
from pyramid_debugtoolbar.utils import STATIC_PATH
from pyramid_debugtoolbar.utils import ToolbarStorage

html_types = ('text/html', 'application/xhtml+xml')

class IToolbarWSGIApp(Interface):
""" Marker interface for the toolbar WSGI application."""
def __call__(environ, start_response):
pass

class IRequestAuthorization(Interface):

class IRequestAuthorization(Interface):
def __call__(request):
"""
Toolbar per-request authorization.
Should return bool values whether toolbar is permitted to be shown
within provided request.
Should return bool values whether toolbar is permitted to monitor
the provided request.
"""


Expand Down Expand Up @@ -120,11 +126,13 @@ def beforerender_subscriber(event):
panel.process_beforerender(event)


def toolbar_tween_factory(handler, registry, _logger=None):
def toolbar_tween_factory(handler, registry, _logger=None, _dispatch=None):
""" Pyramid tween factory for the debug toolbar """
# _logger passed for testing purposes only
# _logger and _dispatch are passed for testing purposes only
if _logger is None:
_logger = logger
if _dispatch is None:
_dispatch = lambda app, request: request.get_response(app)
settings = registry.settings

def sget(opt, default=None):
Expand Down Expand Up @@ -157,36 +165,43 @@ def sget(opt, default=None):
registry.exc_history = exc_history = ExceptionHistory()
exc_history.eval_exc = intercept_exc == 'debug'

def toolbar_tween(request):
request.exc_history = exc_history
request.history = request_history
root_url = request.route_path('debugtoolbar', subpath='')
exclude = [root_url] + exclude_prefixes
last_proxy_addr = None
toolbar_app = registry.getUtility(IToolbarWSGIApp)
dispatch = lambda request: _dispatch(toolbar_app, request)

def toolbar_tween(request):
try:
p = request.path
p = request.path_info
except UnicodeDecodeError as e:
raise URLDecodeError(e.encoding, e.object, e.start, e.end, e.reason)
starts_with_excluded = list(filter(None, map(p.startswith, exclude)))

last_proxy_addr = None
if request.remote_addr:
last_proxy_addr = last_proxy(request.remote_addr)

if last_proxy_addr is None \
or starts_with_excluded \
or not addr_in(last_proxy_addr, hosts) \
or auth_check and not auth_check(request):
return handler(request)
if (
last_proxy_addr is None
or any(p.startswith(e) for e in exclude_prefixes)
or not addr_in(last_proxy_addr, hosts)
or auth_check and not auth_check(request)
):
return handler(request)

root_path = debug_toolbar_url(request, '', _app_url='')
if p.startswith(root_path):
# we know root_path will always have a trailing slash
# but script_name doesn't want it
request.script_name += root_path[:-1]
request.path_info = request.path_info[len(root_path) - 1:]
return dispatch(request)

request.exc_history = exc_history
request.history = request_history
request.pdtb_id = hexlify(id(request))
toolbar = DebugToolbar(request, panel_classes, global_panel_classes,
default_active_panels)
request.debug_toolbar = toolbar

_handler = handler

# XXX
for panel in toolbar.panels:
_handler = panel.wrap_handler(_handler)

Expand All @@ -204,20 +219,14 @@ def toolbar_tween(request):
exc_history.tracebacks[tb.id] = tb
request.pdbt_tb = tb

qs = {'token': registry.pdtb_token, 'tb': str(tb.id)}
msg = 'Exception at %s\ntraceback url: %s'
exc_url = debug_toolbar_url(request, 'exception', _query=qs)
exc_msg = msg % (request.url, exc_url)
qs = {'token': registry.pdtb_token, 'tb': str(tb.id)}
subrequest = make_subrequest(
request, root_path, 'exception', qs)
exc_msg = msg % (request.url, subrequest.url)
_logger.exception(exc_msg)

subenviron = request.environ.copy()
del subenviron['PATH_INFO']
del subenviron['QUERY_STRING']
subrequest = type(request).blank(exc_url, subenviron)
subrequest.script_name = request.script_name
subrequest.path_info = \
subrequest.path_info[len(request.script_name):]
response = request.invoke_subrequest(subrequest)
response = dispatch(subrequest)

# The original request must be processed so that the panel data exists
# if the request is later examined in the full toolbar view.
Expand All @@ -242,17 +251,16 @@ def toolbar_tween(request):
redirect_to = response.location
redirect_code = response.status_int
if redirect_to:
content = render(
'pyramid_debugtoolbar:templates/redirect.dbtmako',
{
'redirect_to': redirect_to,
'redirect_code': redirect_code,
},
request=request)
content = content.encode(response.charset)
response.content_length = len(content)
qs = {
'token': registry.pdtb_token,
'redirect_to': redirect_to,
'redirect_code': str(redirect_code),
}
subrequest = make_subrequest(
request, root_path, 'redirect', qs)
content = dispatch(subrequest).text
response.location = None
response.app_iter = [content]
response.text = content
response.status_int = 200

toolbar.process_response(request, response)
Expand Down
12 changes: 11 additions & 1 deletion pyramid_debugtoolbar/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,20 @@ def find_request_history(request):
def debug_toolbar_url(request, *elements, **kw):
return request.route_url('debugtoolbar', subpath=elements, **kw)


def hexlify(value):
"""Hexlify int, str then returns native str type."""
# If integer
str_ = str(value)
hexified = text_(binascii.hexlify(bytes_(str_)))
return hexified

def make_subrequest(request, root_path, path, params=None):
# we know root_path will have a trailing slash and
# path will need one
subrequest = type(request).blank(
'/' + path,
base_url=request.application_url + root_path[:-1],
)
if params is not None:
subrequest.GET.update(params)
return subrequest
Loading

0 comments on commit 2e4aa25

Please sign in to comment.