diff --git a/docs/advanced.rst b/docs/advanced.rst index 43847cfd..a6cf001c 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -363,6 +363,27 @@ used to write them to a file:: pytest==3.2.3 setuptools==65.4.1 ; python_version >= '3.7' +If you have multiple categories in your Pipfile and wish to generate +a requirements file for only some categories, you can do that too, +using the ``--categories`` option:: + + $ pipenv requirements --categories="tests" > requirements-tests.txt + $ pipenv requirements --categories="docs" > requirements-docs.txt + $ cat requirements-tests.txt + -i https://pypi.org/simple + attrs==22.1.0 ; python_version >= '3.5' + iniconfig==1.1.1 + packaging==21.3 ; python_version >= '3.6' + pluggy==1.0.0 ; python_version >= '3.6' + py==1.11.0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' + pyparsing==3.0.9 ; python_full_version >= '3.6.8' + pytest==7.1.3 + tomli==2.0.1 ; python_version >= '3.7' + +It can be used to specify multiple categories also. + + $ pipenv requirements --categories="tests,docs" + ☤ Detection of Security Vulnerabilities --------------------------------------- diff --git a/news/5431.feature.rst b/news/5431.feature.rst new file mode 100644 index 00000000..627693cd --- /dev/null +++ b/news/5431.feature.rst @@ -0,0 +1 @@ +Add support to export requirements file for a specified set of categories. diff --git a/pipenv/cli/command.py b/pipenv/cli/command.py index 7e642eab..d5047adc 100644 --- a/pipenv/cli/command.py +++ b/pipenv/cli/command.py @@ -740,8 +740,16 @@ def verify(state): ) @option("--hash", is_flag=True, default=False, help="Add package hashes.") @option("--exclude-markers", is_flag=True, default=False, help="Exclude markers.") +@option( + "--categories", + is_flag=False, + default="", + help="Only add requirement of the specified categories.", +) @pass_state -def requirements(state, dev=False, dev_only=False, hash=False, exclude_markers=False): +def requirements( + state, dev=False, dev_only=False, hash=False, exclude_markers=False, categories="" +): from pipenv.utils.dependencies import convert_deps_to_pip @@ -752,11 +760,17 @@ def requirements(state, dev=False, dev_only=False, hash=False, exclude_markers=F echo(" ".join([prefix, package_index["url"]])) deps = {} + categories_list = categories.split(",") if categories else [] - if dev or dev_only: - deps.update(lockfile["develop"]) - if not dev_only: - deps.update(lockfile["default"]) + if categories_list: + for category in categories_list: + category = category.strip() + deps.update(lockfile.get(category, {})) + else: + if dev or dev_only: + deps.update(lockfile["develop"]) + if not dev_only: + deps.update(lockfile["default"]) pip_deps = convert_deps_to_pip( deps, diff --git a/tests/integration/test_requirements.py b/tests/integration/test_requirements.py index 3ad1e978..31ea8d54 100644 --- a/tests/integration/test_requirements.py +++ b/tests/integration/test_requirements.py @@ -72,6 +72,47 @@ def test_requirements_generates_requirements_from_lockfile_multiple_sources(pipe assert '--extra-index-url https://some_other_source.org' in c.stdout +@pytest.mark.requirements +def test_requirements_generates_requirements_from_lockfile_from_categories(pipenv_instance_private_pypi): + with pipenv_instance_private_pypi(chdir=True) as p: + packages = ('six', '1.12.0') + dev_packages = ('itsdangerous', '1.1.0') + test_packages = ('pytest', '7.1.3') + doc_packages = ('docutils', '0.19') + + with open(p.pipfile_path, 'w') as f: + contents = f""" + [[source]] + name = "pypi" + url = "https://pypi.org/simple" + verify_ssl = true + [packages] + {packages[0]}= "=={packages[1]}" + [dev-packages] + {dev_packages[0]}= "=={dev_packages[1]}" + [test] + {test_packages[0]}= "=={test_packages[1]}" + [doc] + {doc_packages[0]}= "=={doc_packages[1]}" + """.strip() + f.write(contents) + l = p.pipenv('lock') + assert l.returncode == 0 + + c = p.pipenv('requirements --dev-only') + assert c.returncode == 0 + assert f'{packages[0]}=={packages[1]}' not in c.stdout + assert f'{test_packages[0]}=={test_packages[1]}' not in c.stdout + assert f'{doc_packages[0]}=={doc_packages[1]}' not in c.stdout + assert f'{dev_packages[0]}=={dev_packages[1]}' in c.stdout + + d = p.pipenv('requirements --categories="test, doc"') + assert d.returncode == 0 + assert f'{packages[0]}=={packages[1]}' not in d.stdout + assert f'{dev_packages[0]}=={dev_packages[1]}' not in d.stdout + assert f'{test_packages[0]}=={test_packages[1]}' in d.stdout + assert f'{doc_packages[0]}=={doc_packages[1]}' in d.stdout + @pytest.mark.requirements def test_requirements_with_git_requirements(pipenv_instance_pypi): req_name, req_hash = 'example-repo', 'cc858e89f19bc0dbd70983f86b811ab625dc9292'