带有Python Setuptools的后安装脚本

发布于 2025-02-11 23:27:40 字数 555 浏览 1 评论 0原文

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

python setup.py install

在本地项目文件存档或

pip install <name>

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

我遇到几年前解决这个主题,听起来好像当时的共识是您需要创建一个安装子命令。如果仍然如此,是否可以提供一个如何执行此操作的示例,以便用户不必输入第二个命令来运行脚本?

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

I came across this SO answer from several years ago that 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

弱骨蛰伏 2025-02-18 23:27:40

注意:下面的解决方案仅在安装源分布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参数to setup() setup() 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: ps没有任何可用的预启动输入点setuptools。阅读为什么没有。

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 not work when installing from a binary wheel (.whl)


This solution is more transparent:

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

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-install script to 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 cmdclass argument to setup() function in 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:

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 discussion if you are wondering why there is none.

鹿童谣 2025-02-18 23:27:40

注意:下面的解决方案仅在安装源分布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},

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 not work when installing from a binary wheel (.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},
过潦 2025-02-18 23:27:40

注意:下面的解决方案仅在安装源分布zip或tarball或从源树中安装可编辑模式时起作用。从二进制轮安装(.whl)时,它将无法使用


可以在post_setup.py in 设置中包含一个解决方案.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中,如果__name __ =='__-main __':语句允许您从命令行手动启动后安装。

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 not work when installing from a binary wheel (.whl)


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

In 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:

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

if __name__ == '__main__':
    main()

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

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

月竹挽风 2025-02-18 23:27:40

结合@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中的软件包的安装路径,以进行一些外壳工作。

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

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.

美胚控场 2025-02-18 23:27:40

我认为执行后安装并保留要求的最简单方法是装饰呼叫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()声明设置时。完成要求安装后,它将运行_POST_INSTALL()函数,该功能将运行内部函数_post_actions()

I think the easiest way to perform the post-install, and keep the requirements, is to decorate the call to 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().

忆悲凉 2025-02-18 23:27:40

如果使用Atexit,则无需创建新的CMDClass。您只需在设置()调用之前立即创建ATEXIT注册即可。它做同样的事情。

另外,如果您需要首先安装依赖关系,则可以使用PIP安装 ,因为在PIP将软件包移动到位之前,请调用您的Anexit处理程序。

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.

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

江城子 2025-02-18 23:27:40

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

您可以在setup() in setup.py.py之类的setup()之后拨打函数,就像:

from setuptools import setup

def _post_install():
    <your code>

setup(...)

_post_install()

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:

from setuptools import setup

def _post_install():
    <your code>

setup(...)

_post_install()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文