Ticket #14861: 14861-4.diff

File 14861-4.diff, 5.5 KB (added by claudep, 3 years ago)
  • django/conf/__init__.py

    diff --git a/django/conf/__init__.py b/django/conf/__init__.py
    index 6272f4e..d636ff0 100644
    a b class LazySettings(LazyObject): 
    4343                % (name, ENVIRONMENT_VARIABLE))
    4444
    4545        self._wrapped = Settings(settings_module)
    46 
     46        self._configure_logging()
    4747
    4848    def __getattr__(self, name):
    4949        if self._wrapped is empty:
    5050            self._setup(name)
    5151        return getattr(self._wrapped, name)
    5252
     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)
    5368
    5469    def configure(self, default_settings=global_settings, **options):
    5570        """
    class Settings(BaseSettings): 
    133148            os.environ['TZ'] = self.TIME_ZONE
    134149            time.tzset()
    135150
    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 
    149151
    150152class UserSettingsHolder(BaseSettings):
    151153    """
  • docs/topics/logging.txt

    diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt
    index 94236ba..5fc229e 100644
    a b This logging configuration does the following things: 
    347347
    348348.. admonition:: Custom handlers and circular imports
    349349
    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'
     350    Logging configuration happens soon in the Django setup machinery. If your
     351    ``settings.py`` specifies a custom handler class, you should be careful
     352    about the modules you import in the file defining this class, unless a
     353    circular import may occur.
    377354
    378355.. _formatter documentation: http://docs.python.org/library/logging.html#formatter-objects
    379356
  • new file tests/regressiontests/logging_tests/logconfig.py

    diff --git a/tests/regressiontests/logging_tests/logconfig.py b/tests/regressiontests/logging_tests/logconfig.py
    new file mode 100644
    index 0000000..8524aa2
    - +  
     1import logging
     2
     3from django.conf import settings
     4
     5class MyHandler(logging.Handler):
     6    def __init__(self, *args, **kwargs):
     7        self.config = settings.LOGGING
  • tests/regressiontests/logging_tests/tests.py

    diff --git a/tests/regressiontests/logging_tests/tests.py b/tests/regressiontests/logging_tests/tests.py
    index f444e0f..a54b425 100644
    a b from django.test import TestCase, RequestFactory 
    1010from django.test.utils import override_settings
    1111from django.utils.log import CallbackFilter, RequireDebugFalse
    1212
     13from ..admin_scripts.tests import AdminScriptTestCase
     14
    1315
    1416# logging config prior to using filter with mail_admins
    1517OLD_LOGGING = {
    class AdminEmailHandlerTest(TestCase): 
    253255
    254256        self.assertEqual(len(mail.outbox), 1)
    255257        self.assertEqual(mail.outbox[0].subject, expected_subject)
     258
     259
     260class 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")
Back to Top