Как обращаться к относительным путям ресурсов при работе с репозиторием кода

Мы работаем с репозиторием кода, который развертывается как для Windows, так и для Linux - иногда в разных каталогах. Как один из модулей внутри проекта должен ссылаться на один из ресурсов проекта, отличных от Python (файлы CSV и т. Д.)?

Если мы сделаем что-то вроде:

thefile=open('test.csv')

or:

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

Он будет работать только в том случае, если сценарий запускается из одного определенного каталога или подмножества каталогов.

Я бы хотел сделать что-то вроде:

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

Является ли это возможным?


person olamundo    schedule 13.08.2009    source источник


Ответы (8)


Попробуйте использовать имя файла относительно текущего пути к файлам. Пример для './my_file':

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

В Python 3.4+ вы также можете использовать pathlib:

fn = pathlib.Path(__file__).parent / 'my_file'
person c089    schedule 13.08.2009
comment
Я думаю, что это решение будет работать только в том случае, если ресурс находится в том же каталоге файла python или в его подкаталоге. Как решить эту проблему, если у вас есть следующая древовидная структура: / Project_Root_dir / python_files_dir / Еще несколько подкаталогов здесь py_file.py / resources / некоторые подкаталоги здесь resource_file.csv - person olamundo; 14.08.2009
comment
Извините, дерево файлов было искажено в этом последнем сообщении ... вторая попытка: у вас есть файл в /Project_Root_dir/python_files_dir/some_subdirs/py_file.py, а у вас есть файл ресурсов в /Project_Root_dir/resources/some_subdirs/resource_file.csv - person olamundo; 14.08.2009
comment
Вы должны иметь возможность попасть в родительский каталог с помощью join (foo, '..'). Итак, из / root / python_files / module / myfile используйте os.path.join (os.path.dirname (__file__), '..', '..', 'resources') - person c089; 14.08.2009
comment
os.pardir немного лучше, чем '..', хотя они эквивалентны как в POSIX, так и в Windows. - person davidchambers; 22.01.2013
comment
@davidchambers это эквивалент или лучше, в конце концов? - person cedbeu; 22.03.2013
comment
@cedbeu: Это эквивалентно для каждой системы, с которой я когда-либо сталкивался, и я думаю, что каждый системный питон работает сегодня (пожалуйста, поправьте меня, если я ошибаюсь). Однако, если вы ожидаете, что python будет перенесен в систему с использованием другого разделителя путей в будущем, и хотите, чтобы ваш код был готов к этому, os.pardir будет более переносимым. Я бы сказал, что каждый программист, даже тот, кто никогда не читал Python, знает значение ..., в то время как os.pardir - это уровень косвенного обращения, который нужно искать в документации, поэтому лично я буду придерживаться. .. - person c089; 24.03.2013
comment
Будет ли это работать с общей папкой? Например, если у меня есть файлы и данные на моем компьютере в общей папке, а затем мой коллега в сети запускает файл .py, мне нужен код для ссылки на правильные файлы. - person trench; 12.05.2016
comment
Есть ли какие-либо риски для безопасности, если я использую его для предоставления пользователю файлов через Flask? (Другими словами, сможет ли пользователь увидеть всю мою структуру каталогов, и если да, то плохо ли это?) - person Pro Q; 08.05.2019
comment
С помощью решения pathlib я получаю AttributeError: 'PosixPath' object has no attribute 'split' Python 3.7.5 - person Adam Parkin; 08.05.2020

Если вы используете инструменты установки или распространяете (установка setup.py), то «правильный» способ доступа к этим упакованным ресурсам, по-видимому, заключается в использовании package_resources.

В вашем случае пример будет

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

Что, конечно, читает ресурс, и прочитанные двоичные данные будут значением my_data

Если вам просто нужно имя файла, вы также можете использовать

resource_filename(package_or_requirement, resource_name)

Пример:

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

Преимущество в том, что он гарантированно будет работать, даже если это архивный дистрибутив, как яйцо.

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

person Sharoon Thomas    schedule 07.02.2012
comment
Я знаю, что это старый ответ, я предпочитаю (а может быть?) Использовать pkg_resources, но с исчезновением заархивированных яиц есть ли какой-то вред в простом использовании __file__, как в старые добрые времена? - person Pykler; 09.04.2014
comment
Это надежный подход. Даже если соглашение о яйцах уходит, setuptools нет, и многие все еще устанавливают deps против репозиториев git, где яйцо создается во время выполнения - person deepelement; 22.08.2017

В Python пути указываются относительно текущего рабочего каталога, который в большинстве случаев является каталогом, из которого вы запускаете свою программу. Текущий рабочий каталог, скорее всего, не совпадает с каталогом вашего файла модуля, поэтому использование пути относительно текущего файла модуля всегда является плохим выбором.

Использование абсолютного пути должно быть лучшим решением:

import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')
person skyfree    schedule 25.03.2017

Я часто использую что-то подобное:

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))
    # ...

Переменная

__file__

содержит имя файла сценария, в котором вы пишете этот код, поэтому вы можете указывать пути относительно сценария, но при этом записывать абсолютные пути. Он работает достаточно хорошо по нескольким причинам:

  • путь является абсолютным, но все же относительным
  • проект все еще можно развернуть в относительном контейнере

Но вам нужно следить за совместимостью платформ - os.pathsep Windows отличается от UNIX.

person user137673    schedule 13.08.2009

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

Вы также пытаетесь нормализовать свой cwd с помощью os.path.abspath(os.getcwd()). Дополнительная информация здесь.

person gavoja    schedule 13.08.2009
comment
очень мало вариантов использования, где cwd - это путь к модулю, хотя - person cedbeu; 22.03.2013
comment
он не работает внутри пакета, просто из того же каталога (или рабочего каталога), установленного сценарием. - person alexandra; 25.04.2019
comment
Это не сработает, если пользователь запускает программу, используя абсолютный путь из другого каталога. например python3 /usr/someone/test.py - person sgrpwr; 04.02.2020

Вы можете использовать сборку в __file__ переменной. Он содержит путь к текущему файлу. Я бы реализовал getBaseOfProject в модуле в корне вашего проекта. Там я получу часть пути __file__ и верну его. Затем этот метод можно использовать везде в вашем проекте.

person Achim    schedule 13.08.2009

Я здесь немного запутался. Хотел упаковать некоторые файлы ресурсов в файл колеса и получить к ним доступ. Выполнял упаковку с использованием файла манифеста, но pip install не устанавливал его, если он не был подкаталогом. Надеюсь, эти снимки помогут

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

Создал велотренажер с помощью стандартного setup.py. pip установил файл колеса. После установки проверил, установлены ли ресурсы. Они есть

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  

В tfclient.py для доступа к этим файлам. из

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

И это работает.

person Alex Punnen    schedule 13.05.2019

Я долго искал ответ на этот вопрос, но наконец получил его (и это действительно очень просто):

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

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

person Rutger Semp    schedule 02.02.2011
comment
Это будет работать только в том случае, если вы запускаете программу Python из того же каталога, что и рассматриваемый файл .py. И в этом случае вы все равно могли бы просто сделать open('your/subfolder/of/choice'). - person Paul Fisher; 02.02.2011
comment
и OP упомянул, что код должен работать как в Windows, так и в Linux. Этого не будет. - person user183037; 29.07.2011