From f48039e3bad9e24a59a25ed5636b428e11784da9 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Tue, 12 Nov 2013 22:05:52 +0100 Subject: Move the logic to load panels inside the DebugToolbar class. This has the additional advantage of eliminating a side-effect that happened at import time unnecessarily. It justified refactoring the way we handle settings and defaults. --- debug_toolbar/middleware.py | 6 +-- debug_toolbar/models.py | 5 --- debug_toolbar/panels/cache.py | 4 +- debug_toolbar/panels/signals.py | 4 +- debug_toolbar/panels/template.py | 4 +- debug_toolbar/toolbar.py | 80 +++++++++++++++++--------------------- debug_toolbar/utils/settings.py | 24 ++++++++++++ debug_toolbar/utils/tracking/db.py | 6 +-- tests/__init__.py | 14 +++---- 9 files changed, 78 insertions(+), 69 deletions(-) diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index 80e8ae0..44d0700 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -12,7 +12,7 @@ from django.shortcuts import render from django.utils.encoding import force_text from debug_toolbar.toolbar import DebugToolbar -from debug_toolbar.utils.settings import CONFIG +from debug_toolbar.utils import settings as dt_settings _HTML_TYPES = ('text/html', 'application/xhtml+xml') # Handles python threading module bug - http://bugs.python.org/issue14308 @@ -52,10 +52,10 @@ class DebugToolbarMiddleware(object): def __init__(self): # The method to call to decide to show the toolbar - self.show_toolbar = CONFIG['SHOW_TOOLBAR_CALLBACK'] or show_toolbar + self.show_toolbar = dt_settings.CONFIG['SHOW_TOOLBAR_CALLBACK'] or show_toolbar # The tag to attach the toolbar to - self.tag = '' % CONFIG['TAG'] + self.tag = '' % dt_settings.CONFIG['TAG'] def process_request(self, request): __traceback_hide__ = True # noqa diff --git a/debug_toolbar/models.py b/debug_toolbar/models.py index d39e7ec..a204b13 100644 --- a/debug_toolbar/models.py +++ b/debug_toolbar/models.py @@ -5,7 +5,6 @@ from django.conf.urls import include, patterns, url from django.core.urlresolvers import reverse, NoReverseMatch from django.utils.importlib import import_module -from debug_toolbar.toolbar import load_panel_classes from debug_toolbar.middleware import DebugToolbarMiddleware @@ -59,7 +58,3 @@ if settings.DEBUG: patch_internal_ips() patch_middleware_classes() patch_root_urlconf() - - -if is_toolbar_middleware_installed(): - load_panel_classes() diff --git a/debug_toolbar/panels/cache.py b/debug_toolbar/panels/cache.py index 831eacb..7d42c1f 100644 --- a/debug_toolbar/panels/cache.py +++ b/debug_toolbar/panels/cache.py @@ -16,7 +16,7 @@ from django.utils.translation import ugettext_lazy as _, ungettext from debug_toolbar.panels import DebugPanel from debug_toolbar.utils import (tidy_stacktrace, render_stacktrace, get_template_info, get_stack) -from debug_toolbar.utils.settings import CONFIG +from debug_toolbar.utils import settings as dt_settings cache_called = Signal(providing_args=[ @@ -29,7 +29,7 @@ def send_signal(method): value = method(self, *args, **kwargs) t = time.time() - t - if CONFIG['ENABLE_STACKTRACES']: + if dt_settings.CONFIG['ENABLE_STACKTRACES']: stacktrace = tidy_stacktrace(reversed(get_stack())) else: stacktrace = [] diff --git a/debug_toolbar/panels/signals.py b/debug_toolbar/panels/signals.py index 3dd035a..1d24b80 100644 --- a/debug_toolbar/panels/signals.py +++ b/debug_toolbar/panels/signals.py @@ -16,7 +16,7 @@ except ImportError: connection_created = None from debug_toolbar.panels import DebugPanel -from debug_toolbar.utils.settings import CONFIG +from debug_toolbar.utils import settings as dt_settings class SignalDebugPanel(DebugPanel): @@ -63,7 +63,7 @@ class SignalDebugPanel(DebugPanel): @property def signals(self): signals = self.SIGNALS.copy() - for signal in CONFIG['EXTRA_SIGNALS']: + for signal in dt_settings.CONFIG['EXTRA_SIGNALS']: mod_path, signal_name = signal.rsplit('.', 1) signals_mod = import_module(mod_path) signals[signal_name] = getattr(signals_mod, signal_name) diff --git a/debug_toolbar/panels/template.py b/debug_toolbar/panels/template.py index 172765a..d918cd7 100644 --- a/debug_toolbar/panels/template.py +++ b/debug_toolbar/panels/template.py @@ -15,7 +15,7 @@ from django.utils.translation import ugettext_lazy as _ from debug_toolbar.panels import DebugPanel from debug_toolbar.utils.tracking.db import recording, SQLQueryTriggered -from debug_toolbar.utils.settings import CONFIG +from debug_toolbar.utils import settings as dt_settings # Code taken and adapted from Simon Willison and Django Snippets: # http://www.djangosnippets.org/snippets/766/ @@ -140,7 +140,7 @@ class TemplateDebugPanel(DebugPanel): template.origin_name = 'No origin' info['template'] = template # Clean up context for better readability - if CONFIG['SHOW_TEMPLATE_CONTEXT']: + if dt_settings.CONFIG['SHOW_TEMPLATE_CONTEXT']: context_list = template_data.get('context', []) info['context'] = '\n'.join(context_list) template_context.append(info) diff --git a/debug_toolbar/toolbar.py b/debug_toolbar/toolbar.py index c0c210f..32dc1b7 100644 --- a/debug_toolbar/toolbar.py +++ b/debug_toolbar/toolbar.py @@ -5,11 +5,12 @@ The main DebugToolbar class that loads and renders the Toolbar. from __future__ import unicode_literals from django.conf import settings +from django.core.exceptions import ImproperlyConfigured from django.template.loader import render_to_string from django.utils.datastructures import SortedDict from django.utils.importlib import import_module -from debug_toolbar.utils.settings import CONFIG +from debug_toolbar.utils import settings as dt_settings class DebugToolbar(object): @@ -19,7 +20,7 @@ class DebugToolbar(object): self._panels = SortedDict() base_url = self.request.META.get('SCRIPT_NAME', '') self.config = {} - self.config.update(CONFIG) + self.config.update(dt_settings.CONFIG) self.template_context = { 'BASE_URL': base_url, # for backwards compatibility 'STATIC_URL': settings.STATIC_URL, @@ -40,8 +41,7 @@ class DebugToolbar(object): """ Populate debug panels """ - global panel_classes - for panel_class in panel_classes: + for panel_class in self.get_panel_classes(): panel_instance = panel_class(self, context=self.template_context) self._panels[panel_class] = panel_instance @@ -65,7 +65,7 @@ class DebugToolbar(object): cls = type(self) cls._counter += 1 cls._storage[cls._counter] = self - for _ in range(len(cls._storage) - CONFIG['RESULTS_CACHE_SIZE']): + for _ in range(len(cls._storage) - dt_settings.CONFIG['RESULTS_CACHE_SIZE']): # When we drop support for Python 2.6 and switch to # collections.OrderedDict, use popitem(last=False). del cls._storage[cls._storage.keyOrder[0]] @@ -75,45 +75,35 @@ class DebugToolbar(object): def fetch(cls, storage_id): return cls._storage.get(storage_id) + # Manually implement class-level caching of the list of panels because + # it's more obvious than going through an abstraction. + _panel_classes = None -panel_classes = [] - - -def load_panel_classes(): - from django.conf import settings - from django.core.exceptions import ImproperlyConfigured - - # Check if settings has a DEBUG_TOOLBAR_PANELS, otherwise use default - panels = getattr(settings, 'DEBUG_TOOLBAR_PANELS', ( - 'debug_toolbar.panels.version.VersionDebugPanel', - 'debug_toolbar.panels.timer.TimerDebugPanel', - 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', - 'debug_toolbar.panels.headers.HeaderDebugPanel', - 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', - 'debug_toolbar.panels.sql.SQLDebugPanel', - 'debug_toolbar.panels.template.TemplateDebugPanel', - 'debug_toolbar.panels.cache.CacheDebugPanel', - 'debug_toolbar.panels.signals.SignalDebugPanel', - 'debug_toolbar.panels.logger.LoggingPanel', - )) - for panel_path in panels: - try: - dot = panel_path.rindex('.') - except ValueError: - raise ImproperlyConfigured( - "%s isn't a debug panel module" % panel_path) - panel_module, panel_classname = panel_path[:dot], panel_path[dot + 1:] - try: - mod = import_module(panel_module) - except ImportError as e: - raise ImproperlyConfigured( - 'Error importing debug panel %s: "%s"' % - (panel_module, e)) - try: - panel_class = getattr(mod, panel_classname) - except AttributeError: - raise ImproperlyConfigured( - 'Toolbar Panel module "%s" does not define a "%s" class' % - (panel_module, panel_classname)) - panel_classes.append(panel_class) + @classmethod + def get_panel_classes(cls): + if cls._panel_classes is None: + # Load panels in a temporary variable for thread safety. + panel_classes = [] + for panel_path in dt_settings.PANELS: + # This logic could be replaced with import_by_path in Django 1.6. + try: + panel_module, panel_classname = panel_path.rsplit('.', 1) + except ValueError: + raise ImproperlyConfigured( + "%s isn't a debug panel module" % panel_path) + try: + mod = import_module(panel_module) + except ImportError as e: + raise ImproperlyConfigured( + 'Error importing debug panel %s: "%s"' % + (panel_module, e)) + try: + panel_class = getattr(mod, panel_classname) + except AttributeError: + raise ImproperlyConfigured( + 'Toolbar Panel module "%s" does not define a "%s" class' % + (panel_module, panel_classname)) + panel_classes.append(panel_class) + cls._panel_classes = panel_classes + return cls._panel_classes diff --git a/debug_toolbar/utils/settings.py b/debug_toolbar/utils/settings.py index 12dc231..45f661f 100644 --- a/debug_toolbar/utils/settings.py +++ b/debug_toolbar/utils/settings.py @@ -4,6 +4,13 @@ from django.conf import settings from django.utils import six +# Always import this module as follows: +# from debug_toolbar.utils import settings [as dt_settings] + +# Don't import directly CONFIG or PANELs, or you will miss changes performed +# with override_settings in tests. + + CONFIG_DEFAULTS = { 'INTERCEPT_REDIRECTS': False, 'SHOW_TOOLBAR_CALLBACK': None, @@ -27,3 +34,20 @@ CONFIG_DEFAULTS = { CONFIG = {} CONFIG.update(CONFIG_DEFAULTS) CONFIG.update(getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {})) + + +PANELS_DEFAULTS = ( + 'debug_toolbar.panels.version.VersionDebugPanel', + 'debug_toolbar.panels.timer.TimerDebugPanel', + 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', + 'debug_toolbar.panels.headers.HeaderDebugPanel', + 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', + 'debug_toolbar.panels.sql.SQLDebugPanel', + 'debug_toolbar.panels.template.TemplateDebugPanel', + 'debug_toolbar.panels.cache.CacheDebugPanel', + 'debug_toolbar.panels.signals.SignalDebugPanel', + 'debug_toolbar.panels.logger.LoggingPanel', +) + + +PANELS = getattr(settings, 'DEBUG_TOOLBAR_PANELS', PANELS_DEFAULTS) diff --git a/debug_toolbar/utils/tracking/db.py b/debug_toolbar/utils/tracking/db.py index 157334a..fd56ff9 100644 --- a/debug_toolbar/utils/tracking/db.py +++ b/debug_toolbar/utils/tracking/db.py @@ -11,7 +11,7 @@ from django.utils.encoding import force_text from django.utils import six from debug_toolbar.utils import tidy_stacktrace, get_template_info, get_stack -from debug_toolbar.utils.settings import CONFIG +from debug_toolbar.utils import settings as dt_settings class SQLQueryTriggered(Exception): @@ -92,7 +92,7 @@ class NormalCursorWrapper(object): finally: stop_time = time() duration = (stop_time - start_time) * 1000 - if CONFIG['ENABLE_STACKTRACES']: + if dt_settings.CONFIG['ENABLE_STACKTRACES']: stacktrace = tidy_stacktrace(reversed(get_stack())) else: stacktrace = [] @@ -135,7 +135,7 @@ class NormalCursorWrapper(object): 'stacktrace': stacktrace, 'start_time': start_time, 'stop_time': stop_time, - 'is_slow': duration > CONFIG['SQL_WARNING_THRESHOLD'], + 'is_slow': duration > dt_settings.CONFIG['SQL_WARNING_THRESHOLD'], 'is_select': sql.lower().strip().startswith('select'), 'template_info': template_info, } diff --git a/tests/__init__.py b/tests/__init__.py index 0abb8a3..b3b9440 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,7 +1,7 @@ # Refresh the debug toolbar's configuration when overriding settings. -from debug_toolbar.utils.settings import CONFIG, CONFIG_DEFAULTS -from debug_toolbar.toolbar import load_panel_classes, panel_classes # noqa +from debug_toolbar.toolbar import DebugToolbar +from debug_toolbar.utils import settings as dt_settings from django.dispatch import receiver from django.test.signals import setting_changed @@ -10,13 +10,13 @@ from django.test.signals import setting_changed @receiver(setting_changed) def update_toolbar_config(**kwargs): if kwargs['setting'] == 'DEBUG_TOOLBAR_CONFIG': - CONFIG.update(CONFIG_DEFAULTS) - CONFIG.update(kwargs['value'] or {}) + dt_settings.CONFIG = {} + dt_settings.CONFIG.update(dt_settings.CONFIG_DEFAULTS) + dt_settings.CONFIG.update(kwargs['value'] or {}) @receiver(setting_changed) def update_toolbar_panels(**kwargs): if kwargs['setting'] == 'DEBUG_TOOLBAR_PANELS': - global panel_classes - panel_classes = [] # noqa - load_panel_classes() + dt_settings.PANELS = kwargs['value'] or dt_settings.PANELS_DEFAULTS + DebugToolbar._panel_classes = None -- cgit v1.2.3