setuptools: розташування папки даних пакета


94

Я використовую setuptools для розповсюдження мого пакету python. Тепер мені потрібно розподілити додаткові файли даних.

З того, що я зібрав із документації setuptools, мені потрібно мати файли даних всередині каталогу пакунків. Однак я б віддав перевагу своїм файлам даних всередині підкаталогу в кореневому каталозі.

Чого б я хотів уникати:

/ #root
|- src/
|  |- mypackage/
|  |  |- data/
|  |  |  |- resource1
|  |  |  |- [...]
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

Що я хотів би мати замість цього:

/ #root
|- data/
|  |- resource1
|  |- [...]
|- src/
|  |- mypackage/
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

Мені просто неприємно мати таку кількість підкаталогів, якщо це не важливо. Я не можу знайти причину, чому я / маю / розміщувати файли всередині каталогу пакунків. Також громіздко працювати з такою кількістю вкладених підкаталогів IMHO. Або є якась вагома причина, яка б виправдовувала це обмеження?


8
Я задав подібне запитання щодо використання 'data_files' для розподілу ресурсів (документів, зображень тощо): stackoverflow.com/questions/5192386/… ... і (дві) відповіді обидва зазначають, що замість них використовують 'package_data'. Зараз я використовую дані пакету, але це означає, що я повинен поміщати свої дані та документи всередину мого пакету, тобто змішувати серед свого вихідного коду. Мені це не подобається. Знімаючи своє джерело, я знаходжу не просто визначення класу, яке я шукаю, а також десятки згадок, які вони отримують у моїх RST, HTML та проміжних файлах. :-(
Джонатан Хартлі,

2
Я знаю, що ця відповідь дуже пізня, @JonathanHartley, але ти можеш зробити будь-який каталог "пакетом", додавши __init__.py файл, навіть якщо цей файл порожній. Таким чином, ви можете тримати каталог даних окремо порожнім __init__.pyфайлом, щоб він виглядав як пакет. Це повинно утримати grep у вашому дереві-джерелі, щоб не забрати їх, але він все одно буде розпізнаний як пакет за допомогою python та його інструментів побудови.
dhj

@dhj Цікава ідея, дякую.
Джонатан Хартлі,

4
@dhj єдиною проблемою такого підходу є те, що python вважає, що ви встановили пакет під назвою 'data'. Якщо інший встановлений вами пакет намагався упакувати дані таким же чином, у вас були б встановлені два суперечливі пакети даних.
пальці ніг

Відповіді:


111

Варіант 1: Встановити як дані пакета

Основною перевагою розміщення файлів даних всередині кореневого пакета Python є те, що він дозволяє вам не турбуватися про те, де будуть знаходитись файли в системі користувача, якою може бути Windows, Mac, Linux, якась мобільна платформа або всередині Egg. Ви завжди можете знайти каталог dataщодо кореня пакета Python, незалежно від того, де і як він встановлений.

Наприклад, якщо я маю макет проекту приблизно так:

project/
    foo/
        __init__.py
        data/
            resource1/
                foo.txt

Ви можете додати функцію, щоб __init__.pyзнайти абсолютний шлях до файлу даних:

import os

_ROOT = os.path.abspath(os.path.dirname(__file__))
def get_data(path):
    return os.path.join(_ROOT, 'data', path)

print get_data('resource1/foo.txt')

Виходи:

/Users/pat/project/foo/data/resource1/foo.txt

Після встановлення проекту як яйце шлях до data зміниться, але код не потрібно змінювати:

/Users/pat/virtenv/foo/lib/python2.6/site-packages/foo-0.0.0-py2.6.egg/foo/data/resource1/foo.txt

Варіант 2: Встановіть у фіксованому місці

Альтернативою може бути розміщення даних поза пакетом Python, а потім:

  1. Майте місце розташування data передане через файл конфігурації, аргументи командного рядка або
  2. Вбудуйте розташування у свій код Python.

Це набагато менш бажано, якщо ви плануєте поширювати свій проект. Якщо ви дійсно хочете зробити це, ви можете встановити свій dataде завгодно на цільовій системі, вказавши призначення для кожної групи файлів, передавши список кортежів:

from setuptools import setup
setup(
    ...
    data_files=[
        ('/var/data1', ['data/foo.txt']),
        ('/var/data2', ['data/bar.txt'])
        ]
    )

Оновлено : Приклад функції оболонки для рекурсивного grep-файлів Python:

atlas% function grep_py { find . -name '*.py' -exec grep -Hn $* {} \; }
atlas% grep_py ": \["
./setup.py:9:    package_data={'foo': ['data/resource1/foo.txt']}

7
Щиро дякую за допомогу змиритися із ситуацією. Тож я із задоволенням побіжу з використанням package_data, як ви (і всі інші) пропонуєте. Однак: чи лише мені здається, що розміщення їх даних та документів всередині їхнього джерельного каталогу є незручним? (наприклад, grepping мій джерело повертає десятки небажаних звернень з моєї документації. Я міг би додати параметри '--exclude-dir' для grep кожного разу, коли я його коли-небудь використовую, що буде відрізнятися від одного проекту до іншого, але це здається нестабільним) можна щось на зразок включити піддиректору 'src' всередину мого пакунка, не порушуючи імпорт тощо
Джонатан Хартлі

Зазвичай я розміщую лише файли даних, які потрібні пакунку, під папку каталогу. Я б встановив документи як data_files. Крім того, ви можете придумати псевдонім оболонки для grep, щоб ігнорувати не-Python файли, щось на зразок grep_py.
samplebias

Гей, samplebias. Дякуємо за оновлення. Це не просто grep, це все - від пошуку файлів у текстовому редакторі до ctags до awk. Я спробую змінити свій проект, щоб розмістити документи в data_files, як ви пропонуєте, подивіться, як це вийде. Скоро назад ... :-)
Джонатан Хартлі,

... це, здається, виходить нормально. Дякую, що налаштували мене на правильний шлях. Чи смачні +50 балів за репутацію?
Джонатан Хартлі,

Дякую! Приємно чути, рада, що це вдалося, і ви робите успіхи!
samplebias

13

Я думаю, що знайшов хороший компроміс, який дозволить вам зберегти таку структуру:

/ #root
|- data/
|  |- resource1
|  |- [...]
|- src/
|  |- mypackage/
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

Вам слід встановити дані як package_data, щоб уникнути проблем, описаних у відповіді samplebias, але для того, щоб зберегти структуру файлу, слід додати до вашого setup.py:

try:
    os.symlink('../../data', 'src/mypackage/data')
    setup(
        ...
        package_data = {'mypackage': ['data/*']}
        ...
    )
finally:
    os.unlink('src/mypackage/data')

Таким чином ми створюємо відповідну структуру "точно вчасно" та організовуємо наше дерево джерел.

Щоб отримати доступ до таких файлів даних у коді, ви просто "використовуєте":

data = resource_filename(Requirement.parse("main_package"), 'mypackage/data')

Мені все ще не подобається вказувати в коді "mypackage", оскільки дані не можуть мати нічого спільного з цим модулем, але я думаю, це хороший компроміс.


-4

Я думаю, що ви можете навести що завгодно як аргумент * data_files * для setup () .


Хм ... я бачу, що це є в документації distutils, хоча я не бачу цього в документації setuptools. У будь-якому разі, як я зможу з часом отримати до нього доступ?
phant0m

Я думаю, що data_files слід використовувати лише для даних, які спільно використовуються між декількома пакетами. наприклад, якщо ви pip встановлюєте з PyPI, тоді файли, перелічені в data_files, встановлюються в каталоги безпосередньо під вашим основним директорією встановлення Python. (тобто не в Python27 / Lib / site-Packages / mypackage, а паралельно з 'Python27 / Lib')
Джонатан Хартлі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.