1

I'm try to create python package in 3.6 But I also want backward compatibility to 2.7 How can I write a code for 3.6 and 2.7

For example I have method called geo_point().

def geo_point(lat: float, lng: float):
    pass

This function work fine in 3.6 but not in 2.7 it show syntax error I think 2.7 not support type hinting. So I want to write another function which is supported by 2.7 and when user run my package on 2.7 it ignore all the function which is not supported

For example

 @python_version(2.7)
 def geo_point(lat, lng):
     pass

Is it possible to have both function and python decide which function is compatible?

gelonida
  • 5,327
  • 2
  • 23
  • 41
  • Python 2 is End of Life in 2 month. Is it worse the effort to still support it in a new project? – Klaus D. Oct 27 '19 at 09:56
  • Though python is end of life January 2020 I know quite some projects who will probably stay much longer python2. This is of course not a smart choice, but for huge legacy code there's sometimes no option. Still I agree with Klaus. If not really necessary, then spend time on this effort. – gelonida Oct 27 '19 at 11:09
  • correction of previous comment: "If not really necessary, then spend time on this effort" should have been "If not really necessary, then spend *don't* time on this effort" – gelonida Oct 27 '19 at 20:30

4 Answers4

1

If type hinting is the only issue you have with your code, then look at SO question Type hinting in Python 2

It says, that python3 respects also type hinting in comment lines. Python2 will ignore it and python3 respects this alternative syntax. It has specifically been designed for code that still had to be python2 compatible.

However please note, that just because the code compiles with python2, it doesn't mean it will yield the correct result.

If you have more compatibility issues I strongly propose to look at the future module (not to be mixed up with the from __future__ import xxx statement.

You can install future ( https://pypi.org/project/future/ ) with pip install future.

As you don't show any other code that causes problems I cannot advise on specific issues.

The url https://python-future.org/compatible_idioms.html shows a quite extensive list of potential python 2 / python 3 issues and how you might resolve them.

For example for opening files in python 2 with less encoding / unicode issues can be done by importing an alternative version of open with the line

from io import open

https://python-future.org/compatible_idioms.html

Addendum:

If you really are in the need of declaring one function for python3 and one function for python2 you can do:

import sys

if sys.version_info < (3, 0):
    def myfunc(a, b):  # python 2 compatible function
        bla bla bla
else:
    def myfunc(a, b):  # python 3 compatible function
        bla bla bla

However: Both function must be syntactically correct for python 2 and python 3

If you really want to have functions, which are only python2 or only python3 syntactically correct (e.g. print statement or await), then you could do something like:

import sys

if sys.version_info < (3, 0):
    from myfunc_py2 import myfunc  # the file myfunc_py2.py does not have to be python3 compatible
else:
    from myfunc_py3 import myfunc  # the file myfunc_py3.py does not have to be python2 compatible
gelonida
  • 5,327
  • 2
  • 23
  • 41
  • I added a section explaing how to add version specific code for the cases where neither `six` nor `future` can help you to write version independent code. This shouldn't happen too often, but I worked on a project with over 100.000 lines of code and had to do this once or twice. – gelonida Oct 27 '19 at 20:42
0

I doubt it's worth the trouble, but as a proof of concept: You could use a combination of a decorator and the built-in exec() function. Using exec() is a way to avoid syntax errors due to language differences.

Here's what I mean:

import sys

sys_vers_major, sys_vers_minor, sys_vers_micro = sys.version_info[:3]
sys_vers = sys_vers_major + sys_vers_minor*.1 # + sys_vers_micro*.01
print('sys_vers: %s' % sys_vers)

def python_version(vers, code1, code2):
    lcls = {}  # Dictionary to temporarily store  version of function defined.

    if sys_vers == vers:
        exec(code1, globals(), lcls)
    else:
        exec(code2, globals(), lcls)

    def decorator(func):
        return lcls[func.__name__]

    return decorator


@python_version(2.7,
"""
def geo_point(lat, lng):
    print('code1 version')
""",
"""
def geo_point(lat: float, lng: float):
    print('code2 version')
""")
def geo_point(): pass  # Needed to know name of function that's being defined.

geo_point(121, 47)  # Show which version was defined.
martineau
  • 119,623
  • 25
  • 170
  • 301
  • interesting idea, could be fun if really needed – gelonida Oct 28 '19 at 00:45
  • @gelonida: Yeah, I did it mostly to see it was even feasible. One issue I glossed-over was the comparing versions — it's actually a rather complex subject — see [How do I compare version numbers in Python?](https://stackoverflow.com/questions/11887762/how-do-i-compare-version-numbers-in-python) Anyway, if you found my answer useful or helpful please consider upvoting it. – martineau Oct 28 '19 at 00:50
  • Hmm why in this particular case not just `if sys.version_info < (3, 0)` ? But you're right. You try to make your version comparison very very generic. I'd just go with minor / major versions, that's where syntax normally breaks. – gelonida Oct 28 '19 at 01:12
  • 1
    I considered making the `vers` parameter a tuple, but it didn't seem readable enough (and comparing versions isn't really the topic). As you can see, the code in my answer is only comparing major + minor. – martineau Oct 28 '19 at 01:15
0

I think 2.7 not support type hinting

Actually, Python 2 supports type hinting, and you can write backwards-compatible code. See the answer about Python 2 type hinting on StackOverflow.

Pavel Vergeev
  • 3,060
  • 1
  • 31
  • 39
0

While the other answers emphasize type hinting, I'm thinking that the

Six package

may be of help. The project base and link to documentation is at https://pypi.org/project/six/.

Six is a Python 2 and 3 compatibility library. It provides utility functions for smoothing over the differences between the Python versions with the goal of writing Python code that is compatible on both Python versions. See the documentation for more information on what is provided.

One of the notable differences between Python 2 and Python 3 is that lack of parens in a print statement will cause a syntax error in Python 3, for example,

print "Hello, World"  # OK in Python 2, syntax error in Python 3.

Six addresses these differences.

rajah9
  • 11,645
  • 5
  • 44
  • 57
  • I used six initially and meanwhile I prefer `future` ( https://pypi.org/project/future/ ) . Both can do the job. I have the impression future is a little nicer, but as this is opinionated I suggest to look at `six` and at `future` and to choose afterwards. Both packages are intended to allow writing code, that can be executed with python2 and python3 until you're sure, that code is no more required to run with python2. In many cases at this time you just remove some imports. – gelonida Oct 27 '19 at 20:25
  • 1
    In fact for addressing the issue with the print statement you need neither `six` nor `future` you just need the line `from __future__ import print_function` at the beginning of the file. It is part of std python. However `six` / `future` adress many other issues like range / xrange, name changes of some modules, ... – gelonida Oct 27 '19 at 20:27
  • rajah9: FWIW, in some instances the `six` module uses the `exec()` trick shown in my answer — at least that's where I got the idea for using it to avoid syntax errors from. Also note that it's fairly trivial to use `print` in ways that will work in both Python 2 and 3 without needing the `import print_function`. – martineau Oct 28 '19 at 01:05