158 votes

Accéder aux données dans le sous-répertoire du paquet

J'écris un paquetage python avec des modules qui doivent ouvrir des fichiers de données dans un format ./data/ sous-répertoire. Pour l'instant, les chemins d'accès aux fichiers sont codés en dur dans mes classes et mes fonctions. J'aimerais écrire un code plus robuste qui puisse accéder au sous-répertoire quel que soit l'endroit où il est installé sur le système de l'utilisateur.

J'ai essayé plusieurs méthodes, mais jusqu'à présent, je n'ai pas eu de chance. Il semble que la plupart des commandes "current directory" renvoient le répertoire de l'interpréteur python du système, et non le répertoire du module.

Cela semble être un problème banal et courant. Pourtant, je n'arrive pas à le résoudre. Le problème vient en partie du fait que mes fichiers de données ne sont pas .py et je ne peux donc pas utiliser les fonctions d'importation et autres.

Des suggestions ?

Pour l'instant, mon répertoire de paquets ressemble à ceci :

/
__init__.py
module1.py
module2.py
data/   
   data.txt

J'essaie d'accéder à data.txt de module*.py !

196voto

Emrit Points 11

La méthode standard consiste à utiliser les paquets setuptools et les ressources pkg_resources.

Vous pouvez organiser votre paquet selon la hiérarchie suivante, et configurer le fichier d'installation du paquet pour qu'il pointe vers vos ressources de données, comme indiqué sur ce lien :

http://docs.python.org/distutils/setupscript.html#installing-package-data

Vous pouvez ensuite retrouver et utiliser ces fichiers en utilisant pkg_resources, comme indiqué sur ce lien :

http://peak.telecommunity.com/DevCenter/PkgResources#basic-resource-access

import pkg_resources

DATA_PATH = pkg_resources.resource_filename('<package name>', 'data/')
DB_FILE = pkg_resources.resource_filename('<package name>', 'data/sqlite.db')

36voto

Antti Haapala Points 11542

Il n'est souvent pas utile de répondre en détaillant un code qui n'a pas d'utilité. no Le système fonctionne tel quel, mais je pense qu'il s'agit d'une exception. Ajout de Python 3.7 importlib.resources qui est censé remplacer pkg_resources . Il permet d'accéder aux fichiers contenus dans les paquets qui n'ont pas de barres obliques dans leur nom, c'est-à-dire

foo/
    __init__.py
    module1.py
    module2.py
    data/   
       data.txt
    data2.txt

c'est-à-dire que vous pouvez accéder à data2.txt emballage intérieur foo avec par exemple

importlib.resources.open_binary('foo', 'data2.txt')

mais il échouerait avec une exception pour

>>> importlib.resources.open_binary('foo', 'data/data.txt')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.7/importlib/resources.py", line 87, in open_binary
    resource = _normalize_path(resource)
  File "/usr/lib/python3.7/importlib/resources.py", line 61, in _normalize_path
    raise ValueError('{!r} must be only a file name'.format(path))
ValueError: 'data/data2.txt' must be only a file name

Ce problème ne peut être résolu qu'en plaçant __init__.py en data et de l'utiliser comme un paquet :

importlib.resources.open_binary('foo.data', 'data.txt')

La raison de ce comportement est la suivante "C'est à dessein" ; mais la conception pourrait changer ...

23voto

RichieHindle Points 98544

Vous pouvez utiliser __file__ pour obtenir le chemin d'accès au paquet, comme ceci :

import os
this_dir, this_filename = os.path.split(__file__)
DATA_PATH = os.path.join(this_dir, "data", "data.txt")
print open(DATA_PATH).read()

17voto

Sascha Gottfried Points 1014

Fournir une solution qui fonctionne aujourd'hui. Il faut absolument utiliser cette API pour ne pas réinventer toutes ces roues.

Un vrai nom de fichier de système de fichiers est nécessaire. Les œufs zippés seront extraits dans un répertoire de cache :

from pkg_resources import resource_filename, Requirement

path_to_vik_logo = resource_filename(Requirement.parse("enb.portals"), "enb/portals/reports/VIK_logo.png")

Renvoie un objet lisible de type fichier pour la ressource spécifiée ; il peut s'agir d'un fichier réel, d'un StringIO ou d'un objet similaire. Le flux est en "mode binaire", c'est-à-dire que les octets contenus dans la ressource seront lus tels quels.

from pkg_resources import resource_stream, Requirement

vik_logo_as_stream = resource_stream(Requirement.parse("enb.portals"), "enb/portals/reports/VIK_logo.png")

Découverte de paquetages et accès aux ressources à l'aide de pkg_resources

8voto

ThorSummoner Points 396

Vous avez besoin d'un nom pour l'ensemble de votre module, l'arborescence des répertoires qui vous est donnée ne donne pas ce détail, pour moi cela a fonctionné :

import pkg_resources
print(    
    pkg_resources.resource_filename(__name__, 'data/data.txt')
)

Notamment, setuptools ne semble pas résoudre les fichiers sur la base d'une correspondance de nom avec les fichiers de données emballés, de sorte que vous devrez inclure le fichier data/ préfixe à peu près quoi qu'il arrive. Vous pouvez utiliser os.path.join('data', 'data.txt) si vous avez besoin de séparateurs de répertoires alternatifs, en général je ne trouve pas de problèmes de compatibilité avec les séparateurs de répertoires codés en dur dans le style Unix.

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