使用Python setuptools的安装后脚本


97

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

python setup.py install

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

pip install <name>

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

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


4
我希望自动化脚本运行,而不是要求用户输入第二个命令。有什么想法吗?
克里斯·辛普金斯

1
这可能是你要找的东西:stackoverflow.com/questions/17806485/...
limp_chimp

1
谢谢!我会检查一下的
克里斯·辛普金斯

1
如果您确实需要这样做,那么我由一个快速的Google找到的这篇博客文章似乎很有用。(另请参阅文档中的扩展和重用Setuptools。)
abarnert 2014年

1
@Simon嗯,您正在查看4年前有关某问题可能不是人们想要的东西的评论,因此您无法真正期望对其进行监视和保持最新。如果这是一个答案,那么值得去寻找新的资源来替代它们,但是事实并非如此。如果您需要过时的信息,则可以始终使用Wayback Machine,也可以在当前文档中搜索等效的部分。
abarnert

Answers:


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

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

setup(
    ...

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

    ...
)

您甚至可以在安装过程中调用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(
    ...

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


看起来比其他的更干净,但是这不是在实际命令之前执行自定义代码install吗?
raphinesse

7
这取决于您:如果您先调用run项,则命令是安装后的,否则是安装前的。我已经更新了答案以反映这一点。
kynan '17

1
使用此解决方案,似乎install_requires依赖项被忽略了
ealfonso '18

6
这对我不起作用pip3。发布包时运行安装脚本,但安装时不运行。
埃里克·维纳

1
@JuanAntonioOrozco我已经使用Wayback Machine更新了断开的链接。我不知道为什么现在就坏了。bugs.python.org可能现在有问题。
mertyildiran

13

注意:以下解决方案仅在安装源分发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 '17

1
@kynan因为setuptools文档不足。其他人已经使用正确的解决方案修改了此问答的答案。
阿巴拉拉'17

3
那么其他答案对我不起作用:要么不执行安装后脚本,要么不再处理依赖项。到目前为止,我将坚持atexit不是重新定义install.run()(这就是不再处理依赖项的原因)。另外,为了知道安装目录,我将_post_install()用作方法new_install,使我可以访问self.install_purelibself.install_platlib(不知道要使用哪个,但是self.install_lib很奇怪,这是错误的)。
zezollo

2
我也遇到了依赖关系方面的问题,而atexit对我来说也
有用

7
这里似乎没有一种方法适用于车轮。轮子不运行setup.py,因此消息仅在构建时显示,而在安装软件包时不显示。
JCGB

7

注意:以下解决方案仅在安装源分发zip或tarball或从源树以可编辑模式安装时有效。从二元轮()安装时将无法使用.whl


一个解决方案可能是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 __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 '17

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,以进行一些shell工作。


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_actions未执行...
dojuba

1

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

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


就像这里发布的一些建议一样,这并不能说明您是否以“安装”模式运行。这就是为什么要使用自定义“命令”类的要点。
BuvinJ

0

我无法通过提出的任何建议来解决问题,因此这对我有所帮助。

你可以调用功能,你想刚过安装之后运行setup()setup.py,这样的:

from setuptools import setup

def _post_install():
    <your code>

setup(...)

_post_install()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.