Workaround for errors in Python packages

I recently had the following problem: I am developing a numerical library in Python (called spuq ) that needs scipy in its kernel. Now one of the functions in scipy, called btdtri , had an error for a specific set of input parameters. However, this bug is now fixed in scipy version 0.9 according to scipy developers. So there is something like this in my code:

 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 mutating my code with bug fixes for third-party packages. I would prefer it to be contained in one module that implements a workaround, and all the rest of my modules automatically use the fixed module.

Now my question is: what would be the best practice for such cases in general? For instance. write my 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 it's hard and easy to forget (and probably sloppy and ugly). Maybe there is a way to "automatically connect" to download the package and change the scipy module directly so that every other package sees only the patch package? I think this problem is quite common, so there probably should be some “best practices”.

+6
source share
2 answers

You can "decapitate the patch" of 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 the modules are imported only once, there will be only one scipy.special module, and all other modules will only see the version with the fixed monkey.

A monkey patch is often considered useful for testing, but not for production code. In this case, I think this is normal, since you really do not change the behavior of the package - you are fixing the confirmed error.

+5
source

The best option is to create a module, say fixes , which exports a fixed version or an external version of btdtri depending on the version of SciPy, as in your example. This can be made even simpler:

 if scipy.version.version < (0,9,0): def btdtri(...): # whatever else: btdtri = scipy.btdtri 

This has an advantage over your version that code checking tools like pyflakes will not throttle to import * and that you don’t need to extract all SciPy APIs from the fixes module, just the part that needs to be fixed (remember: explicit is better than implicit )

+1
source

Source: https://habr.com/ru/post/900426/


All Articles