Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 236 – Back to the __future__

Author:
Tim Peters <tim.peters at gmail.com>
Status:
Final
Type:
Standards Track
Created:
26-Feb-2001
Python-Version:
2.1
Post-History:
26-Feb-2001

Table of Contents

Motivation

From time to time, Python makes an incompatible change to the advertisedsemantics of core language constructs, or changes their accidental(implementation-dependent) behavior in some way. While this is never donecapriciously, and is always done with the aim of improving the language overthe long term, over the short term it’s contentious and disrupting.

PEP 5, Guidelines for Language Evolution suggests ways to ease the pain,and this PEP introduces some machinery in support of that.

PEP 227, Statically Nested Scopes is the first application, and will beused as an example here.

Intent

[Note: This is policy, and so should eventually move intoPEP 5]

When an incompatible change to core language syntax or semantics is beingmade:

  1. The release C that introduces the change does not change the syntax orsemantics by default.
  2. A future release R is identified in which the new syntax or semantics willbe enforced.
  3. The mechanisms described inPEP 230, Warning Framework are used togenerate warnings, whenever possible, about constructs or operations whosemeaning may[1] change in release R.
  4. The new future_statement (see below) can be explicitly included in a moduleM to request that the code in module M use the new syntax or semantics inthe current release C.

So old code continues to work by default, for at least one release, althoughit may start to generate new warning messages. Migration to the new syntax orsemantics can proceed during that time, using the future_statement to makemodules containing it act as if the new syntax or semantics were already beingenforced.

Note that there is no need to involve the future_statement machinery in newfeatures unless they can break existing code; fully backward- compatibleadditions can– and should –be introduced without a correspondingfuture_statement.

Syntax

A future_statement is simply a from/import statement using the reserved modulename__future__:

future_statement:"from""__future__""import"feature["as"name](","feature["as"name])*feature:identifiername:identifier

In addition, all future_statements must appear near the top of the module. Theonly 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__importnested_scopesfrommathimportsinfrom__future__importalabaster_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 bygenerating different code. It may even be the case that a new featureintroduces new incompatible syntax (such as a new reserved word), in whichcase the compiler may need to parse the module differently. Such decisionscannot be pushed off until runtime.

For any given release, the compiler knows which feature names have beendefined, and raises a compile-time error if a future_statement contains afeature not known to it[2].

The direct runtime semantics are the same as for anyimport statement:there is a standard module__future__.py, described later, and it will beimported in the usual way at the time the future_statement is executed.

Theinteresting runtime semantics depend on the specific feature(s)“imported” by the future_statement(s) appearing in the module.

Note that there is nothing special about the statement:

import__future__[asname]

That is not a future_statement; it’s an ordinary import statement, with nospecial semantics or syntax restrictions.

Example

Consider this code, in file scope.py:

x=42deff():x=666defg():print"x is",xg()f()

Under 2.0, it prints:

xis42

Nested scopes (PEP 227) are being introduced in 2.1. But under 2.1, it stillprints:

xis42

and also generates a warning.

In 2.2, and also in 2.1iffrom__future__importnested_scopes isincluded at the top ofscope.py, it prints:

xis666

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 expectto find the modules they’re importing.
  2. To ensure that future_statements run under releases prior to 2.1 at leastyield runtime exceptions (the import of__future__ will fail, becausethere was no module of that name prior to 2.1).
  3. To document when incompatible changes were introduced, and when they willbe– or were –made mandatory. This is a form of executable documentation,and can be inspected programmatically via importing__future__ andexamining its contents.

Each statement in__future__.py is of the form:

FeatureName="_Feature("OptionalRelease","MandatoryRelease")"

where, normally,OptionalRelease <MandatoryRelease, and both are5-tuples of the same form assys.version_info:

(PY_MAJOR_VERSION,# the 2 in 2.1.0a3; an intPY_MINOR_VERSION,# the 1; an intPY_MICRO_VERSION,# the 0; an intPY_RELEASE_LEVEL,# "alpha", "beta", "candidate" or "final"; stringPY_RELEASE_SERIAL# the 3; an int )

OptionalRelease records the first release in which:

from__future__importFeatureName

was accepted.

In the case ofMandatoryReleases that have not yet occurred,MandatoryRelease predicts the release in which the feature will become partof the language.

ElseMandatoryRelease records when the feature became part of the language;in releases at or after that, modules no longer need:

from__future__importFeatureName

to use the feature in question, but may continue to use such imports.

MandatoryRelease may also beNone, meaning that a planned feature gotdropped.

Instances ofclass_Feature have two corresponding methods,.getOptionalRelease() and.getMandatoryRelease().

No feature line will ever be deleted from__future__.py.

Example line:

nested_scopes=_Feature((2,1,0,"beta",1),(2,2,0,"final",0))

This means that:

from__future__importnested_scopes

will work in all releases at or after 2.1b1, and that nested_scopes areintended to be enforced starting in release 2.2.

Resolved Problem: Runtime Compilation

Several Python features can compile code during a module’s runtime:

  1. Theexec statement.
  2. Theexecfile() function.
  3. Thecompile() function.
  4. Theeval() function.
  5. Theinput() function.

Since a module M containing a future_statement naming feature F explicitlyrequests that the current release act like a future release with respect to F,any code compiled dynamically from text passed to one of these from within Mshould probably also use the new syntax or semantics associated with F. The2.1 release does behave this way.

This isn’t always desired, though. For example,doctest.testmod(M)compiles examples taken from strings in M, and those examples should use M’schoices, not necessarily the doctest module’s choices. In the 2.1 release,this isn’t possible, and no scheme has yet been suggested for working aroundthis. NOTE:PEP 264 later addressed this in a flexible way, by addingoptional arguments tocompile().

In any case, a future_statement appearing “near the top” (see Syntax above) oftext compiled dynamically by anexec,execfile() orcompile()applies to the code block generated, but has no further effect on the modulethat executes such anexec,execfile() orcompile(). This can’tbe used to affecteval() orinput(), however, because they only allowexpression input, and a future_statement is not an expression.

Resolved Problem: Native Interactive Shells

There are two ways to get an interactive shell:

  1. By invoking Python from a command line without a script argument.
  2. By invoking Python from a command line with the-i switch and with ascript argument.

An interactive shell can be seen as an extreme case of runtime compilation(see above): in effect, each statement typed at an interactive shell promptruns a new instance ofexec,compile() orexecfile(). Afuture_statement typed at an interactive shell applies to the rest of theshell session’s life, as if the future_statement had appeared at the top of amodule.

Resolved Problem: Simulated Interactive Shells

Interactive shells “built by hand” (by tools such as IDLE and the EmacsPython-mode) should behave like native interactive shells (see above).However, the machinery used internally by native interactive shells has notbeen exposed, and there isn’t a clear way for tools building their owninteractive shells to achieve the desired behavior.

NOTE:PEP 264 later addressed this, by adding intelligence to the standardcodeop.py. Simulated shells that don’t use the standard library shellhelpers can get a similar effect by exploiting the new optional arguments tocompile() added byPEP 264.

Questions and Answers

What about a “from __past__” version, to get backold behavior?

Outside the scope of this PEP. Seems unlikely to the author, though. Write aPEP if you want to pursue it.

What about incompatibilities due to changes in the Python virtual machine?

Outside the scope of this PEP, althoughPEP 5 suggests a grace periodthere too, and the future_statement may also have a role to play there.

What about incompatibilities due to changes in Python’s C API?

Outside the scope of this PEP.

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?

Sorry!try/except is a runtime feature; future_statements are primarilycompile-time gimmicks, and yourtry/except happens long after the compileris done. That is, by the time you dotry/except, the semantics in effectfor the module are already a done deal. Since thetry/except wouldn’taccomplish what itlooks like it should accomplish, it’s simply not allowed.We also want to keep these special statements very easy to find and torecognize.

Note that youcan import__future__ directly, and use the information init, along withsys.version_info, to figure out where the release you’rerunning under stands in relation to a given feature’s status.

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?

By continuing to use 2.1, and not moving to 2.2 until you do change yourcode. The purpose of future_statement is to make life easier for people whokeep current with the latest release in a timely fashion. We don’t hate youif you don’t, but your problems are much harder to solve, and somebody withthose problems will need to write a PEP addressing them. future_statement isaimed at a different audience.

Overloadingimport sucks. Why not introduce a new statement for this?

Like maybelambdalambdanested_scopes? That is, unless we introduce anew keyword, we can’t introduce an entirely new statement. But if weintroduce a new keyword, that in itself would break old code. That would betoo ironic to bear. Yes, overloadingimport does suck, but not asenergetically as the alternatives – as is, future_statements are 100%backward compatible.

Copyright

This document has been placed in the public domain.

References and Footnotes

[1]
Note that this ismay and notwill: better safe than sorry. Of coursespurious warnings won’t be generated when avoidable with reasonable cost.
[2]
This ensures that a future_statement run under a release prior to thefirst one in which a given feature is known (but >= 2.1) will raise acompile-time error rather than silently do a wrong thing. If transportedto a release prior to 2.1, a runtime error will be raised because of thefailure to import__future__ (no such module existed in the standarddistribution before the 2.1 release, and the double underscores make it areserved name).

Source:https://github.com/python/peps/blob/main/peps/pep-0236.rst

Last modified:2025-02-01 08:59:27 GMT


[8]ページ先頭

©2009-2025 Movatter.jp