blz*_*blz 255 python pip setuptools requirements.txt
我有一个requirements.txt文件,我正在使用Travis-CI.看来愚蠢复制中都要求requirements.txt和setup.py,所以我希望到一个文件句柄传递给install_requires在kwarg setuptools.setup.
这可能吗?如果是这样,我该怎么做呢?
这是我的requirements.txt档案:
guessit>=0.5.2
tvdb_api>=1.8.2
hachoir-metadata>=1.3.3
hachoir-core>=1.3.3
hachoir-parser>=1.3.4
Run Code Online (Sandbox Code Playgroud)
Rom*_*uin 231
您可以翻转过来,并列出依赖关系setup.py,并有单字符-点.-在requirements.txt代替.
或者,即使不建议,仍然可以requirements.txt使用以下hack 解析文件(如果它没有通过URL引用任何外部要求)(测试时pip 9.0.1):
install_reqs = parse_requirements('requirements.txt', session='hack')
Run Code Online (Sandbox Code Playgroud)
但这并不过滤环境标记.
在旧版本的pip中,更具体地说,早于6.0版本,有一个公共API可用于实现此目的.需求文件可以包含comments(#),并且可以包含一些其他文件(--requirement或-r).因此,如果你真的想解析一个,requirements.txt你可以使用pip解析器:
from pip.req import parse_requirements
# parse_requirements() returns generator of pip.req.InstallRequirement objects
install_reqs = parse_requirements(<requirements_path>)
# reqs is a list of requirement
# e.g. ['django==1.5.1', 'mezzanine==1.4.6']
reqs = [str(ir.req) for ir in install_reqs]
setup(
...
install_requires=reqs
)
Run Code Online (Sandbox Code Playgroud)
Jon*_*son 156
在它面前,它似乎是requirements.txt和setup.py是愚蠢的重复,而是要明白,虽然形式是相似的,预定的功能有很大的不同是很重要的.
在指定依赖关系时,程序包作者的目标是"无论您在何处安装此程序包,这些都是您需要的其他程序包,以便此程序包能够正常运行."
相比之下,部署作者(可能在不同时间可能是同一个人)有不同的工作,因为他们说"这是我们收集在一起测试的包的列表,我现在需要安装".
软件包作者为各种各样的场景写作,因为他们将他们的工作放在那里,以他们可能不知道的方式使用,并且无法知道将在他们的软件包旁边安装哪些软件包.为了成为一个好邻居并避免与其他包的依赖版本冲突,他们需要指定尽可能广泛的依赖版本.这是做什么install_requires的setup.py.
部署作者编写了一个非常不同的,非常具体的目标:安装在特定计算机上的已安装应用程序或服务的单个实例.为了精确控制部署,并确保测试和部署正确的软件包,部署作者必须指定要安装的每个软件包的确切版本和源位置,包括依赖项和依赖项的依赖项.使用此规范,部署可以重复应用于多台计算机,或在测试计算机上进行测试,部署作者可以确信每次都部署相同的程序包.这就是一个requirements.txt.
所以你可以看到,虽然它们看起来像一个包和版本的大列表,但这两个东西有着截然不同的工作.并且很容易将它混合起来并弄错!但正确的思考方式是requirements.txt对所有各种setup.py包文件中的要求所带来的"问题"的"回答" .而不是手工编写,它通常是通过告诉pip查看setup.py一组所需包中的所有文件,找到它认为符合所有要求的一组包,然后在安装完成后"冻结"而生成的. "将包列表放入文本文件中(这是pip freeze名称的来源).
所以外卖:
setup.py应该声明仍然可行的最松散的依赖版本.它的工作是说一个特定的包可以使用什么.requirements.txt是一个定义整个安装作业的部署清单,不应该被认为是绑定到任何一个包.它的工作是声明所有必要包的详尽列表,以使部署工作.Fre*_*nan 86
它不能采用文件句柄.该install_requires参数可以仅仅是一个字符串或字符串列表.
当然,您可以在设置脚本中读取文件并将其作为字符串列表传递给install_requires.
import os
from setuptools import setup
with open('requirements.txt') as f:
required = f.read().splitlines()
setup(...
install_requires=required,
...)
Run Code Online (Sandbox Code Playgroud)
Tob*_*obu 60
需求文件使用扩展的pip格式,只有在需要补充setup.py更强的约束时才有用,例如指定一些依赖项必须来自的确切URL,或者pip freeze将整个包集冻结为已知工作的输出版本.如果您不需要额外的约束,请仅使用a setup.py.如果你觉得你真的需要发货requirements.txt,你可以把它变成一条线:
.
Run Code Online (Sandbox Code Playgroud)
它将是有效的,并且完全引用setup.py同一目录中的内容.
fam*_*kin 37
虽然不是这个问题的确切答案,但我建议Donald Stufft的博客文章https://caremad.io/2013/07/setup-vs-requirement/以便很好地解决这个问题.我一直在用它取得巨大成功.
简而言之,requirements.txt不是setup.py替代方案,而是部署的补充.保持适当的包依赖性抽象setup.py.设置requirements.txt或更多的'em来获取开发,测试或生产的特定版本的包依赖项.
例如,包含在回购下的包裹deps/:
# fetch specific dependencies
--no-index
--find-links deps/
# install package
# NOTE: -e . for editable mode
.
Run Code Online (Sandbox Code Playgroud)
pip执行package setup.py并安装声明的依赖项的特定版本install_requires.没有两面性,两个工件的目的都得以保留.
fab*_*nvf 19
上面的大多数其他答案都不适用于当前版本的pip的API.这是使用当前版本的pip(在编写本文时为6.0.8,也在7.1.2中工作)的正确方法.您可以使用pip -V检查您的版本.
from pip.req import parse_requirements
from pip.download import PipSession
install_reqs = parse_requirements(<requirements_path>, session=PipSession())
reqs = [str(ir.req) for ir in install_reqs]
setup(
...
install_requires=reqs
....
)
Run Code Online (Sandbox Code Playgroud)
*正确,因为它是使用当前pip的parse_requirements的方法.它仍然可能不是最好的方法,因为,正如上面的海报所说,pip并没有真正维护API.
Wil*_*ega 18
使用parse_requirements是有问题的,因为pip API未公开记录和支持.在pip 1.6中,该函数实际上正在移动,因此它的现有用途可能会中断.
一种更可靠的方法来消除之间的重复,setup.py并将requirements.txt特定于您的依赖项setup.py,然后放入-e .您的requirements.txt文件.其中一位pip开发人员提供了有关为什么这是一种更好的方法的信息,请访问:https://caremad.io/blog/setup-vs-requirement/
vdb*_*oor 14
在Travis中安装当前包.这样可以避免使用requirements.txt文件.例如:
language: python
python:
- "2.7"
- "2.6"
install:
- pip install -q -e .
script:
- python runtests.py
Run Code Online (Sandbox Code Playgroud)
from pip.req import parse_requirements对我不起作用,我认为它适用于我的requirements.txt中的空白行,但这个函数确实有效
def parse_requirements(requirements):
with open(requirements) as f:
return [l.strip('\n') for l in f if l.strip('\n') and not l.startswith('#')]
reqs = parse_requirements(<requirements_path>)
setup(
...
install_requires=reqs,
...
)
Run Code Online (Sandbox Code Playgroud)
这种简单的方法从setup.py. 这是Dmitiry S.答案的变体。此答案仅与 Python 3.6+ 兼容。
每DS,requirements.txt可以记录与特定的版本号具体要求,而setup.py可以记录与宽松版范围抽象的要求。
下面是我的摘录setup.py。
import distutils.text_file
from pathlib import Path
from typing import List
def _parse_requirements(filename: str) -> List[str]:
"""Return requirements from requirements file."""
# Ref: /sf/answers/2942318571/
return distutils.text_file.TextFile(filename=str(Path(__file__).with_name(filename))).readlines()
setup(...
install_requires=_parse_requirements('requirements.txt'),
...)
Run Code Online (Sandbox Code Playgroud)
请注意,这distutils.text_file.TextFile将删除注释。此外,根据我的经验,您显然不需要采取任何特殊步骤来捆绑需求文件。
我不建议做这样的事情。正如多次提到的install_requires,requirements.txt绝对不应该是同一个列表。但是,由于涉及pip 的私有内部 API 有很多误导性的答案,因此可能值得寻找更明智的替代方案......
pip不需要requirements.txt从setuptools setup.py脚本解析文件。该setuptools的项目已经包含在了所有必要的工具顶层包pkg_resources。
它或多或少看起来像这样:
#!/usr/bin/env python3
import pathlib
import pkg_resources
import setuptools
with pathlib.Path('requirements.txt').open() as requirements_txt:
install_requires = [
str(requirement)
for requirement
in pkg_resources.parse_requirements(requirements_txt)
]
setuptools.setup(
install_requires=install_requires,
)
Run Code Online (Sandbox Code Playgroud)
注意事项:
如果您不想强迫用户安装pip,可以使用以下方法模拟其行为:
import sys
from os import path as p
try:
from setuptools import setup, find_packages
except ImportError:
from distutils.core import setup, find_packages
def read(filename, parent=None):
parent = (parent or __file__)
try:
with open(p.join(p.dirname(parent), filename)) as f:
return f.read()
except IOError:
return ''
def parse_requirements(filename, parent=None):
parent = (parent or __file__)
filepath = p.join(p.dirname(parent), filename)
content = read(filename, parent)
for line_number, line in enumerate(content.splitlines(), 1):
candidate = line.strip()
if candidate.startswith('-r'):
for item in parse_requirements(candidate[2:].strip(), filepath):
yield item
else:
yield candidate
setup(
...
install_requires=list(parse_requirements('requirements.txt'))
)
Run Code Online (Sandbox Code Playgroud)
以下接口在 pip 10 中已弃用:
from pip.req import parse_requirements
from pip.download import PipSession
Run Code Online (Sandbox Code Playgroud)
所以我把它切换到简单的文本解析:
with open('requirements.txt', 'r') as f:
install_reqs = [
s for s in [
line.split('#', 1)[0].strip(' \t\n') for line in f
] if s != ''
]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
94036 次 |
| 最近记录: |