Сценарій після інсталяції з Python setuptools


97

Чи можна вказати файл сценарію Python після інсталяції як частину файлу setup.py setuptools, щоб користувач міг запустити команду:

python setup.py install

у локальному архіві файлів проекту, або

pip install <name>

для проекту PyPI і сценарій буде запущений після завершення встановлення стандартної програми Я прагну виконати завдання після встановлення, які можна закодувати в одному файлі сценарію Python (наприклад, доставити користувачеві повідомлення після встановлення користувачеві, витягнути додаткові файли даних з іншого віддаленого сховища джерел).

Я натрапив на цю SO-відповідь декількох років тому, що стосується теми, і звучить так, ніби консенсус тоді був таким, що вам потрібно створити підкоманду встановлення. Якщо це все ще так, чи можна було б, щоб хтось навів приклад, як це зробити, щоб користувачеві не потрібно було вводити другу команду для запуску сценарію?


4
Я сподіваюся автоматизувати запуск сценарію, а не вимагати від користувача введення другої команди. Будь-які думки?
Кріс Сімпкінс,

1
Це може бути те , що ви шукаєте: stackoverflow.com/questions/17806485 / ...
limp_chimp

1
Дякую! Я перевірю це
Кріс Сімпкінс

1
Якщо вам це потрібно, ця публікація в блозі, яку я швидко знайшов у Google, виглядає як корисна. (Також див. Розширення та повторне використання засобів налаштування в документах.)
abarnert

1
@Simon Ну, ти дивишся на коментар 4-річної давності про те, що, мабуть, не є тим, що хоче хтось із цією проблемою, тож насправді не можна очікувати, що його відстежуватимуть та постійно оновлять. Якби це була відповідь, варто було б докласти зусиль, щоб знайти нові ресурси, щоб замінити їх, але це не так. Якщо вам потрібна застаріла інформація, ви завжди можете скористатися Wayback Machine або ж знайти відповідний розділ у поточних документах.
abarnert

Відповіді:


92

Примітка: Наведене нижче рішення працює лише під час встановлення ZIP-коду розповсюдження або tarball або встановлення в редагованому режимі з дерева джерел. Це не буде працювати під час встановлення з двійкового колеса ( .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командою?
raphinesse

7
Це залежить від вас: якщо ви спочатку зателефонуєте runбатькові, тоді ваша команда буде після встановлення, інакше це попередня установка. Я оновив відповідь, щоб відобразити це.
kynan

1
використовуючи це рішення, здається, що install_requiresзалежності ігноруються
ealfonso

7
Це мені не вдалося pip3. Сценарій встановлення запускався під час публікації пакету, але не під час його встановлення.
Ерік Вінер,

1
@JuanAntonioOrozco Я оновив бите посилання за допомогою Wayback Machine. Я не знаю, чому вона зламана саме в цей момент. Можливо, зараз щось не так із bugs.python.org .
mertyildiran

14

Примітка: Наведене нижче рішення працює лише під час встановлення ZIP-коду розповсюдження або tarball або встановлення в редагованому режимі з дерева джерел. Це не буде працювати під час встановлення з двійкового колеса ( .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обробник, а не просто викликаєте функцію після встановлення після кроку встановлення?
kynan

1
@kynan, оскільки setuptoolsвін досить недокументований . Інші вже внесли відповіді у відповіді на ці запитання правильними рішеннями.
Апалала

3
Ну, інші відповіді для мене не працюють: або сценарій після встановлення не виконується, або залежності більше не обробляються. Поки що я дотримуватимусь, atexitа не перевизначати install.run()(це причина, через яку залежності більше не обробляються). Крім того, для того , щоб знати каталог установки, я поставив в _post_install()якості методу new_install, то , що дозволяє мені отримати доступ до self.install_purelibі self.install_platlib(не знаю , який з них використовувати, але self.install_libце неправильно, щасливий).
zezollo

2
У мене також були проблеми із залежностями, і atexit працює для мене
ealfonso

7
Здається, жоден із методів тут не працює з колесами. Колеса не запускаються setup.py, тому повідомлення відображаються лише під час створення, а не під час встановлення пакета.
JCGB

7

Примітка: Наведене нижче рішення працює лише під час встановлення ZIP-коду розповсюдження або tarball або встановлення в редагованому режимі з дерева джерел. Це не буде працювати під час встановлення з двійкового колеса ( .whl)


Рішення може бути включати post_setup.pyв setup.pyдиректорії «S. 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__':оператор дозволяє вручну запускати після установки з командного рядка.


4
У моєму випадку перевизначення run()призводить до того, що залежності пакета не встановлюються.
Апалала,

1
@Apalala, це було тому, що cmdclassзамінено неправильне , я це виправив.
kynan

1
Ах, нарешті, ми знаходимо правильну відповідь. Як так, неправильні відповіді набирають стільки голосів на StackOverflow? У самому справі, ви повинні запустити свій post_install() після в install_data.run(self)іншому випадку ви будете відсутні деякі речі. Як data_filesпринаймні. Дякую, kynan.
personal_cloud

1
Не працює у мене. Я думаю, з будь-якої причини команда install_dataне виконується в моєму випадку. Отже, хіба не atexitперевага в тому, що сценарій після встановлення буде виконаний врешті-решт, у будь-якій ситуації?
zezollo

3

Поєднуючи відповіді від @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, щоб виконати деякі оболонки.


2

Я думаю, що найпростіший спосіб виконати післявстановку та дотримати вимоги - це оформити виклик до 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().


1
Ви спробували це? Я намагаюся з Python 3.4 і встановлюю працює як зазвичай, але post_action не виконуються ...
dojuba

1

Якщо використовується atexit, немає необхідності створювати новий cmdclass. Ви можете просто створити свій atexit реєстр безпосередньо перед викликом setup (). Це робить те саме.

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


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

1

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

Ви можете викликати функцію, що ви хочете запустити після установки відразу після setup()в setup.py, як це:

from setuptools import setup

def _post_install():
    <your code>

setup(...)

_post_install()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.