在pip安装时运行shell脚本
我发布了一个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 个回答
现在的问题是,pip
这个工具会在你当前所在的文件夹里运行。也就是说,如果你在某个文件夹里执行命令,pip
就会在那个文件夹中工作。你可以看看这个链接了解什么是当前工作目录:什么是当前工作目录?
解决这个问题的方法是查看一下当前执行的Python文件的路径和名称,具体可以参考这个链接:如何获取当前执行的Python文件的路径和名称?。简单来说,就是通过查看__file__
来找到你想要访问的目录。
不过,你的问题本身似乎有些不太对劲。你应该明确声明你的代码依赖哪些Python包,然后让pip
来处理这些依赖;或者,你可以创建一个Debian包,并在其中声明所有依赖的Debian包。(当然,你可以同时做这两件事。在后者的情况下,你可能还需要做前者,这样你的包才能在非Debian平台上安装。)
你没有告诉我们你的包依赖哪些其他包,所以我们只能用比较宽泛的说法来讨论。假设你的包依赖于requests
和beautifulsoup4
,那么你的setup.py
文件应该声明这些依赖:
setup(
...
install_requires=["requests", "beautifulsoup4"],
...
)
为了方便,你也可以在requirements.txt
文件中声明这些依赖,但这个文件在Python打包中并没有正式的作用。你的依赖必须在setup.py
中声明(对于现代包,最好使用pyproject.toml
或setup.cfg
)。
如果这些依赖可以通过操作系统的包管理器来满足,那么提供另一种安装方式只是为了方便,这个是可选的,但如果能做到的话,确实是个不错的补充。你的包脚本不应该依赖于这个(但反过来可能会依赖)。
#!/bin/sh
apt-get install -y python3-requests python3-bs4
(也许你会注意到,Debian包的名称通常和对应的Python包名称不同。这虽然有点麻烦,但基本上是不可避免的。)
出现这个问题的原因是你的 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
来处理。