Включение файлов, отличных от Python, с помощью setup.py

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

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


person Ram Rachum    schedule 23.10.2009    source источник
comment
как ты это делаешь в данный момент? ваш предыдущий вопрос указывает на то, что вы знакомы с тем, как добавить файл лицензии, так какой у вас код, который не работает?   -  person SilentGhost    schedule 23.10.2009
comment
data_files = [('', ['lgpl2.1_license.txt',]),] помещает его в папку Python26.   -  person Ram Rachum    schedule 23.10.2009
comment
После некоторого отрицательного отзыва я снова прочитал ваш вопрос и понял, чего мне не хватало. Я обновил свой ответ, чтобы предоставить нехакерское решение вашего вопроса, которое не требует каких-либо дополнительных модулей (таких как setuptools или распространять).   -  person Evan Plaice    schedule 19.11.2010
comment
Спасибо, Эван. Тем не менее, я совершенно нормально отношусь к использованию setuptools, поскольку это очень распространено.   -  person Ram Rachum    schedule 20.11.2010


Ответы (13)


Вероятно, лучший способ сделать это - использовать директиву setuptools package_data. Это означает использование setuptools (или _ 4_) вместо 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-packages, вам необходимо указать include_package_data=True функции setup(). Дополнительную информацию см. В разделе Добавление файлов без кода.

person Hans L    schedule 07.12.2009
comment
Ссылка на презентацию Яна не работает :( - person Ken Cochrane; 30.09.2011
comment
package_data также доступен для чистых сценариев установки distutils, начиная с Python 2.3. - person Éric Araujo; 09.12.2011
comment
Этот ответ выглядит разумным, но мне не подходит. Поскольку package_data заведомо ненадежен (требуется координация MANIFEST.in и setup.py для добавления файлов в sdist и их установки в качестве отдельных шагов), и автор этого ответа отмечает, что он не протестирован, может ли кто-нибудь подтвердить работает ли у них? Мой файл LICENSE включен в sdist, но не устанавливается, когда я запускаю python setup.py install или pip install Package - person Jonathan Hartley; 12.02.2012
comment
В презентации Яна Бикинга показано, как установить данные пакета только для файлов, находящихся внутри пакета. Мой ЛИЦЕНЗИОННЫЙ файл находится на верхнем уровне моего проекта, то есть не входит ни в какие пакеты. Могу ли я использовать package_data? Использование data_files не является запуском, потому что оно помещает файлы в общесистемную папку. не связан с моим проектом, и, что еще хуже, расположение меняется в зависимости от того, запускаю ли я setup.py install или pip install из того же sdist. - person Jonathan Hartley; 12.02.2012
comment
Я предполагаю, что причина, по которой это не работает для меня, заключается в том, что файл не находится в каком-либо пакете - это ЛИЦЕНЗИОННЫЙ файл на верхнем уровне репозитория и, следовательно, не может быть установлен с помощью package_data. - person Jonathan Hartley; 12.02.2012
comment
MANIFEST.in - это то, что вам нужно. Кажется, что sdist полностью игнорирует package_data в некоторых версиях setuptools. - person jkp; 13.07.2012
comment
Для вашего первого примера вы захотите удалить include_package_data, поскольку это выглядит в SVN и Subversion и иногда кажется, что оно наступает на package_data. См. http://stackoverflow.com/questions/7522250/how-to-include-package-data-with-setuptools-distribute/ - person Joe; 09.12.2012
comment
Поскольку package_data заведомо ненадежен (...) Возможно, причина в ошибке, описанной в python distutils не включать файлы данных? - person Piotr Dobrogost; 18.03.2013
comment
Этот ответ мне не подходит. Дополнительные файлы не помещаются в архив ... - person lpapp; 26.11.2013
comment
Можно ли отредактировать ответ, включив в него комментарий @ Éric-araujo: package_data доступен в distutils с версии 2.4. - person WhyNotHugo; 25.07.2014
comment
Конечно, вы хотите отредактировать его так, как считаете нужным? - person Hans L; 26.07.2014
comment
Я видел, читал и пробовал package_data, но он не работал, пока я не заметил здесь include_package_data = True. Кажется глупым требовать эту настройку - зачем еще кому-то указывать package_data, если не для включения? - person Edi Bice; 02.11.2017
comment
Обратите внимание, что в ваших папках, содержащих файлы, отличные от Python, по-прежнему должен быть __init__.py маркер пакета. Если он отсутствует, предупреждение не генерируется и файлы не копируются. - person Bostone; 21.08.2018
comment
У меня также была проблема, когда package_data не работал. Выполнение обеих вещей (добавление в MANIFEST.in и добавление в package_data) исправило это для меня. - person MultipleMonomials; 31.12.2018
comment
Сейчас 2019 год, ни один из ответов, получивших наибольшее количество голосов, у меня не сработал - я нашел свой путь и добавил еще один ответ. (Извините за рассылку спама, но из-за того, что он новый, он мало заметен и действительно может помочь людям попасть сюда) - person jsbueno; 03.10.2019

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

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

Шаг 1. Чтобы добавить файл в исходный архив, включите его в МАНИФЕСТ

Создайте шаблон 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'])]

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

person Evan Plaice    schedule 15.06.2010
comment
MANIFEST - только управляющие файлы, включенные в исходный архив (созданный sdist). Перечисленные там файлы не будут установлены. - person David Cournapeau; 17.11.2010
comment
@ Дэвид Я не понимал, как далеко я был в своем первом подходе. Я обновил ответ, чтобы он был правильным, чтобы выполнить то, что задавал вопрос, без каких-либо дополнительных сторонних библиотек. - person Evan Plaice; 19.11.2010
comment
Я считаю, что редактирование схем установки вручную - очень плохая идея. - person Éric Araujo; 09.12.2011
comment
@ Эрик Какая-то конкретная причина? и есть ли у вас жизнеспособная альтернатива установщику, для работы которой не требуются сторонние пакеты (например, setup_tools). Я выбрал distutils вместо setuptools, потому что он включен в обычную установку python, и я создавал модули для PYPI. Теперь должен быть лучший способ сделать это, используя distutils2, но я давно не касался python, поэтому не знаю, как это сделать. Поскольку вы, кажется, знакомы с distutils2, я думаю, что для всех нас было бы полезно иметь подходящую альтернативу distutils2. - person Evan Plaice; 15.12.2011
comment
Потому что использование внутренних объектов, от которых зависит distutils и другие инструменты, по своей сути плохая идея. Для решения pure-distutils используйте package_data. - person Éric Araujo; 21.12.2011
comment
Как уже упоминалось в других потоках, package_data не работает, если файл отсутствует в пакете. - person Gringo Suave; 03.01.2013
comment
@ ÉricAraujo: Это неплохая идея, потому что другого выхода нет. Плохой дизайн distutils - это правда. Но это де-факто публичный API, который никогда не изменится, потому что он многое сломает. Будем надеяться, что distutils2 предоставит лучшие рекомендуемые способы. - person anatoly techtonik; 09.06.2013
comment
Созданное таким образом колесо поместит файлы данных в каталог данных, и никто точно не знает, где оно находится. По сути, выгрузка файлов данных в мусорное ведро. Это вообще не выход ... - person wvxvw; 01.07.2021

Сейчас 2019 год, и вот что работает - несмотря на советы здесь и там, то, что я нашел в Интернете, наполовину задокументировано, это использование setuptools_scm, переданное в качестве параметров setuptools.setup. Это будет включать в пакет wheel любые файлы данных с версией на вашей VCS, будь то git или любой другой, и будет производить установку pip из репозитория 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, который я ввел, но оставлять важные файлы для работы программы - это несколько важный)

person jsbueno    schedule 14.09.2019

создайте MANIFEST.in в корне проекта с помощью recursive-include в требуемый каталог или include с именем файла.

include LICENSE
include README.rst
recursive-include package/static *
recursive-include package/templates *

документацию можно найти здесь

person All Іѕ Vаиітy    schedule 20.09.2017

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

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

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

Шаг 3: установите include_package_data=True в функции setup(), чтобы скопировать эти файлы в site-package.

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

person debuglife    schedule 26.10.2018

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

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

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

include_package_data: False

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

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

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

person rv.kvetch    schedule 28.09.2018
comment
Я искал способ не создавать MANIFEST.in, у меня это сработало. Последняя строчка тоже была для меня решающей. Мои строки были include_package_data=False, package_data={ "": ["../CHANGELOG.md"] }, - person Mendhak; 23.05.2020

В setup.py в разделе настройки (:

setup(
   name = 'foo library'
   ...
  package_data={
   'foolibrary.folderA': ['*'],     # All files from folder A
   'foolibrary.folderB': ['*.txt']  #All text files from folder B
   },
person Dashing Adam Hughes    schedule 27.12.2014
comment
На самом деле это ничего не делает для достижения цели ОП. Все, что вы напишете в package_data, не повлияет на действия setup.py install, если вы не измените саму команду установки. Если только эти файлы не находятся в каталоге пакета, чего обычно следует избегать. - person wvxvw; 06.12.2018

Это работает в 2020 году!

Как говорили другие, создайте MANIFEST.in, где находится ваш setup.py.

Далее в манифесте включаем / исключаем все необходимое. Будьте осторожны с синтаксисом. Пример: допустим, у нас есть папка с шаблоном, которую нужно включить в исходный пакет.

в файле манифеста сделайте следующее:

recursive-include template *

Убедитесь, что вы оставили пробел между именем каталога и шаблоном для файлов / каталогов, как указано выше. Не делайте этого, как мы делаем в .gitignore

recursive-include template/* [this won't work]

Другой вариант - использовать include. Вариантов масса. Посмотрите здесь их документы для Manifest.in

И последний важный шаг: включите этот параметр в свой setup.py, и все готово!

   setup(
    ...
    include_package_data=True,
    ......
)

Надеюсь, это поможет! Удачного кодирования!

person Deekshith Anand    schedule 14.07.2020

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

Во-первых, согласно приведенному выше комментарию разработчика Python, 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']},
)
person Gringo Suave    schedule 05.08.2013
comment
Он будет жаловаться, что каталог pkgame не существует - person Anthony Kong; 05.01.2016

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

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

from setuptools import setup


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

файл - MANIFEST.in:

include license.txt

file - filewithinstallcommands:

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
person Scott Bowers    schedule 01.06.2015

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

  1. Как уже упоминалось - добавьте MANIFEST.in в проект и укажите папку / файлы, которые вы хотите включить. В моем случае: recursive-include folder_with_extra_stuff *
  2. Опять же, как уже было сказано - добавьте include_package_data=True в свой setup.py. Это очень важно, потому что без него будут перенесены только файлы, соответствующие *.py.
  3. Вот чего не хватало! - Добавьте пустой __init__.py в папку с данными. Мне пришлось добавить этот файл в свой folder-with-extra-stuff.
  4. Дополнительно - Не уверен, что это требование, но с моими собственными модулями Python я увидел, что они заархивированы внутри файла .egg в site-packages. Поэтому мне пришлось добавить zip_safe=False в мой setup.py файл.

Окончательная структура каталогов

my-app/
├─ app/
│  ├─ __init__.py
│  ├─ __main__.py
├─ folder-with-extra-stuff/
│  ├─ __init__.py
│  ├─ data_file.json
├─ setup.py
├─ MANIFEST.in
person Alex Weitz    schedule 26.03.2021
comment
Хороший обзор - для меня проблема заключалась в том, как я объявлял файлы в MANIFEST.in - спасибо! - person Bede Constantinides; 06.05.2021
comment
вы можете обновить это с помощью структуры каталогов? вот хороший сайт для этого ascii-tree-generator.com - person Led; 16.06.2021
comment
@Led уверен, без проблем. - person Alex Weitz; 17.06.2021
comment
большое спасибо, это решение сработало для меня. Дополнительный init .py сделал свое дело - person Led; 25.06.2021

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

import os
import setuptools
from setuptools.command.build_py import build_py
from shutil import copyfile

HERE = os.path.abspath(os.path.dirname(__file__))
NAME = "thepackage"

class BuildCommand(build_py):
    def run(self):
        build_py.run(self)

        if not self.dry_run:
            target_dir = os.path.join(self.build_lib, NAME)
            for fn in ["VERSION", "LICENSE.txt"]:
                copyfile(os.path.join(HERE, fn), os.path.join(target_dir,fn))

 
 
setuptools.setup(
    name=NAME,
    cmdclass={"build_py": BuildCommand},
    description=DESCRIPTION,
    ...
)
person Julian Mann    schedule 19.09.2020

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

person Ram Rachum    schedule 23.10.2009
comment
Смотрите мой пост. Это не должно быть уродливо. Просто сложно найти хороший пример в сети, потому что трудно найти хорошую документацию по установке пакетов. - person Evan Plaice; 15.06.2010