Python: obtenga la ruta de la estructura del proyecto raíz

9 minutos de lectura

Tengo un proyecto de python con un archivo de configuración en la raíz del proyecto. Se debe acceder al archivo de configuración en algunos archivos diferentes a lo largo del proyecto.

Entonces se ve algo como: <ROOT>/configuration.conf
<ROOT>/A/a.py, <ROOT>/A/B/b.py (cuando b,a.py acceda al archivo de configuración).

¿Cuál es la forma mejor/más fácil de obtener la ruta a la raíz del proyecto y el archivo de configuración sin depender de en qué archivo dentro del proyecto estoy? es decir sin usar ../../? Está bien asumir que sabemos el nombre de la raíz del proyecto.

  • lo hace <ROOT>/__init__.py ¿existir?

    – mgilson

    19 de agosto de 2014 a las 17:08

  • O su archivo de configuración es un módulo de python, y puede acceder fácilmente a él simplemente con una declaración de importación, o no es un módulo de python y debe colocarlo en una ubicación conocida. Por ejemplo $HOME/.mi_proyecto/mi_proyecto.conf.

    – John Smith Opcional

    19 de agosto de 2014 a las 17:09

  • @JohnSmithOptional: es un archivo JSON. Necesito poder acceder a él usando la ruta. Sí. Todas las carpetas lo incluyen.

    – matanc1

    19 de agosto de 2014 a las 17:09


  • _ Está bien suponer que sabemos el nombre de la raíz del proyecto._ ¿Significa eso que conoce la ruta al proyecto? ¿No es solo os.path.join(known_root_name, “configuration.conf”) entonces?

    – tdelaney

    19 de agosto de 2014 a las 17:19

  • Si se trata de una configuración de usuario, generalmente usaría algo como os.path.expanduser('~/.myproject/myproject.conf'). Funciona en Unix y Windows.

    – John Smith Opcional

    19 de agosto de 2014 a las 17:22


avatar de usuario
jrd1

Puedes hacer esto como lo hace Django: defina una variable para la raíz del proyecto desde un archivo que se encuentra en el nivel superior del proyecto. Por ejemplo, si así es como se ve la estructura de su proyecto:

project/
    configuration.conf
    definitions.py
    main.py
    utils.py

En definitions.py puede definir (esto requiere import os):

ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) # This is your Project Root

Así, con el Raíz del proyecto conocido, puedes crear una variable que apunte a la ubicación de la configuración (esto se puede definir en cualquier lugar, pero un lugar lógico sería ponerlo en una ubicación donde se definen las constantes, por ejemplo definitions.py):

CONFIG_PATH = os.path.join(ROOT_DIR, 'configuration.conf')  # requires `import os`

Luego, puede acceder fácilmente a la constante (en cualquiera de los otros archivos) con la declaración de importación (por ejemplo, en utils.py): from definitions import CONFIG_PATH.

  • Para incluir el archivo definitions.py de esa manera, ¿será necesario agregar un __init__.py archivo al directorio raíz del proyecto también? ¿Debería ser eso correcto? Acabo de comenzar con python y no estoy seguro de las mejores prácticas. Gracias.

    – akskap

    30 de agosto de 2016 a las 8:20


  • @akskap: No, un __init__.py no será necesario, ya que ese archivo solo es necesario cuando se definen paquetes: los __init__.py los archivos son necesarios para que Python trate los directorios como si contuvieran paquetes; esto se hace para evitar que los directorios con un nombre común, como una cadena, oculten involuntariamente módulos válidos que aparecen más adelante en la ruta de búsqueda del módulo. En el caso más simple, __init__.py puede ser simplemente un archivo vacío, pero también puede ejecutar código de inicialización para el paquete o configurar el __all__ variable, descrita más adelante. Ver: docs.python.org/3/tutorial/modules.html#packages

    – jrd1

    31 de agosto de 2016 a las 6:02


  • @JavNoor: no, en el ejemplo que citó, os.path.abspath está llamando a una cadena, '__file__'. Recordar que __file__ es en realidad un atributo de importación definido para los módulos de Python. En este caso, __file__ devolverá la ruta desde la que se carga el módulo. Lea más aquí (vea la sección de módulos): docs.python.org/3/reference/datamodel.html

    – jrd1

    17 de febrero de 2019 a las 8:18

  • @AstroFloyd: solo fallará si subdir no es un subpaquete (es decir, falta __init__.py en el subdirectorio). Sin la estructuración del paquete, Python no sabrá desde dónde se debe hacer referencia a ROOT_DIR

    – jrd1

    18 mayo 2021 a las 15:26

  • Comprendí que para permitir importaciones relativas, debe “convertir su código en un paquete”, pero me tomó un tiempo darme cuenta de que eso significa todo 1) poner un __init__.py en el directorio superior de tu código y 2) agregar el directorio principal del directorio superior a su PYTHONPATH y 3) establecer el __package__ variable en su programa Python al nombre del directorio que contiene __init__.py. Todo está funcionando ahora. ¡Gracias!

    – AstroFloyd

    20 de mayo de 2021 a las 11:44

avatar de usuario
RikH

Otras respuestas aconsejan usar un archivo en el nivel superior del proyecto. Esto no es necesario si utiliza pathlib.Path y parent (Python 3.4 y superior). Considere la siguiente estructura de directorios donde todos los archivos excepto README.md y utils.py han sido omitidos.

project
│   README.md
|
└───src
│   │   utils.py
|   |   ...
|   ...

En utils.py definimos la siguiente función.

from pathlib import Path

def get_project_root() -> Path:
    return Path(__file__).parent.parent

En cualquier módulo del proyecto ahora podemos obtener la raíz del proyecto de la siguiente manera.

from src.utils import get_project_root

root = get_project_root()

Beneficios: Cualquier módulo que llama get_project_root se puede mover sin cambiar el comportamiento del programa. Sólo cuando el módulo utils.py se mueve tenemos que actualizar get_project_root y las importaciones (se pueden usar herramientas de refactorización para automatizar esto).

  • Cualquier módulo que esté en la raíz. Llamar a src.utils desde fuera de la raíz no debería funcionar. ¿Me equivoco?

    – aerijman

    30 oct 2019 a las 16:15


  • nombre ‘expediente‘ no está definido, ¿por qué?

    – Luk Aron

    25 de marzo de 2020 a las 7:38

  • @LukAron: Asegúrate de usar __file__ (tenga en cuenta los guiones bajos), que es un atributo de módulo que contiene la ruta absoluta del módulo; de lo contrario, no funcionará.

    – Nerxis

    8 de febrero de 2021 a las 9:48

  • Para mí, esto todavía no ayuda porque todavía tienes que saber dónde se encuentra src/utils.py si no estás en src

    – JSON K.

    20 de febrero de 2021 a las 0:28

  • En mi caso (¿debido al sistema operativo Linux?), Path() devuelve el pariente sendero. Por lo tanto, necesito Path(__file__).absolute().parent.parent para este ejemplo.

    – AstroFloyd

    18 de mayo de 2021 a las 14:07

Todas las soluciones anteriores parecen ser demasiado complicadas para lo que creo que necesita y, a menudo, no me funcionaron. El siguiente comando de una línea hace lo que quieres:

import os
ROOT_DIR = os.path.abspath(os.curdir)

  • Pon eso en config.py, en la raíz del directorio, .. ¡bamn! Tienes un singleton.

    – swdev

    12 de marzo de 2019 a las 15:35

  • Este método supone que ejecuta la aplicación desde dentro de la ruta que existe. Muchos “usuarios” tienen un icono en el que hacen clic desde un escritorio o pueden ejecutar la aplicación desde otro directorio por completo.

    – DevPlayer

    22 oct 2019 a las 20:12

Debajo del código Devuelve la ruta hasta la raíz de su proyecto

import sys
print(sys.path[1])

avatar de usuario
DevPlayer

Para obtener la ruta del módulo “raíz”, puede usar:

import os
import sys
os.path.dirname(sys.modules['__main__'].__file__)

Pero lo que es más interesante, si tiene un “objeto” de configuración en su módulo superior, podría -leer- así:

app = sys.modules['__main__']
stuff = app.config.somefunc()

  • Aquí os no está disponible por defecto. Necesidad de importar os. Así que agregando la línea import os haría la respuesta más completa.

    – Dr. Abu Nafee Ibna Zahid

    3 mayo 2018 a las 11:50

  • Esto proporciona el directorio que contiene el script que se ejecutó. Por ejemplo, al ejecutar python3 -m topmodule.submodule.script dará /path/to/topmodule/submodule en vez de /path/to/topmodule.

    – danijar

    31 de marzo de 2019 a las 18:18

  • En el contexto de las pruebas invocadas por pytest tengo /home/user/.local/lib/python3.9/site-packages/pytest/__main__.py. La respuesta de RikH es más sólida para mí (Path(__file__).parent.parent en módulo fijo).

    – x’ES

    11 de abril a las 0:42

Una forma estándar de lograr esto sería usar el pkg_resources módulo que forma parte del setuptools paquete. setuptools se utiliza para crear un paquete de python instalable.

Puedes usar pkg_resources para devolver el contenido de su archivo deseado como una cadena y puede usar pkg_resources para obtener la ruta real del archivo deseado en su sistema.

Digamos que tienes un paquete llamado stackoverflow.

stackoverflow/
|-- app
|   `-- __init__.py
`-- resources
    |-- bands
    |   |-- Dream\ Theater
    |   |-- __init__.py
    |   |-- King's\ X
    |   |-- Megadeth
    |   `-- Rush
    `-- __init__.py

3 directories, 7 files

Ahora supongamos que desea acceder al archivo Rush desde un módulo app.run. Usar pkg_resources.resouces_filename para obtener el camino a Rush y pkg_resources.resource_string para obtener el contenido de Rush; así:

import pkg_resources

if __name__ == "__main__":
    print pkg_resources.resource_filename('resources.bands', 'Rush')
    print pkg_resources.resource_string('resources.bands', 'Rush')

La salida:

/home/sri/workspace/stackoverflow/resources/bands/Rush
Base: Geddy Lee
Vocals: Geddy Lee
Guitar: Alex Lifeson
Drums: Neil Peart

Esto funciona para todos los paquetes en su ruta de python. Así que si quieres saber dónde lxml.etree existe en su sistema:

import pkg_resources

if __name__ == "__main__":
    print pkg_resources.resource_filename('lxml', 'etree')

producción:

/usr/lib64/python2.7/site-packages/lxml/etree

El punto es que puede usar este método estándar para acceder a archivos que están instalados en su sistema (por ejemplo, pip install xxx o yum -y install python-xxx) y archivos que están dentro del módulo en el que está trabajando actualmente.

  • Aquí os no está disponible por defecto. Necesidad de importar os. Así que agregando la línea import os haría la respuesta más completa.

    – Dr. Abu Nafee Ibna Zahid

    3 mayo 2018 a las 11:50

  • Esto proporciona el directorio que contiene el script que se ejecutó. Por ejemplo, al ejecutar python3 -m topmodule.submodule.script dará /path/to/topmodule/submodule en vez de /path/to/topmodule.

    – danijar

    31 de marzo de 2019 a las 18:18

  • En el contexto de las pruebas invocadas por pytest tengo /home/user/.local/lib/python3.9/site-packages/pytest/__main__.py. La respuesta de RikH es más sólida para mí (Path(__file__).parent.parent en módulo fijo).

    – x’ES

    11 de abril a las 0:42

avatar de usuario
U12-delantero

Probar:

ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

  • Esto es exactamente lo que necesitaba. Solución simple, funciona para mí porque mi estructura era root->config->conf.py. Quería definir la raíz del proyecto en conf.py y la raíz estaba exactamente dos niveles por encima de ese archivo.

    – Daniyal Arshad

    27 de agosto de 2019 a las 13:04

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad