summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVinay Sajip <vinay_sajip@yahoo.co.uk>2008-12-03 23:22:58 (GMT)
committerVinay Sajip <vinay_sajip@yahoo.co.uk>2008-12-03 23:22:58 (GMT)
commit213faca204ffcd48c465beac2351c65a15187e8c (patch)
tree411c48c94ef6d8e0311c18201c28ec8ea556f2f1
parent7989a4dccb39aa954057cbc4205473f09daae84b (diff)
downloadcpython-213faca204ffcd48c465beac2351c65a15187e8c.zip
cpython-213faca204ffcd48c465beac2351c65a15187e8c.tar.gz
cpython-213faca204ffcd48c465beac2351c65a15187e8c.tar.bz2
Issue #4384: Added logging integration with warnings module using captureWarnings(). This change includes a NullHandler which does nothing; it will be of use to library developers who want to avoid the "No handlers could be found for logger XXX" message which can appear if the library user doesn't configure logging.
-rw-r--r--Doc/library/logging.rst15
-rw-r--r--Lib/logging/__init__.py59
-rw-r--r--Lib/test/test_logging.py31
-rw-r--r--Misc/NEWS8
4 files changed, 108 insertions, 5 deletions
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst
index dac6aeb1..c5fde33 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -461,6 +461,12 @@ should have the desired effect. If an organisation produces a number of
libraries, then the logger name specified can be "orgname.foo" rather than
just "foo".
+.. versionadded:: 2.7
+
+The :class:`NullHandler` class was not present in previous versions, but is now
+included, so that it need not be defined in library code.
+
+
Logging Levels
--------------
@@ -553,6 +559,15 @@ provided:
#. :class:`HTTPHandler` instances send error messages to an HTTP server using
either ``GET`` or ``POST`` semantics.
+#. :class:`NullHandler` instances do nothing with error messages. They are used
+ by library developers who want to use logging, but want to avoid the "No
+ handlers could be found for logger XXX" message which can be displayed if
+ the library user has not configured logging.
+
+.. versionadded:: 2.7
+
+The :class:`NullHandler` class was not present in previous versions.
+
The :class:`StreamHandler` and :class:`FileHandler` classes are defined in the
core logging package. The other handlers are defined in a sub- module,
:mod:`logging.handlers`. (There is also another sub-module,
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 7b790d2..c28d7c8 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -46,8 +46,8 @@ except ImportError:
__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
__status__ = "production"
-__version__ = "0.5.0.5"
-__date__ = "24 January 2008"
+__version__ = "0.5.0.6"
+__date__ = "03 December 2008"
#---------------------------------------------------------------------------
# Miscellaneous module data
@@ -1488,3 +1488,58 @@ except ImportError: # for Python versions < 2.0
old_exit(status)
sys.exit = exithook
+
+# Null handler
+
+class NullHandler(Handler):
+ """
+ This handler does nothing. It's intended to be used to avoid the
+ "No handlers could be found for logger XXX" one-off warning. This is
+ important for library code, which may contain code to log events. If a user
+ of the library does not configure logging, the one-off warning might be
+ produced; to avoid this, the library developer simply needs to instantiate
+ a NullHandler and add it to the top-level logger of the library module or
+ package.
+ """
+ def emit(self, record):
+ pass
+
+# Warnings integration
+
+_warnings_showwarning = None
+
+def _showwarning(message, category, filename, lineno, file=None, line=None):
+ """
+ Implementation of showwarnings which redirects to logging, which will first
+ check to see if the file parameter is None. If a file is specified, it will
+ delegate to the original warnings implementation of showwarning. Otherwise,
+ it will call warnings.formatwarning and will log the resulting string to a
+ warnings logger named "py.warnings" with level logging.WARNING.
+ """
+ if file is not None:
+ if _warnings_showwarning is not None:
+ _warnings_showwarning(message, category, filename, lineno, file, line)
+ else:
+ import warnings
+ s = warnings.formatwarning(message, category, filename, lineno, line)
+ logger = getLogger("py.warnings")
+ if not logger.handlers:
+ logger.addHandler(NullHandler())
+ logger.warning("%s", s)
+
+def captureWarnings(capture):
+ """
+ If capture is true, redirect all warnings to the logging package.
+ If capture is False, ensure that warnings are not redirected to logging
+ but to their original destinations.
+ """
+ import warnings
+ global _warnings_showwarning
+ if capture:
+ if _warnings_showwarning is None:
+ _warnings_showwarning = warnings.showwarning
+ warnings.showwarning = _showwarning
+ else:
+ if _warnings_showwarning is not None:
+ warnings.showwarning = _warnings_showwarning
+ _warnings_showwarning = None
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index e837d41..09fdf4b 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -18,7 +18,7 @@
"""Test harness for the logging module. Run all tests.
-Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved.
+Copyright (C) 2001-2008 Vinay Sajip. All Rights Reserved.
"""
import logging
@@ -44,6 +44,7 @@ import threading
import time
import types
import unittest
+import warnings
import weakref
@@ -885,6 +886,32 @@ class EncodingTest(BaseTest):
if os.path.isfile(fn):
os.remove(fn)
+class WarningsTest(BaseTest):
+ def test_warnings(self):
+ logging.captureWarnings(True)
+ warnings.filterwarnings("always", category=UserWarning)
+ try:
+ file = cStringIO.StringIO()
+ h = logging.StreamHandler(file)
+ logger = logging.getLogger("py.warnings")
+ logger.addHandler(h)
+ warnings.warn("I'm warning you...")
+ logger.removeHandler(h)
+ s = file.getvalue()
+ h.close()
+ self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
+
+ #See if an explicit file uses the original implementation
+ file = cStringIO.StringIO()
+ warnings.showwarning("Explicit", UserWarning, "dummy.py", 42, file,
+ "Dummy line")
+ s = file.getvalue()
+ file.close()
+ self.assertEqual(s, "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
+ finally:
+ warnings.resetwarnings()
+ logging.captureWarnings(False)
+
# Set the locale to the platform-dependent default. I have no idea
# why the test does this, but in any case we save the current locale
# first and restore it at the end.
@@ -893,7 +920,7 @@ def test_main():
run_unittest(BuiltinLevelsTest, BasicFilterTest,
CustomLevelsAndFiltersTest, MemoryHandlerTest,
ConfigFileTest, SocketHandlerTest, MemoryTest,
- EncodingTest)
+ EncodingTest, WarningsTest)
if __name__ == "__main__":
test_main()
diff --git a/Misc/NEWS b/Misc/NEWS
index 025ca4c..78578e3 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -22,7 +22,7 @@ Core and Builtins
- Issue #4367: Python would segfault during compiling when the unicodedata
module couldn't be imported and \N escapes were present.
-- Issue #4233: Changed semantic of ``_fileio.FileIO``'s ``close()``
+- Issue #4233: Changed semantic of ``_fileio.FileIO``'s ``close()``
method on file objects with closefd=False. The file descriptor is still
kept open but the file object behaves like a closed file. The ``FileIO``
object also got a new readonly attribute ``closefd``.
@@ -60,6 +60,12 @@ Core and Builtins
Library
-------
+- Issue #4384: Added integration with warnings module using captureWarnings().
+ This change includes a NullHandler which does nothing; it will be of use to
+ library developers who want to avoid the "No handlers could be found for
+ logger XXX" message which can appear if the library user doesn't configure
+ logging.
+
- Issue #3741: DISTUTILS_USE_SDK set causes msvc9compiler.py to raise an
exception.