[Python-3000-checkins] r60053 - in python/branches/py3k: Doc/builddoc.bat Doc/library/logging.rst Doc/reference/executionmodel.rst Lib/logging/__init__.py Tools/scripts/win_add2path.py

christian.heimes python-3000-checkins at python.org
Fri Jan 18 19:40:46 CET 2008


Author: christian.heimes
Date: Fri Jan 18 19:40:46 2008
New Revision: 60053

Added:
   python/branches/py3k/Doc/builddoc.bat
      - copied unchanged from r60052, python/trunk/Doc/builddoc.bat
   python/branches/py3k/Tools/scripts/win_add2path.py
      - copied unchanged from r60052, python/trunk/Tools/scripts/win_add2path.py
Modified:
   python/branches/py3k/   (props changed)
   python/branches/py3k/Doc/library/logging.rst
   python/branches/py3k/Doc/reference/executionmodel.rst
   python/branches/py3k/Lib/logging/__init__.py
Log:
Merged revisions 60043-60052 via svnmerge from 
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r60043 | christian.heimes | 2008-01-18 10:51:43 +0100 (Fri, 18 Jan 2008) | 2 lines
  
  Build _ctypes after the other extensions. Its build process depends
  on the _weakref extension (and maybe other modules, too)
........
  r60048 | christian.heimes | 2008-01-18 12:58:50 +0100 (Fri, 18 Jan 2008) | 2 lines
  
  Added win_add2path.py to Tools/scripts/
  Added builddoc.bat to Doc/
........
  r60049 | vinay.sajip | 2008-01-18 16:54:14 +0100 (Fri, 18 Jan 2008) | 1 line
  
  Added section on passing contextual information to logging and documentation for the LoggerAdapter class.
........
  r60050 | vinay.sajip | 2008-01-18 16:55:57 +0100 (Fri, 18 Jan 2008) | 1 line
  
  Added LoggerAdapter class, changed copyright dates, made check for extra parameter passed to logging methods explicitly against None rather than a truth value.
........
  r60051 | georg.brandl | 2008-01-18 17:42:57 +0100 (Fri, 18 Jan 2008) | 2 lines
  
  Note that genexps are function scopes too and therefore won't see class attributes.
........
  r60052 | christian.heimes | 2008-01-18 19:24:07 +0100 (Fri, 18 Jan 2008) | 1 line
  
  Added bytes and b'' as aliases for str and ''
........


Modified: python/branches/py3k/Doc/library/logging.rst
==============================================================================
--- python/branches/py3k/Doc/library/logging.rst	(original)
+++ python/branches/py3k/Doc/library/logging.rst	Fri Jan 18 19:40:46 2008
@@ -1136,33 +1136,113 @@
 be hard to manage if the number of :class:`Logger` instances becomes
 effectively unbounded.
 
-There are a number of other ways you can pass contextual information to be
-output along with logging event information.
+An easy way in which you can pass contextual information to be output along
+with logging event information is to use the :class:`LoggerAdapter` class.
+This class is designed to look like a :class:`Logger`, so that you can call
+:meth:`debug`, :meth:`info`, :meth:`warning`, :meth:`error`,
+:meth:`exception`, :meth:`critical` and :meth:`log`. These methods have the
+same signatures as their counterparts in :class:`Logger`, so you can use the
+two types of instances interchangeably.
+
+When you create an instance of :class:`LoggerAdapter`, you pass it a
+:class:`Logger` instance and a dict-like object which contains your contextual
+information. When you call one of the logging methods on an instance of
+:class:`LoggerAdapter`, it delegates the call to the underlying instance of
+:class:`Logger` passed to its constructor, and arranges to pass the contextual
+information in the delegated call. Here's a snippet from the code of
+:class:`LoggerAdapter`::
+
+    def debug(self, msg, *args, **kwargs):
+        """
+        Delegate a debug call to the underlying logger, after adding
+        contextual information from this adapter instance.
+        """
+        msg, kwargs = self.process(msg, kwargs)
+        self.logger.debug(msg, *args, **kwargs)
+
+The :meth:`process` method of :class:`LoggerAdapter` is where the contextual
+information is added to the logging output. It's passed the message and
+keyword arguments of the logging call, and it passes back (potentially)
+modified versions of these to use in the call to the underlying logger. The
+default implementation of this method leaves the message alone, but inserts
+an "extra" key in the keyword argument whose value is the dict-like object
+passed to the constructor. Of course, if you had passed an "extra" keyword
+argument in the call to the adapter, it will be silently overwritten.
+
+The advantage of using "extra" is that the values in the dict-like object are
+merged into the :class:`LogRecord` instance's __dict__, allowing you to use
+customized strings with your :class:`Formatter` instances which know about
+the keys of the dict-like object. If you need a different method, e.g. if you
+want to prepend or append the contextual information to the message string,
+you just need to subclass :class:`LoggerAdapter` and override :meth:`process`
+to do what you need. Here's an example script which uses this class, which
+also illustrates what dict-like behaviour is needed from an arbitrary
+"dict-like" object for use in the constructor::
+
+import logging
+
+class ConnInfo:
+    """
+    An example class which shows how an arbitrary class can be used as
+    the 'extra' context information repository passed to a LoggerAdapter.
+    """
+
+    def __getitem__(self, name):
+        """
+        To allow this instance to look like a dict.
+        """
+        from random import choice
+        if name == "ip":
+            result = choice(["127.0.0.1", "192.168.0.1"])
+        elif name == "user":
+            result = choice(["jim", "fred", "sheila"])
+        else:
+            result = self.__dict__.get(name, "?")
+        return result
+
+    def __iter__(self):
+        """
+        To allow iteration over keys, which will be merged into
+        the LogRecord dict before formatting and output.
+        """
+        keys = ["ip", "user"]
+        keys.extend(self.__dict__.keys())
+        return keys.__iter__()
+
+if __name__ == "__main__":
+    from random import choice
+    levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
+    a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"),
+                               { "ip" : "123.231.231.123", "user" : "sheila" })
+    logging.basicConfig(level=logging.DEBUG,
+                        format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s")
+    a1.debug("A debug message")
+    a1.info("An info message with %s", "some parameters")
+    a2 = logging.LoggerAdapter(logging.getLogger("d.e.f"), ConnInfo())
+    for x in range(10):
+        lvl = choice(levels)
+        lvlname = logging.getLevelName(lvl)
+        a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters")
+
+When this script is run, the output should look something like this::
+
+2008-01-18 14:49:54,023 a.b.c DEBUG    IP: 123.231.231.123 User: sheila   A debug message
+2008-01-18 14:49:54,023 a.b.c INFO     IP: 123.231.231.123 User: sheila   An info message with some parameters
+2008-01-18 14:49:54,023 d.e.f CRITICAL IP: 192.168.0.1     User: jim      A message at CRITICAL level with 2 parameters
+2008-01-18 14:49:54,033 d.e.f INFO     IP: 192.168.0.1     User: jim      A message at INFO level with 2 parameters
+2008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: sheila   A message at WARNING level with 2 parameters
+2008-01-18 14:49:54,033 d.e.f ERROR    IP: 127.0.0.1       User: fred     A message at ERROR level with 2 parameters
+2008-01-18 14:49:54,033 d.e.f ERROR    IP: 127.0.0.1       User: sheila   A message at ERROR level with 2 parameters
+2008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: sheila   A message at WARNING level with 2 parameters
+2008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: jim      A message at WARNING level with 2 parameters
+2008-01-18 14:49:54,033 d.e.f INFO     IP: 192.168.0.1     User: fred     A message at INFO level with 2 parameters
+2008-01-18 14:49:54,033 d.e.f WARNING  IP: 192.168.0.1     User: sheila   A message at WARNING level with 2 parameters
+2008-01-18 14:49:54,033 d.e.f WARNING  IP: 127.0.0.1       User: jim      A message at WARNING level with 2 parameters
+
+.. versionadded:: 2.6
+
+The :class:`LoggerAdapter` class was not present in previous versions.
 
-* Use an adapter class which has access to the contextual information and
-  which defines methods :meth:`debug`, :meth:`info` etc. with the same
-  signatures as used by :class:`Logger`. You instantiate the adapter with a
-  name, which will be used to create an underlying :class:`Logger` with that
-  name. In each adpater method, the passed-in message is modified to include
-  whatever contextual information you want.
-
-* Use something other than a string to pass the message. Although normally
-  the first argument to a logger method such as :meth:`debug`, :meth:`info`
-  etc. is usually a string, it can in fact be any object. This object is the
-  argument to a :func:`str()` call which is made, in
-  :meth:`LogRecord.getMessage`, to obtain the actual message string. You can
-  use this behavior to pass an instance which may be initialized with a
-  logging message, which redefines :meth:__str__ to return a modified version
-  of that message with the contextual information added.
-
-* Use a specialized :class:`Formatter` subclass to add additional information
-  to the formatted output. The subclass could, for instance, merge some thread
-  local contextual information (or contextual information obtained in some
-  other way) with the output generated by the base :class:`Formatter`.
-
-In each of these three approaches, thread locals can sometimes be a useful way
-of passing contextual information without undue coupling between different
-parts of your code.
 
 .. _network-logging:
 
@@ -2047,6 +2127,33 @@
    Returns the message for this :class:`LogRecord` instance after merging any
    user-supplied arguments with the message.
 
+LoggerAdapter Objects
+---------------------
+
+.. versionadded:: 2.6
+
+:class:`LoggerAdapter` instances are used to conveniently pass contextual
+information into logging calls. For a usage example , see context-info_.
+
+.. class:: LoggerAdapter(logger, extra)
+
+  Returns an instance of :class:`LoggerAdapter` initialized with an
+  underlying :class:`Logger` instance and a dict-like object.
+
+.. method:: LoggerAdapter.process(msg, kwargs)
+
+  Modifies the message and/or keyword arguments passed to a logging call in
+  order to insert contextual information. This implementation takes the
+  object passed as *extra* to the constructor and adds it to *kwargs* using
+  key 'extra'. The return value is a (*msg*, *kwargs*) tuple which has the
+  (possibly modified) versions of the arguments passed in.
+
+In addition to the above, :class:`LoggerAdapter` supports all the logging
+methods of :class:`Logger`, i.e. :meth:`debug`, :meth:`info`, :meth:`warning`,
+:meth:`error`, :meth:`exception`, :meth:`critical` and :meth:`log`. These
+methods have the same signatures as their counterparts in :class:`Logger`, so
+you can use the two types of instances interchangeably.
+
 
 Thread Safety
 -------------

Modified: python/branches/py3k/Doc/reference/executionmodel.rst
==============================================================================
--- python/branches/py3k/Doc/reference/executionmodel.rst	(original)
+++ python/branches/py3k/Doc/reference/executionmodel.rst	Fri Jan 18 19:40:46 2008
@@ -50,7 +50,13 @@
 definition occurs in a function block, the scope extends to any blocks contained
 within the defining one, unless a contained block introduces a different binding
 for the name.  The scope of names defined in a class block is limited to the
-class block; it does not extend to the code blocks of methods.
+class block; it does not extend to the code blocks of methods -- this includes
+generator expressions since they are implemented using a function scope.  This
+means that the following will fail::
+
+   class A:
+       a = 42
+       b = list(a + i for i in range(10))
 
 .. index:: single: environment
 

Modified: python/branches/py3k/Lib/logging/__init__.py
==============================================================================
--- python/branches/py3k/Lib/logging/__init__.py	(original)
+++ python/branches/py3k/Lib/logging/__init__.py	Fri Jan 18 19:40:46 2008
@@ -1,4 +1,4 @@
-# Copyright 2001-2007 by Vinay Sajip. All Rights Reserved.
+# Copyright 2001-2008 by Vinay Sajip. All Rights Reserved.
 #
 # Permission to use, copy, modify, and distribute this software and its
 # documentation for any purpose and without fee is hereby granted,
@@ -18,7 +18,7 @@
 Logging package for Python. Based on PEP 282 and comments thereto in
 comp.lang.python, and influenced by Apache's log4j system.
 
-Copyright (C) 2001-2007 Vinay Sajip. All Rights Reserved.
+Copyright (C) 2001-2008 Vinay Sajip. All Rights Reserved.
 
 To use, simply 'import logging' and log away!
 """
@@ -38,8 +38,8 @@
 
 __author__  = "Vinay Sajip <vinay_sajip at red-dove.com>"
 __status__  = "production"
-__version__ = "0.5.0.3"
-__date__    = "26 September 2007"
+__version__ = "0.5.0.4"
+__date__    = "18 January 2008"
 
 #---------------------------------------------------------------------------
 #   Miscellaneous module data
@@ -1076,7 +1076,7 @@
         specialized LogRecords.
         """
         rv = LogRecord(name, level, fn, lno, msg, args, exc_info, func)
-        if extra:
+        if extra is not None:
             for key in extra:
                 if (key in ["message", "asctime"]) or (key in rv.__dict__):
                     raise KeyError("Attempt to overwrite %r in LogRecord" % key)
@@ -1189,6 +1189,96 @@
 
 _loggerClass = Logger
 
+class LoggerAdapter:
+    """
+    An adapter for loggers which makes it easier to specify contextual
+    information in logging output.
+    """
+
+    def __init__(self, logger, extra):
+        """
+        Initialize the adapter with a logger and a dict-like object which
+        provides contextual information. This constructor signature allows
+        easy stacking of LoggerAdapters, if so desired.
+
+        You can effectively pass keyword arguments as shown in the
+        following example:
+
+        adapter = LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
+        """
+        self.logger = logger
+        self.extra = extra
+
+    def process(self, msg, kwargs):
+        """
+        Process the logging message and keyword arguments passed in to
+        a logging call to insert contextual information. You can either
+        manipulate the message itself, the keyword args or both. Return
+        the message and kwargs modified (or not) to suit your needs.
+
+        Normally, you'll only need to override this one method in a
+        LoggerAdapter subclass for your specific needs.
+        """
+        kwargs["extra"] = self.extra
+        return msg, kwargs
+
+    def debug(self, msg, *args, **kwargs):
+        """
+        Delegate a debug call to the underlying logger, after adding
+        contextual information from this adapter instance.
+        """
+        msg, kwargs = self.process(msg, kwargs)
+        self.logger.debug(msg, *args, **kwargs)
+
+    def info(self, msg, *args, **kwargs):
+        """
+        Delegate an info call to the underlying logger, after adding
+        contextual information from this adapter instance.
+        """
+        msg, kwargs = self.process(msg, kwargs)
+        self.logger.info(msg, *args, **kwargs)
+
+    def warning(self, msg, *args, **kwargs):
+        """
+        Delegate a warning call to the underlying logger, after adding
+        contextual information from this adapter instance.
+        """
+        msg, kwargs = self.process(msg, kwargs)
+        self.logger.warning(msg, *args, **kwargs)
+
+    def error(self, msg, *args, **kwargs):
+        """
+        Delegate an error call to the underlying logger, after adding
+        contextual information from this adapter instance.
+        """
+        msg, kwargs = self.process(msg, kwargs)
+        self.logger.error(msg, *args, **kwargs)
+
+    def exception(self, msg, *args, **kwargs):
+        """
+        Delegate an exception call to the underlying logger, after adding
+        contextual information from this adapter instance.
+        """
+        msg, kwargs = self.process(msg, kwargs)
+        kwargs["exc_info"] = 1
+        self.logger.error(msg, *args, **kwargs)
+
+    def critical(self, msg, *args, **kwargs):
+        """
+        Delegate a critical call to the underlying logger, after adding
+        contextual information from this adapter instance.
+        """
+        msg, kwargs = self.process(msg, kwargs)
+        self.logger.critical(msg, *args, **kwargs)
+
+    def log(self, level, msg, *args, **kwargs):
+        """
+        Delegate a log call to the underlying logger, after adding
+        contextual information from this adapter instance.
+        """
+        msg, kwargs = self.process(msg, kwargs)
+        self.logger.log(level, msg, *args, **kwargs)
+
 root = RootLogger(WARNING)
 Logger.root = root
 Logger.manager = Manager(Logger.root)


More information about the Python-3000-checkins mailing list