Cómo referirse a rutas relativas de recursos cuando se trabaja con un repositorio de código

188

Estamos trabajando con un repositorio de código que se implementa en Windows y Linux, a veces en diferentes directorios. ¿Cómo debería uno de los módulos dentro del proyecto referirse a uno de los recursos que no son de Python en el proyecto (archivos CSV, etc.)?

Si hacemos algo como:

thefile=open('test.csv')

o:

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

Solo funcionará cuando el script se ejecute desde un directorio específico o un subconjunto de directorios.

Lo que me gustaría hacer es algo como:

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

¿Es posible?

olamundo
fuente

Respuestas:

255

Intente utilizar un nombre de archivo relativo a la ruta de los archivos actuales. Ejemplo para './my_file':

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

En Python 3.4+ también puedes usar pathlib :

fn = pathlib.Path(__file__).parent / 'my_file'
c089
fuente
3
Creo que esta solución solo funcionará si el recurso está en el mismo directorio del archivo python, o en un subdirectorio del mismo. ¿Cómo lo resuelve cuando tiene la siguiente estructura de árbol: / Project_Root_dir / python_files_dir / Algunos subdirecciones aquí py_file.py / resources / some subdirs here resource_file.csv
olamundo
1
Lo sentimos, el árbol de archivos quedó enredado en ese último mensaje ... segundo intento: tiene su archivo en /Project_Root_dir/python_files_dir/some_subdirs/py_file.py y tiene su archivo de recursos en /Project_Root_dir/resources/some_subdirs/resource_file.csv
olamundo
28
Debería poder acceder al directorio principal utilizando join (foo, '..'). Entonces, desde / root / python_files / module / myfile, use os.path.join (os.path.dirname ( __file__), '..', '..', 'resources')
c089
77
os.pardires ligeramente mejor que '..', aunque los dos son equivalentes tanto en POSIX como en Windows.
davidchambers
44
@cedbeu: es equivalente en todos los sistemas con los que me he encontrado y creo que todos los sistemas python se ejecutan hoy (corrígeme si estoy equivocado aquí). Sin embargo, si espera que python sea portado a un sistema que utilice un separador de ruta diferente en el futuro y desea que su código esté listo para ello, os.pardir será más portátil. Yo diría que todos los programadores, incluso aquellos que nunca leen ninguna pitón, saben el significado de "..", mientras que "os.pardir" es un nivel de indirección que uno debería buscar en la documentación tan personalmente. d se adhieren a "..".
c089
41

Si está utilizando herramientas de configuración o distribuye (una instalación setup.py), la forma "correcta" de acceder a estos recursos empaquetados parece estar utilizando package_resources.

En su caso, el ejemplo sería

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

Lo que, por supuesto, lee el recurso y los datos binarios leídos serían el valor de my_data

Si solo necesita el nombre de archivo, también puede usar

resource_filename(package_or_requirement, resource_name)

Ejemplo:

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

La ventaja es que está garantizado que funcione incluso si se trata de una distribución de archivo como un huevo.

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

Sharoon Thomas
fuente
3
Sé que esta es una respuesta anterior, mi forma preferida es (¿/ tal vez?) Usar pkg_resources, pero con la desaparición de los huevos comprimidos, ¿hay algún daño en usarlo __file__como en los viejos tiempos?
Pykler
1
Este es un enfoque sólido. Incluso si la convención de huevo va a desaparecer, no se setuptools y muchos siguen instalando deps contra repositorios Git donde el huevo se construye en tiempo de ejecución
deepelement
18

En Python, las rutas son relativas al directorio de trabajo actual , que en la mayoría de los casos es el directorio desde el que ejecuta su programa. El directorio de trabajo actual es muy probable que no lo mismo que el directorio de su archivo de módulo, por lo que usar una ruta relativa al archivo de módulo actual es siempre una mala elección.

Usar la ruta absoluta debería ser la mejor solución:

import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')
sin cielo
fuente
15

A menudo uso algo similar a esto:

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__

contiene el nombre del archivo de la secuencia de comandos en la que escribe ese código, por lo que puede hacer rutas relativas a la secuencia de comandos, pero aún escrito con rutas absolutas. Funciona bastante bien por varias razones:

  • el camino es absoluto, pero aún relativo
  • el proyecto aún se puede implementar en un contenedor relativo

Pero debe tener en cuenta la compatibilidad de la plataforma: os.pathsep de Windows es diferente de UNIX.

usuario137673
fuente
5
import os
cwd = os.getcwd()
path = os.path.join(cwd, "my_file")
f = open(path)

También intenta normalizar su cwduso os.path.abspath(os.getcwd()). Más información aquí .

gavoja
fuente
3
cwdsin embargo, muy pocos casos de uso donde es la ruta de un módulo
cedbeu
no funciona dentro de un paquete, solo desde el mismo directorio (o el directorio de trabajo) establecido por el script.
alexandra
Esto no funcionará si el usuario ejecuta el programa utilizando la ruta absoluta desde un directorio diferente. por ejemplo, python3 /usr/someone/test.py
sgrpwr
2

Puede usar la compilación en __file__variable. Contiene la ruta del archivo actual. Implementaría getBaseOfProject en un módulo en la raíz de su proyecto. Allí obtendría la parte del camino __file__y la devolvería. Este método se puede usar en todas partes en su proyecto.

Achim
fuente
0

Me quedé perplejo aquí un poco. Quería empaquetar algunos archivos de recursos en un archivo de rueda y acceder a ellos. Hizo el paquete usando el archivo de manifiesto, pero pip install no lo estaba instalando a menos que fuera un subdirectorio. Esperar que estos disparos de sceen ayuden

├── 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 *

Creó un weel usando setup.py estándar. pip instaló el archivo de la rueda. Después de la instalación se verifica si los recursos están instalados. Son

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  

En tfclient.py para acceder a estos archivos. 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()

Y funciona.

Alex Punnen
fuente
-5

Pasé mucho tiempo averiguando la respuesta a esto, pero finalmente lo obtuve (y en realidad es muy 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

Esto agregará la ruta relativa de su subcarpeta a los directorios para que Python los vea. Es bastante rápido y sucio, pero funciona de maravilla :)

Semp Rutger
fuente
66
Esto solo funcionará si está ejecutando el programa Python desde el mismo directorio que el archivo .py en cuestión. Y en ese caso, podrías hacerlo de open('your/subfolder/of/choice')todos modos.
Paul Fisher
44
y el OP mencionó que el código debe funcionar tanto en Windows como en Linux. Esto no lo hará.
user183037