2009-04-22 7 views
65

J'écris un paquet python avec des modules qui doivent ouvrir des fichiers de données dans un sous-répertoire ./data/. En ce moment j'ai les chemins vers les fichiers codés en dur dans mes classes et fonctions. Je voudrais écrire du code plus robuste qui peut accéder au sous-répertoire, peu importe où il est installé sur le système de l'utilisateur.Données d'accès Python dans le sous-répertoire du paquet

J'ai essayé une variété de méthodes, mais jusqu'ici je n'ai pas eu de chance. Il semble que la plupart des commandes "répertoire courant" 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 trivial et courant. Pourtant, je n'arrive pas à comprendre. Une partie du problème est que mes fichiers de données ne sont pas .py fichiers, donc je ne peux pas utiliser les fonctions d'importation et autres.

Des suggestions?

En ce moment, mon répertoire paquet ressemble à:

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

Je suis en train d'accéder data.txt de module*.py

Merci!

Répondre

24

Vous pouvez utiliser underscore-underscore- fichier-underscore-underscore (__file__) pour obtenir le chemin du paquet, comme celui-ci:

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() 
+24

Cela ne fonctionnera pas si les fichiers sont dans une distribution (IE. Utilisez pkg_resources pour obtenir le fichier de données. – Chris

+0

En effet, c'est cassé. – Federico

6

Je crois que j'ai cherché une réponse.

Je fais un data_path.py module, que j'importer dans mes autres modules contenant:

data_path = os.path.join(os.path.dirname(__file__),'data') 

Et puis j'ouvre tous mes fichiers avec

open(os.path.join(data_path,'filename'), <param>) 
+0

Cela ne fonctionnera pas lorsque la ressource est dans une distribution d'archives (comme un oeuf zippé). Préférez quelque chose comme ça: 'pkg_resources.resource_string ('pkg_name', 'data/file.txt')' – ankostis

+0

@ankostis setuptools est assez intelligent pour extraire l'archive s'il détecte que vous avez utilisé '__file__' quelque part. Dans mon cas, j'utilise une bibliothèque qui veut vraiment des chemins et pas des flux. Bien sûr, je pourrais écrire les fichiers temporairement sur le disque, mais étant paresseux, je viens d'utiliser la fonction de setuptools. – letmaik

95

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

Vous pouvez mettre votre paquet selon la hiérarchie suivante et configurer le fichier de configuration du package pour pointer vos ressources de données, selon ce lien:

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

Vous pouvez ensuite trouver et utiliser ces fichiers à l'aide pkg_resources, selon 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') 
+0

Je pense que c'est la manière préférée, je ne suis pas entièrement sûr de la raison, mais les projets montrent des avertissements lorsque vous vous référez au paquet/module avec '__file__'. – lukecampbell

+1

* pkg_resources * ne créera-t-il pas une dépendance d'exécution sur * setuptools *? Par exemple, je redistribue un paquet Debian alors pourquoi devrais-je dépendre de 'python-setuptools' juste pour ça? Jusqu'ici '__file__' fonctionne bien pour moi. – mlt

+3

Pourquoi cela est-il meilleur? La classe ResourceManager fournit un accès uniforme aux ressources du package, que ces ressources existent en tant que fichiers et répertoires ou qu'elles soient compressées dans une archive quelconque – vrdhn

11

Pour fournir une solution qui fonctionne aujourd'hui. Définitivement 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") 

Renvoyer un objet de type fichier lisible 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", en ce sens 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") 

Forfait Découverte et accès aux ressources avec pkg_resources

3

Vous avez besoin d'un nom pour votre module entier, vous arborescence donné ne liste ce détail, pour moi cela a fonctionné:

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

De toute évidence, setuptools ne semble pas résoudre les fichiers basés sur un nom correspondant à des fichiers de données compressés, donc vous devez inclure le préfixe data/ à peu près n'importe quoi. Vous pouvez utiliser os.path.join('data', 'data.txt) si vous avez besoin d'autres séparateurs de répertoires. En général, je ne trouve pas de problèmes de compatibilité avec les séparateurs de répertoires de style Unix codés en dur.

Questions connexes