使用 Python setuptools 的安装后脚本
- 2024-12-27 08:47:00
- admin 原创
- 149
问题描述:
是否可以将安装后 Python 脚本文件指定为 setuptools setup.py 文件的一部分,以便用户可以运行以下命令:
python setup.py install
在本地项目文件存档中,或
pip install <name>
对于 PyPI 项目,脚本将在标准 setuptools 安装完成时运行?我希望执行可以在单个 Python 脚本文件中编码的安装后任务(例如,向用户提供自定义安装后消息,从不同的远程源存储库中提取其他数据文件)。
几年前我偶然发现了这个 SO 答案,它解决了这个主题,听起来当时的共识是您需要创建一个 install 子命令。如果情况仍然如此,是否有人可以提供一个如何执行此操作的示例,以便用户无需输入第二个命令来运行脚本?
解决方案 1:
注意:以下解决方案仅在安装源分发 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
参数: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:setuptools 上没有任何可用的预安装入口点。如果您想知道为什么没有,请阅读此讨论。
解决方案 2:
注意:以下解决方案仅在安装源分发 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},
解决方案 3:
注意:以下解决方案仅在安装源分发 zip 或 tarball 时有效,或者在可编辑模式下从源树安装时有效。从二进制轮安装时无效.whl
( )
一种解决方案可能是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__':
语句允许您从命令行手动启动后安装。
解决方案 4:
结合@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 工作。
解决方案 5:
我认为执行后安装并满足要求的最简单方法是修饰对以下内容的调用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()
。
解决方案 6:
如果使用 atexit,则无需创建新的 cmdclass。您只需在 setup() 调用之前创建 atexit 寄存器即可。它的作用相同。
此外,如果您需要先安装依赖项,则这不适用于pip install,因为您的 atexit 处理程序将在 pip 将包移动到位之前被调用。
解决方案 7:
我无法通过任何提出的建议解决问题,所以这是对我有帮助的。
您可以在安装后立即调用想要运行的函数,如下setup()
所示setup.py
:
from setuptools import setup
def _post_install():
<your code>
setup(...)
_post_install()