diff --git a/news/5076.bugfix.rst b/news/5076.bugfix.rst new file mode 100644 index 0000000000..0bd7a9c39f --- /dev/null +++ b/news/5076.bugfix.rst @@ -0,0 +1 @@ +Fixes issue of requirements command where git requirements cause the command to fail, solved by using existing convert_deps_to_pip function. diff --git a/pipenv/cli/command.py b/pipenv/cli/command.py index e40ad3313c..61673ef07d 100644 --- a/pipenv/cli/command.py +++ b/pipenv/cli/command.py @@ -756,28 +756,34 @@ def verify(state): @option("--hash", is_flag=True, default=False, help="Add package hashes.") @pass_state def requirements(state, dev=False, dev_only=False, hash=False): + + from pipenv.utils.dependencies import convert_deps_to_pip + lockfile = state.project.lockfile_content + for i, package_index in enumerate(lockfile["_meta"]["sources"]): prefix = "-i" if i == 0 else "--extra-index-url" echo(" ".join([prefix, package_index["url"]])) + + deps = {} + if not dev_only: - for req_name, value in lockfile["default"].items(): - if value.get("editable", False): - echo("-e " + value["path"]) - elif hash: - hashes = [f" \\\n --hash={h}" for h in value.get("hashes", [])] - echo("".join([req_name, value["version"], *hashes])) - else: - echo("".join([req_name, value["version"]])) + deps.update(lockfile["default"]) if dev or dev_only: - for req_name, value in lockfile["develop"].items(): - if value.get("editable", False): - echo("-e " + value["path"]) - elif hash: - hashes = [f" \\\n --hash={h}" for h in value.get("hashes", [])] - echo("".join([req_name, value["version"], *hashes])) - else: - echo("".join([req_name, value["version"]])) + deps.update(lockfile["develop"]) + + pip_deps = convert_deps_to_pip( + deps, + project=None, + r=False, + include_index=False, + include_hashes=hash, + include_markers=False, + ) + + for d in pip_deps: + echo(d) + sys.exit(0) diff --git a/pipenv/utils/dependencies.py b/pipenv/utils/dependencies.py index 2839ff8c33..f2348a1c6b 100644 --- a/pipenv/utils/dependencies.py +++ b/pipenv/utils/dependencies.py @@ -243,7 +243,14 @@ def is_pinned_requirement(ireq): return spec.operator in {"==", "==="} and not spec.version.endswith(".*") -def convert_deps_to_pip(deps, project=None, r=True, include_index=True): +def convert_deps_to_pip( + deps, + project=None, + r=True, + include_index=True, + include_hashes=True, + include_markers=True, +): """ "Converts a Pipfile-formatted dependency to a pip-formatted one.""" from pipenv.vendor.requirementslib.models.requirements import Requirement @@ -255,7 +262,12 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=True): new_dep = Requirement.from_pipfile(dep_name, dep) if new_dep.index: include_index = True - req = new_dep.as_line(sources=indexes if include_index else None).strip() + sources = indexes if include_index else None + req = new_dep.as_line( + sources=sources, + include_hashes=include_hashes, + include_markers=include_markers, + ).strip() dependencies.append(req) if not r: return dependencies diff --git a/tests/integration/test_requirements.py b/tests/integration/test_requirements.py index 7eb077f57f..11aef3a441 100644 --- a/tests/integration/test_requirements.py +++ b/tests/integration/test_requirements.py @@ -1,3 +1,4 @@ +import json import pytest @@ -66,3 +67,27 @@ def test_requirements_generates_requirements_from_lockfile_multiple_sources(Pipe assert '-i https://pypi.org/simple' in c.stdout assert '--extra-index-url https://some_other_source.org' in c.stdout + +@pytest.mark.requirements +def test_requirements_with_git_requirements(PipenvInstance): + req_name, req_hash = 'example-repo', 'cc858e89f19bc0dbd70983f86b811ab625dc9292' + lockfile = { + "_meta": {"sources": []}, + "default": { + req_name: { + "editable": True, + "git": F"ssh://git@bitbucket.org/code/{req_name}.git", + "ref": req_hash + } + }, + "develop": {} + } + + with PipenvInstance(chdir=True) as p: + with open(p.lockfile_path, 'w') as f: + json.dump(lockfile, f) + + c = p.pipenv('requirements') + assert c.returncode == 0 + assert req_name in c.stdout + assert req_hash in c.stdout