__future__ PEP, first cut, a bit out of date, will update later.
This commit is contained in:
parent
f0813dc433
commit
83e170bd5d
|
@ -0,0 +1,303 @@
|
|||
PEP: 236
|
||||
Title: Back to the __future__
|
||||
Version: $Revision$
|
||||
Author: Tim Peters <tim@digicool.com>
|
||||
Python-Version: 2.1
|
||||
Status: Active
|
||||
Type: Standards Track
|
||||
Post-History:
|
||||
|
||||
|
||||
Motivation
|
||||
|
||||
From time to time, Python makes an incompatible change to the
|
||||
advertised semantics of core language constructs, or changes their
|
||||
accidental (implementation-dependent) behavior in some way. While
|
||||
this is never done capriciously, and is always done with the aim of
|
||||
improving the language over the long term, over the short term it's
|
||||
contentious and disrupting.
|
||||
|
||||
The "Guidelines for Language Evolution" PEP [1] suggests ways to ease
|
||||
the pain, and this PEP introduces some machinery in support of that.
|
||||
|
||||
The "Statically Nested Scopes" PEP [2] is the first application, and
|
||||
will be used as an example here.
|
||||
|
||||
|
||||
Intent
|
||||
|
||||
When an incompatible change to core language syntax or semantics is
|
||||
being made:
|
||||
|
||||
1. The release C that introduces the change does not change the
|
||||
syntax or semantics by default.
|
||||
|
||||
2. A future release R is identified in which the new syntax or semantics
|
||||
will be enforced.
|
||||
|
||||
3. The mechanisms described in the "Warning Framework" PEP [3] are used
|
||||
to generate warnings, whenever possible, about constructs or
|
||||
operations whose meaning may[4] change in release R.
|
||||
|
||||
4. The new future_statement (see below) can be explicitly included in a
|
||||
module M to request that the code in module M use the new syntax or
|
||||
semantics in the current release C.
|
||||
|
||||
So old code continues to work by default, for at least one release,
|
||||
although it may start to generate new warning messages. Migration to
|
||||
the new syntax or semantics can proceed during that time, using the
|
||||
future_statement to make modules containing it act as if the new syntax
|
||||
or semantics were already being enforced.
|
||||
|
||||
|
||||
Syntax
|
||||
|
||||
A future_statement is simply a from/import statement using the reserved
|
||||
module name __future__:
|
||||
|
||||
future_statement: "from" "__future__" "import" feature ["as" name]
|
||||
("," feature ["as" name])*
|
||||
|
||||
feature: identifier
|
||||
|
||||
In addition, all future_statments must appear near the top of the
|
||||
module. The only lines that can appear before a future_statement are:
|
||||
|
||||
+ The module docstring (if any).
|
||||
+ Comments.
|
||||
+ Blank lines.
|
||||
+ Other future_statements.
|
||||
|
||||
Example:
|
||||
"""This is a module docstring."""
|
||||
|
||||
# This is a comment, preceded by a blank line and followed by
|
||||
# a future_statement.
|
||||
from __future__ import nested_scopes
|
||||
|
||||
from math import sin
|
||||
from __future__ import alabaster_weenoblobs # compile-time error!
|
||||
# That was an error because preceded by a non-future_statement.
|
||||
|
||||
|
||||
Semantics
|
||||
|
||||
A future_statement is recognized and treated specially at compile time:
|
||||
changes to the semantics of core constructs are often implemented by
|
||||
generating different code. It may even be the case that a new feature
|
||||
introduces new incompatible syntax (such as a new reserved word), in
|
||||
which case the compiler may need to parse the module differently. Such
|
||||
decisions cannot be pushed off until runtime.
|
||||
|
||||
For any given release, the compiler knows which feature names have been
|
||||
defined, and raises a compile-time error if a future_statement contains
|
||||
a feature not known to it[5].
|
||||
|
||||
The direct runtime semantics are the same as for any import statement:
|
||||
there is a standard module __future__.py, described later, and it will
|
||||
be imported in the usual way at the time the future_statement is
|
||||
executed.
|
||||
|
||||
The *interesting* runtime semantics depend on the feature(s) "imported"
|
||||
by the future_statement(s) appearing in the module.
|
||||
|
||||
Since a module M containing a future_statement naming feature F
|
||||
explicitly requests that the current release act like a future release
|
||||
with respect to F, any code interpreted dynamically from an eval, exec
|
||||
or execfile executed by M will also use the new syntax or semantics
|
||||
associated with F.
|
||||
|
||||
A future_statement appearing "near the top" (see Syntax above) of
|
||||
code interpreted dynamically by an exec or execfile applies to the code
|
||||
block executed by the exec or execfile, but has no further effect on
|
||||
the module that executed the exec or execfile.
|
||||
|
||||
Note that there is nothing special about the statement:
|
||||
|
||||
import __future__ [as name]
|
||||
|
||||
That is not a future_statement; it's an ordinary import statement, with
|
||||
no special syntax restrictions or special semantics.
|
||||
|
||||
Interactive shells may pose special problems. The intent is that a
|
||||
future_statement typed at an interactive shell prompt affect all code
|
||||
typed to that shell for the remaining life of the shell session. It's
|
||||
not clear how to achieve that.
|
||||
|
||||
|
||||
Example
|
||||
|
||||
Consider this code, in file scope.py:
|
||||
|
||||
x = 42
|
||||
def f():
|
||||
x = 666
|
||||
def g():
|
||||
print "x is", x
|
||||
g()
|
||||
f()
|
||||
|
||||
Under 2.0, it prints:
|
||||
|
||||
x is 42
|
||||
|
||||
Nested scopes[2] are being introduced in 2.1. But under 2.1, it still
|
||||
prints
|
||||
|
||||
x is 42
|
||||
|
||||
and also generates a warning.
|
||||
|
||||
In 2.2, and also in 2.1 *if* "from __future__ import nested_scopes" is
|
||||
included at the top of scope.py, it prints
|
||||
|
||||
x is 666
|
||||
|
||||
|
||||
Standard Module __future__.py
|
||||
|
||||
Lib/__future__.py is a real module, and serves three purposes:
|
||||
|
||||
1. To avoid confusing existing tools that analyze import statements and
|
||||
expect to find the modules they're importing.
|
||||
|
||||
2. To ensure that future_statements run under releases prior to 2.1
|
||||
at least yield runtime exceptions (the import of __future__ will
|
||||
fail, because there was no module of that name prior to 2.1).
|
||||
|
||||
3. To document when incompatible changes were introduced, and when they
|
||||
will be-- or were --made mandatory. This is a form of executable
|
||||
documentation, and can be inspected programatically via importing
|
||||
__future__ and examining its contents.
|
||||
|
||||
Each statment in __future__.py is of the form:
|
||||
|
||||
FeatureName = ReleaseInfo
|
||||
|
||||
ReleaseInfo is a pair of the form:
|
||||
|
||||
(OptionalRelease, MandatoryRelease)
|
||||
|
||||
where, normally, OptionalRelease < MandatoryRelease, and both are
|
||||
5-tuples of the same form as sys.version_info:
|
||||
|
||||
(PY_MAJOR_VERSION, # the 2 in 2.1.0a3; an int
|
||||
PY_MINOR_VERSION, # the 1; an int
|
||||
PY_MICRO_VERSION, # the 0; an int
|
||||
PY_RELEASE_LEVEL, # "alpha", "beta", "candidate" or "final"; string
|
||||
PY_RELEASE_SERIAL # the 3; an int
|
||||
)
|
||||
|
||||
OptionalRelease records the first release in which
|
||||
|
||||
from __future__ import FeatureName
|
||||
|
||||
was accepted.
|
||||
|
||||
In the case of MandatoryReleases that have not yet occurred,
|
||||
MandatoryRelease predicts the release in which the feature will become
|
||||
part of the language.
|
||||
|
||||
Else MandatoryRelease records when the feature became part of the
|
||||
language; in releases at or after that, modules no longer need
|
||||
|
||||
from __future__ import FeatureName
|
||||
|
||||
to use the feature in question, but may continue to use such imports.
|
||||
|
||||
MandatoryRelease may also be None, meaning that a planned feature got
|
||||
dropped.
|
||||
|
||||
No line will ever be deleted from __future__.py.
|
||||
|
||||
Example line:
|
||||
|
||||
nested_scopes = (2, 1, 0, "beta", 1), (2, 2, 0, "final", 0)
|
||||
|
||||
This means that
|
||||
|
||||
from __future__ import nested_scopes
|
||||
|
||||
will work in all releases at or after 2.1b1, and that nested_scopes are
|
||||
intended to be enforced starting in release 2.2.
|
||||
|
||||
|
||||
Questions and Answers
|
||||
|
||||
Q: What about a "from __past__" version, to get back *old* behavior?
|
||||
|
||||
A: Outside the scope of this PEP. Seems unlikely to the author,
|
||||
though. Write a PEP if you want to pursue it.
|
||||
|
||||
Q: What about incompatibilites due to changes in the Python virtual
|
||||
machine?
|
||||
|
||||
A: Outside the scope of this PEP, although PEP 5[1] suggests a grace
|
||||
period there too, and the future_statement may also have a role to
|
||||
play there.
|
||||
|
||||
Q: What about incompatibilites due to changes in Python's C API?
|
||||
|
||||
A: Outside the scope of this PEP.
|
||||
|
||||
Q: I want to wrap future_statements in try/except blocks, so I can
|
||||
use different code depending on which version of Python I'm running.
|
||||
Why can't I?
|
||||
|
||||
A: Sorry! try/except is a runtime feature; future_statements are
|
||||
primarily compile-time gimmicks, and your try/except happens long
|
||||
after the compiler is done. That is, by the time you do
|
||||
try/except, the semantics in effect for the module are already a
|
||||
done deal. Since the try/except wouldn't accomplish what it
|
||||
*looks* like it should accomplish, it's simply not allowed. We
|
||||
also want to keep these special statements very easy to find and to
|
||||
recognize.
|
||||
|
||||
Note that you *can* import __future__ directly, and use the
|
||||
information in it, along with sys.version_info, to figure out where
|
||||
the release you're running under stands in relation to a given
|
||||
feature's status.
|
||||
|
||||
Q: Going back to the nested_scopes example, what if release 2.2 comes
|
||||
along and I still haven't changed my code? How can I keep the 2.1
|
||||
behavior then?
|
||||
|
||||
A: By continuing to use 2.1, and not moving to 2.2 until you do change
|
||||
your code. The purpose of future_statement is to make life easier
|
||||
for people who keep keep current with the latest release in a timely
|
||||
fashion. We don't hate you if you don't, but your problems are
|
||||
much harder to solve, and somebody with those problems will need to
|
||||
write a PEP addressing them. future_statement is aimed at a
|
||||
different audience.
|
||||
|
||||
|
||||
Copyright
|
||||
|
||||
This document has been placed in the public domain.
|
||||
|
||||
|
||||
References and Footnotes
|
||||
|
||||
[1] http://python.sourceforge.net/peps/pep-0005.html
|
||||
|
||||
[2] http://python.sourceforge.net/peps/pep-0227.html
|
||||
|
||||
[3] http://python.sourceforge.net/peps/pep-0230.html
|
||||
|
||||
[4] Note that this is "may" and not "will": better safe than sorry. Of
|
||||
course spurious warnings won't be generated when avoidable with
|
||||
reasonable cost.
|
||||
|
||||
[5] This ensures that a future_statement run under a release prior to
|
||||
the first one in which a given feature is known (but >= 2.1) will
|
||||
raise a compile-time error rather than silently do a wrong thing.
|
||||
If transported to a release prior to 2.1, a runtime error will be
|
||||
raised because of the failure to import __future__ (no such module
|
||||
existed in the standard distribution before the 2.1 release, and
|
||||
the double underscores make it a reserved name).
|
||||
|
||||
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
End:
|
Loading…
Reference in New Issue