Reformat according to PEP 12.

This commit is contained in:
Georg Brandl 2009-10-19 13:49:12 +00:00
parent 00888ff3be
commit 1842cd2321
1 changed files with 300 additions and 260 deletions

View File

@ -14,195 +14,211 @@ Post-History:
Abstract Abstract
======== ========
This PEP describes a new way of configuring logging using a dictionary to hold This PEP describes a new way of configuring logging using a dictionary
configuration information. to hold configuration information.
Rationale Rationale
========= =========
The present means for configuring Python's logging package is either by using The present means for configuring Python's logging package is either
the logging API to configure logging programmatically, or else by means of by using the logging API to configure logging programmatically, or
ConfigParser-based configuration files. else by means of ConfigParser-based configuration files.
Programmatic configuration, while offering maximal control, fixes the Programmatic configuration, while offering maximal control, fixes the
configuration in Python code. This does not facilitate changing it easily at configuration in Python code. This does not facilitate changing it
runtime, and, as a result, the ability to flexibly turn the verbosity of easily at runtime, and, as a result, the ability to flexibly turn the
logging up and down for different parts of a using application is lost. This verbosity of logging up and down for different parts of a using
limits the usability of logging as an aid to diagnosing problems - and application is lost. This limits the usability of logging as an aid
sometimes, logging is the only diagnostic aid available in production to diagnosing problems - and sometimes, logging is the only diagnostic
environments. aid available in production environments.
The ConfigParser-based configuration system is usable, but does not allow its The ConfigParser-based configuration system is usable, but does not
users to configure all aspects of the logging package. For example, Filters allow its users to configure all aspects of the logging package. For
cannot be configured using this system. Furthermore, the ConfigParser format example, Filters cannot be configured using this system. Furthermore,
appears to engender dislike (sometimes strong dislike) in some quarters. the ConfigParser format appears to engender dislike (sometimes strong
Though it was chosen because it was the only configuration format supported in dislike) in some quarters. Though it was chosen because it was the
the Python standard at that time, many people regard it (or perhaps just the only configuration format supported in the Python standard at that
particular schema chosen for logging's configuration) as 'crufty' or 'ugly', time, many people regard it (or perhaps just the particular schema
in some cases apparently on purely aesthetic grounds. chosen for logging's configuration) as 'crufty' or 'ugly', in some
cases apparently on purely aesthetic grounds.
Recent versions of Python inlude JSON support in the standard library, and Recent versions of Python include JSON support in the standard
this is also usable as a configuration format. In other environments, such as library, and this is also usable as a configuration format. In other
Google App Engine, YAML is used to configure applications, and usually the environments, such as Google App Engine, YAML is used to configure
configuration of logging would be considered an integral part of the applications, and usually the configuration of logging would be
application configuration. Although the standard library does not contain considered an integral part of the application configuration.
YAML support at present, support for both JSON and YAML can be provided in a Although the standard library does not contain YAML support at
common way because both of these serialization formats allow deserialization present, support for both JSON and YAML can be provided in a common
way because both of these serialization formats allow deserialization
of Python dictionaries. of Python dictionaries.
By providing a way to configure logging by passing the configuration in a By providing a way to configure logging by passing the configuration
dictionary, logging will be easier to configure not only for users of JSON in a dictionary, logging will be easier to configure not only for
and/or YAML, but also for users of bespoke configuration methods, by providing users of JSON and/or YAML, but also for users of bespoke configuration
a common format in which to describe the desired configuration. methods, by providing a common format in which to describe the desired
configuration.
Another drawback of the current ConfigParser-based configuration
system is that it does not support incremental configuration: a new
configuration completely replaces the existing configuration.
Although full flexibility for incremental configuration is difficult
to provide in a multi-threaded environment, the new configuration
mechanism will allow the provision of limited support for incremental
configuration.
Another drawback of the current ConfigParser-based configuration system is
that it does not support incremental configuration: a new configuration
completely replaces the existing configuration. Although full flexibility for
incremental configuration is difficult to provide in a multi-threaded
environment, the new configuration mechanism will allow the provision of
limited support for incremental configuration.
Specification Specification
============= =============
The specification consists of two parts: the API and the format of the The specification consists of two parts: the API and the format of the
dictionary used to convey configuration information (i.e. the schema to which dictionary used to convey configuration information (i.e. the schema
it must conform). to which it must conform).
Naming Naming
------ ------
Historically, the logging package has not been PEP-8 conformant. At some Historically, the logging package has not been PEP 8 conformant [1]_.
future time, this will be corrected by changing method and function names in At some future time, this will be corrected by changing method and
the package in order to conform with PEP-8. However, in the interests of function names in the package in order to conform with PEP 8.
uniformity, the proposed additions to the API use a naming scheme which is However, in the interests of uniformity, the proposed additions to the
consistent with the present scheme used by logging. API use a naming scheme which is consistent with the present scheme
used by logging.
API API
--- ---
The logging.config module will have the following additions: The logging.config module will have the following additions:
* A class, called ``DictConfigurator``, whose constructor is passed the * A class, called ``DictConfigurator``, whose constructor is passed
dictionary used for configuration, and which has a ``configure()`` method. the dictionary used for configuration, and which has a
``configure()`` method.
* A callable, called ``dictConfigClass``, which will (by default) be set to * A callable, called ``dictConfigClass``, which will (by default) be
``DictConfigurator``. This is provided so that if desired, set to ``DictConfigurator``. This is provided so that if desired,
``DictConfigurator`` can be replaced with a suitable user-defined ``DictConfigurator`` can be replaced with a suitable user-defined
implementation. implementation.
* A function, called ``dictConfig()``, which takes a single argument - the * A function, called ``dictConfig()``, which takes a single argument
dictionary holding the configuration. This function will call - the dictionary holding the configuration. This function will
``dictConfigClass`` passing the specified dictionary, and then call the call ``dictConfigClass`` passing the specified dictionary, and then
``configure()`` method on the returned object to actually put the call the ``configure()`` method on the returned object to actually
configuration into effect:: put the configuration into effect::
def dictConfig(config):
dictConfigClass(config).configure()
def dictConfig(config):
dictConfigClass(config).configure()
Dictionary Schema - Overview Dictionary Schema - Overview
---------------------------- ----------------------------
Before describing the schema in detail, it is worth saying a few words about Before describing the schema in detail, it is worth saying a few words
object connections, support for user-defined objects and access to external about object connections, support for user-defined objects and access
objects. to external objects.
Object connections Object connections
'''''''''''''''''' ''''''''''''''''''
The schema is intended to describe a set of logging objects - loggers, The schema is intended to describe a set of logging objects - loggers,
handlers, formatters, filters - which are connected to each other in an handlers, formatters, filters - which are connected to each other in
object graph. Thus, the schema needs to represent connections between the an object graph. Thus, the schema needs to represent connections
objects. For example, say that, once configured, a particular logger has an between the objects. For example, say that, once configured, a
attached to it a particular handler. For the purposes of this discussion, particular logger has an attached to it a particular handler. For the
we can say that the logger represents the source, and the handler the purposes of this discussion, we can say that the logger represents the
destination, of a connection between the two. Of course in the configured source, and the handler the destination, of a connection between the
objects this is represented by the logger holding a reference to the two. Of course in the configured objects this is represented by the
handler. In the configuration dict, this is done by giving each destination logger holding a reference to the handler. In the configuration dict,
object an id which identifies it unambiguously, and then using the id in the this is done by giving each destination object an id which identifies
source object's configuration to indicate that a connection exists between it unambiguously, and then using the id in the source object's
the source and the destination object with that id. configuration to indicate that a connection exists between the source
and the destination object with that id.
So, for example, consider the following YAML snippet:: So, for example, consider the following YAML snippet::
handlers: handlers:
h1: #This is an id h1: #This is an id
# configuration of handler with id h1 goes here # configuration of handler with id h1 goes here
h2: #This is another id h2: #This is another id
# configuration of handler with id h2 goes here # configuration of handler with id h2 goes here
loggers: loggers:
foo.bar.baz: foo.bar.baz:
# other configuration for logger "foo.bar.baz" # other configuration for logger "foo.bar.baz"
handlers: [h1, h2] handlers: [h1, h2]
(Note: YAML will be used in this document as it is more readable than the (Note: YAML will be used in this document as it is more readable than
equivalent Python source form for the dictionary.) the equivalent Python source form for the dictionary.)
The ids for loggers are the logger names which would be used The ids for loggers are the logger names which would be used
programmatically to obtain a reference to those loggers, e.g. programmatically to obtain a reference to those loggers, e.g.
``foo.bar.baz``. The ids for other objects can be any string value (such as ``foo.bar.baz``. The ids for other objects can be any string value
``h1``, ``h2`` above) and they are transient, in that they are only (such as ``h1``, ``h2`` above) and they are transient, in that they
meaningful for processing the configuration dictionary and used to are only meaningful for processing the configuration dictionary and
determine connections between objects, and are not persisted anywhere when used to determine connections between objects, and are not persisted
the configuration call is complete. anywhere when the configuration call is complete.
The above snippet indicates that logger named ``foo.bar.baz`` should
have two handlers attached to it, which are described by the handler
ids ``h1`` and ``h2``.
The above snippet indicates that logger named ``foo.bar.baz`` should have
two handlers attached to it, which are described by the handler ids ``h1``
and ``h2``.
User-defined objects User-defined objects
'''''''''''''''''''' ''''''''''''''''''''
The schema should support user-defined objects for handlers, filters and The schema should support user-defined objects for handlers, filters
formatters. (Loggers do not need to have different types for different and formatters. (Loggers do not need to have different types for
instances, so there is no support - in the configuration - for user-defined different instances, so there is no support - in the configuration -
logger classes.) for user-defined logger classes.)
Objects to be configured will typically be described by dictionaries which Objects to be configured will typically be described by dictionaries
detail their configuration. In some places, the logging system will be able which detail their configuration. In some places, the logging system
to infer from the context how an object is to be instantiated, but when a will be able to infer from the context how an object is to be
user-defined object is to be instantiated, the system will not know how to do instantiated, but when a user-defined object is to be instantiated,
this. In order to provide complete flexibility for user-defined object the system will not know how to do this. In order to provide complete
instantiation, the user will need to provide a 'factory' - a callable which flexibility for user-defined object instantiation, the user will need
is called with a configuration dictionary and which returns the instantiated to provide a 'factory' - a callable which is called with a
object. This will be signalled by the factory being made available under configuration dictionary and which returns the instantiated object.
the special key ``'()'``. Here's a concrete example:: This will be signalled by the factory being made available under the
special key ``'()'``. Here's a concrete example::
formatters: formatters:
brief: brief:
format: '%(message)s' format: '%(message)s'
default: default:
format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s' format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
datefmt: '%Y-%m-%d %H:%M:%S' datefmt: '%Y-%m-%d %H:%M:%S'
custom: custom:
(): my.package.customFormatterFactory (): my.package.customFormatterFactory
bar: baz bar: baz
spam: 99.9 spam: 99.9
answer: 42 answer: 42
The above YAML snippet defines three formatters. The first, with id The above YAML snippet defines three formatters. The first, with id
``brief``, is a standard ``logging.Formatter`` instance with the ``brief``, is a standard ``logging.Formatter`` instance with the
specified format string. The second, with id ``default``, has a longer specified format string. The second, with id ``default``, has a
format and also defines the time format explicitly, and will result in a longer format and also defines the time format explicitly, and will
``logging.Formatter`` initialized with those two format strings. Shown in result in a ``logging.Formatter`` initialized with those two format
Python source form, the ``brief`` and ``default`` formatters have strings. Shown in Python source form, the ``brief`` and ``default``
have configuration sub-dictionaries:: formatters have configuration sub-dictionaries::
{ {
'format' : '%(message)s' 'format' : '%(message)s'
} }
and:: and::
{ {
'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s', 'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',
'datefmt' : '%Y-%m-%d %H:%M:%S' 'datefmt' : '%Y-%m-%d %H:%M:%S'
} }
respectively, and as these dictionaries do not contain the special key respectively, and as these dictionaries do not contain the special key
``'()'``, the instantiation is inferred from the context: as a result, ``'()'``, the instantiation is inferred from the context: as a result,
standard ``logging.Formatter`` instances are created. The configuration standard ``logging.Formatter`` instances are created. The
sub-dictionary for the third formatter, with id ``custom``, is:: configuration sub-dictionary for the third formatter, with id
``custom``, is::
{ {
'()' : 'my.package.customFormatterFactory', '()' : 'my.package.customFormatterFactory',
@ -211,118 +227,128 @@ sub-dictionary for the third formatter, with id ``custom``, is::
'answer' : 42 'answer' : 42
} }
and this contains the special key ``'()'``, which means that user-defined and this contains the special key ``'()'``, which means that
instantiation is wanted. In this case, the specified factory callable will be user-defined instantiation is wanted. In this case, the specified
located using normal import mechanisms and called with the *remaining* items factory callable will be located using normal import mechanisms and
in the configuration sub-dictionary as keyword arguments. In the above called with the *remaining* items in the configuration sub-dictionary
example, the formatter with id ``custom`` will be assumed to be returned by as keyword arguments. In the above example, the formatter with id
the call:: ``custom`` will be assumed to be returned by the call::
my.package.customFormatterFactory(bar="baz", spam=99.9, answer=42) my.package.customFormatterFactory(bar="baz", spam=99.9, answer=42)
The key ``'()'`` has been used as the special key because it is not a
valid keyword parameter name, and so will not clash with the names of
the keyword arguments used in the call. The ``'()'`` also serves as a
mnemonic that the corresponding value is a callable.
The key ``'()'`` has been used as the special key because it is not a valid
keyword parameter name, and so will not clash with the names of the keyword
arguments used in the call. The ``'()'`` also serves as a mnemonic that the
corresponding value is a callable.
Access to external objects Access to external objects
'''''''''''''''''''''''''' ''''''''''''''''''''''''''
There are times where a configuration will need to refer to objects external There are times where a configuration will need to refer to objects
to the configuration, for example ``sys.stderr``. If the configuration dict external to the configuration, for example ``sys.stderr``. If the
is constructed using Python code then this is straightforward, but a problem configuration dict is constructed using Python code then this is
arises when the configuration is provided via a text file (e.g. JSON, YAML). straightforward, but a problem arises when the configuration is
In a text file, there is no standard way to distinguish ``sys.stderr`` from provided via a text file (e.g. JSON, YAML). In a text file, there is
the literal string ``'sys.stderr'``. To facilitate this distinction, the no standard way to distinguish ``sys.stderr`` from the literal string
configuration system will look for certain special prefixes in string values ``'sys.stderr'``. To facilitate this distinction, the configuration
and treat them specially. For example, if the literal string system will look for certain special prefixes in string values and
``'ext://sys.stderr'`` is provided as a value in the configuration, then the treat them specially. For example, if the literal string
``ext://`` will be stripped off and the remainder of the value processed using ``'ext://sys.stderr'`` is provided as a value in the configuration,
normal import mechanisms. then the ``ext://`` will be stripped off and the remainder of the
value processed using normal import mechanisms.
The handling of such prefixes will be done in a way analogous to protocol The handling of such prefixes will be done in a way analogous to
handling: there will be a generic mechanism to look for prefixes which match protocol handling: there will be a generic mechanism to look for
the regular expression ``^(?P<prefix>[a-z]+)://(?P<suffix>.*)$`` whereby, if prefixes which match the regular expression
the ``prefix`` is recognised, the ``suffix`` is processed in a ``^(?P<prefix>[a-z]+)://(?P<suffix>.*)$`` whereby, if the ``prefix``
prefix-dependent manner and the result of the processing replaces the string is recognised, the ``suffix`` is processed in a prefix-dependent
value. If the prefix is not recognised, then the string value will be left manner and the result of the processing replaces the string value. If
the prefix is not recognised, then the string value will be left
as-is. as-is.
The implementation will provide for a set of standard prefixes such as The implementation will provide for a set of standard prefixes such as
``ext://`` but it will be possible to disable the mechanism completely or ``ext://`` but it will be possible to disable the mechanism completely
provide additional or different prefixes for special handling. or provide additional or different prefixes for special handling.
Dictionary Schema - Detail Dictionary Schema - Detail
-------------------------- --------------------------
The dictionary passed to ``dictConfig()`` must contain the following keys: The dictionary passed to ``dictConfig()`` must contain the following
keys:
* `version` - to be set to an integer value representing the schema * `version` - to be set to an integer value representing the schema
version. The only valid value at present is 1, but having this key allows version. The only valid value at present is 1, but having this key
the schema to evolve while still preserving backwards compatibility. allows the schema to evolve while still preserving backwards
compatibility.
All other keys are optional, but if present they will be interpreted as described All other keys are optional, but if present they will be interpreted
below. In all cases below where a 'configuring dict' is mentioned, it will be as described below. In all cases below where a 'configuring dict' is
checked for the special ``'()'`` key to see if a custom instantiation is mentioned, it will be checked for the special ``'()'`` key to see if a
required. If so, the mechanism described above is used to instantiate; custom instantiation is required. If so, the mechanism described
otherwise, the context is used to determine how to instantiate. above is used to instantiate; otherwise, the context is used to
determine how to instantiate.
* `formatters` - the corresponding value will be a dict in which each key is * `formatters` - the corresponding value will be a dict in which each
a formatter id and each value is a dict describing how to configure the key is a formatter id and each value is a dict describing how to
corresponding Formatter instance. configure the corresponding Formatter instance.
The configuring dict is searched for keys ``format`` and ``datefmt`` (with The configuring dict is searched for keys ``format`` and ``datefmt``
defaults of ``None``) and these are used to construct a (with defaults of ``None``) and these are used to construct a
``logging.Formatter`` instance. ``logging.Formatter`` instance.
* `filters` - the corresponding value will be a dict in which each key is * `filters` - the corresponding value will be a dict in which each key
a filter id and each value is a dict describing how to configure the is a filter id and each value is a dict describing how to configure
corresponding Filter instance. the corresponding Filter instance.
The configuring dict is searched for key ``name`` (defaulting to the empty The configuring dict is searched for key ``name`` (defaulting to the
string) and this is used to construct a ``logging.Filter`` instance. empty string) and this is used to construct a ``logging.Filter``
instance.
* `handlers` - the corresponding value will be a dict in which each key is * `handlers` - the corresponding value will be a dict in which each
a handler id and each value is a dict describing how to configure the key is a handler id and each value is a dict describing how to
corresponding Handler instance. configure the corresponding Handler instance.
The configuring dict is searched for the following keys: The configuring dict is searched for the following keys:
* ``class`` (mandatory). This is the fully qualified name of the handler * ``class`` (mandatory). This is the fully qualified name of the
class. handler class.
* ``level`` (optional). The level of the handler. * ``level`` (optional). The level of the handler.
* ``formatter`` (optional). The id of the formatter for this handler. * ``formatter`` (optional). The id of the formatter for this
handler.
* ``filters`` (optional). A list of ids of the filters for this handler. * ``filters`` (optional). A list of ids of the filters for this
handler.
All *other* keys are passed through as keyword arguments to the handler's All *other* keys are passed through as keyword arguments to the
constructor. For example, given the snippet:: handler's constructor. For example, given the snippet::
handlers: handlers:
console: console:
class : logging.StreamHandler class : logging.StreamHandler
formatter: brief formatter: brief
level : INFO level : INFO
filters: [allow_foo] filters: [allow_foo]
stream : ext://sys.stdout stream : ext://sys.stdout
file: file:
class : logging.handlers.RotatingFileHandler class : logging.handlers.RotatingFileHandler
formatter: precise formatter: precise
filename: logconfig.log filename: logconfig.log
maxBytes: 1024 maxBytes: 1024
backupCount: 3 backupCount: 3
the handler with id ``console`` is instantiated as a the handler with id ``console`` is instantiated as a
``logging.StreamHandler``, using ``sys.stdout`` as the underlying stream. ``logging.StreamHandler``, using ``sys.stdout`` as the underlying
The handler with id ``file`` is instantiated as a stream. The handler with id ``file`` is instantiated as a
``logging.handlers.RotatingFileHandler`` with the keyword arguments ``logging.handlers.RotatingFileHandler`` with the keyword arguments
``filename="logconfig.log", maxBytes=1024, backupCount=3``. ``filename="logconfig.log", maxBytes=1024, backupCount=3``.
* `loggers` - the corresponding value will be a dict in which each key is * `loggers` - the corresponding value will be a dict in which each key
a logger name and each value is a dict describing how to configure the is a logger name and each value is a dict describing how to
corresponding Logger instance. configure the corresponding Logger instance.
The configuring dict is searched for the following keys: The configuring dict is searched for the following keys:
@ -330,31 +356,34 @@ otherwise, the context is used to determine how to instantiate.
* ``propagate`` (optional). The propagation setting of the logger. * ``propagate`` (optional). The propagation setting of the logger.
* ``filters`` (optional). A list of ids of the filters for this logger. * ``filters`` (optional). A list of ids of the filters for this
logger.
* ``handlers`` (optional). A list of ids of the handlers for this logger. * ``handlers`` (optional). A list of ids of the handlers for this
logger.
The specified loggers will be configured according to the level, The specified loggers will be configured according to the level,
propagation, filters and handlers specified. propagation, filters and handlers specified.
* `root` - this will be the configuration for the root logger. Processing of * `root` - this will be the configuration for the root logger.
the configuration will be as for any logger, except that the ``propagate`` Processing of the configuration will be as for any logger, except
setting will not be applicable. that the ``propagate`` setting will not be applicable.
* `incremental` - whether the configuration is to be interpreted as * `incremental` - whether the configuration is to be interpreted as
incremental to the existing configuration. This value defaults to False, incremental to the existing configuration. This value defaults to
which means that the specified configuration replaces the existing ``False``, which means that the specified configuration replaces the
configuration with the same semantics as used by the existing existing configuration with the same semantics as used by the
``fileConfig()`` API. existing ``fileConfig()`` API.
If the specified value is ``True``, the configuration is processed
as described in the section on `Incremental Configuration`_, below.
If the specified value is True, the configuration is processed as described
in the section on "Incremental Configuration", below.
A Working Example A Working Example
----------------- -----------------
The following is an actual working configuration in YAML format (except that The following is an actual working configuration in YAML format
the email addresses are bogus):: (except that the email addresses are bogus)::
formatters: formatters:
brief: brief:
@ -404,45 +433,57 @@ the email addresses are bogus)::
level : DEBUG level : DEBUG
handlers : [console, file] handlers : [console, file]
Incremental Configuration Incremental Configuration
========================= =========================
It is difficult to provide complete flexibility for incremental configuration. It is difficult to provide complete flexibility for incremental
For example, because objects such as handlers, filters and formatters are configuration. For example, because objects such as handlers, filters
anonymous, once a configuration is set up, it is not possible to refer to such and formatters are anonymous, once a configuration is set up, it is
anonymous objects when augmenting a configuration. For example, if an initial not possible to refer to such anonymous objects when augmenting a
call is made to configure the system where logger ``foo`` has a handler with configuration. For example, if an initial call is made to configure
id ``console`` attached, then a subsequent call to configure a logger ``bar`` the system where logger ``foo`` has a handler with id ``console``
with id ``console`` would create a new handler instance, as the id ``console`` attached, then a subsequent call to configure a logger ``bar`` with id
``console`` would create a new handler instance, as the id ``console``
from the first call isn't kept. from the first call isn't kept.
Furthermore, there is not a compelling case for arbitrarily altering the Furthermore, there is not a compelling case for arbitrarily altering
object graph of loggers, handlers, filters, formatters at run-time, once a the object graph of loggers, handlers, filters, formatters at
configuration is set up; the verbosity of loggers can be controlled just by run-time, once a configuration is set up; the verbosity of loggers can
setting levels (and perhaps propagation flags). be controlled just by setting levels (and perhaps propagation flags).
Thus, when the ``incremental`` key of a configuration dict is present
and is ``True``, the system will ignore the ``formatters``,
``filters``, ``handlers`` entries completely, and process only the
``level`` and ``propagate`` settings in the ``loggers`` and ``root``
entries.
Thus, when the ``incremental`` key of a configuration dict is present and
is ``True``, the system will ignore the ``formatters``, ``filters``,
``handlers`` entries completely, and process only the ``level`` and
``propagate`` settings in the ``loggers`` and ``root`` entries.
Configuration Errors Configuration Errors
==================== ====================
If an error is encountered during configuration, the system will raise a If an error is encountered during configuration, the system will raise
``ValueError`` or a ``TypeError`` with a suitably descriptive message. The a ``ValueError`` or a ``TypeError`` with a suitably descriptive
following is a (possibly incomplete) list of conditions which will raise an message. The following is a (possibly incomplete) list of conditions
error: which will raise an error:
* A ``level`` which is not a string or which is a string not corresponding to * A ``level`` which is not a string or which is a string not
an actual logging level corresponding to an actual logging level
* A ``propagate`` value which is not a Boolean * A ``propagate`` value which is not a boolean
* An id which does not have a corresponding destination * An id which does not have a corresponding destination
* An invalid logger name * An invalid logger name
References
==========
.. [1] PEP 8, Style Guide for Python Code, van Rossum, Warsaw
(http://www.python.org/dev/peps/pep-0008)
Copyright Copyright
========= =========
@ -458,4 +499,3 @@ This document has been placed in the public domain.
fill-column: 70 fill-column: 70
coding: utf-8 coding: utf-8
End: End: