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 ''
........
This commit is contained in:
Christian Heimes 2008-01-18 18:40:46 +00:00
parent 679db4aa99
commit 04c420f605
5 changed files with 341 additions and 29 deletions

52
Doc/builddoc.bat Normal file
View File

@ -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

View File

@ -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.
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.
* 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.
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`::
* 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.
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)
* 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`.
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.
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 @@ 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
-------------

View File

@ -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

View File

@ -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)

View File

@ -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()