Dynamically choosing Python library based on Python version - python

I am writing a Python script which needs to run on RHEL 6.x which defaults to Python 2.6 but also should work with the latest Python. I need to parse command-line arguments, however Python 2.6 only includes optparse which in recent Python has been deprecated in favor of argparse. Is there a way to dynamically select whether to use argparse or opt parse (I realize it would require two parsing algorithms)?

Try to import argparse first and then use optparse if it can't be found:
try:
import argparse
# Argparse-specific stuff
except ImportError:
import optparse
# Optparse-specific stuff
You can also check the python version using sys.version or sys.version_info.
import sys
if (sys.version_info.major >= 2 and sys.version_info.minor >= 7):
import argparse
# ...
else:
import optparse
Or, you could just use optparse on all versions, if you do not need the improved features argparse has

Stick with optparse for now, or install argparse from PyPI for the Python 2.6 installation.
Don't try to support both just to support two Python versions; save yourself the headache. optparse may be deprecated but still works just fine when run with Python 2.7. It just has fewer features than argparse can offer.
If you have to run on Python 3.x installations too, then you could try to support both with an import guard:
try:
import argparse
# configure argparse
except ImportError:
import optparse
# configure optparse
but it'd be much easier to just ship with argparse as a dependency.

Related

Implement pkg_resources.resource_filename in setuptools

I am working to incorporate the symspellpy package for spell checking and correcting large amounts of data. However, the package suggests using pkg_resources.resource_filename, which is no longer supported. Can you please provide guidance on how to access the necessary resources using the currently preferred method?
dictionary_path = pkg_resources.resource_filename("symspellpy", "frequency_dictionary_en_82_765.txt")
bigram_path = pkg_resources.resource_filename("symspellpy", "frequency_bigramdictionary_en_243_342.txt")
The replacement is the importlib_resources.files function.
It is integrated in standard library from Python 3.9, as importlib.resources.files
If you just need to support Python 3.9 or newer, it's straightforward
import importlib.resources
importlib.resources.files(...)
Otherwise, if you want to support Python 3.8 and older, here's how you do it:
add importlib_resources>=1.3; python_version < '3.9' to your dependencies (requirements.txt, setup.cfg, setup.py or pyproject.toml, depending how the project is organised)
In your code, adapt as
import sys
if sys.version_info >= (3, 9):
import importlib.resources as importlib_resources
else:
import importlib_resources
importlib_resources.files(...)
See https://importlib-resources.readthedocs.io/en/latest/migration.html

ENUM module and cStringIO module in PYVISA

I have some trouble to fix. I am using Python 3.2 with pyvisa for Python 3.2 32bits. When i used:
import pyvisa
It displayed:
ImportError: No module named enum
But when I use:
import pyqtgraph, pyvisa
I get:
ImportError: No module named cStringIO
I just want to use pyvisa for using an Agilent 33250a by GPIB.
The enum module wasn't part of Python until Python 3.4, so 3.2 is too early; you need to upgrade, or you need to live without enum (upgrading is a good idea mind you; the performance and features of Python have improved markedly since then; on performance in particular, strings and user defined class instances dramatically reduced their memory overhead). I'm guessing pyvisa dropped support for Python versions older than 3.4 if they're depending on enum.
cStringIO is a Python 2.x only accelerator module for StringIO; in Python 3.0 and higher, you just import io and use io.StringIO, and it will automatically use the C accelerated code under the hood when available, and pure Python code otherwise. If you're only targeting Python 3, just do import io or from io import StringIO. For code that should run under both Python 2 and Python 3, and use str in both, you can do the following for imports:
try:
from cStringIO import StringIO # Py2 C accelerated version
except ImportError:
try:
from StringIO import StringIO # Py2 fallback version
except ImportError:
from io import StringIO # Py3 version
If you want to handle Unicode text regardless of Python version (well, in 2.6 and up), you can just use io.StringIO exclusively; it works with unicode in Py2, and str in Py3, which means it handles all text in both versions (where cStringIO only handles str in Py2, so it can't handle the whole Unicode range).
I suspect your other import error for pyqtgraph would be because you tried installing a version of pyqtgraph written for Python 2; the pyqtgraph page claims Python 3.x compatibility, and use of cStringIO without a fallback would not meet that claim, so either you installed the wrong version, or it was installed incorrectly (e.g. if they were using a single code base and 2to3-ing it, but you somehow installed it without 2to3-ing it; no idea how you'd do that).

Python: How can I create a Python project compatible to Python 2 and Python 3?

I want to create a package which should be compatible to Python 2.7 and Python 3.3.
The logic should exist only once.
How do you normally represent the programming logic - in Python 2.7 or Python 3.3?
Or do you choose a different way?
EDIT: How this issue is handled in the core?
You do it by quite simply only using those bits of the language that is the same, and by having conditions or exception handling during other parts.
This is harder or easier depending on what your software do, and what versions of Python you need to support. By supporting just Python 2.6, 2.7 and 3.3 it becomes relatively simple.
Exactly how you do it is not answerable in a question on SO, because there is a lot to it.
I recommend you check out this book: http://python3porting.com/ , especially the chapter "Supporting Python 2 and 3 without 2to3 conversion" and the Appendices, that has loads of examples of how to solve most incompatibilities.
The six library may also be useful.
I tend to use a number of __future__ imports (which are, presumably, safely ignored in Python 3), and the occasionaly try except for some import statements. In addition, I define a stringtype for basestring / str compatibility. Just depending on what I need per module.
For example:
from __future__ import absolute_import
from __future__ import unicode_literals
from __future__ import division
from __future__ import print_function
try:
import ConfigParser as cfgparser
except ImportError:
import configparser as cfgparser
try:
stringtype = basestring
except NameError:
stringtype = str
I think the six package already does this, so that may be another good option.

python shelve ... bsddb deprecated ... how to get shelve to use another database?

I have an app developed in python 2.7.2 on OS X.
I use the module shelve and seems to default to bsddb on the mac.
The program won't run on a Windows 7 machine with ActiveState python 2.7 because the module bsddb is not present and is not in ActiveState's package manager (pypm). ActiveState's documentation says deprecated at v 2.6.
I guess it tries bdddb because the OS X python which created the DB defaults to bsddb.
When I delete the shelve database and run it on Windows, it happily uses some other underlying database. The Mac's python is also happy.
So I think I should enforce the use of a non-bdsdb backend for shelve. Like the gdbm module.
But I can't work out how to do that.
You can set the type of db created by setting anydbm._defaultmod before calling shelve.open.
This works for Python 2.6 (and maybe for 2.7?), but since anydbm._defaultmod is a private variable, be aware that this is a hack.
anydbm._defaultmod=__import__('gdbm')
For example:
import anydbm
import whichdb
import contextlib
anydbm._defaultmod=__import__('gdbm')
filename='/tmp/shelf.dat'
with contextlib.closing(shelve.open(filename)) as f: pass
result=whichdb.whichdb(filename)
print(result)
# gdbm
I seemed to have asked the wrong question. When building the windows exe, py2exe was not including an dbm modules (it couldn't infer this dependency), so at runtime python in desperation tried to find the bdbm module.
this script setup.py includes a module which makes the py2exe version behave like the version run normally. It includes a dbm-clone module (I'm only storing ten simple dictionaries so the basic dumbdbm module is good enough
from distutils.core import setup
import py2exe, sys, os
from glob import glob
sys.argv.append('py2exe')
data_files = [("Microsoft.VC90.CRT", glob(r'C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT\*.*'))]
setup(
data_files=data_files,
windows = ["cashflowSim.py"],
options={
"py2exe":{"includes":["dumbdbm"]}},
zipfile = None
)

Perl's BEGIN{} block in Python

I have Python code that uses the "with" keyword (new in 2.6) and I want to check if the interpreter version is at least 2.6, so I use this code:
import sys
if sys.version < '2.6':
raise Exception( "python 2.6 required" )
However, the 2.4 interpreter chokes on the with keyword (later in the script) because it doesn't recognize the syntax, and it does this before it evaluates my check.
Is there something in Python analogous to Perl's BEGIN{} block?
Take a look here:
How can I check for Python version in a program that uses new language features?
Perhaps someone has a better answer, but my first thought would be to have a separate script to perform the check, then import the "real" script once the check has passed. Python won't check the syntax until the import happens.
import sys
if sys.version < '2.6':
raise Exception( "python 2.6 required" )
import myscript # runs myscript

Categories