使用 Python setuptools 的安装后脚本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20288711/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:05:36  来源:igfitidea点击:

Post-install script with Python setuptools

pythonsetuptoolssoftware-distributionsetup.pypypi

提问by Chris Simpkins

Is it possible to specify a post-install Python script file as part of the setuptools setup.py file so that a user can run the command:

是否可以将安装后 Python 脚本文件指定为 setuptools setup.py 文件的一部分,以便用户可以运行以下命令:

python setup.py install

on a local project file archive, or

在本地项目文件存档上,或

pip install <name>

for a PyPI project and the script will be run at the completion of the standard setuptools install? I am looking to perform post-install tasks that can be coded in a single Python script file (e.g. deliver a custom post-install message to the user, pull additional data files from a different remote source repository).

对于 PyPI 项目,脚本将在标准 setuptools 安装完成时运行?我希望执行可以在单个 Python 脚本文件中编码的安装后任务(例如,向用户提供自定义安装后消息,从不同的远程源存储库中提取其他数据文件)。

I came across this SO answer from several years agothat addresses the topic and it sounds as though the consensus at that time was that you need to create an install subcommand. If that is still the case, would it be possible for someone to provide an example of how to do this so that it is not necessary for the user to enter a second command to run the script?

几年前我遇到了这个 SO 答案,它解决了这个话题,听起来好像当时的共识是你需要创建一个安装子命令。如果情况仍然如此,是否有人可以提供一个如何执行此操作的示例,以便用户无需输入第二个命令来运行脚本?

回答by Zulu

Note:The solution below only works when installing a source distribution zip or tarball, or installing in editable mode from a source tree. It will notwork when installing from a binary wheel (.whl)

注意:以下解决方案仅适用于安装源代码分发 zip 或 tarball,或从源树以可编辑模式安装时。它会不会从一个二进制轮安装时的工作(.whl



A solution could be to include a post_setup.pyin setup.py's directory. post_setup.pywill contain a function which does the post-install and setup.pywill only import and launch it at the appropriate time.

一个解决方案可能是包含一个post_setup.pyinsetup.py的目录。post_setup.py将包含一个执行安装后的函数,并且setup.py只会在适当的时间导入和启动它。

In 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},
        ...
    )

In post_setup.py:

post_setup.py

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

if __name__ == '__main__':
    main()

With the common idea of launching setup.pyfrom its directory, you will be able to import post_setup.pyelse it will launch an empty function.

使用setup.py从其目录启动的常见想法,您将能够导入,post_setup.py否则它将启动一个空函数。

In post_setup.py, the if __name__ == '__main__':statement allows you to manually launch post-install from command line.

在 中post_setup.py,该if __name__ == '__main__':语句允许您从命令行手动启动安装后。

回答by mertyildiran

Note:The solution below only works when installing a source distribution zip or tarball, or installing in editable mode from a source tree. It will notwork when installing from a binary wheel (.whl)

注意:以下解决方案仅适用于安装源代码分发 zip 或 tarball,或从源树以可编辑模式安装时。它会不会从一个二进制轮安装时的工作(.whl



This solution is more transparent:

这个解决方案更透明:

You will make a few additions to setup.pyand there is no need for an extra file.

您将添加一些内容,setup.py不需要额外的文件。

Also you need to consider two different post-installations; one for development/editable mode and the other one for install mode.

您还需要考虑两种不同的安装后;一种用于开发/可编辑模式,另一种用于安装模式。

Add these two classes that includes your post-installscript to 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

and insert cmdclassargument to setup()function in setup.py:

cmdclasssetup()函数中插入参数setup.py

setup(
    ...

    cmdclass={
        'develop': PostDevelopCommand,
        'install': PostInstallCommand,
    },

    ...
)

You can even call shell commands during installation, like in this example which does pre-installation preparation:

你甚至可以在安装过程中调用 shell 命令,就像这个例子中的预安装准备:

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(
    ...

P.S. there are no any pre-install entry points available on setuptools. Read this discussionif you are wondering why there is none.

PS setuptools 上没有任何预安装入口点。如果您想知道为什么没有,请阅读此讨论

回答by Apalala

Note:The solution below only works when installing a source distribution zip or tarball, or installing in editable mode from a source tree. It will notwork when installing from a binary wheel (.whl)

注意:以下解决方案仅适用于安装源代码分发 zip 或 tarball,或从源树以可编辑模式安装时。它会不会从一个二进制轮安装时的工作(.whl



This is the only strategy that has worked for me when the post-install script requires that the package dependencies have already been installed:

当安装后脚本要求已经安装了包依赖项时,这是唯一对我有用的策略:

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

回答by Ezbob

Combining the answers from @Apalala, @Zulu and @mertyildiran; this worked for me in a Python 3.5 environment:

结合@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},
...

This also gives you access the to the installation path of the package in install_path, to do some shell work on.

这也使您可以访问 中包的安装路径install_path,以进行一些 shell 工作。

回答by Mbm

I think the easiest way to perform the post-install, and keep the requirements, is to decorate the call to setup(...):

我认为执行安装后并保持要求的最简单方法是将调用装饰为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=['...
    )
)

This will run setup()when declaring setup. Once done with the requirements installation, it will run the _post_install()function, which will run the inner function _post_actions().

这将setup()在声明时运行setup。完成需求安装后,它将运行_post_install()函数,该函数将运行内部函数_post_actions()

回答by myjay610

If using atexit, there is no need to create a new cmdclass. You can simply create your atexit register right before the setup() call. It does the same thing.

如果使用 atexit,则无需创建新的 cmdclass。您可以在 setup() 调用之前简单地创建 atexit 寄存器。它做同样的事情。

Also, if you need dependencies to be installed first, this does notwork with pip install since your atexit handler will be called before pip moves the packages into place.

另外,如果你需要依赖先安装,但这不是用PIP工作进行安装,因为你的atexit处理程序之前PIP移动套餐到位调用。

回答by sdrenn00

I wasn't able to solve a problem with any presented recommendations, so here is what helped me.

我无法解决任何提出的建议的问题,所以这对我有帮助。

You can call function, that you want to run after installation just after setup()in setup.py, like that:

您可以在setup()in之后调用要在安装后运行的函数setup.py,如下所示:

from setuptools import setup

def _post_install():
    <your code>

setup(...)

_post_install()