Python decapado después de cambiar el directorio de un módulo

Recientemente cambié el diseño del directorio de mi progtwig: antes, tenía todos mis módulos dentro de la carpeta “principal”. Ahora, los moví a un directorio que lleva el nombre del progtwig y coloqué un __init__.py allí para hacer un paquete.

Ahora tengo un solo archivo .py en mi directorio principal que se usa para iniciar mi progtwig, que es mucho más limpio.

De todos modos, el bash de cargar en archivos decapados de versiones anteriores de mi progtwig está fallando. Recibo “ImportError: No hay herramientas con el nombre del módulo”, lo que supongo que se debe a que mi módulo estaba anteriormente en la carpeta principal, y ahora está en whyteboard.tools, no simplemente en herramientas simples. Sin embargo, el código que se está importando en el módulo de herramientas se encuentra en el mismo directorio que él, por lo que dudo que sea necesario especificar un paquete.

Entonces, mi directorio de progtwigs se ve algo como esto:

whyteboard-0.39.4

-->whyteboard.py

-->README.txt

-->CHANGELOG.txt

---->whyteboard/

---->whyteboard/__init__.py

---->whyteboard/gui.py

---->whyteboard/tools.py

whyteboard.py lanza un bloque de código desde whyteboard / gui.py, que activa la GUI. Este problema de decapado definitivamente no estaba ocurriendo antes de la reorganización del directorio.

Como dicen los documentos de Pickle , para guardar y restaurar una instancia de clase (en realidad también una función), debe respetar ciertas restricciones:

pickle puede guardar y restaurar instancias de clase de forma transparente, sin embargo, la definición de la clase debe ser importable y vivir en el mismo módulo que cuando se almacenó el objeto

whyteboard.tools no es el “mismo módulo que las tools ” (aunque puede importarse mediante import tools con otros módulos en el mismo paquete, termina en sys.modules como sys.modules['whyteboard.tools'] : esto es absolutamente crucial, de lo contrario, el mismo módulo importado por uno en el mismo paquete en comparación con uno en otro paquete terminaría con entradas múltiples y posiblemente conflictivas.

Si sus archivos de pickle están en un formato bueno / avanzado (a diferencia del formato antiguo ascii, que es el predeterminado solo por razones de compatibilidad), migrarlos una vez que realice dichos cambios puede no ser tan trivial como “editar el archivo” ( que es binario & c …!), a pesar de lo que sugiere otra respuesta. Sugiero que, en cambio, hagas un pequeño “script de migración de pickle”: déjalo parchear a sys.modules como este …

 import sys from whyteboard import tools sys.modules['tools'] = tools 

y luego cPickle.load cada archivo, del sys.modules['tools'] , y cPickle.dump cada objeto cargado de nuevo en el archivo: esa entrada adicional temporal en sys.modules debería permitir que los pickles se carguen con éxito, luego debe sys.modules nuevamente. usar el nombre de módulo correcto para las clases de las instancias (eliminar esa entrada adicional debería asegurarse de eso).

Me sucedió, lo resolví agregando la nueva ubicación del módulo a sys.path antes de cargar pickle:

 import sys sys.path.append('path/to/whiteboard') f = open("pickled_file", "rb") pickle.load(f) 

Este es el comportamiento normal de pickle, los objetos no seleccionados deben tener su módulo de definición importable .

Debería poder cambiar la ruta de los módulos (es decir, de las tools a whyteboard.tools ) editando los archivos encurtidos, ya que normalmente son archivos de texto simples.

pickle serializa las clases por referencia, por lo que si cambias la vida de la clase, no se desmarcará porque no se encontrará la clase. Si usa dill lugar de pickle , entonces puede serializar clases por referencia o directamente (serializando directamente la clase en lugar de su ruta de importación). Puede simular esto con bastante facilidad simplemente cambiando la definición de clase después de un dump y antes de una load .

 Python 2.7.8 (default, Jul 13 2014, 02:29:54) [GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import dill >>> >>> class Foo(object): ... def bar(self): ... return 5 ... >>> f = Foo() >>> >>> _f = dill.dumps(f) >>> >>> class Foo(object): ... def bar(self, x): ... return x ... >>> g = Foo() >>> f_ = dill.loads(_f) >>> f_.bar() 5 >>> g.bar(4) 4 

Esto se puede hacer con un “desempaquetador” personalizado que usa find_class() :

 import io import pickle class RenameUnpickler(pickle.Unpickler): def find_class(self, module, name): renamed_module = module if module == "tools": renamed_module = "whyteboard.tools" return super(RenameUnpickler, self).find_class(renamed_module, name) def renamed_load(file_obj): return RenameUnpickler(file_obj).load() def renamed_loads(pickled_bytes): file_obj = io.BytesIO(pickled_bytes) return renamed_load(file_obj) 

Entonces deberías usar renamed_load() lugar de pickle.load() y renamed_loads() lugar de pickle.loads() .