diff options
-rw-r--r-- | Doc/library/logging.rst | 15 | ||||
-rw-r--r-- | Lib/logging/__init__.py | 59 | ||||
-rw-r--r-- | Lib/test/test_logging.py | 31 | ||||
-rw-r--r-- | Misc/NEWS | 8 |
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() @@ -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. |