Backwards-compatible input calls in Python - python

I was wondering if anyone has suggestions for writing a backwards-compatible input() call for retrieving a filepath?
In Python 2.x, raw_input worked fine for input like /path/to/file. Using input works fine in this case for 3.x, but complains in 2.x because of the eval behavior.
One solution is to check the version of Python and, based on the version, map either input or raw_input to a new function:
if sys.version_info[0] >= 3:
get_input = input
else:
get_input = raw_input
I'm sure there is a better way to do this though. Anyone have any suggestions?

Since the Python 2.x version of input() is essentially useless, you can simply overwrite it by raw_input:
try:
input = raw_input
except NameError:
pass
In general, I would not try to aim at code that works with both, Python 2.x and 3.x, but rather write your code in a way that it works on 2.x and you get a working 3.x version by using the 2to3 script.

This code is taught in many Python education and training programs now.
Usually taught together:
from __future__ import print_function
if hasattr(__builtins__, 'raw_input'):
input = raw_input
First line: imports the Python 3.x print() function into Python 2.7 so print() behaves the same under both versions of Python. If this breaks your code due to older print "some content" calls, you can leave this line off.
Second and third lines: sets Python 2.7 raw_input() to input() so input() can be used under both versions of Python without problems. This can be used all by itself if this is the only compatibility fix you wish to include in your code.
There are more from __future__ imports available on the Python.org site for other language compatibility issues. There is also a library called "six" that can be looked up for compatibility solutions when dealing with other issues.

The way you are handling it is just fine. There are probably more similar ways using the sys module, but just in keep in mind that if you are program is doing something more than trivial with strings and files, it is better to have two versions of your program instead of having a backwards compatible python3 program.

You could import the function:
from builtins import input
Unfortunately though this method requires an external dependency via pip install future

Related

How can I avoid this Python catch-22 of version testing -vs- initial syntax checker

I have a python script that requires Python 3.8 or better to support the walrus operator and other Python3 operations. I want to test the version and output a "nice" message if the minimum version is not detected, however, I am getting the following syntax checking error if I am on Python2 and the script will not run to give the "nice" message.
File "./te_add_for_wcs.py", line 743
if (cert_count := apiResponse.get("X-Total-Count","NO_COUNT")) == 'NO_COUNT':
^
SyntaxError: invalid syntax
Is there a way to get around this, or am I out of luck when users of Python2 attempt to use my script and would need to figure out the error means wrong version?
Trying to keep this to just one script file, as I can think of ways use multiple scripts that call each other to take care of prerequisites.
I appreciate all the "comment" answers!
#MattDMo answer is what I will need to do, as I have no interest a ton of extra work (as indicated by #chepner), because #Kraigolas is absolutely correct - version 2 should not be used by anyone in production, or only used in isolated environments.
Developers like myself should assume that people will be using version 3 of Python and I should be documenting it well that scripts will only support Python3 and have logic that detects the minimum version of Python3 that is required.
Thank you again!
The problem is that := is a syntax error, raised before any portion of the script can execute. You could "hide" the use of := in a module that is imported conditionally, based on a version check.
if sys.version_info.major < 3 or sys.version_info.minor < 8:
sys.exit("Script requires Python 3.8 or later")
else:
import module.with_function_using_assignment_expression
Rather than an error, though, I would just hold off using := in the code, but adding a deprecation warning to the script to let users know that a future version of the script (with :=) will require Python 3.8 or later.
import warnings
# Or maybe DecprecationWarning; I'm not entirely clear on the
# distinction between the two.
warnings.warn("This script will require Python 3.8 or later in the near future",
warnings.FutureWarning)
...
# TODO: use an assignment expression to define cert_count directly
# in the if condition.
cert_count = apiResponse.get("X-Total-Count", "NO_COUNT")
if cert_count == "NO_COUNT":
...
After a suitable waiting period, you can go ahead and use :=, and let the users accept the consequences of not upgrading to Python 3.8.
While I understand wanting to keep this in one file, for completeness, here's an easy 2-file solution:
wrapper_main.py
import sys
if sys.version_info.major < 3: # replace with your exact version requirements
print("Please upgrade to python3!")
sys.exit(0)
import main
main.main()
main.py
def main():
a := 1
print("Entering main!")
Tested on python 2.7 that this runs and exits without syntax error.

Getting input from user without using "input" function [duplicate]

I was wondering if anyone has suggestions for writing a backwards-compatible input() call for retrieving a filepath?
In Python 2.x, raw_input worked fine for input like /path/to/file. Using input works fine in this case for 3.x, but complains in 2.x because of the eval behavior.
One solution is to check the version of Python and, based on the version, map either input or raw_input to a new function:
if sys.version_info[0] >= 3:
get_input = input
else:
get_input = raw_input
I'm sure there is a better way to do this though. Anyone have any suggestions?
Since the Python 2.x version of input() is essentially useless, you can simply overwrite it by raw_input:
try:
input = raw_input
except NameError:
pass
In general, I would not try to aim at code that works with both, Python 2.x and 3.x, but rather write your code in a way that it works on 2.x and you get a working 3.x version by using the 2to3 script.
This code is taught in many Python education and training programs now.
Usually taught together:
from __future__ import print_function
if hasattr(__builtins__, 'raw_input'):
input = raw_input
First line: imports the Python 3.x print() function into Python 2.7 so print() behaves the same under both versions of Python. If this breaks your code due to older print "some content" calls, you can leave this line off.
Second and third lines: sets Python 2.7 raw_input() to input() so input() can be used under both versions of Python without problems. This can be used all by itself if this is the only compatibility fix you wish to include in your code.
There are more from __future__ imports available on the Python.org site for other language compatibility issues. There is also a library called "six" that can be looked up for compatibility solutions when dealing with other issues.
The way you are handling it is just fine. There are probably more similar ways using the sys module, but just in keep in mind that if you are program is doing something more than trivial with strings and files, it is better to have two versions of your program instead of having a backwards compatible python3 program.
You could import the function:
from builtins import input
Unfortunately though this method requires an external dependency via pip install future

What was the rationale behind creating input() in Python 2?

In Python 2, there were two ways of getting input. raw_input() and input(), which was a wrapper around eval(raw_input()). In Python 3 however, input() replaced raw_input() and the old meaing of input() was deprecated. This is documented in What's new in Python 3:
PEP 3111: raw_input() was renamed to input(). That is, the new input() function reads a line from sys.stdin and returns it with the trailing newline stripped. It raises EOFError if the input is terminated prematurely. To get the old behavior of input(), use eval(input()).
But why exactly was input() around in Python 2 in the first place? What was the rationale for having user input that was evaluated as literal Python 2 code? This is what the Python 2 documentation had to say:
[input() is] Equivalent to eval(raw_input(prompt)).
This function does not catch user errors. If the input is not syntactically valid, a SyntaxError will be raised. Other exceptions may be raised if there is an error during evaluation.
If the readline module was loaded, then input() will use it to provide elaborate line editing and history features.
Consider using the raw_input() function for general input from users.
Notice the part in bold (which I emphasized). What exactly does this mean? I looked over the documentation for the readline module and found a few things. The only real relevant bit I found, however, was this:
Settings made using this module affect the behavior of both the interpreter’s interactive prompt and the prompts offered by the raw_input() and input() built-in functions.
Which doesn't really help explain why input() was created or needed in the first place, though.
Needless to say, using eval(any_user_input()) is very dangerous security wise, can cause debugging difficulties, and, from what I've read, is slow. So why did they create input() in Python 2 in the first place? Were the developers unaware at the time of the downfalls of input()?
References:
Is using eval in Python a bad practice?
eval(input()) in python 2to3
What’s New In Python 3.0
Python 2.7.12 documentation
First of all, probably the only person who can answer this question decisively is the BDFL.
input can be useful in programs that are meant to be used by a programmer, so that they can enter complex structures, like {'foo': 42}, or even expressions, but less so in programs intended to be used by an unskilled user.
From the SCM history we can see that both input and raw_input were present in 1990; or pre-0.9, when Python was in its infancy - back then exec was a function, and int('42') would have thrown an exception. Most notably, eval was already present as well, so one could have used eval(raw_input()) even back then to get much of the same effect.
Back then there was no Zen of Python yet, and the "only one obvious way" wasn't as much a guiding principle, so this could have been an oversight.
And both raw_input and input remained. During the history of Python, the backwards-compatibility was a guiding principle, so input was unchanged until backwards-incompatible Python 3 was released.
As for the bolded part about readline module: if you import readline, then you can use arrow keys to move cursor keys around on the input() line, and configurable bindings; if readline is not imported in the program, then no such behaviour exists.
Again, this wouldn't have been the reason for input existing in the first place; back in 1990 Python didn't support such editing at all, regardless of whether input or raw_input was used.
For what it worths, input builtin was there in a first available Python version (0.9.1), it is from 1991. I can imagine Python 2.x had it for backwards compatibility with Python 1.x, and Python 1.x had it for backwards compatibility with 0.x.
Say no to 0.x -> 1.x and 1.x -> 2.x porting issues!

Syntax error, print with sep= statement

First and foremost i am very new into Python. I am using a notebook version of Ipython called jupyter and its provided by my University, so I don't know whether this is a standard version or not.
I was busy in a slide course about Python and encountered this exercice:
This is the code I used and the syntax error I get
in the Ipython environment
I don't get why it is not working.
Thank you in advance
Olivier
print([object, ...][, sep=' '][, end='\n'][, file=sys.stdout]) is a function in Python 3.x, which has a sep keyword argument (among others).
If you are using Python 2.7 (try print "Hello!" - if it runs, you have Python 2.x), print is a statement there, which means that if you want to get the behaviour as in your slide (make print a function), you need to import print_function from __future__ module.
That way you can use print("Hi!", "Hello!", sep='\t') as in your slide.
As mentioned by #Kevin in his comment below this post, if your course uses Python 3.x, you would be better off to upgrade to this version since things like async, yield from or lzma are not available in Python 2.x.

How to write a Python 2.6+ script that gracefully fails with older Python?

I'm using the new print from Python 3.x and I observed that the following code does not compile due to the end=' '.
from __future__ import print_function
import sys
if sys.hexversion < 0x02060000:
raise Exception("py too old")
...
print("x",end=" ") # fails to compile with py24
How can I continue using the new syntax but make the script fails nicely? Is it mandatory to call another script and use only safe syntax in this one?
The easy method for Python 2.6 is just to add a line like:
b'You need Python 2.6 or later.'
at the start of the file. This exploits the fact that byte literals were introduced in 2.6 and so any earlier versions will raise a SyntaxError with whatever message you write given as the stack trace.
There are some suggestions in this question here, but it looks like it is not easily possible. You'll have to create a wrapper script.
One way is to write your module using python 2.x print statement, then when you want to port it into python 3, you use 2to3 script. I think there are scripts for 3to2 conversion as well, although they seems to be less mature than 2to3.
Either way, in biggers scripts, you should always separate domain logic and input/output; that way, all the print statements/functions are bunched up together in a single file. For logging, you should use the logging module.

Categories