web-dev-qa-db-ja.com

Python setuptoolsを使用したインストール後スクリプト

ユーザーがコマンドを実行できるように、setuptools setup.pyファイルの一部としてインストール後Pythonスクリプトファイルを指定することは可能ですか?

python setup.py install

ローカルプロジェクトファイルアーカイブ、または

pip install <name>

pyPIプロジェクトの場合、スクリプトは標準のsetuptoolsインストールの完了時に実行されますか?単一のPythonスクリプトファイル(たとえば、カスタムポストインストールメッセージをユーザーに配信する、別のリモートソースから追​​加のデータファイルをプルする)でコーディングできるポストインストールタスクを実行したいリポジトリ)。

私は this SO数年前からの回答 トピックに対処し、その時点でコンセンサスはinstallサブコマンドを作成する必要があるように聞こえます。それでも問題が解決しない場合、ユーザーがスクリプトを実行するために2番目のコマンドを入力する必要がないように、これを行う方法の例を提供することは可能でしょうか?

76
Chris Simpkins

このソリューションはより透明です:

setup.pyにいくつか追加します。追加のファイルは必要ありません。

また、2つの異なるインストール後を考慮する必要があります。 1つは開発/編集モード用で、もう1つはインストールモード用です。

post-installスクリプトを含む次の2つのクラスを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(
    ...
_

PS setuptoolsには、インストール前のエントリポイントはありません。なぜ存在しないのか疑問に思っているなら、 この議論 を読んでください。

70
mertyildiran

これは、インストール後のスクリプトでパッケージの依存関係が既にインストールされている必要がある場合に私にとって有効な唯一の戦略です。

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},
11
Apalala

解決策としては、post_setup.py in 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 __== '__main__':
    setup(
        ...
        cmdclass={'install_data': my_install},
        ...
    )

post_setup.py

def main():
    """Do here your post-install"""
    pass

if __== '__main__':
    main()

setup.pyそのディレクトリから、インポートできますpost_setup.pyそれ以外の場合は、空の関数を起動します。

post_setup.pyif __== '__main__':ステートメントを使用すると、コマンドラインから手動でポストインストールを起動できます。

6
Zulu

ポストインストールを実行し、要件を維持する最も簡単な方法は、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()が実行されます。

3
Mbm

Atexitを使用する場合、新しいcmdclassを作成する必要はありません。 setup()呼び出しの直前にatexitレジスタを作成するだけです。同じことをします。

また、依存関係を最初にインストールする必要がある場合、これはnotを実行します。これは、pipがパッケージを所定の場所に移動する前にatexitハンドラーが呼び出されるためです。

1
myjay610

@ 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にあるパッケージのインストールパスにアクセスして、シェルの作業を行うことができます。

0
Ezbob