MethodPickle (methodpickle) is a quick library that allows simple pickling and unpickling of function and method invocation. Function & method module loading is handled automatically, and methods can be specified by name as well.
The ability to pickle a method invocation allows for queueing and delayed execution of arbitrary code. This is useful for parallelization, logging, queueing, etc.
Contact:
Steve Lacy <github@slacy.com> Twitter: @sklacy http://slacy.com/blog
Please see the unit tests in test.py for some more verbose examples, but I'll go through a quick example here.:
from methodpickle.defer import defer # These are the functions that we're going to defer def some_function(x, y): return x*x + y*y # methodpickle supports deferring execution of classmethods as well, so # here's a simple class with a method: def some_class(object): def __init__(self, x): self._x = x def calc(self, y): return (self._x * self._x + y * y) if __name__ == '__main__': # the defer function takes a method and it's arguments, and turns it # into a pickleable object. storable_func = defer(some_function, 5, 4) # So, we pickle that guy into a string. method_str = pickle.dumps(storable_func) # You can now take method_str and do whatever you like with it. Write # it to a database, send it to another process, put it in your logs, # whatever. # Then, you can unpickle the stored method invocation, and run it, # like this: recovered_func = pickle.loads() assert(recovered_func.run() == 5*5 + 4*4) # methodpickle also supports pickling of classmethods. Note that your # class must support pickling and the methods should have no side # effects. i = some_class(2) storable_classmethod = defer(i, 3) classmethod_str = storable_method.dumps() recovered_classmethod = pickle.loads(classmethod_str) assert(recovered_classmethod.run() == 2*2 + 3*3)
For convenience, there's also a decorator form of the defer function, called deferred. Again, see the implementation or test.py for more details.
- All arguments to functions must themselves be pickle-able. This
- includes 'self' for class method invocations
- Functions and classes must be at the module level. Inner classes and
- inner functions don't have an easy-to-discover import path, so all the deferred functions should be at the top level of your module. I'd suggest putting them all in the same file (say, tasks.py)
- All method arguments are deepcopied at the time of the deferral. Thus,
- if you pass a very large datastructure to the deferral methods, it may have a performance impact. In addition, if you pass a mutable datastructur (dict, list, etc.) then subsequent modifications will have no effect.
- Watch out for double invocation of functions & methods. This is both
- a feature and a caveat. Once you pickle a function call, that value could be unpickled and run more than once. Watch out for anything that has unexpected side effects!