Get project name python

Python — Получить путь к корневой структуре проекта

У меня есть проект python с конфигурационным файлом в корне проекта. Файл конфигурации должен быть доступен в нескольких файлах по всему проекту. Итак, это выглядит примерно так: /configuration.conf /A/a.py , /A/B/b.py (когда b, a.py обращается к файлу конфигурации). Какой лучший/самый простой способ получить путь к корню проекта и конфигурационному файлу без зависимости от того, в каком файле находится внутри проекта? без использования ../../ ? Можно предположить, что мы знаем имя проекта.

Либо ваш файл конфигурации является модулем Python, и вы можете легко получить к нему доступ только с помощью оператора import, либо это не модуль Python, и вы должны поместить его в хорошо известное место. Например, $ HOME / .my_project / my_project.conf.

@JohnSmithOptional — это файл JSON. Мне нужно иметь доступ к нему, используя путь. Да. Все папки включают его.

_ Можно предположить, что мы знаем имя корня проекта ._ Значит ли это, что вы знаете путь к проекту? Разве это не просто os.path.join (известное_рутое_имя, «configuration.conf») тогда?

Если это пользовательская конфигурация, я бы обычно использовал что-то вроде os.path.expanduser(‘~/.myproject/myproject.conf’) . Работает на Unix и Windows.

@tdelaney — я знаю имя проекта (то есть имя корня проекта) и знаю имя файла конфигурации и то, что он находится в корневом каталоге. Я хочу извлечь путь к корню, используя имя проекта.

8 ответов

Вы можете сделать это так, как это делает Django: определить переменную для корня проекта из файла, который находится на верхнем уровне проекта. Например, если так выглядит структура вашего проекта:

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

В definitions.py вы можете определить (это требует import os ):

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

Таким образом, с известным корнем проекта вы можете создать переменную, которая указывает на местоположение конфигурации (это может быть определено где угодно, но логичным было бы поместить ее в место, где определены константы — например, definitions.py ):

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

Затем вы можете легко получить доступ к константе (в любом другом файле) с помощью оператора import (например, в utils.py ): from definitions import CONFIG_PATH .

Чтобы включить подобный файл defines.py, потребуется ли также добавить файл __init__.py в корневой каталог проекта? Это должно быть правильно? Я только начал с Python и не уверен в лучших практиках. Благодарю.

@akskap: Нет, __init__.py не потребуется, так как этот файл требуется только при определении пакетов: файлы __init__.py необходимы, чтобы Python рассматривал каталоги как содержащие пакеты; это сделано для предотвращения непреднамеренного скрытия действительными модулями каталогов с общим именем, например, строки, которые встречаются позже в пути поиска модулей. В простейшем случае __init__.py может быть просто пустым файлом, но он также может выполнить код инициализации для пакета или установить переменную __all__ , как описано ниже. Смотрите: docs.python.org/3/tutorial/modules.html#packages

Мне любопытно, по стилю, приемлемо или не __init.py__ добавлять эти определения в __init.py__ корневого пакета. Это позволит сохранить создание другого файла, а также разрешить более приятный синтаксис from root_pack import ROOT_DIR, CONFIG_PATH .

@Johndt6: Johndt6: соглашение заключается в том, чтобы сохранять __init__.py пустым, но это не совсем верно (в конце концов, это соглашение). Смотрите это больше: stackoverflow.com/questions/2361124/using-init-py

Здесь os по умолчанию недоступна. Нужно импортировать os . Таким образом, добавление строки import os сделает ответ более полным.

@akskap Я не уверен, что вы правы, когда говорите, что __init__.py не требуется. Без этого, запуск файла .py в подкаталоге (т. $ python src/foo.py ) возвращает ModuleNotFoundError

ROOT_DIR = os.path.dirname(os.path.abspath(‘__file__’)) это не должно быть ROOT_DIR = os.path.dirname(os.path.abspath(‘__file__’)) ? Я просто указываю на символы кавычек.

@JavNoor: нет — в приведенном вами примере os.path.abspath вызывает строку ‘__file__’ . Напомним, что __file__ на самом деле является атрибутом импорта, который определен для модулей Python. В этом случае __file__ вернет путь, из которого загружен модуль. Подробнее читайте здесь (см. Раздел модулей): docs.python.org/3/reference/datamodel.html

Спасибо @jrd1. jrd1. Я попробовал это в блокноте Jupyter, и там он не принимал ввод без цитат. Но после прочтения ссылки, которую вы отправили, имеет смысл, что цитаты не должны быть там. Я полагаю, они не предназначены для Юпитера.

@JavNoor: Действительно. Поведение Jupyter является правильным — __file__ применяется к модулям и сценариям Python. Ноутбуки Jupyter не соответствуют этому критерию, поскольку их веб-приложения запускаются через сервер.

Чтобы получить путь к «корневому» модулю, вы можете использовать:

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

Но что более интересно, если у вас есть конфигурационный «объект» в вашем самом верхнем модуле, вы можете прочитать его следующим образом:

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

Здесь os по умолчанию недоступна. Нужно импортировать os . Таким образом, добавление строки import os сделает ответ более полным.

Это дает каталог, который содержит скрипт, который был выполнен. Например, при запуске python3 -m topmodule.submodule.script он выдаст /path/to/topmodule/submodule вместо /path/to/topmodule .

Стандартным способом достижения этого является использование модуля pkg_resources , который является частью пакета setuptools . setuptools используется для создания установочного пакета python.

Вы можете использовать pkg_resources , чтобы вернуть содержимое нужного файла в виде строки, и вы можете использовать pkg_resources , чтобы получить фактический путь к нужному файлу в вашей системе.

Скажем, что у вас есть пакет под названием stackoverflow .

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

Теперь скажем, что вы хотите получить доступ к файлу Rush из модуля app.run . Используйте pkg_resources.resouces_filename , чтобы получить путь к Rush и pkg_resources.resource_string , чтобы получить содержимое Rush; Таким образом:

import pkg_resources if __name__ == "__main__": print pkg_resources.resource_filename('resources.bands', 'Rush') print pkg_resources.resource_string('resources.bands', 'Rush') 
/home/sri/workspace/stackoverflow/resources/bands/Rush Base: Geddy Lee Vocals: Geddy Lee Guitar: Alex Lifeson Drums: Neil Peart 

Это работает для всех пакетов на вашем пути python. Поэтому, если вы хотите знать, где lxml.etree существует в вашей системе:

import pkg_resources if __name__ == "__main__": print pkg_resources.resource_filename('lxml', 'etree') 
/usr/lib64/python2.7/site-packages/lxml/etree 

Дело в том, что вы можете использовать этот стандартный метод для доступа к файлам, которые установлены в вашей системе (например, pip install xxx или yum -y install python-xxx) и файлы, которые находятся в модуле, который вы сейчас работаете.

Другие ответы советуют использовать файл на верхнем уровне проекта. В этом нет необходимости, если вы используете pathlib.Path и parent . Рассмотрим следующую структуру каталогов, в которой все файлы, кроме README.md и utils.py , опущены.

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

В utils.py мы определяем следующую функцию.

from pathlib import Path def get_project_root() -> Path: """Returns project root folder.""" return Path(__file__).parent.parent 

Теперь в любом модуле проекта мы можем получить корень проекта следующим образом.

from src.utils import get_project_root root = get_project_root() 

Преимущества: Любой модуль, который вызывает get_project_root может быть перемещен без изменения поведения программы. Только когда модуль utils.py перемещен, мы должны обновлять get_project_root и импорт (используйте рефакторинг IDE для автоматизации этого).

Недавно я пытался сделать что-то подобное, и я нашел эти ответы не подходящими для моих сценариев использования (распределенная библиотека, которая должна обнаруживать корень проекта). В основном я боролся с различными средами и платформами и до сих пор не нашел что-то совершенно универсальное.

Код локальный для проекта

Я видел этот пример, упомянутый и используемый в нескольких местах, Django и т.д.

import os print(os.path.dirname(os.path.abspath(__file__))) 

Как это просто, это работает, только когда файл, в котором находится фрагмент, на самом деле является частью проекта. Мы не получаем каталог проекта, а вместо этого каталог фрагмента

Аналогичным образом, sys.modules подход разбивает когда вызывается из — за пределов EntryPoint применения, в частности, я заметил ребенок нить не может определить это без всякого отношения назад к «основному» модуля. Я явно поместил импорт внутри функции, чтобы продемонстрировать импорт из дочернего потока, переместив его на верхний уровень app.py, чтобы исправить это.

app/ |-- config | '-- __init__.py | '-- settings.py '-- app.py 
#!/usr/bin/env python import threading def background_setup(): # Explicitly importing this from the context of the child thread from config import settings print(settings.ROOT_DIR) # Spawn a thread to background preparation tasks t = threading.Thread(target=background_setup) t.start() # Do other things during initialization t.join() # Ready to take traffic 
import os import sys ROOT_DIR = None def setup(): global ROOT_DIR ROOT_DIR = os.path.dirname(sys.modules['__main__'].__file__) # Do something slow 

Запуск этой программы приводит к ошибке атрибута:

>>> import main >>> Exception in thread Thread-1: Traceback (most recent call last): File "C:\Python2714\lib\threading.py", line 801, in __bootstrap_inner self.run() File "C:\Python2714\lib\threading.py", line 754, in run self.__target(*self.__args, **self.__kwargs) File "main.py", line 6, in background_setup from config import settings File "config\settings.py", line 34, in ROOT_DIR = get_root() File "config\settings.py", line 31, in get_root return os.path.dirname(sys.modules['__main__'].__file__) AttributeError: 'module' object has no attribute '__file__' 

. следовательно, решение на основе потоков

Расположение не зависит

Используя ту же структуру приложения, что и раньше, но изменяя settings.py

import os import sys import inspect import platform import threading ROOT_DIR = None def setup(): main_id = None for t in threading.enumerate(): if t.name == 'MainThread': main_id = t.ident break if not main_id: raise RuntimeError("Main thread exited before execution") current_main_frame = sys._current_frames()[main_id] base_frame = inspect.getouterframes(current_main_frame)[-1] if platform.system() == 'Windows': filename = base_frame.filename else: filename = base_frame[0].f_code.co_filename global ROOT_DIR ROOT_DIR = os.path.dirname(os.path.abspath(filename)) 

Разбивка: сначала мы хотим точно найти идентификатор основного потока. В Python3. 4+ библиотека потоков имеет threading.main_thread() однако, все не используют 3. 4+, поэтому мы ищем все потоки в поисках основного потока, сохраняя его ID. Если основной поток уже завершен, он не будет указан в threading.enumerate() . В этом случае мы RuntimeError() пока не найду лучшее решение.

main_id = None for t in threading.enumerate(): if t.name == 'MainThread': main_id = t.ident break if not main_id: raise RuntimeError("Main thread exited before execution") 

Далее мы находим самый первый кадр стека основного потока. Используя специальную функцию sys._current_frames() мы получаем словарь текущего фрейма стека каждого потока. Затем с помощью inspect.getouterframes() мы можем получить весь стек для основного потока и самого первого кадра. current_main_frame = sys._current_frames() [main_id] base_frame = inspect.getouterframes(current_main_frame) [-1] Наконец, различия между реализациями inspect.getouterframes() в Windows и Linux должны быть обработаны. Используя очищенное имя файла, os.path.abspath() и os.path.dirname() убирают вещи.

if platform.system() == 'Windows': filename = base_frame.filename else: filename = base_frame[0].f_code.co_filename global ROOT_DIR ROOT_DIR = os.path.dirname(os.path.abspath(filename)) 

До сих пор я проверял это на Python2.7 и 3.6 на Windows, а также Python3.4 на WSL

Источник

Читайте также:  Почему открывает html код
Оцените статью