I recently had the following problem: I'm developing a numerical library in Python (called spuq
) that needs scipy
at its core. Now one of the functions in scipy, its called btdtri
, had a bug for a certain tuple of input parameters. This bug, however, is now fixed in scipy version 0.9 according to the developers of scipy. So in my code I have something like this:
import scipy
def foo(a, b, c):
if scipy.__version__>=(0, 9):
return btdtri(a, b, c)
else:
return my_fixed_btdtri(a, b, c)
This works, however, I don't really like to litter my code with bug fixes for third party packages. I would rather have that contained in one module, that implements the workaround, and have all the other of my modules uses the patched module automatically.
Now my question is: what would be the best practice to handle cases like this in general? E.g. write my own spuq.contrib.scipy
and say there
from scipy import *
if __version__ < (0, 9):
btdtri = my_fixed_btdtri
and instead of importing scipy
import spuq.contrib.scipy
everywhere? I think that's complicated and easy to forget (and probably unpythonic and ugly). Maybe there is a way to "hook" automatically into the package loading and modify the scipy
module directly so that every other package sees only the patched up package? I think this problem is quite common, so probably there should be some 'best-practices' around.
There are mainly 5 types of bugs in Python: Syntax errors: When you receive a syntax error message, it indicates that you typed something wrong. Indentation errors: Python uses indentation to understand where blocks of code start and end.
To instruct Python to treat a folder containing files as a package, you need to create a __init__.py file in the folder. Note that starting with Python 3.3, Python introduced the implicit namespace packages feature. This allows Python to treat a folder as a package without the __init__.py .
You could "monkey patch" the scipy module. Somewhere in your initialization code, do
import scipy.special
if scipy.version.version < "0.9.0":
scipy.special.btdtri = my_btdtri
Since modules are imported only once, there will be only one module scipy.special
, and all other modules will only see the monkey-patched version.
Monkey patching is often seen as useful for testing, but not for production code. In this case, though, I think it is fine since you don't really change the behaviour of the package -- you are fixing a confirmed bug.
The best option is to make a module, say fixes
, that exports the fixed version or the external version of btdtri
depending on the SciPy version, as in your example. It can be done even simpler:
if scipy.version.version < (0,9,0):
def btdtri(...):
# whatever
else:
btdtri = scipy.btdtri
This has the advantage over your version that code checking tools like pyflakes
won't choke on the import *
and that you don't need to fetch all of the SciPy API from the fixes
module, just the part that needs fixing (remember: explicit is better than implicit).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With