Comment faire référence aux chemins relatifs des ressources lors de l'utilisation d'un référentiel de code

188

Nous travaillons avec un référentiel de code qui est déployé à la fois sur Windows et Linux - parfois dans des répertoires différents. Comment l'un des modules du projet doit-il faire référence à l'une des ressources non Python du projet (fichiers CSV, etc.)?

Si nous faisons quelque chose comme:

thefile=open('test.csv')

ou:

thefile=open('../somedirectory/test.csv')

Cela ne fonctionnera que lorsque le script est exécuté à partir d'un répertoire spécifique ou d'un sous-ensemble de répertoires.

Ce que je voudrais faire, c'est quelque chose comme:

path=getBasePathOfProject()+'/somedirectory/test.csv'
thefile=open(path)

C'est possible?

olamundo
la source

Réponses:

255

Essayez d'utiliser un nom de fichier relatif au chemin d'accès actuel des fichiers. Exemple pour './my_file':

fn = os.path.join(os.path.dirname(__file__), 'my_file')

Dans Python 3.4+, vous pouvez également utiliser pathlib :

fn = pathlib.Path(__file__).parent / 'my_file'
c089
la source
3
Je pense que cette solution ne fonctionnera que si la ressource est dans le même répertoire du fichier python, ou dans un sous-répertoire de celui-ci. Comment le résoudre lorsque vous avez l'arborescence suivante: / Project_Root_dir / python_files_dir / Quelques sous-répertoires supplémentaires ici py_file.py / resources / quelques sous-répertoires ici resource_file.csv
olamundo
1
Désolé, l'arborescence des fichiers s'est brouillée sur ce dernier message ... deuxième essai: vous avez votre fichier dans /Project_Root_dir/python_files_dir/some_subdirs/py_file.py et vous avez votre fichier de ressources dans /Project_Root_dir/resources/some_subdirs/resource_file.csv
olamundo
28
Vous devriez pouvoir accéder au répertoire parent en utilisant join (foo, '..'). Donc à partir de / root / python_files / module / myfile, utilisez os.path.join (os.path.dirname ( __file__), '..', '..', 'resources')
c089
7
os.pardirest légèrement meilleur que '..', bien que les deux soient équivalents sur POSIX et Windows.
davidchambers
4
@cedbeu: C'est équivalent sur tous les systèmes que j'ai rencontrés et je pense que tous les systèmes python fonctionnent aujourd'hui (veuillez me corriger si je me trompe ici). Cependant, si vous vous attendez à ce que python soit porté sur un système en utilisant un séparateur de chemin différent à l'avenir et que vous souhaitez que votre code soit prêt pour cela, os.pardir sera plus portable. Je ferais valoir que chaque programmeur, même celui qui n'a jamais lu aucun python connaît la signification de "..", tandis que "os.pardir" est un niveau d'indirection qu'il faudrait rechercher dans la documentation si personnellement je ' d s'en tenir à "..".
c089
41

Si vous utilisez des outils de configuration ou distribuez (une installation setup.py), la "bonne" façon d'accéder à ces ressources packagées semble être d'utiliser package_resources.

Dans votre cas, l'exemple serait

import pkg_resources
my_data = pkg_resources.resource_string(__name__, "foo.dat")

Lequel bien sûr lit la ressource et les données binaires lues serait la valeur de my_data

Si vous avez juste besoin du nom de fichier, vous pouvez également utiliser

resource_filename(package_or_requirement, resource_name)

Exemple:

resource_filename("MyPackage","foo.dat")

L'avantage est qu'il est garanti de fonctionner même s'il s'agit d'une distribution d'archives comme un œuf.

Voir http://packages.python.org/distribute/pkg_resources.html#resourcemanager-api

Sharoon Thomas
la source
3
Je sais que c'est une vieille réponse, ma façon préférée est (/ était peut-être?) D'utiliser pkg_resources, mais avec la disparition des œufs zippés, y a-t-il un mal à utiliser __file__comme au bon vieux temps?
Pykler le
1
C'est une approche solide. Même si la convention de l'œuf disparaît, setuptools ne l'est pas et beaucoup installent encore deps contre les dépôts git où l'œuf est construit au moment de l'exécution
deepelement
18

En Python, les chemins sont relatifs au répertoire de travail actuel , qui dans la plupart des cas est le répertoire à partir duquel vous exécutez votre programme. Le répertoire de travail actuel est très probablement différent du répertoire de votre fichier de module, donc utiliser un chemin relatif à votre fichier de module actuel est toujours un mauvais choix.

L'utilisation d'un chemin absolu devrait être la meilleure solution:

import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')
sans ciel
la source
15

J'utilise souvent quelque chose de similaire à ceci:

import os
DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 'datadir'))

# if you have more paths to set, you might want to shorten this as
here = lambda x: os.path.abspath(os.path.join(os.path.dirname(__file__), x))
DATA_DIR = here('datadir') 

pathjoin = os.path.join
# ...
# later in script
for fn in os.listdir(DATA_DIR):
    f = open(pathjoin(DATA_DIR, fn))
    # ...

La variable

__file__

contient le nom de fichier du script dans lequel vous écrivez ce code, vous pouvez donc créer des chemins relatifs au script, mais toujours écrits avec des chemins absolus. Cela fonctionne assez bien pour plusieurs raisons:

  • le chemin est absolu, mais toujours relatif
  • le projet peut toujours être déployé dans un conteneur relatif

Mais vous devez surveiller la compatibilité de la plate-forme - os.pathsep de Windows est différent d'UNIX.

utilisateur137673
la source
5
import os
cwd = os.getcwd()
path = os.path.join(cwd, "my_file")
f = open(path)

Vous essayez également de normaliser votre cwdutilisation os.path.abspath(os.getcwd()). Plus d'infos ici .

gavoja
la source
3
très peu de cas d'utilisation où le cwdest le chemin d'un module, cependant
cedbeu
cela ne fonctionne pas dans un package, juste à partir du même répertoire (ou du répertoire de travail) défini par le script.
alexandra
Cela ne fonctionnera pas si l'utilisateur exécute le programme en utilisant le chemin absolu d'un répertoire différent. par exemple python3 /usr/someone/test.py
sgrpwr
2

Vous pouvez utiliser la __file__variable build in . Il contient le chemin du fichier actuel. J'implémenterais getBaseOfProject dans un module à la racine de votre projet. Là, j'obtiendrais une partie du chemin __file__et je le retournerais. Cette méthode peut ensuite être utilisée partout dans votre projet.

Achim
la source
0

Je suis un peu perplexe ici. Je voulais regrouper certains fichiers de ressources dans un fichier de roue et y accéder. L'empaquetage a-t-il utilisé le fichier manifeste, mais pip install ne l'installait pas sauf s'il s'agissait d'un sous-répertoire. En espérant que ces photos vous aideront

├── cnn_client
   ├── image_preprocessor.py
   ├── __init__.py
   ├── resources
      ├── mscoco_complete_label_map.pbtxt
      ├── retinanet_complete_label_map.pbtxt
      └── retinanet_label_map.py
   ├── tf_client.py

MANIFEST.in

recursive-include cnn_client/resources *

Créé un weel en utilisant setup.py standard. pip a installé le fichier de roue. Après l'installation, vérifié si les ressources sont installées. Elles sont

ls /usr/local/lib/python2.7/dist-packages/cnn_client/resources

mscoco_complete_label_map.pbtxt
retinanet_complete_label_map.pbtxt 
 retinanet_label_map.py  

Dans tfclient.py pour accéder à ces fichiers. de

templates_dir = os.path.join(os.path.dirname(__file__), 'resources')
 file_path = os.path.join(templates_dir, \
            'mscoco_complete_label_map.pbtxt')
        s = open(file_path, 'r').read()

Et il fonctionne.

Alex Punnen
la source
-5

J'ai passé beaucoup de temps à trouver la réponse à cela, mais je l'ai finalement eu (et c'est en fait très simple):

import sys
import os
sys.path.append(os.getcwd() + '/your/subfolder/of/choice')

# now import whatever other modules you want, both the standard ones,
# as the ones supplied in your subfolders

Cela ajoutera le chemin relatif de votre sous-dossier aux répertoires dans lesquels python pourra regarder.C'est assez rapide et sale, mais cela fonctionne comme un charme :)

Rutger Semp
la source
6
Cela ne fonctionnera que si vous exécutez le programme Python à partir du même répertoire que le fichier .py en question. Et dans ce cas, vous pouvez le faire de open('your/subfolder/of/choice')toute façon.
Paul Fisher
4
et l'OP a mentionné que le code doit fonctionner à la fois sous Windows et Linux. Ce ne sera pas le cas.
user183037 le