diff --git a/news/5784.bugfix.rst b/news/5784.bugfix.rst new file mode 100644 index 0000000000..eeb9a08dff --- /dev/null +++ b/news/5784.bugfix.rst @@ -0,0 +1 @@ +Fix regressions in the ``requirements`` command related to standard index extras and handling of local file requirements. diff --git a/pipenv/routines/requirements.py b/pipenv/routines/requirements.py index 0e4c6102d5..3b910756a1 100644 --- a/pipenv/routines/requirements.py +++ b/pipenv/routines/requirements.py @@ -19,8 +19,17 @@ def requirements_from_deps(deps, include_hashes=True, include_markers=True): else "" ) pip_package = f"{package_name}{extras} @ git+{git}@{ref}" + # Handling file-sourced packages + elif "file" in package_info or "path" in package_info: + file = package_info.get("file") or package_info.get("path") + extras = ( + "[{}]".format(",".join(package_info.get("extras", []))) + if "extras" in package_info + else "" + ) + pip_package = f"{file}{extras}" else: - # Handling packages with hashes and markers + # Handling packages from standard pypi like indexes version = package_info.get("version", "").replace("==", "") hashes = ( " --hash={}".format(" --hash=".join(package_info["hashes"])) @@ -32,7 +41,12 @@ def requirements_from_deps(deps, include_hashes=True, include_markers=True): if include_markers and "markers" in package_info else "" ) - pip_package = f"{package_name}=={version}{markers}{hashes}" + extras = ( + "[{}]".format(",".join(package_info.get("extras", []))) + if "extras" in package_info + else "" + ) + pip_package = f"{package_name}{extras}=={version}{markers}{hashes}" # Append to the list pip_packages.append(pip_package) diff --git a/tests/integration/test_requirements.py b/tests/integration/test_requirements.py index ed957e892a..94a98530f1 100644 --- a/tests/integration/test_requirements.py +++ b/tests/integration/test_requirements.py @@ -3,7 +3,7 @@ import pytest from pipenv.utils.shell import temp_environ - +from pipenv.routines.requirements import requirements_from_deps @pytest.mark.requirements def test_requirements_generates_requirements_from_lockfile(pipenv_instance_pypi): @@ -192,6 +192,7 @@ def test_requirements_markers_get_excluded(pipenv_instance_pypi): assert c.returncode == 0 assert markers not in c.stdout + @pytest.mark.requirements def test_requirements_hashes_get_included(pipenv_instance_pypi): package, version, markers = "werkzeug", "==2.1.2", "python_version >= '3.7'" @@ -220,6 +221,7 @@ def test_requirements_hashes_get_included(pipenv_instance_pypi): assert c.returncode == 0 assert f'{package}{version}; {markers} --hash={first_hash} --hash={second_hash}' in c.stdout + def test_requirements_generates_requirements_from_lockfile_without_env_var_expansion( pipenv_instance_pypi, ): @@ -250,3 +252,48 @@ def test_requirements_generates_requirements_from_lockfile_without_env_var_expan "-i https://${redacted_user}:${redacted_pwd}@private_source.org" in c.stdout ) + + +@pytest.mark.requirements +@pytest.mark.parametrize( + "deps, include_hashes, include_markers, expected", + [ + ( + { + "django-storages": { + "version": "==1.12.3", + "extras": ["azure"] + } + }, + True, + True, + ["django-storages[azure]==1.12.3"] + ), + ( + { + "evotum-cripto": { + "file": "https://gitlab.com/eVotUM/Cripto-py/-/archive/develop/Cripto-py-develop.zip" + } + }, + True, + True, + ["https://gitlab.com/eVotUM/Cripto-py/-/archive/develop/Cripto-py-develop.zip"] + ), + ( + { + "pyjwt": { + "git": "https://github.com/jpadilla/pyjwt.git", + "ref": "7665aa625506a11bae50b56d3e04413a3dc6fdf8", + "extras": ["crypto"] + } + }, + True, + True, + ["pyjwt[crypto] @ git+https://github.com/jpadilla/pyjwt.git@7665aa625506a11bae50b56d3e04413a3dc6fdf8"] + ) + ] +) +def test_requirements_from_deps(deps, include_hashes, include_markers, expected): + result = requirements_from_deps(deps, include_hashes, include_markers) + assert result == expected +