Программно определить расположение файлов данных distutils в Python
Я пытаюсь включить файлы данных в distutils для своего пакета, а затем ссылаюсь на них с использованием относительных путей (следуя http://docs.python.org/distutils/setupscript.html#distutils-additional-files)
Моя структура:
myproject/
mycode.py
data/
file1.dat
код в mycode.py
, который на самом деле является script в пакете. Он полагается на доступ к data/file1.dat
, ссылаясь на него, используя этот относительный путь. В setup.py
у меня есть:
setup(
...
scripts = "myproject/mycode.py"
data_files = [('data', 'myproject/data/file1.dat')]
)
Предположим, что пользователь теперь использует:
python setup.py --prefix=/home/user/
Тогда mycode.py
появится в некотором месте, как /home/user/bin/
. Но ссылка на data/file1.dat
теперь сломана, так как script живет в другом месте от данных.
Как узнать, от mycode.py
, абсолютный путь к myproject/data/file1.dat
, поэтому я могу правильно обращаться к нему в зависимости от того, где пользователь установил пакет?
ИЗМЕНИТЬ
Когда я устанавливаю это с помощью prefix=/home/user/
, я получаю data/file1.dat
, созданный в /home/user/
, который является именно тем, что я хочу, единственная недостающая часть - это то, как получить абсолютный путь к этому файлу программно, учитывая только относительный путь и не зная где пользователь установил пакет. Когда я пытаюсь использовать package_data
вместо data_files
, он не работает - я просто не получаю data/file1.dat
, созданный где угодно, даже если я удалю файл MANIFEST
.
Я прочитал все текущие обсуждения этой, по-видимому, очень распространенной проблемы. Однако все предлагаемые решения не имеют отношения к случаю, когда у меня есть выше, , где код, которому необходимо получить доступ к data_files
, является script, и его местоположение может измениться в зависимости от аргумента --prefix
до setup.py
. Единственное, что я могу решить, это добавить файл данных в scripts=
в setup()
, как в:
setup(
...
scripts = ["myproject/mycode.py", "myproject/data/file1.data"]
)
Это ужасный взлом, но это единственный способ, которым я могу думать, чтобы file1.data
был в том же месте, что и скрипты, определенные в scripts=
, так как я не могу найти какой-либо независимый от платформы и чувствительный к установке API для восстановления местоположения data_files
после запуска пользователем setup.py install
(возможно, с аргументами --prefix=
).
Ответы
Ответ 1
Я думаю, что путаница возникает из-за использования скриптов. Сценарии должны ссылаться на исполняемый исполняемый файл, возможно, утилиту script, связанную с вашим пакетом или, возможно, точку входа в функциональность вашего пакета. В любом случае вы должны ожидать, что любые скрипты не будут установлены вместе с остальной частью вашего пакета. Это ожидание объясняется главным образом тем, что пакеты считаются библиотеками (и устанавливаются в каталоги lib), тогда как сценарии считаются исполняемыми (и устанавливаются в каталоги bin или скриптов). Кроме того, файлы данных не являются ни исполняемыми файлами, ни библиотеками, а являются полностью отдельными.
Итак, из script вам нужно определить, где находятся файлы данных. В соответствии с документами Python,
Если каталог является относительным путем, он интерпретируется относительно установочный префикс.
Следовательно, вы должны написать что-то вроде следующего в mycode script, чтобы найти файл данных:
import sys
import os
def my_func():
with open(os.path.join(sys.prefix, 'data', 'file1.dat')) as f:
print(next(f))
if __name__ == '__main__':
my_func()
Если вам не нравится, что ваш код и данные не объединены вместе (и я бы этого не сделал), я бы реструктурировал ваш пакет, чтобы у вас был реальный пакет (и модуль) Python и используйте пакеты = и package_data = для ввода данных в пакет, а затем создать простой script, который вызывает модуль в пакете.
Я сделал это, создав это дерево:
.
│ setup.py
│
├───myproject
│ │ mycode.py
│ │ __init__.py
│ │
│ └───data
│ file1.dat
│
└───scripts
run-my-code.py
С setup.py:
from distutils.core import setup
setup(
name='myproject',
version='1.0',
scripts=['scripts/run-my-code.py'],
packages=['myproject'],
package_data = {
'myproject': ['data/file1.dat'],
},
)
run-my-code.py просто:
from myproject import mycode
mycode.my_func()
__init__
пуст, а mycode.py выглядит так:
import os
here = os.path.dirname(__file__)
def my_func():
with open(os.path.join(here, 'data', 'file1.dat')) as f:
print(next(f))
Этот последний подход хранит данные и код вместе (в site-packages/myproject) и устанавливает только script в другом месте (поэтому он отображается в $PATH).
Ответ 2
Вы можете использовать pkg_resources.resource_filename, чтобы получить имя файла в ваших файлах data_files.
Ответ 3
Для решения, которое будет хорошо работать внутри/снаружи virtualenv
в Windows/Linux pip
import и os
запустите:
os.path.split(os.path.split(pip.__file__)[0])[0]
Полный пример
from setuptools import setup, find_packages
from os import path
from functools import partial
from pip import __file__ as pip_loc
if __name__ == '__main__':
package_name = 'gen'
templates_join = partial(path.join, path.dirname(__file__),
package_name, 'templates')
install_to = path.join(path.split(path.split(pip_loc)[0])[0],
package_name, 'templates')
setup(
name=package_name,
version='0.0.1',
test_suite=package_name + '.tests',
packages=find_packages(),
package_dir={package_name: package_name},
data_files=[(install_to, [templates_join('.gitignore'),
templates_join('logging.conf')])]
)
Ссылка (моя собственная): fooobar.com/info/526338/...