Commit 44046e8a authored by Preston Holmes's avatar Preston Holmes
Browse files

Fixed #18985 -- made DeprecationWarnings loud

Capture warnings in Python >= 2.7 and route through
console handler, which is subject to DEBUG==True

Thanks to dstufft for the idea, and claudep for initial patch
parent b4a98e02
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ variable, and then from django.conf.global_settings; see the global settings fil
a list of all possible variables.
"""

import logging
import os
import time     # Needed for Windows
import warnings
@@ -55,6 +56,15 @@ class LazySettings(LazyObject):
        """
        Setup logging from LOGGING_CONFIG and LOGGING settings.
        """
        try:
            # Route warnings through python logging
            logging.captureWarnings(True)
            # Allow DeprecationWarnings through the warnings filters
            warnings.simplefilter("default", DeprecationWarning)
        except AttributeError:
            # No captureWarnings on Python 2.6, DeprecationWarnings are on anyway
            pass

        if self.LOGGING_CONFIG:
            from django.utils.log import DEFAULT_LOGGING
            # First find the logging configuration function ...
+3 −0
Original line number Diff line number Diff line
@@ -62,6 +62,9 @@ DEFAULT_LOGGING = {
            'level': 'ERROR',
            'propagate': False,
        },
        'py.warnings': {
            'handlers': ['console'],
        },
    }
}

+7 −0
Original line number Diff line number Diff line
@@ -306,6 +306,13 @@ Django 1.5 also includes several smaller improvements worth noting:
  :attr:`~django.db.models.Options.index_together` documentation for more
  information.

* During Django's logging configuration verbose Deprecation warnings are
  enabled and warnings are captured into the logging system. Logged warnings
  are routed through the ``console`` logging handler, which by default requires
  :setting:`DEBUG` to be True for output to be generated. The result is that
  DeprecationWarnings should be printed to the console in development
  environments the way they have been in Python versions < 2.7.

Backwards incompatible changes in 1.5
=====================================

+29 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ from __future__ import unicode_literals

import copy
import logging
import sys
import warnings

from django.conf import compat_patch_logging_config, LazySettings
@@ -10,9 +11,11 @@ from django.test import TestCase, RequestFactory
from django.test.utils import override_settings
from django.utils.log import CallbackFilter, RequireDebugFalse
from django.utils.six import StringIO
from django.utils.unittest import skipUnless

from ..admin_scripts.tests import AdminScriptTestCase

PYVERS = sys.version_info[:2]

# logging config prior to using filter with mail_admins
OLD_LOGGING = {
@@ -131,6 +134,32 @@ class DefaultLoggingTest(TestCase):
            self.logger.error("Hey, this is an error.")
            self.assertEqual(output.getvalue(), 'Hey, this is an error.\n')

@skipUnless(PYVERS > (2,6), "warnings captured only in Python >= 2.7")
class WarningLoggerTests(TestCase):
    """
    Tests that warnings output for DeprecationWarnings is enabled
    and captured to the logging system
    """
    def setUp(self):
        self.logger = logging.getLogger('py.warnings')
        self.old_stream = self.logger.handlers[0].stream

    def tearDown(self):
        self.logger.handlers[0].stream = self.old_stream

    @override_settings(DEBUG=True)
    def test_warnings_capture(self):
        output = StringIO()
        self.logger.handlers[0].stream = output
        warnings.warn('Foo Deprecated', DeprecationWarning)
        self.assertTrue('Foo Deprecated' in output.getvalue())

    def test_warnings_capture_debug_false(self):
        output = StringIO()
        self.logger.handlers[0].stream = output
        warnings.warn('Foo Deprecated', DeprecationWarning)
        self.assertFalse('Foo Deprecated' in output.getvalue())


class CallbackFilterTest(TestCase):
    def test_sense(self):