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:
parent
679db4aa99
commit
04c420f605
|
@ -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
|
|
@ -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
|
||||
-------------
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
Loading…
Reference in New Issue