2013-11-29 2 views
40

Можно указать послеустановочный файл сценария Python как часть Setuptools setup.py файла, так что пользователь может выполнить команду:после установки скрипта с Python Setuptools

python setup.py install 

на местном архивный файл проект или

pip install <name> 

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

Я столкнулся с this SO answer from several years ago, который обращается к теме, и это звучит так, как будто в то время было консенсусом, что вам нужно создать подкоманду установки. Если это так, то возможно ли кому-нибудь дать пример того, как это сделать, чтобы пользователю не нужно вводить вторую команду для запуска скрипта?

+0

У многих setup.py есть команда 'setup.py test', которую вы используете после' setup.py install'. – User

+2

Я надеюсь автоматизировать запуск скрипта, а не требовать от пользователя ввода второй команды. Есть предположения? –

+1

Это может быть то, что вы ищете: http://stackoverflow.com/questions/17806485/execute-a-python-script-post-install-using-distutils-setuptools –

ответ

7

Решением может быть включение 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__': позволяет вручную запускать пост-установку из командной строки.

+1

В моем случае переопределение '' run() '' вызывает невозможность установки зависимостей пакета. – Apalala

+1

@Apalala, потому что неправильный 'cmdclass' был заменен, я исправил это. – kynan

+1

А, наконец, мы находим правильный ответ. Почему неправильные ответы получают так много голосов на StackOverflow? В самом деле, вы должны запустить свой 'post_install()' * после * 'install_data.run (self)' в противном случае вы будете пропускать некоторые вещи. Как, например, 'data_files'. Спасибо, кинан. –

28

Это решение является более прозрачным:

Вы совершите несколько дополнений к 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): 
     # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION 
     develop.run(self) 

class PostInstallCommand(install): 
    """Post-installation for installation mode.""" 
    def run(self): 
     # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION 
     install.run(self) 

и вставить 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 PostDevelopCommand(develop): 
    """Post-installation for development mode.""" 
    def run(self): 
     check_call("apt-get install this-package".split()) 
     develop.run(self) 

class PostInstallCommand(install): 
    """Post-installation for installation mode.""" 
    def run(self): 
     check_call("apt-get install this-package".split()) 
     install.run(self) 


setup(
    ... 

P.S. в setuptools нет предустановленных точек входа.Прочитайте this discussion, если вам интересно, почему их нет.

+0

Выглядит чище, чем другие, но не выполняет ли этот пользовательский код * перед * фактической командой 'install'? – raphinesse

+0

@raphinesse Я так думаю, я не помню полностью. Но было обходное решение. Я сделаю полный тест и обновить свой ответ. Спасибо за уведомление. – mertyildiran

+2

Это зависит от вас: если вы сначала вызываете 'run' на родительском *, то ваша команда является пост-установкой, в противном случае это предварительная установка. Я обновил ответ, чтобы отразить это. – kynan

8

Это единственная стратегия, которая работала на меня, когда пост-скрипт установки требует, чтобы уже установлены зависимости пакетов:

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}, 
+0

Почему вы регистрируете обработчик 'atexit', а не просто вызываете функцию после установки после этапа установки? – kynan

+0

@kynan Это то, что сработало для меня. 'setuptools' очень недокументирован. См. Другие (исправленные) ответы в этом Q & A, в котором люди наконец выяснили, кто это делает, только с помощью 'setuptools'. – Apalala

+0

Почему вы используете 'atexit' вместо того, чтобы просто называть' _post_install() 'в вашем' cmdclass'? – kynan

1

Объединяя ответы от @Apalala, @Zulu и @mertyildiran ; это работало для меня в 3.5 среде Python:

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, чтобы сделать некоторые оболочки работать.