From 0d49fdb573d44794cc78c6af4761cc79c5330315 Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Fri, 16 Nov 2012 16:50:50 -0800 Subject: [PATCH] [1.5.x] 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 --- django/conf/__init__.py | 10 +++++++ django/utils/log.py | 3 ++ docs/releases/1.5.txt | 7 +++++ tests/regressiontests/logging_tests/tests.py | 29 ++++++++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/django/conf/__init__.py b/django/conf/__init__.py index 4e6f0f92114..dec4cf94181 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -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 ... diff --git a/django/utils/log.py b/django/utils/log.py index ea0122794ba..3852271bf19 100644 --- a/django/utils/log.py +++ b/django/utils/log.py @@ -62,6 +62,9 @@ DEFAULT_LOGGING = { 'level': 'ERROR', 'propagate': False, }, + 'py.warnings': { + 'handlers': ['console'], + }, } } diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 8b7af2cf36c..c53518feaa2 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -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 ===================================== diff --git a/tests/regressiontests/logging_tests/tests.py b/tests/regressiontests/logging_tests/tests.py index 96f81981c69..0e56195c41c 100644 --- a/tests/regressiontests/logging_tests/tests.py +++ b/tests/regressiontests/logging_tests/tests.py @@ -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):