Renumber 344 -> 3134 and 367 -> 3135.
This commit is contained in:
parent
03d35ea623
commit
d5deae29b7
13
pep-0000.txt
13
pep-0000.txt
|
@ -91,18 +91,18 @@ Index by Category
|
|||
S 286 Enhanced Argument Tuples von Loewis
|
||||
S 335 Overloadable Boolean Operators Ewing
|
||||
S 337 Logging Usage in the Standard Library Dubner
|
||||
S 344 Exception Chaining and Embedded Tracebacks Yee
|
||||
S 345 Metadata for Python Software Packages 1.2 Jones
|
||||
S 362 Function Signature Object Cannon, Seo
|
||||
S 364 Transitioning to the Py3K Standard Library Warsaw
|
||||
S 365 Adding the pkg_resources module Eby
|
||||
S 366 Main module explicit relative imports Coghlan
|
||||
S 367 New Super Spealman
|
||||
S 3101 Advanced String Formatting Talin
|
||||
S 3108 Standard Library Reorganization Cannon
|
||||
S 3116 New I/O Stutzbach, Verdone, GvR
|
||||
S 3118 Revising the buffer protocol Oliphant, Banks
|
||||
S 3124 Overloading, Generic Functions, Interfaces Eby
|
||||
S 3134 Exception Chaining and Embedded Tracebacks Yee
|
||||
S 3135 New Super Spealman, Delaney
|
||||
S 3141 A Type Hierarchy for Numbers Yasskin
|
||||
|
||||
Finished PEPs (done, implemented in Subversion)
|
||||
|
@ -251,6 +251,7 @@ Index by Category
|
|||
SW 334 Simple Coroutines via SuspendIteration Evans
|
||||
SR 336 Make None Callable McClelland
|
||||
SR 340 Anonymous Block Statements GvR
|
||||
SR 344 Exception Chaining and Embedded Tracebacks Yee
|
||||
SW 346 User Defined ("with") Statements Coghlan
|
||||
SR 348 Exception Reorganization for Python 3.0 Cannon
|
||||
SD 349 Allow str() to return unicode strings Schemenauer
|
||||
|
@ -260,6 +261,7 @@ Index by Category
|
|||
SR 355 Path - Object oriented filesystem paths Lindqvist
|
||||
SW 359 The "make" Statement Bethard
|
||||
SR 363 Syntax For Dynamic Attribute Access North
|
||||
SR 367 New Super Spealman, Delaney
|
||||
SR 666 Reject Foolish Indentation Creighton
|
||||
SR 754 IEEE 754 Floating Point Special Values Warnes
|
||||
SR 3103 A Switch/Case Statement GvR
|
||||
|
@ -440,7 +442,7 @@ Numerical Index
|
|||
SF 341 Unifying try-except and try-finally Brandl
|
||||
SF 342 Coroutines via Enhanced Generators GvR, Eby
|
||||
SF 343 Anonymous Block Redux and Generator Enhancements GvR
|
||||
S 344 Exception Chaining and Embedded Tracebacks Yee
|
||||
SR 344 Exception Chaining and Embedded Tracebacks Yee
|
||||
S 345 Metadata for Python Software Packages 1.2 Jones
|
||||
SW 346 User Defined ("with") Statements Coghlan
|
||||
PA 347 Migrating the Python CVS to Subversion von Löwis
|
||||
|
@ -463,7 +465,7 @@ Numerical Index
|
|||
S 364 Transitioning to the Py3K Standard Library Warsaw
|
||||
S 365 Adding the pkg_resources module Eby
|
||||
S 366 Main module explicit relative imports Coghlan
|
||||
S 367 New Super Spealman
|
||||
SR 367 New Super Spealman, Delaney
|
||||
SR 666 Reject Foolish Indentation Creighton
|
||||
SR 754 IEEE 754 Floating Point Special Values Warnes
|
||||
P 3000 Python 3000 GvR
|
||||
|
@ -504,6 +506,8 @@ Numerical Index
|
|||
SA 3131 Supporting Non-ASCII Identifiers von Löwis
|
||||
SF 3132 Extended Iterable Unpacking Brandl
|
||||
SR 3133 Introducing Roles Winter
|
||||
S 3134 Exception Chaining and Embedded Tracebacks Yee
|
||||
S 3135 New Super Spealman, Delaney
|
||||
S 3141 A Type Hierarchy for Numbers Yasskin
|
||||
|
||||
|
||||
|
@ -544,6 +548,7 @@ Owners
|
|||
Cole, Dave djc@object-craft.com.au
|
||||
Craig, Christopher python-pep@ccraig.org
|
||||
Creighton, Laura lac@strakt.com
|
||||
Delaney, Tim timothy.c.delaney@gmail.com
|
||||
Dörwald, Walter
|
||||
Drake, Fred fdrake@acm.org
|
||||
Dubner, Michael P. dubnerm@mindless.com
|
||||
|
|
|
@ -3,13 +3,19 @@ Title: Exception Chaining and Embedded Tracebacks
|
|||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Ka-Ping Yee
|
||||
Status: Active
|
||||
Status: Replaced
|
||||
Type: Standards Track
|
||||
Content-Type: text/plain
|
||||
Created: 12-May-2005
|
||||
Python-Version: 2.5
|
||||
|
||||
|
||||
Numbering Note
|
||||
|
||||
This PEP has been renumbered to PEP 3134. The text below is the
|
||||
last version submitted under the old number.
|
||||
|
||||
|
||||
Abstract
|
||||
|
||||
This PEP proposes three standard attributes on exception instances:
|
||||
|
|
|
@ -4,13 +4,19 @@ Version: $Revision$
|
|||
Last-Modified: $Date$
|
||||
Author: Calvin Spealman <ironfroggy@gmail.com>,
|
||||
Tim Delaney <timothy.c.delaney@gmail.com>
|
||||
Status: Draft
|
||||
Status: Replaced
|
||||
Type: Standards Track
|
||||
Content-Type: text/x-rst
|
||||
Created: 28-Apr-2007
|
||||
Python-Version: 2.6
|
||||
Post-History: 28-Apr-2007, 29-Apr-2007 (1), 29-Apr-2007 (2), 14-May-2007
|
||||
|
||||
Numbering Note
|
||||
==============
|
||||
|
||||
This PEP has been renumbered to PEP 3135. The text below is the last
|
||||
version submitted under the old number.
|
||||
|
||||
Abstract
|
||||
========
|
||||
|
||||
|
|
|
@ -0,0 +1,544 @@
|
|||
PEP: 3134
|
||||
Title: Exception Chaining and Embedded Tracebacks
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Ka-Ping Yee
|
||||
Status: Active
|
||||
Type: Standards Track
|
||||
Content-Type: text/plain
|
||||
Created: 12-May-2005
|
||||
Python-Version: 3.0
|
||||
|
||||
|
||||
Numbering Note
|
||||
|
||||
This PEP started its life as PEP 344. Since it is now targeted
|
||||
for Python 3000, it has been moved into the 3xxx space.
|
||||
|
||||
|
||||
Abstract
|
||||
|
||||
This PEP proposes three standard attributes on exception instances:
|
||||
the '__context__' attribute for implicitly chained exceptions, the
|
||||
'__cause__' attribute for explicitly chained exceptions, and the
|
||||
'__traceback__' attribute for the traceback. A new "raise ... from"
|
||||
statement sets the '__cause__' attribute.
|
||||
|
||||
|
||||
Motivation
|
||||
|
||||
During the handling of one exception (exception A), it is possible
|
||||
that another exception (exception B) may occur. In today's Python
|
||||
(version 2.4), if this happens, exception B is propagated outward
|
||||
and exception A is lost. In order to debug the problem, it is
|
||||
useful to know about both exceptions. The '__context__' attribute
|
||||
retains this information automatically.
|
||||
|
||||
Sometimes it can be useful for an exception handler to intentionally
|
||||
re-raise an exception, either to provide extra information or to
|
||||
translate an exception to another type. The '__cause__' attribute
|
||||
provides an explicit way to record the direct cause of an exception.
|
||||
|
||||
In today's Python implementation, exceptions are composed of three
|
||||
parts: the type, the value, and the traceback. The 'sys' module,
|
||||
exposes the current exception in three parallel variables, exc_type,
|
||||
exc_value, and exc_traceback, the sys.exc_info() function returns a
|
||||
tuple of these three parts, and the 'raise' statement has a
|
||||
three-argument form accepting these three parts. Manipulating
|
||||
exceptions often requires passing these three things in parallel,
|
||||
which can be tedious and error-prone. Additionally, the 'except'
|
||||
statement can only provide access to the value, not the traceback.
|
||||
Adding the '__traceback__' attribute to exception values makes all
|
||||
the exception information accessible from a single place.
|
||||
|
||||
|
||||
History
|
||||
|
||||
Raymond Hettinger [1] raised the issue of masked exceptions on
|
||||
Python-Dev in January 2003 and proposed a PyErr_FormatAppend()
|
||||
function that C modules could use to augment the currently active
|
||||
exception with more information. Brett Cannon [2] brought up
|
||||
chained exceptions again in June 2003, prompting a long discussion.
|
||||
|
||||
Greg Ewing [3] identified the case of an exception occuring in a
|
||||
'finally' block during unwinding triggered by an original exception,
|
||||
as distinct from the case of an exception occuring in an 'except'
|
||||
block that is handling the original exception.
|
||||
|
||||
Greg Ewing [4] and Guido van Rossum [5], and probably others, have
|
||||
previously mentioned adding a traceback attribute to Exception
|
||||
instances. This is noted in PEP 3000.
|
||||
|
||||
This PEP was motivated by yet another recent Python-Dev reposting
|
||||
of the same ideas [6] [7].
|
||||
|
||||
|
||||
Rationale
|
||||
|
||||
The Python-Dev discussions revealed interest in exception chaining
|
||||
for two quite different purposes. To handle the unexpected raising
|
||||
of a secondary exception, the exception must be retained implicitly.
|
||||
To support intentional translation of an exception, there must be a
|
||||
way to chain exceptions explicitly. This PEP addresses both.
|
||||
|
||||
Several attribute names for chained exceptions have been suggested
|
||||
on Python-Dev [2], including 'cause', 'antecedent', 'reason',
|
||||
'original', 'chain', 'chainedexc', 'exc_chain', 'excprev',
|
||||
'previous', and 'precursor'. For an explicitly chained exception,
|
||||
this PEP suggests '__cause__' because of its specific meaning. For
|
||||
an implicitly chained exception, this PEP proposes the name
|
||||
'__context__' because the intended meaning is more specific than
|
||||
temporal precedence but less specific than causation: an exception
|
||||
occurs in the context of handling another exception.
|
||||
|
||||
This PEP suggests names with leading and trailing double-underscores
|
||||
for these three attributes because they are set by the Python VM.
|
||||
Only in very special cases should they be set by normal assignment.
|
||||
|
||||
This PEP handles exceptions that occur during 'except' blocks and
|
||||
'finally' blocks in the same way. Reading the traceback makes it
|
||||
clear where the exceptions occurred, so additional mechanisms for
|
||||
distinguishing the two cases would only add unnecessary complexity.
|
||||
|
||||
This PEP proposes that the outermost exception object (the one
|
||||
exposed for matching by 'except' clauses) be the most recently
|
||||
raised exception for compatibility with current behaviour.
|
||||
|
||||
This PEP proposes that tracebacks display the outermost exception
|
||||
last, because this would be consistent with the chronological order
|
||||
of tracebacks (from oldest to most recent frame) and because the
|
||||
actual thrown exception is easier to find on the last line.
|
||||
|
||||
To keep things simpler, the C API calls for setting an exception
|
||||
will not automatically set the exception's '__context__'. Guido
|
||||
van Rossum has has expressed concerns with making such changes [8].
|
||||
|
||||
As for other languages, Java and Ruby both discard the original
|
||||
exception when another exception occurs in a 'catch'/'rescue' or
|
||||
'finally'/'ensure' clause. Perl 5 lacks built-in structured
|
||||
exception handling. For Perl 6, RFC 88 [9] proposes an exception
|
||||
mechanism that implicitly retains chained exceptions in an array
|
||||
named @@. In that RFC, the most recently raised exception is
|
||||
exposed for matching, as in this PEP; also, arbitrary expressions
|
||||
(possibly involving @@) can be evaluated for exception matching.
|
||||
|
||||
Exceptions in C# contain a read-only 'InnerException' property that
|
||||
may point to another exception. Its documentation [10] says that
|
||||
"When an exception X is thrown as a direct result of a previous
|
||||
exception Y, the InnerException property of X should contain a
|
||||
reference to Y." This property is not set by the VM automatically;
|
||||
rather, all exception constructors take an optional 'innerException'
|
||||
argument to set it explicitly. The '__cause__' attribute fulfills
|
||||
the same purpose as InnerException, but this PEP proposes a new form
|
||||
of 'raise' rather than extending the constructors of all exceptions.
|
||||
C# also provides a GetBaseException method that jumps directly to
|
||||
the end of the InnerException chain; this PEP proposes no analog.
|
||||
|
||||
The reason all three of these attributes are presented together in
|
||||
one proposal is that the '__traceback__' attribute provides
|
||||
convenient access to the traceback on chained exceptions.
|
||||
|
||||
|
||||
Implicit Exception Chaining
|
||||
|
||||
Here is an example to illustrate the '__context__' attribute.
|
||||
|
||||
def compute(a, b):
|
||||
try:
|
||||
a/b
|
||||
except Exception, exc:
|
||||
log(exc)
|
||||
|
||||
def log(exc):
|
||||
file = open('logfile.txt') # oops, forgot the 'w'
|
||||
print >>file, exc
|
||||
file.close()
|
||||
|
||||
Calling compute(0, 0) causes a ZeroDivisionError. The compute()
|
||||
function catches this exception and calls log(exc), but the log()
|
||||
function also raises an exception when it tries to write to a
|
||||
file that wasn't opened for writing.
|
||||
|
||||
In today's Python, the caller of compute() gets thrown an IOError.
|
||||
The ZeroDivisionError is lost. With the proposed change, the
|
||||
instance of IOError has an additional '__context__' attribute that
|
||||
retains the ZeroDivisionError.
|
||||
|
||||
The following more elaborate example demonstrates the handling of a
|
||||
mixture of 'finally' and 'except' clauses:
|
||||
|
||||
def main(filename):
|
||||
file = open(filename) # oops, forgot the 'w'
|
||||
try:
|
||||
try:
|
||||
compute()
|
||||
except Exception, exc:
|
||||
log(file, exc)
|
||||
finally:
|
||||
file.clos() # oops, misspelled 'close'
|
||||
|
||||
def compute():
|
||||
1/0
|
||||
|
||||
def log(file, exc):
|
||||
try:
|
||||
print >>file, exc # oops, file is not writable
|
||||
except:
|
||||
display(exc)
|
||||
|
||||
def display(exc):
|
||||
print ex # oops, misspelled 'exc'
|
||||
|
||||
Calling main() with the name of an existing file will trigger four
|
||||
exceptions. The ultimate result will be an AttributeError due to
|
||||
the misspelling of 'clos', whose __context__ points to a NameError
|
||||
due to the misspelling of 'ex', whose __context__ points to an
|
||||
IOError due to the file being read-only, whose __context__ points to
|
||||
a ZeroDivisionError, whose __context__ attribute is None.
|
||||
|
||||
The proposed semantics are as follows:
|
||||
|
||||
1. Each thread has an exception context initially set to None.
|
||||
|
||||
2. Whenever an exception is raised, if the exception instance does
|
||||
not already have a '__context__' attribute, the interpreter sets
|
||||
it equal to the thread's exception context.
|
||||
|
||||
3. Immediately after an exception is raised, the thread's exception
|
||||
context is set to the exception.
|
||||
|
||||
4. Whenever the interpreter exits an 'except' block by reaching the
|
||||
end or executing a 'return', 'yield', 'continue', or 'break'
|
||||
statement, the thread's exception context is set to None.
|
||||
|
||||
|
||||
Explicit Exception Chaining
|
||||
|
||||
The '__cause__' attribute on exception objects is always initialized
|
||||
to None. It is set by a new form of the 'raise' statement:
|
||||
|
||||
raise EXCEPTION from CAUSE
|
||||
|
||||
which is equivalent to:
|
||||
|
||||
exc = EXCEPTION
|
||||
exc.__cause__ = CAUSE
|
||||
raise exc
|
||||
|
||||
In the following example, a database provides implementations for a
|
||||
few different kinds of storage, with file storage as one kind. The
|
||||
database designer wants errors to propagate as DatabaseError objects
|
||||
so that the client doesn't have to be aware of the storage-specific
|
||||
details, but doesn't want to lose the underlying error information.
|
||||
|
||||
class DatabaseError(StandardError):
|
||||
pass
|
||||
|
||||
class FileDatabase(Database):
|
||||
def __init__(self, filename):
|
||||
try:
|
||||
self.file = open(filename)
|
||||
except IOError, exc:
|
||||
raise DatabaseError('failed to open') from exc
|
||||
|
||||
If the call to open() raises an exception, the problem will be
|
||||
reported as a DatabaseError, with a __cause__ attribute that reveals
|
||||
the IOError as the original cause.
|
||||
|
||||
|
||||
Traceback Attribute
|
||||
|
||||
The following example illustrates the '__traceback__' attribute.
|
||||
|
||||
def do_logged(file, work):
|
||||
try:
|
||||
work()
|
||||
except Exception, exc:
|
||||
write_exception(file, exc)
|
||||
raise exc
|
||||
|
||||
from traceback import format_tb
|
||||
|
||||
def write_exception(file, exc):
|
||||
...
|
||||
type = exc.__class__
|
||||
message = str(exc)
|
||||
lines = format_tb(exc.__traceback__)
|
||||
file.write(... type ... message ... lines ...)
|
||||
...
|
||||
|
||||
In today's Python, the do_logged() function would have to extract
|
||||
the traceback from sys.exc_traceback or sys.exc_info()[2] and pass
|
||||
both the value and the traceback to write_exception(). With the
|
||||
proposed change, write_exception() simply gets one argument and
|
||||
obtains the exception using the '__traceback__' attribute.
|
||||
|
||||
The proposed semantics are as follows:
|
||||
|
||||
1. Whenever an exception is caught, if the exception instance does
|
||||
not already have a '__traceback__' attribute, the interpreter
|
||||
sets it to the newly caught traceback.
|
||||
|
||||
|
||||
Enhanced Reporting
|
||||
|
||||
The default exception handler will be modified to report chained
|
||||
exceptions. The chain of exceptions is traversed by following the
|
||||
'__cause__' and '__context__' attributes, with '__cause__' taking
|
||||
priority. In keeping with the chronological order of tracebacks,
|
||||
the most recently raised exception is displayed last; that is, the
|
||||
display begins with the description of the innermost exception and
|
||||
backs up the chain to the outermost exception. The tracebacks are
|
||||
formatted as usual, with one of the lines:
|
||||
|
||||
The above exception was the direct cause of the following exception:
|
||||
|
||||
or
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
between tracebacks, depending whether they are linked by __cause__
|
||||
or __context__ respectively. Here is a sketch of the procedure:
|
||||
|
||||
def print_chain(exc):
|
||||
if exc.__cause__:
|
||||
print_chain(exc.__cause__)
|
||||
print '\nThe above exception was the direct cause...'
|
||||
elif exc.__context__:
|
||||
print_chain(exc.__context__)
|
||||
print '\nDuring handling of the above exception, ...'
|
||||
print_exc(exc)
|
||||
|
||||
In the 'traceback' module, the format_exception, print_exception,
|
||||
print_exc, and print_last functions will be updated to accept an
|
||||
optional 'chain' argument, True by default. When this argument is
|
||||
True, these functions will format or display the entire chain of
|
||||
exceptions as just described. When it is False, these functions
|
||||
will format or display only the outermost exception.
|
||||
|
||||
The 'cgitb' module should also be updated to display the entire
|
||||
chain of exceptions.
|
||||
|
||||
|
||||
C API
|
||||
|
||||
The PyErr_Set* calls for setting exceptions will not set the
|
||||
'__context__' attribute on exceptions. PyErr_NormalizeException
|
||||
will always set the 'traceback' attribute to its 'tb' argument and
|
||||
the '__context__' and '__cause__' attributes to None.
|
||||
|
||||
A new API function, PyErr_SetContext(context), will help C
|
||||
programmers provide chained exception information. This function
|
||||
will first normalize the current exception so it is an instance,
|
||||
then set its '__context__' attribute. A similar API function,
|
||||
PyErr_SetCause(cause), will set the '__cause__' attribute.
|
||||
|
||||
|
||||
Compatibility
|
||||
|
||||
Chained exceptions expose the type of the most recent exception, so
|
||||
they will still match the same 'except' clauses as they do now.
|
||||
|
||||
The proposed changes should not break any code unless it sets or
|
||||
uses attributes named '__context__', '__cause__', or '__traceback__'
|
||||
on exception instances. As of 2005-05-12, the Python standard
|
||||
library contains no mention of such attributes.
|
||||
|
||||
|
||||
Open Issue: Extra Information
|
||||
|
||||
Walter Dörwald [11] expressed a desire to attach extra information
|
||||
to an exception during its upward propagation without changing its
|
||||
type. This could be a useful feature, but it is not addressed by
|
||||
this PEP. It could conceivably be addressed by a separate PEP
|
||||
establishing conventions for other informational attributes on
|
||||
exceptions.
|
||||
|
||||
|
||||
Open Issue: Suppressing Context
|
||||
|
||||
As written, this PEP makes it impossible to suppress '__context__',
|
||||
since setting exc.__context__ to None in an 'except' or 'finally'
|
||||
clause will only result in it being set again when exc is raised.
|
||||
|
||||
|
||||
Open Issue: Limiting Exception Types
|
||||
|
||||
To improve encapsulation, library implementors may want to wrap all
|
||||
implementation-level exceptions with an application-level exception.
|
||||
One could try to wrap exceptions by writing this:
|
||||
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except:
|
||||
import sys
|
||||
raise ApplicationError from sys.exc_value
|
||||
|
||||
or this:
|
||||
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except Exception, exc:
|
||||
raise ApplicationError from exc
|
||||
|
||||
but both are somewhat flawed. It would be nice to be able to name
|
||||
the current exception in a catch-all 'except' clause, but that isn't
|
||||
addressed here. Such a feature would allow something like this:
|
||||
|
||||
try:
|
||||
... implementation may raise an exception ...
|
||||
except *, exc:
|
||||
raise ApplicationError from exc
|
||||
|
||||
|
||||
Open Issue: yield
|
||||
|
||||
The exception context is lost when a 'yield' statement is executed;
|
||||
resuming the frame after the 'yield' does not restore the context.
|
||||
Addressing this problem is out of the scope of this PEP; it is not a
|
||||
new problem, as demonstrated by the following example:
|
||||
|
||||
>>> def gen():
|
||||
... try:
|
||||
... 1/0
|
||||
... except:
|
||||
... yield 3
|
||||
... raise
|
||||
...
|
||||
>>> g = gen()
|
||||
>>> g.next()
|
||||
3
|
||||
>>> g.next()
|
||||
TypeError: exceptions must be classes, instances, or strings
|
||||
(deprecated), not NoneType
|
||||
|
||||
|
||||
Open Issue: Garbage Collection
|
||||
|
||||
The strongest objection to this proposal has been that it creates
|
||||
cycles between exceptions and stack frames [12]. Collection of
|
||||
cyclic garbage (and therefore resource release) can be greatly
|
||||
delayed.
|
||||
|
||||
>>> try:
|
||||
>>> 1/0
|
||||
>>> except Exception, err:
|
||||
>>> pass
|
||||
|
||||
will introduce a cycle from err -> traceback -> stack frame -> err,
|
||||
keeping all locals in the same scope alive until the next GC happens.
|
||||
|
||||
Today, these locals would go out of scope. There is lots of code
|
||||
which assumes that "local" resources -- particularly open files -- will
|
||||
be closed quickly. If closure has to wait for the next GC, a program
|
||||
(which runs fine today) may run out of file handles.
|
||||
|
||||
Making the __traceback__ attribute a weak reference would avoid the
|
||||
problems with cyclic garbage. Unfortunately, it would make saving
|
||||
the Exception for later (as unittest does) more awkward, and it would
|
||||
not allow as much cleanup of the sys module.
|
||||
|
||||
A possible alternate solution, suggested by Adam Olsen, would be to
|
||||
instead turn the reference from the stack frame to the 'err' variable
|
||||
into a weak reference when the variable goes out of scope [13].
|
||||
|
||||
|
||||
Possible Future Compatible Changes
|
||||
|
||||
These changes are consistent with the appearance of exceptions as
|
||||
a single object rather than a triple at the interpreter level.
|
||||
|
||||
- If PEP 340 or PEP 343 is accepted, replace the three (type, value,
|
||||
traceback) arguments to __exit__ with a single exception argument.
|
||||
|
||||
- Deprecate sys.exc_type, sys.exc_value, sys.exc_traceback, and
|
||||
sys.exc_info() in favour of a single member, sys.exception.
|
||||
|
||||
- Deprecate sys.last_type, sys.last_value, and sys.last_traceback
|
||||
in favour of a single member, sys.last_exception.
|
||||
|
||||
- Deprecate the three-argument form of the 'raise' statement in
|
||||
favour of the one-argument form.
|
||||
|
||||
- Upgrade cgitb.html() to accept a single value as its first
|
||||
argument as an alternative to a (type, value, traceback) tuple.
|
||||
|
||||
|
||||
Possible Future Incompatible Changes
|
||||
|
||||
These changes might be worth considering for Python 3000.
|
||||
|
||||
- Remove sys.exc_type, sys.exc_value, sys.exc_traceback, and
|
||||
sys.exc_info().
|
||||
|
||||
- Remove sys.last_type, sys.last_value, and sys.last_traceback.
|
||||
|
||||
- Replace the three-argument sys.excepthook with a one-argument
|
||||
API, and changing the 'cgitb' module to match.
|
||||
|
||||
- Remove the three-argument form of the 'raise' statement.
|
||||
|
||||
- Upgrade traceback.print_exception to accept an 'exception'
|
||||
argument instead of the type, value, and traceback arguments.
|
||||
|
||||
|
||||
Acknowledgements
|
||||
|
||||
Brett Cannon, Greg Ewing, Guido van Rossum, Jeremy Hylton, Phillip
|
||||
J. Eby, Raymond Hettinger, Walter Dörwald, and others.
|
||||
|
||||
|
||||
References
|
||||
|
||||
[1] Raymond Hettinger, "Idea for avoiding exception masking"
|
||||
http://mail.python.org/pipermail/python-dev/2003-January/032492.html
|
||||
|
||||
[2] Brett Cannon explains chained exceptions
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036063.html
|
||||
|
||||
[3] Greg Ewing points out masking caused by exceptions during finally
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036290.html
|
||||
|
||||
[4] Greg Ewing suggests storing the traceback in the exception object
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036092.html
|
||||
|
||||
[5] Guido van Rossum mentions exceptions having a traceback attribute
|
||||
http://mail.python.org/pipermail/python-dev/2005-April/053060.html
|
||||
|
||||
[6] Ka-Ping Yee, "Tidier Exceptions"
|
||||
http://mail.python.org/pipermail/python-dev/2005-May/053671.html
|
||||
|
||||
[7] Ka-Ping Yee, "Chained Exceptions"
|
||||
http://mail.python.org/pipermail/python-dev/2005-May/053672.html
|
||||
|
||||
[8] Guido van Rossum discusses automatic chaining in PyErr_Set*
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036180.html
|
||||
|
||||
[9] Tony Olensky, "Omnibus Structured Exception/Error Handling Mechanism"
|
||||
http://dev.perl.org/perl6/rfc/88.html
|
||||
|
||||
[10] MSDN .NET Framework Library, "Exception.InnerException Property"
|
||||
http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemexceptionclassinnerexceptiontopic.asp
|
||||
|
||||
[11] Walter Dörwald suggests wrapping exceptions to add details
|
||||
http://mail.python.org/pipermail/python-dev/2003-June/036148.html
|
||||
|
||||
[12] Guido van Rossum restates the objection to cyclic trash
|
||||
http://mail.python.org/pipermail/python-3000/2007-January/005322.html
|
||||
|
||||
[13] Adam Olsen suggests using a weakref from stack frame to exception
|
||||
http://mail.python.org/pipermail/python-3000/2007-January/005363.html
|
||||
|
||||
|
||||
Copyright
|
||||
|
||||
This document has been placed in the public domain.
|
||||
|
||||
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
sentence-end-double-space: t
|
||||
fill-column: 70
|
||||
coding: utf-8
|
||||
End:
|
|
@ -0,0 +1,591 @@
|
|||
PEP: 3135
|
||||
Title: New Super
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Calvin Spealman <ironfroggy@gmail.com>,
|
||||
Tim Delaney <timothy.c.delaney@gmail.com>
|
||||
Status: Draft
|
||||
Type: Standards Track
|
||||
Content-Type: text/x-rst
|
||||
Created: 28-Apr-2007
|
||||
Python-Version: 3.0
|
||||
Post-History: 28-Apr-2007, 29-Apr-2007 (1), 29-Apr-2007 (2), 14-May-2007
|
||||
|
||||
Numbering Note
|
||||
==============
|
||||
|
||||
This PEP started its life as PEP 367. Since it is now targeted
|
||||
for Python 3000, it has been moved into the 3xxx space.
|
||||
|
||||
Abstract
|
||||
========
|
||||
|
||||
This PEP proposes syntactic sugar for use of the ``super`` type to automatically
|
||||
construct instances of the super type binding to the class that a method was
|
||||
defined in, and the instance (or class object for classmethods) that the method
|
||||
is currently acting upon.
|
||||
|
||||
The premise of the new super usage suggested is as follows::
|
||||
|
||||
super.foo(1, 2)
|
||||
|
||||
to replace the old::
|
||||
|
||||
super(Foo, self).foo(1, 2)
|
||||
|
||||
and the current ``__builtin__.super`` be aliased to ``__builtin__.__super__``
|
||||
(with ``__builtin__.super`` to be removed in Python 3.0).
|
||||
|
||||
It is further proposed that assignment to ``super`` become a ``SyntaxError``,
|
||||
similar to the behaviour of ``None``.
|
||||
|
||||
|
||||
Rationale
|
||||
=========
|
||||
|
||||
The current usage of super requires an explicit passing of both the class and
|
||||
instance it must operate from, requiring a breaking of the DRY (Don't Repeat
|
||||
Yourself) rule. This hinders any change in class name, and is often considered
|
||||
a wart by many.
|
||||
|
||||
|
||||
Specification
|
||||
=============
|
||||
|
||||
Within the specification section, some special terminology will be used to
|
||||
distinguish similar and closely related concepts. "super type" will refer to
|
||||
the actual builtin type named "super". A "super instance" is simply an instance
|
||||
of the super type, which is associated with a class and possibly with an
|
||||
instance of that class.
|
||||
|
||||
Because the new ``super`` semantics are not backwards compatible with Python
|
||||
2.5, the new semantics will require a ``__future__`` import::
|
||||
|
||||
from __future__ import new_super
|
||||
|
||||
The current ``__builtin__.super`` will be aliased to ``__builtin__.__super__``.
|
||||
This will occur regardless of whether the new ``super`` semantics are active.
|
||||
It is not possible to simply rename ``__builtin__.super``, as that would affect
|
||||
modules that do not use the new ``super`` semantics. In Python 3.0 it is
|
||||
proposed that the name ``__builtin__.super`` will be removed.
|
||||
|
||||
Replacing the old usage of super, calls to the next class in the MRO (method
|
||||
resolution order) can be made without explicitly creating a ``super``
|
||||
instance (although doing so will still be supported via ``__super__``). Every
|
||||
function will have an implicit local named ``super``. This name behaves
|
||||
identically to a normal local, including use by inner functions via a cell,
|
||||
with the following exceptions:
|
||||
|
||||
1. Assigning to the name ``super`` will raise a ``SyntaxError`` at compile time;
|
||||
|
||||
2. Calling a static method or normal function that accesses the name ``super``
|
||||
will raise a ``TypeError`` at runtime.
|
||||
|
||||
Every function that uses the name ``super``, or has an inner function that
|
||||
uses the name ``super``, will include a preamble that performs the equivalent
|
||||
of::
|
||||
|
||||
super = __builtin__.__super__(<class>, <instance>)
|
||||
|
||||
where ``<class>`` is the class that the method was defined in, and
|
||||
``<instance>`` is the first parameter of the method (normally ``self`` for
|
||||
instance methods, and ``cls`` for class methods). For static methods and normal
|
||||
functions, ``<class>`` will be ``None``, resulting in a ``TypeError`` being
|
||||
raised during the preamble.
|
||||
|
||||
Note: The relationship between ``super`` and ``__super__`` is similar to that
|
||||
between ``import`` and ``__import__``.
|
||||
|
||||
Much of this was discussed in the thread of the python-dev list, "Fixing super
|
||||
anyone?" [1]_.
|
||||
|
||||
|
||||
Open Issues
|
||||
-----------
|
||||
|
||||
|
||||
Determining the class object to use
|
||||
'''''''''''''''''''''''''''''''''''
|
||||
|
||||
The exact mechanism for associating the method with the defining class is not
|
||||
specified in this PEP, and should be chosen for maximum performance. For
|
||||
CPython, it is suggested that the class instance be held in a C-level variable
|
||||
on the function object which is bound to one of ``NULL`` (not part of a class),
|
||||
``Py_None`` (static method) or a class object (instance or class method).
|
||||
|
||||
|
||||
Should ``super`` actually become a keyword?
|
||||
'''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
With this proposal, ``super`` would become a keyword to the same extent that
|
||||
``None`` is a keyword. It is possible that further restricting the ``super``
|
||||
name may simplify implementation, however some are against the actual keyword-
|
||||
ization of super. The simplest solution is often the correct solution and the
|
||||
simplest solution may well not be adding additional keywords to the language
|
||||
when they are not needed. Still, it may solve other open issues.
|
||||
|
||||
|
||||
Closed Issues
|
||||
-------------
|
||||
|
||||
super used with __call__ attributes
|
||||
'''''''''''''''''''''''''''''''''''
|
||||
|
||||
It was considered that it might be a problem that instantiating super instances
|
||||
the classic way, because calling it would lookup the __call__ attribute and
|
||||
thus try to perform an automatic super lookup to the next class in the MRO.
|
||||
However, this was found to be false, because calling an object only looks up
|
||||
the __call__ method directly on the object's type. The following example shows
|
||||
this in action.
|
||||
|
||||
::
|
||||
|
||||
class A(object):
|
||||
def __call__(self):
|
||||
return '__call__'
|
||||
def __getattribute__(self, attr):
|
||||
if attr == '__call__':
|
||||
return lambda: '__getattribute__'
|
||||
a = A()
|
||||
assert a() == '__call__'
|
||||
assert a.__call__() == '__getattribute__'
|
||||
|
||||
In any case, with the renaming of ``__builtin__.super`` to
|
||||
``__builtin__.__super__`` this issue goes away entirely.
|
||||
|
||||
|
||||
Reference Implementation
|
||||
========================
|
||||
|
||||
It is impossible to implement the above specification entirely in Python. This
|
||||
reference implementation has the following differences to the specification:
|
||||
|
||||
1. New ``super`` semantics are implemented using bytecode hacking.
|
||||
|
||||
2. Assignment to ``super`` is not a ``SyntaxError``. Also see point #4.
|
||||
|
||||
3. Classes must either use the metaclass ``autosuper_meta`` or inherit from
|
||||
the base class ``autosuper`` to acquire the new ``super`` semantics.
|
||||
|
||||
4. ``super`` is not an implicit local variable. In particular, for inner
|
||||
functions to be able to use the super instance, there must be an assignment
|
||||
of the form ``super = super`` in the method.
|
||||
|
||||
The reference implementation assumes that it is being run on Python 2.5+.
|
||||
|
||||
::
|
||||
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# autosuper.py
|
||||
|
||||
from array import array
|
||||
import dis
|
||||
import new
|
||||
import types
|
||||
import __builtin__
|
||||
__builtin__.__super__ = __builtin__.super
|
||||
del __builtin__.super
|
||||
|
||||
# We need these for modifying bytecode
|
||||
from opcode import opmap, HAVE_ARGUMENT, EXTENDED_ARG
|
||||
|
||||
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
|
||||
LOAD_NAME = opmap['LOAD_NAME']
|
||||
LOAD_CONST = opmap['LOAD_CONST']
|
||||
LOAD_FAST = opmap['LOAD_FAST']
|
||||
LOAD_ATTR = opmap['LOAD_ATTR']
|
||||
STORE_FAST = opmap['STORE_FAST']
|
||||
LOAD_DEREF = opmap['LOAD_DEREF']
|
||||
STORE_DEREF = opmap['STORE_DEREF']
|
||||
CALL_FUNCTION = opmap['CALL_FUNCTION']
|
||||
STORE_GLOBAL = opmap['STORE_GLOBAL']
|
||||
DUP_TOP = opmap['DUP_TOP']
|
||||
POP_TOP = opmap['POP_TOP']
|
||||
NOP = opmap['NOP']
|
||||
JUMP_FORWARD = opmap['JUMP_FORWARD']
|
||||
ABSOLUTE_TARGET = dis.hasjabs
|
||||
|
||||
def _oparg(code, opcode_pos):
|
||||
return code[opcode_pos+1] + (code[opcode_pos+2] << 8)
|
||||
|
||||
def _bind_autosuper(func, cls):
|
||||
co = func.func_code
|
||||
name = func.func_name
|
||||
newcode = array('B', co.co_code)
|
||||
codelen = len(newcode)
|
||||
newconsts = list(co.co_consts)
|
||||
newvarnames = list(co.co_varnames)
|
||||
|
||||
# Check if the global 'super' keyword is already present
|
||||
try:
|
||||
sn_pos = list(co.co_names).index('super')
|
||||
except ValueError:
|
||||
sn_pos = None
|
||||
|
||||
# Check if the varname 'super' keyword is already present
|
||||
try:
|
||||
sv_pos = newvarnames.index('super')
|
||||
except ValueError:
|
||||
sv_pos = None
|
||||
|
||||
# Check if the callvar 'super' keyword is already present
|
||||
try:
|
||||
sc_pos = list(co.co_cellvars).index('super')
|
||||
except ValueError:
|
||||
sc_pos = None
|
||||
|
||||
# If 'super' isn't used anywhere in the function, we don't have anything to do
|
||||
if sn_pos is None and sv_pos is None and sc_pos is None:
|
||||
return func
|
||||
|
||||
c_pos = None
|
||||
s_pos = None
|
||||
n_pos = None
|
||||
|
||||
# Check if the 'cls_name' and 'super' objects are already in the constants
|
||||
for pos, o in enumerate(newconsts):
|
||||
if o is cls:
|
||||
c_pos = pos
|
||||
|
||||
if o is __super__:
|
||||
s_pos = pos
|
||||
|
||||
if o == name:
|
||||
n_pos = pos
|
||||
|
||||
# Add in any missing objects to constants and varnames
|
||||
if c_pos is None:
|
||||
c_pos = len(newconsts)
|
||||
newconsts.append(cls)
|
||||
|
||||
if n_pos is None:
|
||||
n_pos = len(newconsts)
|
||||
newconsts.append(name)
|
||||
|
||||
if s_pos is None:
|
||||
s_pos = len(newconsts)
|
||||
newconsts.append(__super__)
|
||||
|
||||
if sv_pos is None:
|
||||
sv_pos = len(newvarnames)
|
||||
newvarnames.append('super')
|
||||
|
||||
# This goes at the start of the function. It is:
|
||||
#
|
||||
# super = __super__(cls, self)
|
||||
#
|
||||
# If 'super' is a cell variable, we store to both the
|
||||
# local and cell variables (i.e. STORE_FAST and STORE_DEREF).
|
||||
#
|
||||
preamble = [
|
||||
LOAD_CONST, s_pos & 0xFF, s_pos >> 8,
|
||||
LOAD_CONST, c_pos & 0xFF, c_pos >> 8,
|
||||
LOAD_FAST, 0, 0,
|
||||
CALL_FUNCTION, 2, 0,
|
||||
]
|
||||
|
||||
if sc_pos is None:
|
||||
# 'super' is not a cell variable - we can just use the local variable
|
||||
preamble += [
|
||||
STORE_FAST, sv_pos & 0xFF, sv_pos >> 8,
|
||||
]
|
||||
else:
|
||||
# If 'super' is a cell variable, we need to handle LOAD_DEREF.
|
||||
preamble += [
|
||||
DUP_TOP,
|
||||
STORE_FAST, sv_pos & 0xFF, sv_pos >> 8,
|
||||
STORE_DEREF, sc_pos & 0xFF, sc_pos >> 8,
|
||||
]
|
||||
|
||||
preamble = array('B', preamble)
|
||||
|
||||
# Bytecode for loading the local 'super' variable.
|
||||
load_super = array('B', [
|
||||
LOAD_FAST, sv_pos & 0xFF, sv_pos >> 8,
|
||||
])
|
||||
|
||||
preamble_len = len(preamble)
|
||||
need_preamble = False
|
||||
i = 0
|
||||
|
||||
while i < codelen:
|
||||
opcode = newcode[i]
|
||||
need_load = False
|
||||
remove_store = False
|
||||
|
||||
if opcode == EXTENDED_ARG:
|
||||
raise TypeError("Cannot use 'super' in function with EXTENDED_ARG opcode")
|
||||
|
||||
# If the opcode is an absolute target it needs to be adjusted
|
||||
# to take into account the preamble.
|
||||
elif opcode in ABSOLUTE_TARGET:
|
||||
oparg = _oparg(newcode, i) + preamble_len
|
||||
newcode[i+1] = oparg & 0xFF
|
||||
newcode[i+2] = oparg >> 8
|
||||
|
||||
# If LOAD_GLOBAL(super) or LOAD_NAME(super) then we want to change it into
|
||||
# LOAD_FAST(super)
|
||||
elif (opcode == LOAD_GLOBAL or opcode == LOAD_NAME) and _oparg(newcode, i) == sn_pos:
|
||||
need_preamble = need_load = True
|
||||
|
||||
# If LOAD_FAST(super) then we just need to add the preamble
|
||||
elif opcode == LOAD_FAST and _oparg(newcode, i) == sv_pos:
|
||||
need_preamble = need_load = True
|
||||
|
||||
# If LOAD_DEREF(super) then we change it into LOAD_FAST(super) because
|
||||
# it's slightly faster.
|
||||
elif opcode == LOAD_DEREF and _oparg(newcode, i) == sc_pos:
|
||||
need_preamble = need_load = True
|
||||
|
||||
if need_load:
|
||||
newcode[i:i+3] = load_super
|
||||
|
||||
i += 1
|
||||
|
||||
if opcode >= HAVE_ARGUMENT:
|
||||
i += 2
|
||||
|
||||
# No changes needed - get out.
|
||||
if not need_preamble:
|
||||
return func
|
||||
|
||||
# Our preamble will have 3 things on the stack
|
||||
co_stacksize = max(3, co.co_stacksize)
|
||||
|
||||
# Conceptually, our preamble is on the `def` line.
|
||||
co_lnotab = array('B', co.co_lnotab)
|
||||
|
||||
if co_lnotab:
|
||||
co_lnotab[0] += preamble_len
|
||||
|
||||
co_lnotab = co_lnotab.tostring()
|
||||
|
||||
# Our code consists of the preamble and the modified code.
|
||||
codestr = (preamble + newcode).tostring()
|
||||
|
||||
codeobj = new.code(co.co_argcount, len(newvarnames), co_stacksize,
|
||||
co.co_flags, codestr, tuple(newconsts), co.co_names,
|
||||
tuple(newvarnames), co.co_filename, co.co_name,
|
||||
co.co_firstlineno, co_lnotab, co.co_freevars,
|
||||
co.co_cellvars)
|
||||
|
||||
func.func_code = codeobj
|
||||
func.func_class = cls
|
||||
return func
|
||||
|
||||
class autosuper_meta(type):
|
||||
def __init__(cls, name, bases, clsdict):
|
||||
UnboundMethodType = types.UnboundMethodType
|
||||
|
||||
for v in vars(cls):
|
||||
o = getattr(cls, v)
|
||||
if isinstance(o, UnboundMethodType):
|
||||
_bind_autosuper(o.im_func, cls)
|
||||
|
||||
class autosuper(object):
|
||||
__metaclass__ = autosuper_meta
|
||||
|
||||
if __name__ == '__main__':
|
||||
class A(autosuper):
|
||||
def f(self):
|
||||
return 'A'
|
||||
|
||||
class B(A):
|
||||
def f(self):
|
||||
return 'B' + super.f()
|
||||
|
||||
class C(A):
|
||||
def f(self):
|
||||
def inner():
|
||||
return 'C' + super.f()
|
||||
|
||||
# Needed to put 'super' into a cell
|
||||
super = super
|
||||
return inner()
|
||||
|
||||
class D(B, C):
|
||||
def f(self, arg=None):
|
||||
var = None
|
||||
return 'D' + super.f()
|
||||
|
||||
assert D().f() == 'DBCA'
|
||||
|
||||
Disassembly of B.f and C.f reveals the different preambles used when ``super``
|
||||
is simply a local variable compared to when it is used by an inner function.
|
||||
|
||||
::
|
||||
|
||||
>>> dis.dis(B.f)
|
||||
|
||||
214 0 LOAD_CONST 4 (<type 'super'>)
|
||||
3 LOAD_CONST 2 (<class '__main__.B'>)
|
||||
6 LOAD_FAST 0 (self)
|
||||
9 CALL_FUNCTION 2
|
||||
12 STORE_FAST 1 (super)
|
||||
|
||||
215 15 LOAD_CONST 1 ('B')
|
||||
18 LOAD_FAST 1 (super)
|
||||
21 LOAD_ATTR 1 (f)
|
||||
24 CALL_FUNCTION 0
|
||||
27 BINARY_ADD
|
||||
28 RETURN_VALUE
|
||||
|
||||
::
|
||||
|
||||
>>> dis.dis(C.f)
|
||||
|
||||
218 0 LOAD_CONST 4 (<type 'super'>)
|
||||
3 LOAD_CONST 2 (<class '__main__.C'>)
|
||||
6 LOAD_FAST 0 (self)
|
||||
9 CALL_FUNCTION 2
|
||||
12 DUP_TOP
|
||||
13 STORE_FAST 1 (super)
|
||||
16 STORE_DEREF 0 (super)
|
||||
|
||||
219 19 LOAD_CLOSURE 0 (super)
|
||||
22 LOAD_CONST 1 (<code object inner at 00C160A0, file "autosuper.py", line 219>)
|
||||
25 MAKE_CLOSURE 0
|
||||
28 STORE_FAST 2 (inner)
|
||||
|
||||
223 31 LOAD_FAST 1 (super)
|
||||
34 STORE_DEREF 0 (super)
|
||||
|
||||
224 37 LOAD_FAST 2 (inner)
|
||||
40 CALL_FUNCTION 0
|
||||
43 RETURN_VALUE
|
||||
|
||||
Note that in the final implementation, the preamble would not be part of the
|
||||
bytecode of the method, but would occur immediately following unpacking of
|
||||
parameters.
|
||||
|
||||
|
||||
Alternative Proposals
|
||||
=====================
|
||||
|
||||
No Changes
|
||||
----------
|
||||
|
||||
Although its always attractive to just keep things how they are, people have
|
||||
sought a change in the usage of super calling for some time, and for good
|
||||
reason, all mentioned previously.
|
||||
|
||||
- Decoupling from the class name (which might not even be bound to the
|
||||
right class anymore!)
|
||||
- Simpler looking, cleaner super calls would be better
|
||||
|
||||
Dynamic attribute on super type
|
||||
-------------------------------
|
||||
|
||||
The proposal adds a dynamic attribute lookup to the super type, which will
|
||||
automatically determine the proper class and instance parameters. Each super
|
||||
attribute lookup identifies these parameters and performs the super lookup on
|
||||
the instance, as the current super implementation does with the explicit
|
||||
invokation of a super instance upon a class and instance.
|
||||
|
||||
This proposal relies on sys._getframe(), which is not appropriate for anything
|
||||
except a prototype implementation.
|
||||
|
||||
|
||||
super(__this_class__, self)
|
||||
---------------------------
|
||||
|
||||
This is nearly an anti-proposal, as it basically relies on the acceptance of
|
||||
the __this_class__ PEP, which proposes a special name that would always be
|
||||
bound to the class within which it is used. If that is accepted, __this_class__
|
||||
could simply be used instead of the class' name explicitly, solving the name
|
||||
binding issues [2]_.
|
||||
|
||||
self.__super__.foo(\*args)
|
||||
--------------------------
|
||||
|
||||
The __super__ attribute is mentioned in this PEP in several places, and could
|
||||
be a candidate for the complete solution, actually using it explicitly instead
|
||||
of any super usage directly. However, double-underscore names are usually an
|
||||
internal detail, and attempted to be kept out of everyday code.
|
||||
|
||||
super(self, \*args) or __super__(self, \*args)
|
||||
----------------------------------------------
|
||||
|
||||
This solution only solves the problem of the type indication, does not handle
|
||||
differently named super methods, and is explicit about the name of the
|
||||
instance. It is less flexable without being able to enacted on other method
|
||||
names, in cases where that is needed. One use case this fails is where a base-
|
||||
class has a factory classmethod and a subclass has two factory classmethods,
|
||||
both of which needing to properly make super calls to the one in the base-
|
||||
class.
|
||||
|
||||
super.foo(self, \*args)
|
||||
-----------------------
|
||||
|
||||
This variation actually eliminates the problems with locating the proper
|
||||
instance, and if any of the alternatives were pushed into the spotlight, I
|
||||
would want it to be this one.
|
||||
|
||||
super or super()
|
||||
----------------
|
||||
|
||||
This proposal leaves no room for different names, signatures, or application
|
||||
to other classes, or instances. A way to allow some similar use alongside the
|
||||
normal proposal would be favorable, encouraging good design of multiple
|
||||
inheritence trees and compatible methods.
|
||||
|
||||
super(\*p, \*\*kw)
|
||||
------------------
|
||||
|
||||
There has been the proposal that directly calling ``super(*p, **kw)`` would
|
||||
be equivalent to calling the method on the ``super`` object with the same name
|
||||
as the method currently being executed i.e. the following two methods would be
|
||||
equivalent:
|
||||
|
||||
::
|
||||
|
||||
def f(self, *p, **kw):
|
||||
super.f(*p, **kw)
|
||||
|
||||
::
|
||||
|
||||
def f(self, *p, **kw):
|
||||
super(*p, **kw)
|
||||
|
||||
There is strong sentiment for and against this, but implementation and style
|
||||
concerns are obvious. Guido has suggested that this should be excluded from
|
||||
this PEP on the principle of KISS (Keep It Simple Stupid).
|
||||
|
||||
|
||||
|
||||
History
|
||||
=======
|
||||
29-Apr-2007 - Changed title from "Super As A Keyword" to "New Super"
|
||||
- Updated much of the language and added a terminology section
|
||||
for clarification in confusing places.
|
||||
- Added reference implementation and history sections.
|
||||
|
||||
06-May-2007 - Updated by Tim Delaney to reflect discussions on the python-3000
|
||||
and python-dev mailing lists.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] Fixing super anyone?
|
||||
(http://mail.python.org/pipermail/python-3000/2007-April/006667.html)
|
||||
|
||||
.. [2] PEP 3130: Access to Module/Class/Function Currently Being Defined (this)
|
||||
(http://mail.python.org/pipermail/python-ideas/2007-April/000542.html)
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
This document has been placed in the public domain.
|
||||
|
||||
|
||||
|
||||
..
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
sentence-end-double-space: t
|
||||
fill-column: 70
|
||||
coding: utf-8
|
||||
End:
|
Loading…
Reference in New Issue