Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #14861 -- Moved logging config outside of Settings.__init__

Thanks donspaulding for the report and simonpercivall for the
initial patch.
  • Loading branch information...
commit fc69fff9ab5bba8a6cff3eaf51f02a3204b1c015 1 parent e72e22e
Claude Paroz authored September 24, 2012
30  django/conf/__init__.py
@@ -43,13 +43,28 @@ def _setup(self, name):
43 43
                 % (name, ENVIRONMENT_VARIABLE))
44 44
 
45 45
         self._wrapped = Settings(settings_module)
46  
-
  46
+        self._configure_logging()
47 47
 
48 48
     def __getattr__(self, name):
49 49
         if self._wrapped is empty:
50 50
             self._setup(name)
51 51
         return getattr(self._wrapped, name)
52 52
 
  53
+    def _configure_logging(self):
  54
+        """
  55
+        Setup logging from LOGGING_CONFIG and LOGGING settings.
  56
+        """
  57
+        if self.LOGGING_CONFIG:
  58
+            # First find the logging configuration function ...
  59
+            logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1)
  60
+            logging_config_module = importlib.import_module(logging_config_path)
  61
+            logging_config_func = getattr(logging_config_module, logging_config_func_name)
  62
+
  63
+            # Backwards-compatibility shim for #16288 fix
  64
+            compat_patch_logging_config(self.LOGGING)
  65
+
  66
+            # ... then invoke it with the logging settings
  67
+            logging_config_func(self.LOGGING)
53 68
 
54 69
     def configure(self, default_settings=global_settings, **options):
55 70
         """
@@ -133,19 +148,6 @@ def __init__(self, settings_module):
133 148
             os.environ['TZ'] = self.TIME_ZONE
134 149
             time.tzset()
135 150
 
136  
-        # Settings are configured, so we can set up the logger if required
137  
-        if self.LOGGING_CONFIG:
138  
-            # First find the logging configuration function ...
139  
-            logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1)
140  
-            logging_config_module = importlib.import_module(logging_config_path)
141  
-            logging_config_func = getattr(logging_config_module, logging_config_func_name)
142  
-
143  
-            # Backwards-compatibility shim for #16288 fix
144  
-            compat_patch_logging_config(self.LOGGING)
145  
-
146  
-            # ... then invoke it with the logging settings
147  
-            logging_config_func(self.LOGGING)
148  
-
149 151
 
150 152
 class UserSettingsHolder(BaseSettings):
151 153
     """
30  docs/topics/logging.txt
@@ -345,36 +345,6 @@ This logging configuration does the following things:
345 345
     printed to the console; ``ERROR`` and ``CRITICAL``
346 346
     messages will also be output via email.
347 347
 
348  
-.. admonition:: Custom handlers and circular imports
349  
-
350  
-    If your ``settings.py`` specifies a custom handler class and the file
351  
-    defining that class also imports ``settings.py`` a circular import will
352  
-    occur.
353  
-
354  
-    For example, if ``settings.py`` contains the following config for
355  
-    :setting:`LOGGING`::
356  
-
357  
-        LOGGING = {
358  
-          'version': 1,
359  
-          'handlers': {
360  
-            'custom_handler': {
361  
-              'level': 'INFO',
362  
-              'class': 'myproject.logconfig.MyHandler',
363  
-            }
364  
-          }
365  
-        }
366  
-
367  
-    and ``myproject/logconfig.py`` has the following line before the
368  
-    ``MyHandler`` definition::
369  
-
370  
-        from django.conf import settings
371  
-
372  
-    then the ``dictconfig`` module will raise an exception like the following::
373  
-
374  
-        ValueError: Unable to configure handler 'custom_handler':
375  
-        Unable to configure handler 'custom_handler':
376  
-        'module' object has no attribute 'logconfig'
377  
-
378 348
 .. _formatter documentation: http://docs.python.org/library/logging.html#formatter-objects
379 349
 
380 350
 Custom logging configuration
7  tests/regressiontests/logging_tests/logconfig.py
... ...
@@ -0,0 +1,7 @@
  1
+import logging
  2
+
  3
+from django.conf import settings
  4
+
  5
+class MyHandler(logging.Handler):
  6
+    def __init__(self, *args, **kwargs):
  7
+        self.config = settings.LOGGING
29  tests/regressiontests/logging_tests/tests.py
@@ -10,6 +10,8 @@
10 10
 from django.test.utils import override_settings
11 11
 from django.utils.log import CallbackFilter, RequireDebugFalse
12 12
 
  13
+from ..admin_scripts.tests import AdminScriptTestCase
  14
+
13 15
 
14 16
 # logging config prior to using filter with mail_admins
15 17
 OLD_LOGGING = {
@@ -253,3 +255,30 @@ def test_truncate_subject(self):
253 255
 
254 256
         self.assertEqual(len(mail.outbox), 1)
255 257
         self.assertEqual(mail.outbox[0].subject, expected_subject)
  258
+
  259
+
  260
+class SettingsConfigTest(AdminScriptTestCase):
  261
+    """
  262
+    Test that accessing settings in a custom logging handler does not trigger
  263
+    a circular import error.
  264
+    """
  265
+    def setUp(self):
  266
+        log_config = """{
  267
+    'version': 1,
  268
+    'handlers': {
  269
+        'custom_handler': {
  270
+            'level': 'INFO',
  271
+            'class': 'logging_tests.logconfig.MyHandler',
  272
+        }
  273
+    }
  274
+}"""
  275
+        self.write_settings('settings.py', sdict={'LOGGING': log_config})
  276
+
  277
+    def tearDown(self):
  278
+        self.remove_settings('settings.py')
  279
+
  280
+    def test_circular_dependency(self):
  281
+        # validate is just an example command to trigger settings configuration
  282
+        out, err = self.run_manage(['validate'])
  283
+        self.assertNoOutput(err)
  284
+        self.assertOutput(out, "0 errors found")

0 notes on commit fc69fff

Please sign in to comment.
Something went wrong with that request. Please try again.