Standard solution for supporting Python 2 and Python 3 - python

I'm trying to write a forward compatible program and I was wondering what the "best" way to handle the case where you need different imports.
In my specific case, I am using ConfigParser.SafeConfigParser() from Python2 which becomes configparser.ConfigParser() in Python3.
So far I have made it work either by using a try-except on the import or by using a conditional on the version of Python (using sys). Both work, but I was wondering if there was a recommended solution (maybe one I haven't tried yet).
ETA:
Thanks everyone. I used six.moves with no issues.

Use six! It's a python compatibility module that irons out the differences between python3 and python2. The documentation available here will help you with this problem as well as any other issues you're having..
Specifically for your case you can just
from six.moves import configparser
import six
if six.PY2:
ConfigParser = configparser.SafeConfigParser
else:
ConfigParser = configparser.ConfigParser
and you'll be good to go.

This pattern is pretty standard:
try:
from configparser import ConfigParser
except ImportError:
from ConfigParser import SafeConfigParser as ConfigParser

You can also do this:
import sys
if sys.version[:1] == '2':
from configparser import ConfigParser
else:
from ConfigParser import SafeConfigParser as ConfigParser
Read more Here.

Related

Import pprint by default as a debugging tool

I use pprint.pprint for every debug output I ever make, across every python file I've written is the line from pprint import pprint.
Often times once I have a stable version of that file I forget to drop the import even if all pprint statements are removed.
I would like to have pprint available in all context, all-ways on my development machine so my testing machine will fail when debug output has been left in and so I don't have to spend an alarmingly large portion of my life typing that one line.
How do I pre-import this tool system wide?
As per this answer to a similar question, you could modify /lib/site.py for every different version of Python you have on the system. Note that I highly recommend that you DON'T do this, but if necessary then you could. You should modify it in this way:
import sys
import os
import builtins
import _sitebuiltins
import pprint # <---------------------------- added
...
def main()
"""Add standard site-specific directories to the module search path.
This function is called automatically when this module is imported,
unless the python interpreter was started with the -S flag.
"""
global ENABLE_USER_SITE
builtins.pprint = pprint.pprint # <------ added
...
Beware that this can cause some really funky bugs with production code that uses pprint even though it's never been imported.

Modify the strategy importing module by hacking the sys.modules

As I read the question. I came up with an idea. But I don't know the consequences of my guesswork.
My idea is that change the import strategy by modify the sys.modules, then change the import things without modify old code.
Edit 1
A situation use the method
Hack code:
try:
import concurrent.futures
except ImportError:
concurrent.futures = wrapper_futures
Then this code can use for python2 and python3
Old code:
from concurrent.futures import Future
try:
from servicelibrary.simple import synchronous
except ImportError:
from servicelibrary.simple import alternative as synchronous
is probably a better way to do it if I understand your question properly

Python dependencies?

Is it possible to programmatically detect dependencies given a python project residing in SVN?
Here is a twist which adds some precision, and which might be useful if you find you're frequently checking dependencies of miscellaneous code:
Catches only import statements executed by the code being analyzed.
Automatically excludes all system-loaded modules, so you don't have to weed through it.
Also reports the symbols imported from each module.
Code:
import __builtin__
import collections
import sys
IN_USE = collections.defaultdict(set)
_IMPORT = __builtin__.__import__
def _myimport(name, globs=None, locs=None, fromlist=None, level=-1):
global IN_USE
if fromlist is None:
fromlist = []
IN_USE[name].update(fromlist)
return _IMPORT(name, globs, locs, fromlist, level)
# monkey-patch __import__
setattr(__builtin__, '__import__', _myimport)
# import and run the target project here and run the routine
import foobar
foobar.do_something()
# when it finishes running, dump the imports
print 'modules and symbols imported by "foobar":'
for key in sorted(IN_USE.keys()):
print key
for name in sorted(IN_USE[key]):
print ' ', name
Example foobar module:
import byteplay
import cjson
def _other():
from os import path
from sys import modules
def do_something():
import hashlib
import lxml
_other()
Output:
modules and symbols imported by "foobar":
_hashlib
array
array
byteplay
cStringIO
StringIO
cjson
dis
findlabels
foobar
hashlib
itertools
lxml
opcode
*
__all__
operator
os
path
sys
modules
types
warnings
Absolutely! If you are working from a UNIX or Linux shell, a simple combination of grep and awk would work; basically, all you want to do is search for lines containing the "import" keyword.
However, if you are working from any environment, you could just write a small Python script to do the searching for you (don't forget that strings are treated as immutable sequences, so you can do something like if "import" in line: ....
The one sticky spot, would be associating those imported modules to their package name (the first one that comes to mind is the PIL module, in Ubuntu it's provided by the python-imaging package).
Python code can import modules using runtime-constructed strings, so the only surefire way would be to run the code. Real-world example: when you open a database with SQLAlchemy's dbconnect, the library will load one or more db-api modules depending on the content of your database string.
If you're willing to run the code, here is a relatively simple way to do this by examining sys.modules when it finishes:
>>> from sys import modules
>>> import codeofinterest
>>> execute_code_of_interest()
>>> print modules
[ long, list, of, loaded, modules ]
Here, too, you should keep in mind that this could theoretically fail if execute_code_of_interest() modifies sys.modules, but I believe that's quite rare in production code.

hashlib / md5. Compatibility with python 2.4

python 2.6 reports that the md5 module is obsolete and hashlib should be used. If I change import md5 to import hashlib I will solve for python 2.5 and python 2.6, but not for python 2.4, which has no hashlib module (leading to a ImportError, which I can catch).
Now, to fix it, I could do a try/catch, and define a getMd5() function so that a proper one gets defined according to the result of the try block. Is this solution ok?
How would you solve this issue in a more general case, like, for example: you have two different libraries with the same objective but different interface, and you want to use one, but fall back and use the other if the first one is not found.
In general the following construct is just fine:
try:
import module
except ImportError:
# Do something else.
In your particular case, perhaps:
try:
from hashlib import md5
except ImportError:
from md5 import md5
In the case where the modules have the same interface, as they do here, the solution you described is fine. You could also isolate the import into its own module like this:
hash.py
----
try:
import hashlib.md5 as md5mod
except ImportError:
import md5 as md5mod
-----
prog.py
-----
from hash import md5mod
....
In the case where they have different interfaces you would need to write an adaptor to align the interfaces as you have specified.

Supporting Multiple Python Versions In Your Code?

Today I tried using pyPdf 1.12 in a script I was writing that targets Python 2.6. When running my script, and even importing pyPdf, I get complaints about deprecated functionality (md5->hashsum, sets). I'd like to contribute a patch to make this work cleanly in 2.6, but I imagine the author does not want to break compatibility for older versions (2.5 and earlier).
Searching Google and Stack Overflow have so far turned up nothing. I feel like I have seen try/except blocks around import statements before that accomplish something similar, but can't find any examples. Is there a generally accepted best practice for supporting multiple Python versions?
There are two ways to do this:
(1) Just like you described: Try something and work around the exception for old versions. For example, you could try to import the json module and import a userland implementation if this fails:
try:
import json
except ImportError:
import myutils.myjson as json
This is an example from Django (they use this technique often):
try:
reversed
except NameError:
from django.utils.itercompat import reversed # Python 2.3 fallback
If the iterator reversed is available, they use it. Otherwise, they import their own implementation from the utils package.
(2) Explicitely compare the version of the Python interpreter:
import sys
if sys.version_info < (2, 6, 0):
# Do stuff for old version...
else:
# Do 2.6+ stuff
sys.version_info is a tuple that can easily be compared with similar version tuples.
You can certainly do
try:
import v26
except ImportError:
import v25
Dive Into Python—Using Exceptions for Other Purposes
Multiple versions of Python are supported here. You can a) conditionally use the newer version, which takes a little work, or b) turn off the warnings, which should really be the default (and is on newer Pythons).

Categories