summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/builddoc.bat52
-rw-r--r--Doc/library/logging.rst161
-rw-r--r--Doc/reference/executionmodel.rst8
-rw-r--r--Lib/logging/__init__.py100
-rw-r--r--Tools/scripts/win_add2path.py57
5 files changed, 345 insertions, 33 deletions
diff --git a/Doc/builddoc.bat b/Doc/builddoc.bat
new file mode 100644
index 0000000..a26851f
--- /dev/null
+++ b/Doc/builddoc.bat
@@ -0,0 +1,52 @@
+@echo off
+setlocal
+
+set SVNROOT=http://svn.python.org/projects
+if "%PYTHON%" EQU "" set PYTHON=python25
+
+if "%1" EQU "" goto help
+if "%1" EQU "html" goto build
+if "%1" EQU "htmlhelp" goto build
+if "%1" EQU "web" goto build
+if "%1" EQU "webrun" goto webrun
+if "%1" EQU "checkout" goto checkout
+if "%1" EQU "update" goto update
+
+:help
+echo HELP
+echo.
+echo builddoc checkout
+echo builddoc update
+echo builddoc html
+echo builddoc htmlhelp
+echo builddoc web
+echo builddoc webrun
+echo.
+goto end
+
+:checkout
+svn co %SVNROOT%/doctools/trunk/sphinx tools/sphinx
+svn co %SVNROOT%/external/docutils-0.4/docutils tools/docutils
+svn co %SVNROOT%/external/Pygments-0.9/pygments tools/pygments
+goto end
+
+:update
+svn update tools/sphinx
+svn update tools/docutils
+svn update tools/pygments
+goto end
+
+:build
+if not exist build mkdir build
+if not exist build\%1 mkdir build\%1
+if not exist build\doctrees mkdir build\doctrees
+cmd /C %PYTHON% tools\sphinx-build.py -b%1 -dbuild\doctrees . build\%1
+if "%1" EQU "htmlhelp" "%ProgramFiles%\HTML Help Workshop\hhc.exe" build\htmlhelp\pydoc.hhp
+goto end
+
+:webrun
+set PYTHONPATH=tools
+%PYTHON% -m sphinx.web build\web
+goto end
+
+:end
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst
index af8c867..258bb0a 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -1136,33 +1136,113 @@ level of granularity you want to use in logging an application, it could
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.
-
-* 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.
+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.
+
.. _network-logging:
@@ -2047,6 +2127,33 @@ made, and any exception information to be logged.
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
-------------
diff --git a/Doc/reference/executionmodel.rst b/Doc/reference/executionmodel.rst
index 1f85e49..43515d9 100644
--- a/Doc/reference/executionmodel.rst
+++ b/Doc/reference/executionmodel.rst
@@ -50,7 +50,13 @@ variable is defined in a block, its scope includes that block. If the
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
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 04e05b9..68fd10c 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -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 @@ except ImportError:
__author__ = "Vinay Sajip <vinay_sajip@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 @@ class Logger(Filterer):
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 @@ class RootLogger(Logger):
_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)
diff --git a/Tools/scripts/win_add2path.py b/Tools/scripts/win_add2path.py
new file mode 100644
index 0000000..876bfb2
--- /dev/null
+++ b/Tools/scripts/win_add2path.py
@@ -0,0 +1,57 @@
+"""Add Python to the search path on Windows
+
+This is a simple script to add Python to the Windows search path. It
+modifies the current user (HKCU) tree of the registry.
+
+Copyright (c) 2008 by Christian Heimes <christian@cheimes.de>
+Licensed to PSF under a Contributor Agreement.
+"""
+
+import sys
+import site
+import os
+import _winreg
+
+HKCU = _winreg.HKEY_CURRENT_USER
+ENV = "Environment"
+PATH = "PATH"
+DEFAULT = u"%PATH%"
+
+def modify():
+ pythonpath = os.path.dirname(os.path.normpath(sys.executable))
+ scripts = os.path.join(pythonpath, "Scripts")
+ appdata = os.environ["APPDATA"]
+ if hasattr(site, "USER_SITE"):
+ userpath = site.USER_SITE.replace(appdata, "%APPDATA%")
+ userscripts = os.path.join(userpath, "Scripts")
+ else:
+ userscripts = None
+
+ with _winreg.CreateKey(HKCU, ENV) as key:
+ try:
+ envpath = _winreg.QueryValueEx(key, PATH)[0]
+ except WindowsError:
+ envpath = DEFAULT
+
+ paths = [envpath]
+ for path in (pythonpath, scripts, userscripts):
+ if path and path not in envpath and os.path.isdir(path):
+ paths.append(path)
+
+ envpath = os.pathsep.join(paths)
+ _winreg.SetValueEx(key, PATH, 0, _winreg.REG_EXPAND_SZ, envpath)
+ return paths, envpath
+
+def main():
+ paths, envpath = modify()
+ if len(paths) > 1:
+ print "Path(s) added:"
+ print '\n'.join(paths[1:])
+ else:
+ print "No path was added"
+ print "\nPATH is now:\n%s\n" % envpath
+ print "Expanded:"
+ print _winreg.ExpandEnvironmentStrings(envpath)
+
+if __name__ == '__main__':
+ main()