在pip安装时运行shell脚本

-1 投票
2 回答
66 浏览
提问于 2025-04-12 18:46

我发布了一个PyPI包,这个包需要一些额外的Apt包,所以我在我的Github仓库里有一个叫做setup.sh的脚本,我希望在用户安装这个包的时候(也就是运行pip install mypackage时),这个脚本能够自动执行。

为了实现这个,我在我的setup.py文件里设置了一个叫做cmdclass的参数,像这样:

from setuptools import setup, find_packages
from setuptools.command.install import install
import subprocess
import codecs 
import os

class CustomInstall(install):
    def run(self):
        subprocess.check_call("bash setup.sh", shell=True)
        install.run(self)

with open('requirements.txt') as f:
    requirements = f.read().splitlines()


setup(
    ...
    cmdclass={'install': CustomInstall},   install_requires=requirements,
    ...
)

当我正常使用python setup.py sdist bdist_wheel来构建时,Custominstall这个类运行得很好,但在用pip安装的时候,它却忽略了Custominstall这个类。

最后,我找到了一条StackOverflow的讨论,从中我了解到我只需要运行python setup.py sdist,我照做了,并在dist文件夹里得到了一个tar.gz文件。但是当我尝试用pip安装这个tar.gz文件时,却出现了:

with open('requirements.txt') as f:

           ^^^^^^^^^^^^^^^^^^^^^^^^

  FileNotFoundError: [Errno 2] No such file or directory: 'requirements.txt'

我哪里做错了?路径不对吗?我的需求文件在仓库的父文件夹里(不是在其他任何文件夹里)。

2 个回答

2

现在的问题是,pip这个工具会在你当前所在的文件夹里运行。也就是说,如果你在某个文件夹里执行命令,pip就会在那个文件夹中工作。你可以看看这个链接了解什么是当前工作目录:什么是当前工作目录?

解决这个问题的方法是查看一下当前执行的Python文件的路径和名称,具体可以参考这个链接:如何获取当前执行的Python文件的路径和名称?。简单来说,就是通过查看__file__来找到你想要访问的目录。

不过,你的问题本身似乎有些不太对劲。你应该明确声明你的代码依赖哪些Python包,然后让pip来处理这些依赖;或者,你可以创建一个Debian包,并在其中声明所有依赖的Debian包。(当然,你可以同时做这两件事。在后者的情况下,你可能还需要做前者,这样你的包才能在非Debian平台上安装。)

你没有告诉我们你的包依赖哪些其他包,所以我们只能用比较宽泛的说法来讨论。假设你的包依赖于requestsbeautifulsoup4,那么你的setup.py文件应该声明这些依赖:

setup(
   ...
   install_requires=["requests", "beautifulsoup4"],
   ...
)

为了方便,你也可以在requirements.txt文件中声明这些依赖,但这个文件在Python打包中并没有正式的作用。你的依赖必须在setup.py中声明(对于现代包,最好使用pyproject.tomlsetup.cfg)。

如果这些依赖可以通过操作系统的包管理器来满足,那么提供另一种安装方式只是为了方便,这个是可选的,但如果能做到的话,确实是个不错的补充。你的包脚本不应该依赖于这个(但反过来可能会依赖)。

#!/bin/sh
apt-get install -y python3-requests python3-bs4

(也许你会注意到,Debian包的名称通常和对应的Python包名称不同。这虽然有点麻烦,但基本上是不可避免的。)

0

出现这个问题的原因是你的 setup.py 文件依赖于 requirements.txt,但是你没有把 requirements.txt 文件包含在你的发布包里。如果你打开你的 .tar.gz 文件,你会发现里面没有 requirements.txt 文件。要解决这个问题,你需要创建一个 MANIFEST.in 文件,内容如下:

include requirements.txt

然后在你的 setup 调用中添加 include_package_data=True

你还需要把其他想要包含在 .tar.gz 发布包中的非Python文件也添加进去,比如你的 setup.sh 脚本。

当你按照这些修改重新构建你的源代码发布包时,你会发现 requirements.txt 现在已经包含在构建的发布包中了。

另外,你也可以在 setup 中使用 package_data 关键字来实现这个功能(或者在 setup.cfg 中使用 [options.package_data])。想了解更多,可以查看这个 链接


不过一般来说,在你的 setup.py 中运行 shell 脚本或安装系统包并不是个好主意。这会让你的包变得更难使用,而不是更简单。

如果你的包依赖于 Python 依赖项,请使用 install_requires 来处理。

撰写回答