Skip to content

Commit 04c420f

Browse files
committed
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 '' ........
1 parent 679db4a commit 04c420f

5 files changed

Lines changed: 345 additions & 33 deletions

File tree

‎Doc/builddoc.bat‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
@echo off
2+
setlocal
3+
4+
set SVNROOT=http://svn.python.org/projects
5+
if "%PYTHON%" EQU "" set PYTHON=python25
6+
7+
if "%1" EQU "" goto help
8+
if "%1" EQU "html" goto build
9+
if "%1" EQU "htmlhelp" goto build
10+
if "%1" EQU "web" goto build
11+
if "%1" EQU "webrun" goto webrun
12+
if "%1" EQU "checkout" goto checkout
13+
if "%1" EQU "update" goto update
14+
15+
:help
16+
echo HELP
17+
echo.
18+
echo builddoc checkout
19+
echo builddoc update
20+
echo builddoc html
21+
echo builddoc htmlhelp
22+
echo builddoc web
23+
echo builddoc webrun
24+
echo.
25+
goto end
26+
27+
:checkout
28+
svn co %SVNROOT%/doctools/trunk/sphinx tools/sphinx
29+
svn co %SVNROOT%/external/docutils-0.4/docutils tools/docutils
30+
svn co %SVNROOT%/external/Pygments-0.9/pygments tools/pygments
31+
goto end
32+
33+
:update
34+
svn update tools/sphinx
35+
svn update tools/docutils
36+
svn update tools/pygments
37+
goto end
38+
39+
:build
40+
if not exist build mkdir build
41+
if not exist build\%1 mkdir build\%1
42+
if not exist build\doctrees mkdir build\doctrees
43+
cmd /C %PYTHON% tools\sphinx-build.py -b%1 -dbuild\doctrees . build\%1
44+
if "%1" EQU "htmlhelp" "%ProgramFiles%\HTML Help Workshop\hhc.exe" build\htmlhelp\pydoc.hhp
45+
goto end
46+
47+
:webrun
48+
set PYTHONPATH=tools
49+
%PYTHON% -m sphinx.web build\web
50+
goto end
51+
52+
:end

‎Doc/library/logging.rst‎

Lines changed: 134 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,33 +1136,113 @@ level of granularity you want to use in logging an application, it could
11361136
be hard to manage if the number of :class:`Logger` instances becomes
11371137
effectively unbounded.
11381138

1139-
There are a number of other ways you can pass contextual information to be
1140-
output along with logging event information.
1141-
1142-
* Use an adapter class which has access to the contextual information and
1143-
which defines methods :meth:`debug`, :meth:`info` etc. with the same
1144-
signatures as used by :class:`Logger`. You instantiate the adapter with a
1145-
name, which will be used to create an underlying :class:`Logger` with that
1146-
name. In each adpater method, the passed-in message is modified to include
1147-
whatever contextual information you want.
1148-
1149-
* Use something other than a string to pass the message. Although normally
1150-
the first argument to a logger method such as :meth:`debug`, :meth:`info`
1151-
etc. is usually a string, it can in fact be any object. This object is the
1152-
argument to a :func:`str()` call which is made, in
1153-
:meth:`LogRecord.getMessage`, to obtain the actual message string. You can
1154-
use this behavior to pass an instance which may be initialized with a
1155-
logging message, which redefines :meth:__str__ to return a modified version
1156-
of that message with the contextual information added.
1157-
1158-
* Use a specialized :class:`Formatter` subclass to add additional information
1159-
to the formatted output. The subclass could, for instance, merge some thread
1160-
local contextual information (or contextual information obtained in some
1161-
other way) with the output generated by the base :class:`Formatter`.
1162-
1163-
In each of these three approaches, thread locals can sometimes be a useful way
1164-
of passing contextual information without undue coupling between different
1165-
parts of your code.
1139+
An easy way in which you can pass contextual information to be output along
1140+
with logging event information is to use the :class:`LoggerAdapter` class.
1141+
This class is designed to look like a :class:`Logger`, so that you can call
1142+
:meth:`debug`, :meth:`info`, :meth:`warning`, :meth:`error`,
1143+
:meth:`exception`, :meth:`critical` and :meth:`log`. These methods have the
1144+
same signatures as their counterparts in :class:`Logger`, so you can use the
1145+
two types of instances interchangeably.
1146+
1147+
When you create an instance of :class:`LoggerAdapter`, you pass it a
1148+
:class:`Logger` instance and a dict-like object which contains your contextual
1149+
information. When you call one of the logging methods on an instance of
1150+
:class:`LoggerAdapter`, it delegates the call to the underlying instance of
1151+
:class:`Logger` passed to its constructor, and arranges to pass the contextual
1152+
information in the delegated call. Here's a snippet from the code of
1153+
:class:`LoggerAdapter`::
1154+
1155+
def debug(self, msg, *args, **kwargs):
1156+
"""
1157+
Delegate a debug call to the underlying logger, after adding
1158+
contextual information from this adapter instance.
1159+
"""
1160+
msg, kwargs = self.process(msg, kwargs)
1161+
self.logger.debug(msg, *args, **kwargs)
1162+
1163+
The :meth:`process` method of :class:`LoggerAdapter` is where the contextual
1164+
information is added to the logging output. It's passed the message and
1165+
keyword arguments of the logging call, and it passes back (potentially)
1166+
modified versions of these to use in the call to the underlying logger. The
1167+
default implementation of this method leaves the message alone, but inserts
1168+
an "extra" key in the keyword argument whose value is the dict-like object
1169+
passed to the constructor. Of course, if you had passed an "extra" keyword
1170+
argument in the call to the adapter, it will be silently overwritten.
1171+
1172+
The advantage of using "extra" is that the values in the dict-like object are
1173+
merged into the :class:`LogRecord` instance's __dict__, allowing you to use
1174+
customized strings with your :class:`Formatter` instances which know about
1175+
the keys of the dict-like object. If you need a different method, e.g. if you
1176+
want to prepend or append the contextual information to the message string,
1177+
you just need to subclass :class:`LoggerAdapter` and override :meth:`process`
1178+
to do what you need. Here's an example script which uses this class, which
1179+
also illustrates what dict-like behaviour is needed from an arbitrary
1180+
"dict-like" object for use in the constructor::
1181+
1182+
import logging
1183+
1184+
class ConnInfo:
1185+
"""
1186+
An example class which shows how an arbitrary class can be used as
1187+
the 'extra' context information repository passed to a LoggerAdapter.
1188+
"""
1189+
1190+
def __getitem__(self, name):
1191+
"""
1192+
To allow this instance to look like a dict.
1193+
"""
1194+
from random import choice
1195+
if name == "ip":
1196+
result = choice(["127.0.0.1", "192.168.0.1"])
1197+
elif name == "user":
1198+
result = choice(["jim", "fred", "sheila"])
1199+
else:
1200+
result = self.__dict__.get(name, "?")
1201+
return result
1202+
1203+
def __iter__(self):
1204+
"""
1205+
To allow iteration over keys, which will be merged into
1206+
the LogRecord dict before formatting and output.
1207+
"""
1208+
keys = ["ip", "user"]
1209+
keys.extend(self.__dict__.keys())
1210+
return keys.__iter__()
1211+
1212+
if __name__ == "__main__":
1213+
from random import choice
1214+
levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
1215+
a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"),
1216+
{ "ip" : "123.231.231.123", "user" : "sheila" })
1217+
logging.basicConfig(level=logging.DEBUG,
1218+
format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s")
1219+
a1.debug("A debug message")
1220+
a1.info("An info message with %s", "some parameters")
1221+
a2 = logging.LoggerAdapter(logging.getLogger("d.e.f"), ConnInfo())
1222+
for x in range(10):
1223+
lvl = choice(levels)
1224+
lvlname = logging.getLevelName(lvl)
1225+
a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters")
1226+
1227+
When this script is run, the output should look something like this::
1228+
1229+
2008-01-18 14:49:54,023 a.b.c DEBUG IP: 123.231.231.123 User: sheila A debug message
1230+
2008-01-18 14:49:54,023 a.b.c INFO IP: 123.231.231.123 User: sheila An info message with some parameters
1231+
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
1232+
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
1233+
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
1234+
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
1235+
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
1236+
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
1237+
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
1238+
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
1239+
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
1240+
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
1241+
1242+
.. versionadded:: 2.6
1243+
1244+
The :class:`LoggerAdapter` class was not present in previous versions.
1245+
11661246

11671247
.. _network-logging:
11681248

@@ -2047,6 +2127,33 @@ made, and any exception information to be logged.
20472127
Returns the message for this :class:`LogRecord` instance after merging any
20482128
user-supplied arguments with the message.
20492129

2130+
LoggerAdapter Objects
2131+
---------------------
2132+
2133+
.. versionadded:: 2.6
2134+
2135+
:class:`LoggerAdapter` instances are used to conveniently pass contextual
2136+
information into logging calls. For a usage example , see context-info_.
2137+
2138+
.. class:: LoggerAdapter(logger, extra)
2139+
2140+
Returns an instance of :class:`LoggerAdapter` initialized with an
2141+
underlying :class:`Logger` instance and a dict-like object.
2142+
2143+
.. method:: LoggerAdapter.process(msg, kwargs)
2144+
2145+
Modifies the message and/or keyword arguments passed to a logging call in
2146+
order to insert contextual information. This implementation takes the
2147+
object passed as *extra* to the constructor and adds it to *kwargs* using
2148+
key 'extra'. The return value is a (*msg*, *kwargs*) tuple which has the
2149+
(possibly modified) versions of the arguments passed in.
2150+
2151+
In addition to the above, :class:`LoggerAdapter` supports all the logging
2152+
methods of :class:`Logger`, i.e. :meth:`debug`, :meth:`info`, :meth:`warning`,
2153+
:meth:`error`, :meth:`exception`, :meth:`critical` and :meth:`log`. These
2154+
methods have the same signatures as their counterparts in :class:`Logger`, so
2155+
you can use the two types of instances interchangeably.
2156+
20502157

20512158
Thread Safety
20522159
-------------

‎Doc/reference/executionmodel.rst‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ variable is defined in a block, its scope includes that block. If the
5050
definition occurs in a function block, the scope extends to any blocks contained
5151
within the defining one, unless a contained block introduces a different binding
5252
for the name. The scope of names defined in a class block is limited to the
53-
class block; it does not extend to the code blocks of methods.
53+
class block; it does not extend to the code blocks of methods -- this includes
54+
generator expressions since they are implemented using a function scope. This
55+
means that the following will fail::
56+
57+
class A:
58+
a = 42
59+
b = list(a + i for i in range(10))
5460

5561
.. index:: single: environment
5662

‎Lib/logging/__init__.py‎

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2001-2007 by Vinay Sajip. All Rights Reserved.
1+
# Copyright 2001-2008 by Vinay Sajip. All Rights Reserved.
22
#
33
# Permission to use, copy, modify, and distribute this software and its
44
# documentation for any purpose and without fee is hereby granted,
@@ -18,7 +18,7 @@
1818
Logging package for Python. Based on PEP 282 and comments thereto in
1919
comp.lang.python, and influenced by Apache's log4j system.
2020
21-
Copyright (C) 2001-2007 Vinay Sajip. All Rights Reserved.
21+
Copyright (C) 2001-2008 Vinay Sajip. All Rights Reserved.
2222
2323
To use, simply 'import logging' and log away!
2424
"""
@@ -38,8 +38,8 @@
3838

3939
__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
4040
__status__ = "production"
41-
__version__ = "0.5.0.3"
42-
__date__ = "26 September 2007"
41+
__version__ = "0.5.0.4"
42+
__date__ = "18 January 2008"
4343

4444
#---------------------------------------------------------------------------
4545
# Miscellaneous module data
@@ -1076,7 +1076,7 @@ def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra
10761076
specialized LogRecords.
10771077
"""
10781078
rv = LogRecord(name, level, fn, lno, msg, args, exc_info, func)
1079-
if extra:
1079+
if extra is not None:
10801080
for key in extra:
10811081
if (key in ["message", "asctime"]) or (key in rv.__dict__):
10821082
raise KeyError("Attempt to overwrite %r in LogRecord" % key)
@@ -1189,6 +1189,96 @@ def __init__(self, level):
11891189

11901190
_loggerClass = Logger
11911191

1192+
class LoggerAdapter:
1193+
"""
1194+
An adapter for loggers which makes it easier to specify contextual
1195+
information in logging output.
1196+
"""
1197+
1198+
def __init__(self, logger, extra):
1199+
"""
1200+
Initialize the adapter with a logger and a dict-like object which
1201+
provides contextual information. This constructor signature allows
1202+
easy stacking of LoggerAdapters, if so desired.
1203+
1204+
You can effectively pass keyword arguments as shown in the
1205+
following example:
1206+
1207+
adapter = LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
1208+
"""
1209+
self.logger = logger
1210+
self.extra = extra
1211+
1212+
def process(self, msg, kwargs):
1213+
"""
1214+
Process the logging message and keyword arguments passed in to
1215+
a logging call to insert contextual information. You can either
1216+
manipulate the message itself, the keyword args or both. Return
1217+
the message and kwargs modified (or not) to suit your needs.
1218+
1219+
Normally, you'll only need to override this one method in a
1220+
LoggerAdapter subclass for your specific needs.
1221+
"""
1222+
kwargs["extra"] = self.extra
1223+
return msg, kwargs
1224+
1225+
def debug(self, msg, *args, **kwargs):
1226+
"""
1227+
Delegate a debug call to the underlying logger, after adding
1228+
contextual information from this adapter instance.
1229+
"""
1230+
msg, kwargs = self.process(msg, kwargs)
1231+
self.logger.debug(msg, *args, **kwargs)
1232+
1233+
def info(self, msg, *args, **kwargs):
1234+
"""
1235+
Delegate an info call to the underlying logger, after adding
1236+
contextual information from this adapter instance.
1237+
"""
1238+
msg, kwargs = self.process(msg, kwargs)
1239+
self.logger.info(msg, *args, **kwargs)
1240+
1241+
def warning(self, msg, *args, **kwargs):
1242+
"""
1243+
Delegate a warning call to the underlying logger, after adding
1244+
contextual information from this adapter instance.
1245+
"""
1246+
msg, kwargs = self.process(msg, kwargs)
1247+
self.logger.warning(msg, *args, **kwargs)
1248+
1249+
def error(self, msg, *args, **kwargs):
1250+
"""
1251+
Delegate an error call to the underlying logger, after adding
1252+
contextual information from this adapter instance.
1253+
"""
1254+
msg, kwargs = self.process(msg, kwargs)
1255+
self.logger.error(msg, *args, **kwargs)
1256+
1257+
def exception(self, msg, *args, **kwargs):
1258+
"""
1259+
Delegate an exception call to the underlying logger, after adding
1260+
contextual information from this adapter instance.
1261+
"""
1262+
msg, kwargs = self.process(msg, kwargs)
1263+
kwargs["exc_info"] = 1
1264+
self.logger.error(msg, *args, **kwargs)
1265+
1266+
def critical(self, msg, *args, **kwargs):
1267+
"""
1268+
Delegate a critical call to the underlying logger, after adding
1269+
contextual information from this adapter instance.
1270+
"""
1271+
msg, kwargs = self.process(msg, kwargs)
1272+
self.logger.critical(msg, *args, **kwargs)
1273+
1274+
def log(self, level, msg, *args, **kwargs):
1275+
"""
1276+
Delegate a log call to the underlying logger, after adding
1277+
contextual information from this adapter instance.
1278+
"""
1279+
msg, kwargs = self.process(msg, kwargs)
1280+
self.logger.log(level, msg, *args, **kwargs)
1281+
11921282
root = RootLogger(WARNING)
11931283
Logger.root = root
11941284
Logger.manager = Manager(Logger.root)

0 commit comments

Comments
 (0)