Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Workaround for bugs in Python packages

Tags:

python

package

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.

like image 246
Elmar Zander Avatar asked Oct 31 '11 11:10

Elmar Zander


People also ask

Are there bugs in Python?

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.

How do you treat a package in a folder in Python?

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 .


2 Answers

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.

like image 123
Sven Marnach Avatar answered Oct 12 '22 21:10

Sven Marnach


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).

like image 21
Fred Foo Avatar answered Oct 12 '22 21:10

Fred Foo