Включая не-Python файлы с setup.py

200

Как сделать, чтобы setup.pyвключить файл, который не является частью кода? (В частности, это файл лицензии, но это может быть любая другая вещь.)

Я хочу иметь возможность контролировать местоположение файла. В исходной исходной папке файл находится в корне пакета. (то есть на том же уровне, что и верхний __init__.py.) Я хочу, чтобы он оставался именно там, когда пакет установлен, независимо от операционной системы. Как я могу это сделать?

Рам Рахум
источник
как ты это делаешь в данный момент? Ваш предыдущий вопрос указывает на то, что вы знакомы с тем, как добавить файл лицензии, так каков ваш код, который «не работает»?
SilentGhost
2
data_files = [('', ['lgpl2.1_license.txt',]),]помещает его в папку Python26.
Рам Рахум
После некоторого отрицательного отзыва я снова прочитал ваш вопрос и понял, чего мне не хватало. Я обновил свой ответ, чтобы предоставить не хакерское решение для вашего вопроса, которое не требует каких-либо дополнительных модулей (таких как setuptools или распространять).
Эван Плейс
Спасибо Эван. Тем не менее, я совершенно согласен с использованием setuptools, так как он очень распространен.
Рам Рахум

Ответы:

224

Вероятно, лучший способ сделать это - использовать setuptools package_dataдирективу. Это означает использование setuptools(или distribute) вместо distutils, но это очень плавное «обновление».

Вот полный (но не проверенный) пример:

from setuptools import setup, find_packages

setup(
    name='your_project_name',
    version='0.1',
    description='A description.',
    packages=find_packages(exclude=['ez_setup', 'tests', 'tests.*']),
    package_data={'': ['license.txt']},
    include_package_data=True,
    install_requires=[],
)

Обратите внимание на конкретные строки, которые здесь важны:

package_data={'': ['license.txt']},
include_package_data=True,

package_dataявляется dictименем пакета (пусто = все пакеты) в списке шаблонов (может включать глобусы). Например, если вы хотите указать только файлы в вашем пакете, вы можете сделать это тоже:

package_data={'yourpackage': ['*.txt', 'path/to/resources/*.txt']}

Решение здесь определенно не в том, чтобы переименовывать ваши не pyфайлы с .pyрасширением.

Смотрите презентацию Яна Бикинга для получения дополнительной информации.

ОБНОВЛЕНИЕ: другой [лучший] подход

Другой подход, который работает хорошо, если вы просто хотите контролировать содержимое исходного дистрибутива ( sdist) и иметь файлы вне пакета (например, каталог верхнего уровня), - это добавить MANIFEST.inфайл. См. Документацию Python для формата этого файла.

С тех пор, как я написал этот ответ, я обнаружил, что использование, MANIFEST.inкак правило, менее разочаровывающий подход - просто убедиться, что ваш исходный дистрибутив ( tar.gz) имеет нужные вам файлы.

Например, если вы хотите включить requirements.txtиз верхнего уровня, рекурсивно включите каталог «данных» верхнего уровня:

include requirements.txt
recursive-include data *

Тем не менее, для того, чтобы эти файлы были скопированы во время установки в папку пакета внутри site-пакетов, вам необходимо предоставить include_package_data=Trueэту setup()функцию. См. Добавление файлов без кода для получения дополнительной информации.

Ханс Л
источник
5
Пакетные данные также доступны для чистых скриптов установки distutils начиная с Python 2.3.
Эрик Араужо
15
Этот ответ выглядит разумным, но не работает для меня. Поскольку package_data общеизвестно ненадежен (требуется согласование файлов MANIFEST.in и setup.py для добавления файлов в sdist и их установки, как отдельных шагов), и автор этого ответа отмечает, что он «не проверен», может кто-нибудь еще подтвердите, работает ли у них? Мой файл LICENSE включен в sdist, но не устанавливается, когда я запускаю «python setup.py install» или «pip install Package»
Джонатан Хартли,
11
В презентации Яна Бикинга показано, как установить данные пакета для файлов, которые находятся внутри пакета. Мой файл LICENSE находится на верхнем уровне моего проекта, то есть не в каких-либо пакетах. Могу ли я использовать package_data? Использование data_files не является началом, потому что это помещает файлы в общесистемное местоположение. не связана с моим проектом, и, что еще хуже, расположение меняется в зависимости от того, запускаю ли я "setup.py install" или "pip install" из того же sdist.
Джонатан Хартли
8
Я предполагаю, что причина, по которой он не работает для меня, состоит в том, что файл не находится ни в одном пакете - это файл LICENSE на верхнем уровне хранилища, и, следовательно, его нельзя установить с помощью 'package_data'
Джонатан Хартли
7
Этот ответ не работает для меня. Дополнительные файлы не попадают в архив ...
lpapp
44

Чтобы выполнить то, что вы описываете, потребуется два шага ...

  • Файл должен быть добавлен в исходный архив
  • setup.py необходимо изменить, чтобы установить файл данных в исходный путь

Шаг 1: Чтобы добавить файл в исходный архив, включите его в MANIFEST

Создайте шаблон MANIFEST в папке, содержащей setup.py

MANIFEST - это текстовый файл со списком всех файлов, которые будут включены в исходный архив.

Вот как выглядит МАНИФЕСТ для моего проекта:

  • CHANGELOG.txt
  • INSTALL.txt
  • lICENSE.TXT
  • pypreprocessor.py
  • README.txt
  • setup.py
  • test.py
  • todo.txt

Примечание: В то время как sdist делает добавить некоторые файлы в автоматическом режиме , я предпочитаю , чтобы явно указать их , чтобы убедиться , вместо того , чтобы предсказать , что он делает и не делает.

Шаг 2. Чтобы установить файл данных в исходную папку, измените файл setup.py

Поскольку вы хотите добавить файл данных (LICENSE.txt) в исходную папку установки, вам нужно изменить путь установки данных, чтобы он соответствовал пути установки источника. Это необходимо, поскольку по умолчанию файлы данных устанавливаются в другое место, чем исходные файлы.

Чтобы изменить каталог установки данных в соответствии с каталогом установки источника ...

Извлеките информацию установочного каталога из distutils с помощью:

from distutils.command.install import INSTALL_SCHEMES

Измените каталог установки данных в соответствии с каталогом установки источника:

for scheme in INSTALL_SCHEMES.values():
    scheme['data'] = scheme['purelib']

И добавьте файл данных и местоположение в setup ():

data_files=[('', ['LICENSE.txt'])]

Примечание. Приведенные выше действия должны выполнять то, что вы описали стандартным образом, без каких-либо библиотек расширений.

Эван Плейс
источник
10
MANIFEST контролирует только файлы, включенные в исходный архив (созданный sdist). Файлы, перечисленные там, не будут установлены.
Дэвид Курнапо
@ Давид Я не понимал, как далеко я был в моем первом подходе. Я обновил ответ, чтобы он был правильным, чтобы выполнить заданный вопрос, не требуя дополнительных сторонних библиотек.
Эван Плейс
3
@ Эрик Какая-то конкретная причина почему? и есть ли у вас жизнеспособная альтернатива установщика, для которой не требуются сторонние пакеты (например, setup_tools) для работы. Я выбрал distutils вместо setuptools, потому что он включен в стандартную установку python, и я собирал модули для PYPI. Должен быть лучший способ сделать это сейчас, используя distutils2, но я давно не касался python, поэтому не знаю как. Поскольку вы, кажется, хорошо осведомлены о distutils2, я думаю, что для всех остальных было бы полезно иметь правильную альтернативу distutils2.
Эван Плейс
6
Как уже упоминалось, в других потоках package_dataне работает, если файл не входит в пакет.
Гринго Суаве
2
@ ÉricAraujo: это неплохая идея, так как другого пути нет. Это плохой дизайн distutils - это правда. Но это де-факто публичный API, который никогда не изменится, потому что он сломает многие вещи. Будем надеяться, что distutils2 предоставит лучшие рекомендуемые способы.
анатолий техтоник
7

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

Вот что сработало для меня (придумал это после обращения к документации):

package_data={
    'mypkg': ['../*.txt']
},

include_package_data: False

Последняя строка была, как ни странно, также крайне важной для меня (вы также можете опустить этот аргумент ключевого слова - он работает так же).

Он копирует все текстовые файлы в вашем верхнем или корневом каталоге (на один уровень выше от пакета, который mypkgвы хотите распространять).

Надеюсь это поможет!

rv.kvetch
источник
Я искал способ не создавать MANIFEST.in, это работало для меня. Последняя строчка также имела решающее значение для меня. Мои строки былиinclude_package_data=False, package_data={ "": ["../CHANGELOG.md"] },
Мендхак
7

Это 2019 год, и вот что работает - несмотря на то, что советы здесь и там, то, что я нашел в интернете на полпути, документировано, используется setuptools_scm, и передается как опция setuptools.setup. Это будет включать любые файлы данных, которые являются версиями на вашей VCS, будь то git или любые другие, в пакет wheel, и будет выполнять «pip install» из репозитория git, чтобы привести эти файлы вместе.

Итак, я просто добавил эти две строки в вызов установки «setup.py». Никаких дополнительных установок или импорта не требуется:

    setup_requires=['setuptools_scm'],
    include_package_data=True,

Нет необходимости вручную указывать package_data или в файле MANIFEST.in - если он версионный, он включается в пакет. В документах «setuptools_scm» делается акцент на создании номера версии из позиции фиксации и не учитывается действительно важная часть добавления файлов данных. (Меня не волнует, если мой файл промежуточного колеса называется «* 0.2.2.dev45 + g3495a1f» или будет использовать номер версии в жестком коде «0.3.0dev0», который я набрал - но оставляю важные файлы для программы, чтобы работа позади несколько важна)

jsbueno
источник
7

Шаг 1: создайте MANIFEST.inфайл в той же папке с setup.py

Шаг 2: включите относительный путь к файлам, которые вы хотите добавить вMANIFEST.in

include README.rst
include docs/*.txt
include funniest/data.json

Шаг 3: установите include_package_data=Trueв setup()функции копирование этих файлов в сайт-пакет

Ссылка здесь.

debuglife
источник
5

В setup.py под настройку (:

setup(
   name = 'foo library'
   ...
  package_data={
   'foolibrary.folderA': ['*'],     # All files from folder A
   'foolibrary.folderB': ['*.txt']  #All text files from folder B
   },
Лихий Адам Хьюз
источник
1
Это на самом деле ничего не делает для достижения цели ОП. Все, что вы пишете, не package_dataбудет влиять на то setup.py install, что делает, если вы не измените саму команду установки. Если эти файлы не находятся в каталоге пакета, чего обычно вам следует избегать.
wvxvw
3

Вот более простой ответ, который работал для меня.

Во-первых, согласно комментарию Python Dev выше, setuptools не требуется:

package_data is also available to pure distutils setup scripts 
since 2.3.  Éric Araujo

Это замечательно, потому что, установив требование setuptools на ваш пакет, вы также должны будете установить его. Коротко:

from distutils.core import setup

setup(
    # ...snip...
    packages          = ['pkgname'],
    package_data      = {'pkgname': ['license.txt']},
)
Гринго Суаве
источник
1
Он будет жаловаться, что каталог pkgameне существует
Энтони Конг
1

Я просто хотел продолжить что-то, что я нашел, работая с Python 2.7 на Centos 6. Добавление package_data или data_files, как упомянуто выше, не работает для меня. Я добавил MANIFEST.IN с нужными мне файлами, которые помещают файлы, отличные от python, в tarball, но не устанавливал их на целевой машине через RPM.

В итоге я смог получить файлы в своем решении, используя «опции» в setup / setuptools. Файлы опций позволяют вам изменять различные разделы спецификационного файла из setup.py. Следующим образом.

from setuptools import setup


setup(
    name='theProjectName',
    version='1',
    packages=['thePackage'],
    url='',
    license='',
    author='me',
    author_email='me@email.com',
    description='',
    options={'bdist_rpm': {'install_script': 'filewithinstallcommands'}},
)

файл - MANIFEST.in:

include license.txt

файл - файл с командами установки:

mkdir -p $RPM_BUILD_ROOT/pathtoinstall/
#this line installs your python files
python setup.py install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES
#install license.txt into /pathtoinstall folder
install -m 700 license.txt $RPM_BUILD_ROOT/pathtoinstall/
echo /pathtoinstall/license.txt >> INSTALLED_FILES
Скотт Бауэрс
источник
-12

Разобравшись с обходным путем: я переименовал свое имя lgpl2.1_license.txtв lgpl2.1_license.txt.py, и поместил несколько тройных кавычек вокруг текста. Теперь мне не нужно data_filesни использовать эту опцию, ни указывать абсолютные пути. Я знаю, что сделать его модулем Python некрасиво, но считаю его менее уродливым, чем указание абсолютных путей.

Рам Рахум
источник
7
Смотрите мой пост. Это не должно быть ужасно. Просто найти хороший пример в сети сложно, потому что трудно найти хорошую документацию для установки пакетов.
Эван Плейс