从setup.py安装依赖项

67

我想知道是否可以在我的setup.py文件中配置依赖项,并运行类似.deb软件包的内容:

$ sudo python setup.py install

它们会自动安装。我已经在网上进行了研究,但所有我找到的内容都仅仅让我感到困惑,例如“requires”、“install_requires”和“requirements.txt”。


3
一个好的答案在 https://dev59.com/1F0a5IYBdhLWcg3wCEsw#63743115。 - Qi Luo
4个回答

39
只需在您的lib文件夹中创建一个requirements.txt文件,并像这样添加所有依赖项:
gunicorn
docutils>=0.3
lxml==0.5a7

然后创建一个setup.py脚本并读取requirements.txt文件。
import os
lib_folder = os.path.dirname(os.path.realpath(__file__))
requirement_path = f"{lib_folder}/requirements.txt"
install_requires = [] # Here we'll add: ["gunicorn", "docutils>=0.3", "lxml==0.5a7"]
if os.path.isfile(requirement_path):
    with open(requirement_path) as f:
        install_requires = f.read().splitlines()
setup(name="mypackage", install_requires=install_requires, [...])

执行python setup.py install将安装您的软件包和所有依赖项。就像@jwodder所说,创建一个requirements.txt文件并不是强制性的,您可以直接在setup.py脚本中设置install_requires。但编写requirements.txt文件是一种最佳实践。

setup函数调用中,您还需要设置versionpackagesauthor等信息,请阅读文档以获取完整示例:https://docs.python.org/3/distutils/setupscript.html

您的软件包目录应该如下所示:

├── mypackage
│   ├── mypackage
│   │   ├── __init__.py
│   │   └── mymodule.py
│   ├── requirements.txt
│   └── setup.py

8
使用install_requires = list(f.read().splitlines())比在循环中逐行追加代码更为简单明了。(或许需要进行list调用;请测试并查看结果。) - jpmc26
或者像这样[line for line in f.read().splitlines() if len(line) > 0]来防止空行。 - hayj
14
你也可以完全放弃使用requirements.txt文件,直接在setup.py中编写setup(install_requires=['gunicorn', 'docutils>=0.3', 'lxml==0.5a7'], ...)。保持原意,但使语言更加通俗易懂。 - jwodder
2
我想要补充的是,应该忽略注释和空格前缀:install_requires = [line for line in map(str.lstrip, f.read().splitlines()) if len(line) > 0 and not line.startswith('#')] - Shay Ben-Sasson
这会连接到互联网并安装所有依赖项,对吧?我如何指示它从本地仓库安装?只需执行pip install mypacke.whl --no-index --find-links deps_folder就可以了吗? - Hossein
1
到目前为止,所有提出的解决方案都以某种方式解析requirements.txt文件,并将这些依赖项用作setup.py中的install_requires。然而,有一个争论点,即这是否是一种好的做法:https://caremad.io/posts/2013/07/setup-vs-requirement/ - ketza

10

另一个可能的解决方案

try:
    # for pip >= 10
    from pip._internal.req import parse_requirements
except ImportError:
    # for pip <= 9.0.3
    from pip.req import parse_requirements

def load_requirements(fname):
    reqs = parse_requirements(fname, session="test")
    return [str(ir.req) for ir in reqs]

setup(name="yourpackage", install_requires=load_requirements("requirements.txt"))


1
这对我起作用了。不过我不知道将来会不会失败(如果它曾经失败过)。 - muammar
3
@muammar,它可能会失败,因为pip明确不应以编程方式使用。在pip._internal.req中看到那个下划线吗?这意味着它是包内部的一部分,不应在外部使用,并且可能会在没有警告的情况下更改。绝对不能保证它不会突然改变,事实上它已经改变了。 - MattDMo

6
您从setup.py生成egg信息,然后使用这些egg信息中的requirements.txt
$ python setup.py egg_info
$ pip install -r <your_package_name>.egg-info/requires.txt 

2
在Python 3.4+中,可以使用来自pathlib的Path类,以有效地执行与@hayj答案相同的操作。
from pathlib import Path
import setuptools

...

def get_install_requires() -> List[str]:
    """Returns requirements.txt parsed to a list"""
    fname = Path(__file__).parent / 'requirements.txt'
    targets = []
    if fname.exists():
        with open(fname, 'r') as f:
            targets = f.read().splitlines()
    return targets

...

setuptools.setup(
    ...
    install_requires=get_install_requires(),
    ...
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接