100 votes

Python mise en conserve après avoir changé le répertoire d'un module

J'ai récemment changé la structure de répertoire de mon programme : avant, j'avais tous mes modules à l'intérieur du dossier "main". Maintenant, je les ai déplacés dans un répertoire nommé d'après le programme, et j'ai placé un __init__.py là-bas pour en faire un package.

Maintenant, j'ai un seul fichier .py dans mon répertoire principal qui est utilisé pour lancer mon programme, ce qui est beaucoup plus propre.

Quoi qu'il en soit, essayer de charger des fichiers picklés des versions précédentes de mon programme échoue. Je reçois l'erreur "ImportError: No module named tools" - ce qui je suppose est parce que mon module était précédemment dans le dossier main, et maintenant il est dans whyteboard.tools, pas simplement tools. Cependant, le code qui importe dans le module tools vit dans le même répertoire que lui, donc je doute qu'il y ait besoin de spécifier un package.

Donc, la structure de mon répertoire de programme ressemble à ceci :

whyteboard-0.39.4

-->whyteboard.py

-->README.txt

-->CHANGELOG.txt

---->whyteboard/

---->whyteboard/__init__.py

---->whyteboard/gui.py

---->whyteboard/tools.py

whyteboard.py lance un bloc de code depuis whyteboard/gui.py, qui démarre l'interface graphique. Ce problème de pickling ne se produisait certainement pas avant la réorganisation du répertoire.

2voto

Casimir Points 598

Pour les personnes comme moi ayant besoin de mettre à jour de nombreux fichiers pickle, voici une fonction mettant en œuvre le conseil excellent de @Alex Martelli :

import sys
from types import ModuleType
import pickle

# import torch

def update_module_path_in_pickled_object(
    pickle_path: str, old_module_path: str, new_module: ModuleType
) -> None:
    """Mettez à jour le chemin pointé d'un module Python dans un fichier pickle si le
    fichier correspondant a été renommé.

    Implémente le conseil donné dans https://stackoverflow.com/a/2121918.

    Args:
        pickle_path (str): Chemin vers l'objet picklé.
        old_module_path (str): L'ancien.chemin.pointé.vers.module.renommé.
        new_module (ModuleType): from new.location import module.
    """
    sys.modules[old_module_path] = new_module

    dic = pickle.load(open(pickle_path, "rb"))
    # dic = torch.load(pickle_path, map_location="cpu")

    del sys.modules[old_module_path]

    pickle.dump(dic, open(pickle_path, "wb"))
    # torch.save(dic, pickle_path)

Dans mon cas, les fichiers étaient des checkpoints de modèle PyTorch. D'où le torch.load/save() mis en commentaire.

Exemple

from new.location import new_module

for pickle_path in ('foo.pkl', 'bar.pkl'):
    update_module_path_in_pickled_object(
        pickle_path, "old.module.dotted.path", new_module
    )

1voto

SimoX Points 139

Lorsque vous essayez de charger un fichier pickle contenant une référence de classe, vous devez respecter la même structure que celle que vous avez utilisée lorsque vous avez enregistré le pickle. Si vous souhaitez utiliser le pickle ailleurs, vous devez indiquer où se trouve cette classe ou tout autre objet; pour cela, vous pouvez sauver la situation en effectuant ce qui suit :

import sys
sys.path.append('chemin/vers/dossier contenant le module Python')

0voto

KJ Price Points 4655

Je sais que cela fait un moment, mais cela a résolu le problème pour moi :

Essentiellement, utilisez le chemin d'importation complet (par exemple concurrent.run_concurrent) au lieu du simple nom de module (par exemple run_concurrent)


Code partagé :

import importlib
module_path="concurrent.run_concurrent"

...

module = importlib.util.module_from_spec(spec)

Original (mauvais) :

module_name = module_path.split(".")[-1]

spec = importlib.util.spec_from_file_location(module_name, filepath)

...

sys.modules[module_name] = module

Remplacez par ce qui suit (supprimez toutes les références à module_name) :

# Supprimer "module_name"

# Utiliser "module_path" au lieu de "module_name"
spec = importlib.util.spec_from_file_location(module_path, filepath)

...

# Utiliser "module_path" au lieu de "module_name"
sys.modules[module_path] = module

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X