Можно ли указать файл сценария Python после установки как часть файла setuptools setup.py, чтобы пользователь мог выполнить команду:
python setup.py install
в локальном файловом архиве проекта или
pip install <name>
для проекта PyPI, и сценарий будет запущен после завершения стандартной установки setuptools? Я хочу выполнить задачи после установки, которые можно закодировать в одном файле сценария Python (например, доставить пользователю настраиваемое сообщение после установки, получить дополнительные файлы данных из другого репозитория удаленного источника).
Я наткнулся на этот SO-ответ несколько лет назад, который затрагивает эту тему, и похоже, что в то время консенсус заключался в том, что вам нужно создать подкоманду установки. Если это все еще так, может ли кто-нибудь предоставить пример того, как это сделать, чтобы пользователю не нужно было вводить вторую команду для запуска сценария?
источник
Ответы:
Примечание. Приведенное ниже решение работает только при установке zip- или tar-архива исходного дистрибутива или установке в редактируемом режиме из дерева исходных текстов. Это не сработает при установке с бинарным колеса (
.whl
)Это решение более прозрачное:
Вы сделаете несколько дополнений,
setup.py
и нет необходимости в дополнительном файле.Также вам нужно рассмотреть две разные пост-установки; один для режима разработки / редактирования, а другой - для режима установки.
Добавьте эти два класса, которые включают ваш сценарий после установки, в
setup.py
:from setuptools import setup from setuptools.command.develop import develop from setuptools.command.install import install class PostDevelopCommand(develop): """Post-installation for development mode.""" def run(self): develop.run(self) # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION class PostInstallCommand(install): """Post-installation for installation mode.""" def run(self): install.run(self) # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION
и вставьте
cmdclass
аргументsetup()
функции вsetup.py
:setup( ... cmdclass={ 'develop': PostDevelopCommand, 'install': PostInstallCommand, }, ... )
Вы даже можете вызывать команды оболочки во время установки, как в этом примере, который выполняет предустановочную подготовку:
from setuptools import setup from setuptools.command.develop import develop from setuptools.command.install import install from subprocess import check_call class PreDevelopCommand(develop): """Pre-installation for development mode.""" def run(self): check_call("apt-get install this-package".split()) develop.run(self) class PreInstallCommand(install): """Pre-installation for installation mode.""" def run(self): check_call("apt-get install this-package".split()) install.run(self) setup( ...
PS в setuptools нет предустановленных точек входа. Прочтите это обсуждение, если вам интересно, почему его нет.
источник
install
командой?run
родительский объект, тогда ваша команда выполняется после установки, в противном случае это предварительная установка. Я обновил ответ, чтобы отразить это.install_requires
зависимости игнорируютсяpip3
. Сценарий установки запускался при публикации пакета, но не при его установке.Примечание. Приведенное ниже решение работает только при установке zip- или tar-архива исходного дистрибутива или установке в редактируемом режиме из дерева исходных текстов. Это не сработает при установке с бинарным колеса (
.whl
)Это единственная стратегия, которая сработала для меня, когда сценарий после установки требует, чтобы зависимости пакета уже были установлены:
import atexit from setuptools.command.install import install def _post_install(): print('POST INSTALL') class new_install(install): def __init__(self, *args, **kwargs): super(new_install, self).__init__(*args, **kwargs) atexit.register(_post_install) setuptools.setup( cmdclass={'install': new_install},
источник
atexit
обработчик, а не просто вызываете функцию после установки после этапа установки?setuptools
это недостаточно документировано. Другие уже добавили в свои ответы на этот вопрос и ответ правильные решения.atexit
и не переопределятьinstall.run()
(это причина, по которой зависимости больше не обрабатываются). Кроме того, чтобы узнать каталог установки, я_post_install()
использовал методnew_install
, позволяющий мне получить доступ кself.install_purelib
иself.install_platlib
(не знаю, какой из них использовать, ноself.install_lib
это неправильно, как ни странно).Примечание. Приведенное ниже решение работает только при установке zip- или tar-архива исходного дистрибутива или установке в редактируемом режиме из дерева исходных текстов. Это не сработает при установке с бинарным колеса (
.whl
)Решением может быть включение
post_setup.py
вsetup.py
каталог.post_setup.py
будет содержать функцию, которая выполняет пост-установку иsetup.py
только импортирует и запускает ее в нужное время.В
setup.py
:from distutils.core import setup from distutils.command.install_data import install_data try: from post_setup import main as post_install except ImportError: post_install = lambda: None class my_install(install_data): def run(self): install_data.run(self) post_install() if __name__ == '__main__': setup( ... cmdclass={'install_data': my_install}, ... )
В
post_setup.py
:def main(): """Do here your post-install""" pass if __name__ == '__main__': main()
При обычной идее запуска
setup.py
из его каталога вы сможете импортировать,post_setup.py
иначе он запустит пустую функцию.В
post_setup.py
, тоif __name__ == '__main__':
оператор позволяет вручную запускать после установки из командной строки.источник
run()
приводит к тому, что зависимости пакета не устанавливаются.cmdclass
была заменена, я исправил это.post_install()
после вinstall_data.run(self)
противном случае вы будете отсутствовать некоторые вещи. Поdata_files
крайней мере. Спасибо, kynan.install_data
в моем случае по какой-то причине команда не выполняется. Итак, разве нетatexit
преимущества в том, что сценарий после установки будет выполнен в конце в любой ситуации?Объединяя ответы @Apalala, @Zulu и @mertyildiran; это сработало для меня в среде Python 3.5:
import atexit import os import sys from setuptools import setup from setuptools.command.install import install class CustomInstall(install): def run(self): def _post_install(): def find_module_path(): for p in sys.path: if os.path.isdir(p) and my_name in os.listdir(p): return os.path.join(p, my_name) install_path = find_module_path() # Add your post install code here atexit.register(_post_install) install.run(self) setup( cmdclass={'install': CustomInstall}, ...
Это также дает вам доступ к пути установки пакета в
install_path
, чтобы выполнить некоторую работу с оболочкой.источник
Я думаю, что самый простой способ выполнить пост-установку и соблюсти требования - это украсить вызов
setup(...)
:from setup tools import setup def _post_install(setup): def _post_actions(): do_things() _post_actions() return setup setup = _post_install( setup( name='NAME', install_requires=['... ) )
Это будет выполняться
setup()
при объявленииsetup
. После завершения установки требований он запустит_post_install()
функцию, которая запустит внутреннюю функцию_post_actions()
.источник
При использовании atexit нет необходимости создавать новый cmdclass. Вы можете просто создать свой регистр atexit прямо перед вызовом setup (). То же самое.
Кроме того, если вам нужно сначала установить зависимости, это не работает с установкой pip, поскольку ваш обработчик atexit будет вызван до того, как pip переместит пакеты на место.
источник
Я не смог решить проблему ни с одним из представленных рекомендаций, поэтому вот что мне помогло.
Вы можете вызвать функцию, которую хотите запустить после установки сразу после
setup()
входаsetup.py
, вот так:from setuptools import setup def _post_install(): <your code> setup(...) _post_install()
источник