__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