This commit is contained in:
Daniel Holth 2013-02-11 22:50:02 -05:00
commit 5380293c4b
2 changed files with 48 additions and 28 deletions

View File

@ -2,13 +2,14 @@ PEP: 422
Title: Simple class initialisation hook
Version: $Revision$
Last-Modified: $Date$
Author: Nick Coghlan <ncoghlan@gmail.com>
Author: Nick Coghlan <ncoghlan@gmail.com>,
Daniel Urban <urban.dani+py@gmail.com>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 5-Jun-2012
Python-Version: 3.4
Post-History: 5-Jun-2012
Post-History: 5-Jun-2012, 10-Feb-2012
Abstract
@ -19,7 +20,7 @@ was created (or simply arrange to run other code after the class was created)
by setting the ``__metaclass__`` attribute in the class body. While doing
this implicitly from called code required the use of an implementation detail
(specifically, ``sys._getframes()``), it could also be done explicitly in a
fully supported fashion (for example, by passing ``locals()`` to an
fully supported fashion (for example, by passing ``locals()`` to a
function that calculated a suitable ``__metaclass__`` value)
There is currently no corresponding mechanism in Python 3 that allows the
@ -44,7 +45,7 @@ the metaclass hint that may be provided as part of the class definition.
While in many cases these two meanings end up referring to one and the same
object, there are two situations where that is not the case:
* If the metaclass hint refers to a subclass of ``type``, then it is
* If the metaclass hint refers to an instance of ``type``, then it is
considered as a candidate metaclass along with the metaclasses of all of
the parents of the class being defined. If a more appropriate metaclass is
found amongst the candidates, then it will be used instead of the one
@ -114,7 +115,7 @@ class initialisation hook as follows::
# This is invoked after the class is created, but before any
# explicit decorators are called
# The usual super() mechanisms are used to correctly support
# multiple inheritance. The decorator style invocation helps
# multiple inheritance. The class decorator style signature helps
# ensure that invoking the parent class is as simple as possible.
If present on the created object, this new hook will be called by the class
@ -125,10 +126,17 @@ returning the created class object.
If a metaclass wishes to block class initialisation for some reason, it
must arrange for ``cls.__init_class__`` to trigger ``AttributeError``.
Note, that when ``__init_class__`` is called, the name of the class is not
bound to the new class object yet. As a consequence, the two argument form
of ``super()`` cannot be used to call methods (e.g., ``super(Example, cls)``
wouldn't work in the example above). However, the zero argument form of
``super()`` works as expected, since the ``__class__`` reference is already
initialised.
This general proposal is not a new idea (it was first suggested for
inclusion in the language definition `more than 10 years ago`_, and a
similar mechanism has long been supported by `Zope's ExtensionClass`_),
but I believe the situation has changed sufficiently in recent years that
but the situation has changed sufficiently in recent years that
the idea is worth reconsidering.
@ -156,7 +164,7 @@ the metaclass hint, the actual metaclass, the class object, instances of the
class object) clearly distinct in your mind. Even when you know the rules,
it's still easy to make a mistake if you're not being extremely careful.
An earlier version of this PEP actually included such a mistake: it
stated "instance of type" for a constraint that is actually "subclass of
stated "subclass of type" for a constraint that is actually "instance of
type".
Understanding the proposed class initialisation hook only requires
@ -278,17 +286,24 @@ iterable ``__decorators__`` attributes.
Using the current version of the PEP, the scheme originally proposed could
be implemented as::
class DynamicDecorators:
class DynamicDecorators(Base):
@classmethod
def __init_class__(cls):
super(DynamicDecorators, cls).__init_class__()
# Process any classes later in the MRO
try:
mro_chain = super().__init_class__
except AttributeError:
pass
else:
mro_chain()
# Process any __decorators__ attributes in the MRO
for entry in reversed(cls.mro()):
decorators = entry.__dict__.get("__decorators__", ())
for deco in reversed(decorators):
cls = deco(cls)
Any subclasses of this type would automatically have the contents of any
``__decorators__`` attributes processed and invoked.
Any subclasses of ``DynamicDecorators`` would then automatically have the
contents of any ``__decorators__`` attributes processed and invoked.
The mechanism in the current PEP is considered superior, as many issues
to do with ordering and the same decorator being invoked multiple times
@ -325,6 +340,12 @@ relied on the ``__class__`` reference (or used the zero-argument form of
``super()``), and could not make use of those features themselves.
Reference Implementation
========================
The reference implementation has been posted to the `issue tracker`_.
References
==========
@ -337,12 +358,15 @@ References
.. _Zope's ExtensionClass:
http://docs.zope.org/zope_secrets/extensionclass.html
.. _issue tracker:
http://bugs.python.org/issue17044
Copyright
=========
This document has been placed in the public domain.
..
Local Variables:
mode: indented-text

View File

@ -8,7 +8,7 @@ Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 11-Dec-2012
Post-History: 11-Dec-2012, 28-Dec-2012
Post-History: 11-Dec-2012, 28-Dec-2012, 28-Jan-2013
Abstract
@ -94,7 +94,7 @@ Ambiguous times
When changing over from daylight savings time (DST) the clock is turned back
one hour. This means that the times during that hour happens twice, once
without DST and then once with DST. Similarly, when changing to daylight
with DST and then once without DST. Similarly, when changing to daylight
savings time, one hour goes missing.
The current time zone API can not differentiate between the two ambiguous
@ -156,10 +156,10 @@ The public API of the new time zone support contains one new class, one new
function, one new exception and four new collections. In addition to this, several
methods on the datetime object gets a new ``is_dst`` parameter.
New class ``DstTzInfo``
^^^^^^^^^^^^^^^^^^^^^^^^
New class ``dsttimezone``
^^^^^^^^^^^^^^^^^^^^^^^^^
This class provides a concrete implementation of the ``zoneinfo`` base
This class provides a concrete implementation of the ``tzinfo`` base
class that implements DST support.
@ -176,11 +176,11 @@ The function also takes an optional path to the location of the zoneinfo
database which should be used. If not specified, the function will look for
databases in the following order:
1. Use the database in ``/usr/share/zoneinfo``, if it exists.
2. Check if the `tzdata-update` module is installed, and then use that
1. Check if the `tzdata-update` module is installed, and then use that
database.
2. Use the database in ``/usr/share/zoneinfo``, if it exists.
3. Use the Python-provided database in ``Lib/tzdata``.
If no database is found an ``UnknownTimeZoneError`` or subclass thereof will
@ -206,7 +206,7 @@ The ``is_dst`` parameter can be ``False`` (default), ``True``, or ``None``.
``False`` will specify that the given datetime should be interpreted as not
happening during daylight savings time, i.e. that the time specified is after
the change from DST.
the change from DST. This is default to preserve existing behavior.
``True`` will specify that the given datetime should be interpreted as happening
during daylight savings time, i.e. that the time specified is before the change
@ -224,7 +224,7 @@ New exceptions
This exception is a subclass of KeyError and raised when giving a time
zone specification that can't be found::
>>> datetime.Timezone('Europe/New_York')
>>> datetime.zoneinfo('Europe/New_York')
Traceback (most recent call last):
...
UnknownTimeZoneError: There is no time zone called 'Europe/New_York'
@ -250,8 +250,8 @@ New exceptions
* ``NonExistentTimeError``
This exception is raised when giving a datetime specification that is ambiguous
while setting ``is_dst`` to None::
This exception is raised when giving a datetime specification for a time that due to
daylight saving does not exist, while setting ``is_dst`` to None::
>>> datetime(2012, 3, 25, 2, 0, tzinfo=zoneinfo('Europe/Stockholm'), is_dst=None)
>>>
@ -266,13 +266,9 @@ New collections
* ``all_timezones`` is the exhaustive list of the time zone names that can
be used, listed alphabethically.
* ``all_timezones_set`` is a set of the time zones in ``all_timezones``.
* ``common_timezones`` is a list of useful, current time zones, listed
alphabethically.
* ``common_timezones_set`` is a set of the time zones in ``common_timezones``.
The ``tzdata-update``-package
-----------------------------