如何从现有的Pipfile.lock
生成requirements.txt
文件而不锁定?
当我运行pipenv lock -r
时,它会忽略现有的Pipfile.lock
并再次执行锁定过程。
有一个解决方法:
$ pipenv sync
$ pipenv run pip freeze
在我的特殊情况下,我正在构建 docker 映像并在Dockerfile
中使用requirements.txt
#$ 。 我想避免在主机上创建虚拟环境只是为了能够创建requirements.txt
。
Pipenv 没有为此提供方法,您可以寻找其他 pipfile 实用程序库,例如pipfile-requirements
你可以跑
pipenv run pip freeze > requirements.txt
大声笑,我已经提到过那个图书馆。
我个人不喜欢有一个专门的图书馆。 此外,团队成员很有可能已经安装了jq
或其他一些通用工具。
你甚至可以跑
pipenv lock --requirements > requirements.txt
它不会像您期望的那样工作,因为正如我所写:
当我运行
pipenv lock -r
时,它会忽略现有的Pipfile.lock
并再次执行锁定过程。
换句话说,它执行更新,这可能会破坏分发。 想象一下,您生成requirements.txt
以在 Dockerfile 中使用它来构建 docker 映像。 您的应用程序在本地可以工作,但是当您使用pipenv lock
生成requirements.txt
$ 时,需求可能会更新为不兼容或损坏的版本(但希望这种情况很少见)。 在运行映像之前您不会知道这一点。 因此,您需要在运行pipenv lock
后再次测试应用程序。
如果您不想使用jq
,那么最好使用我在第一篇文章中提出的方法pipenv sync
(它不会更新)。
@Zebradil您的jq
oneliner 方法比@frostming自己的pipfile-requirements
包(100多行python代码)简单得多,因为我已经安装jq
,没有其他依赖项必须的,太好了。
但是,在几次 git 提交之后,我注意到pipenv lock --requirements
输出的内容与jq
通过Pipfile.lock
文件收集并打印出来的内容之间的区别:
jq
输出没有-i https://pypi.org/simple
作为第一行,这与pipenv lock --r
始终作为第一行插入的内容相反。jq
输出不包含包的注释。 例如: pipenv lock --r
输出有这一行appnope==0.1.0 ; sys_platform == 'darwin'
,但在jq
输出中,它是appnope==0.1.0
。 另一个例子是pipenv lock -r
生成pexpect==4.7.0 ; sys_platform != 'win32'
而jq
生成pexpect==4.7.0
,不确定这是否重要。jq
大概采用Pipfile.lock
文件中的包顺序,它总是按字母和字符长度的升序排序,例如flask
在flask-sqlalchemy
前面flask-XXXXX
包,而pipenv lock --r
$ 在flask-sqlalchemy
flask
$ ,这与Pipfile.lock
中的顺序不同。 这是一个主要的烦恼,因为它不会生成最小的 git diff。 我认为这是pipenv
中的错误。嗨@ye ,方法的很好比较。 它可能会帮助人们为他们的特定情况选择适当的解决方案并避免警告。
是的,正如您所说, jq
的建议方法功能有限。 可以扩展它以添加注释和包索引 URL,但我现在不需要这个。
为了避免在生成的 requirements.txt 中出现差异,应该考虑每次都使用相同的方法。 同样,使用不同的代码格式化工具可能会导致结果不一致。 所以,我认为这里没有问题。
你可以跑
pipenv run pip freeze > requirements.txt
这就是我在第一篇文章中提到的解决方法。
但它只有在您的 pipenv 环境同步时才有效(安装了所有软件包)。
直接从Pipfile.lock
中提取依赖项对我来说更方便:jq -r '.default | to_entries[] | .key + .value.version' \ Pipfile.lock > requirements.txt
你好,
感谢您的解决方案。 我遇到了同样的问题,但我还需要pipenv lock -r
创建的源的定义,即:-i,--extra-index-url。 这是因为我使用私人资源。
@Zebradil我认为您提到了这一点。
因此,我在 python 中创建了另一个包含该功能的最小无依赖关系脚本。 如果您在 Pipfile 中以这种方式定义了源,它还会扩展 env var。
如果有人想看看,我会把它留在这里: https ://gist.github.com/rcastill/dab85c234dd10fa7af56755116c75aee
如果它对其他人有帮助,以下是如何在结果中包含散列:
jq --raw-output '.default | to_entries[] | .key + .value.version + (.value.hashes | map(" --hash=\(.)") | join(""))' Pipfile.lock
这会创建类似的条目
paramiko==2.6.0 --hash=sha256:99f0179bdc176281d21961a003ffdb2ec369daac1a1007241f53374e376576cf --hash=sha256:f4b2edfa0d226b70bd4ca31ea7e3893259907283da2304865d
这使得pip
强制执行哈希。
如果您只想包含原始需求文件中的需求(前提是它们已经使用==
锁定到特定版本):
jq --raw-output '.default | to_entries[] | .key + .value.version + (.value.hashes | map(" --hash=\(.)") | join(""))' Pipfile.lock | grep --file=<(grep --only-matching --perl-regexp '^.*(?===)' requirements.txt | tr '[:upper:]' '[:lower:]') > new.txt && mv new.txt requirements.txt
tr
是必需的,因为 requirements.txt 文件可能包含混合大小写的包名称,但pipenv install -r requirements.txt
在 Pipfile 中将它们小写。
这是一个小 Python 脚本,以防您想将Pipfile
(不是锁定文件)转换为 requirements.txt 文件。
import configparser
def main():
parser = configparser.ConfigParser()
parser.read("Pipfile")
packages = "packages"
with open("requirements.txt", "w") as f:
for key in parser[packages]:
value = parser[packages][key]
f.write(key + value.replace("\"", "") + "\n")
if __name__ == "__main__":
main()
@frostming嗨,我发现https://github.com/frostming/pipfile-requirements很有用,但为什么它没有集成到 pipenv 中?
@linusguan该工具是为那些不想安装大pipenv库的人而存在的,当你安装了pipenv时,你可以使用pipenv lock -r
@frostming我发现它与不支持 pipfile.lock 的其他工具一起使用非常有用。
pipenv lock -r
的问题是它更新了 pipfile.lock 所以我不能使用它来生成确定性构建以及其他工具。 像pipenv lock -r --ignore-pipfile
这样的东西是理想的。
这是另一个使用哈希从 Pipfile.lock 文件生成 requirements.txt 的 python 脚本:
import os
import json
__dir__ = os.path.dirname(os.path.realpath(__file__))
def read_json_file(path):
with open(path) as f:
return json.load(f)
def main():
root = read_json_file(os.path.join(__dir__, 'Pipfile.lock'))
for name, pkg in root["default"].items():
version = pkg["version"]
sep = lambda i: "" if i == len(pkg["hashes"]) - 1 else " \\"
hashes = [f'--hash={t}{sep(i)}' for i, t in enumerate(pkg["hashes"])]
tail = '' if len(hashes) == 0 else f' {hashes[0]}'
print(f'{name} {version}{tail}')
for h in hashes[1:]:
print(f' {h}')
if __name__ == "__main__":
main()
@Zebradil谢谢! 你的解决方案真的对我有用。
brew install jq
安装jq
工具Pipfile.lock
生成requirements.txt
#$看起来这可以用--keep-outdated
标志解决,还是我弄错了?
pipenv lock --keep-outdated -d -r > requirements.txt
PS这是一个令人讨厌的冗长标志来解决这个问题
不幸的是@jacobisaliveandwell --keep-outdated 标志似乎更新了子依赖项: https ://github.com/pypa/pipenv/issues/3975
@paytonrules这是一个错误,但旗帜的精神仍然是这个问题的答案。
PS不需要为此大拇指向下:-(
只想提一下,从所述的解决方法中,存在差异:
pipenv run pip freeze
返回区分大小写的包名称(例如PyYAML
)
pipenv lock --requirements
返回所有小写包名(例如pyyaml
)
@Darkless012您应该打开另一张描述该问题的票证,并将此问题作为相关参考。
纯 bash,只是包,没有别的,以防有人不能或不想安装 jq,以防万一它对某人有帮助,
cat Pipfile.lock \
| grep -B1 '"hashes"\|"version": ' \
| grep -v '"markers": \|"hashes": ' \
| grep ": {\|version" \
| sed -e 's/: {$//g' \
| tr '\n' ',' | tr -s ' ' ' ' \
| sed -e 's/, "version": "//g;s/", "/ /g;s/"//g;s/,//g' \
| tr ' ' '\n' \
| grep -v "^$" > requirements.txt
是否有任何东西也可以将散列从 Pipfile 复制到 requirements.txt(例如,给定类似平台 str 的东西)以便pip install --require-hashes
起作用?
$ pip install --help
# ...
--require-hashes Require a hash to check each requirement against, for repeatable installs. This option is implied when any package in a
requirements file has a --hash option.
您可以使用 micropipenv,它可以将 Pipenv.lock(也是诗歌锁)文件转换为 requirements.txt(原始 requirements.txt 或 pip-tools 兼容)。 见https://github.com/thoth-station/micropipenv/#micropipenv -requirements--micropipenv-req
最有用的评论
你可以跑