Each Answer to this Q is separated by one/two green lines.
How do you gracefully handle failed future feature imports? If a user is running using Python 2.5 and the first statement in my module is:
from __future__ import print_function
Compiling this module for Python 2.5 will fail with a:
File "__init__.py", line 1 from __future__ import print_function SyntaxError: future feature print_function is not defined
I’d like to inform the user that they need to rerun the program with Python >= 2.6 and maybe provide some instructions on how to do so. However, to quote PEP 236:
The only lines that can appear before
a future_statement are:
- The module docstring (if any).
- Blank lines.
- Other future_statements.
So I can’t do something like:
import __future__ if hasattr(__future__, 'print_function'): from __future__ import print_function else: raise ImportError('Python >= 2.6 is required')
Because it yields:
File "__init__.py", line 4 from __future__ import print_function SyntaxError: from __future__ imports must occur at the beginning of the file
This snippet from the PEP seems to give hope of doing it inline:
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
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.
“I’d like to inform the user that they need to rerun the program with Python >= 2.6 and maybe provide some instructions on how to do so.”
Isn’t that what a README file is for?
Here’s your alternative. A “wrapper”: a little blob of Python that checks the environment before running your target aop.
import sys major, minor, micro, releaselevel, serial = sys.version_info if (major,minor) <= (2,5): # provide advice on getting version 2.6 or higher. sys.exit(2) import app app.main()
What “direct import” means. You can examine the contents of
__future__. You’re still bound by the fact the a
from __future__ import print_function is information to the compiler, but you can poke around before importing the module that does the real work.
import __future__, sys if hasattr(__future__, 'print_function'): # Could also check sys.version_info >= __future__. print_function.optional import app app.main() else: print "instructions for upgrading"
A rather hacky but simple method I’ve used before is to exploit the fact that byte literals were introduced in Python 2.6 and use something like this near the start of the file:
b'This module needs Python 2.6 or later. Please do xxx.'
This is harmless in Python 2.6 or later, but a
SyntaxError in any earlier versions. Anyone trying to compile your file will still get an error, but they also get whatever message you want to give.
You might think that as you will have to have this line after your
from __future__ import print_function then it will be the import that generates the
SyntaxError and you won’t get to see the useful error message, but strangely enough the later error takes precedence. I suspect that as the error from the import isn’t really a syntax error in itself it isn’t raised on the first compilation pass, and so real syntax errors get raised first (but I’m guessing).
This might not meet you criteria for being ‘graceful’, and it is very Python 2.6 specific, but it is quick and easy to do.
Just put a comment on the same line with the
"from __future__ import ...", like this:
from __future__ import print_function, division # We require Python 2.6 or later
Since Python displays the line containing the error, if you try to run the module with Python 2.5 you’ll get a nice, descriptive error:
from __future__ import print_function, division # We require Python 2.6 or later SyntaxError: future feature print_function is not defined