Определение местоположения файлов данных distutils программно в Python - PullRequest
12 голосов
/ 25 декабря 2011

Я пытаюсь включить файлы данных в distutils для своего пакета и затем обращаться к ним, используя относительные пути (после http://docs.python.org/distutils/setupscript.html#distutils-additional-files)

Моя структура каталогов:

myproject/
  mycode.py
  data/
    file1.dat

код в mycode.py, который на самом деле является скриптом в пакете. Это зависит от доступа к 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 теперь не работает, так как скрипт живет в другом месте из данных.

Как узнать из mycode.py абсолютный путь к myproject/data/file1.dat, чтобы я мог обращаться к нему должным образом в зависимости от того, где пользователь установил пакет?

EDIT
Когда я устанавливаю это с prefix=/home/user/, я получаю data/file1.dat, созданный в /home/user/, который является именно тем, что я хочу, единственная недостающая часть - как программно получить абсолютный путь к этому файлу, учитывая только относительный путь и не зная где пользователь установил пакет. Когда я пытаюсь использовать package_data вместо data_files, это не работает - я просто нигде не создаю data/file1.dat, даже если я удаляю свой MANIFEST файл.

Я прочитал все текущие обсуждения этой, по-видимому, очень распространенной проблемы. Однако все предложенные решения не относятся к описанному выше случаю, , где код, к которому требуется доступ data_files, представляет собой скрипт , и его местоположение может измениться в зависимости от аргумента --prefix на setup.py. Единственный взлом, который я могу придумать, чтобы решить эту проблему, это добавить файл данных в scripts= в setup(), например:

setup(
  ...
  scripts = ["myproject/mycode.py", "myproject/data/file1.data"]
)

это ужасный взлом, но это единственный способ, который я могу придумать, чтобы убедиться, что file1.data будет в том же месте, что и скрипты, определенные в scripts=, так как я не могу найти какой-либо независимый от платформы и чувствительный к установке API восстановить расположение data_files после того, как пользователь запустил setup.py install (потенциально с --prefix= args).

Ответы [ 3 ]

11 голосов
/ 10 февраля 2012

Я думаю, что путаница возникает из-за использования сценариев.Скрипты должны ссылаться на исполняемый исполняемый файл, возможно, на служебный скрипт, связанный с вашим пакетом, или, возможно, на точку входа в функциональность вашего пакета.В любом случае вы должны ожидать, что любые сценарии не будут установлены вместе с остальной частью вашего пакета.Это ожидание обусловлено главным образом соглашением о том, что пакеты считаются библиотеками (и устанавливаются в каталоги lib), тогда как сценарии считаются исполняемыми файлами (и устанавливаются в каталоги bin или Scripts).Кроме того, файлы данных не являются ни исполняемыми файлами, ни библиотеками и полностью отделены.

Таким образом, из сценария необходимо определить, где находятся файлы данных.Согласно Python docs ,

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

Поэтому вам следуетнапишите что-то вроде следующего в сценарий mycode, чтобы найти файл данных:

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 = и package_data =, чтобы вставить данные в пакет, а затем создать простой скрипт, который вызывает модуль в пакете.

Я сделал это, создав это дерево:

.
│   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) и устанавливает только сценарий вдругое место (так это показывает, что тыр в $ PATH).

5 голосов
/ 03 июня 2013

Вы должны иметь возможность использовать pkg_resources.resource_filename , чтобы получить имя файла файла в ваших data_files.

0 голосов
/ 19 марта 2015

Для решения, которое будет хорошо работать внутри / снаружи virtualenv при импорте Windows / Linux pip и 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')])]
    )

Ссылка (моя): https://stackoverflow.com/a/29120636

...