Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unpickling python objects with a changed module path

Tags:

I'm trying to integrate a project Project A built by a colleague into another python project. Now this colleague has not used relative imports in his code but instead done

from packageA.moduleA import ClassA from packageA.moduleA import ClassB 

and consequently pickled the classes with cPickle. For neatness I'd like to hide the package that his (Project A) built inside my project. This however changes the path of the classes defined in packageA. No problem, I'll just redefine the import using

from ..packageA.moduleA import ClassA from ..packageA.moduleA import ClassB 

but now the un pickling the classes fails with the following message

    with open(fname) as infile: self.clzA = cPickle.load(infile) ImportError: No module named packageA.moduleA 

So why doesn't cPickle apparently see the module defs. Do I need to add the root of packageA to system path? Is this the correct way to solve the problem?

The cPickled file looks something like

ccopy_reg _reconstructor p1 (cpackageA.moduleA ClassA p2 c__builtin__ object p3 NtRp4 

The old project hierarchy is of the sort

packageA/     __init__.py     moduleA.py     moduleB.py packageB/     __init__.py     moduleC.py     moduleD.py 

I'd like to put all of that into a WrapperPackage

MyPackage/ .. __init__.py .. myModuleX.py .. myModuleY.py WrapperPackage/ .. __init__.py .. packageA/    .. __init__.py    .. moduleA.py    .. moduleB.py .. packageB/    .. __init__.py    .. moduleC.py    .. moduleD.py 
like image 371
Matti Lyra Avatar asked Nov 15 '12 13:11

Matti Lyra


People also ask

Can you pickle objects Python?

Python comes with a built-in package, known as pickle , that can be used to perform pickling and unpickling operations. Pickling and unpickling in Python is the process that is used to describe the conversion of objects into byte streams and vice versa - serialization and deserialization, using Python's pickle module.

What is the difference between pickling and Unpickling in Python?

“Pickling” is the process whereby a Python object hierarchy is converted into a byte stream, and “unpickling” is the inverse operation, whereby a byte stream (from a binary file or bytes-like object) is converted back into an object hierarchy.

How do you pickle data in Python?

To use pickle, start by importing it in Python. To pickle this dictionary, you first need to specify the name of the file you will write it to, which is dogs in this case. Note that the file does not have an extension. To open the file for writing, simply use the open() function.

How do you pickle a dump in Python?

First, import pickle to use it, then we define an example dictionary, which is a Python object. Next, we open a file (note that we open to write bytes in Python 3+), then we use pickle. dump() to put the dict into opened file, then close. Use pickle.


2 Answers

You'll need to create an alias for the pickle import to work; the following to the __init__.py file of the WrapperPackage package:

from .packageA import * # Ensures that all the modules have been loaded in their new locations *first*. from . import packageA  # imports WrapperPackage/packageA import sys sys.modules['packageA'] = packageA  # creates a packageA entry in sys.modules 

It may be that you'll need to create additional entries though:

sys.modules['packageA.moduleA'] = moduleA # etc. 

Now cPickle will find packageA.moduleA and packageA.moduleB again at their old locations.

You may want to re-write the pickle file afterwards, the new module location will be used at that time. The additional aliases created above should ensure that the modules in question have the new location name for cPickle to pick up when writing the classes again.

like image 63
Martijn Pieters Avatar answered Nov 12 '22 02:11

Martijn Pieters


In addition to @MartinPieters answer the other way of doing this is to define the find_global method of the cPickle.Unpickler class, or extend the pickle.Unpickler class.

def map_path(mod_name, kls_name):     if mod_name.startswith('packageA'): # catch all old module names         mod = __import__('WrapperPackage.%s'%mod_name, fromlist=[mod_name])         return getattr(mod, kls_name)     else:         mod = __import__(mod_name)         return getattr(mod, kls_name)  import cPickle as pickle with open('dump.pickle','r') as fh:     unpickler = pickle.Unpickler(fh)     unpickler.find_global = map_path     obj = unpickler.load() # object will now contain the new class path reference  with open('dump-new.pickle','w') as fh:     pickle.dump(obj, fh) # ClassA will now have a new path in 'dump-new' 

A more detailed explanation of the process for both pickle and cPickle can be found here.

like image 25
Matti Lyra Avatar answered Nov 12 '22 00:11

Matti Lyra