Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements requirements command as per #4959 #5013

Merged
merged 15 commits into from
Apr 5, 2022
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ sphinxcontrib-spelling = "<4.3.0"

[scripts]
tests = "bash ./run-tests.sh"
test = "pytest -vvs"

[pipenv]
allow_prereleases = true
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ Magic shell completions are now enabled!
Use a lower-level pip command:
$ pipenv run pip freeze

Generate a requirements.txt file (including dev):
$ pipenv reqs --dev > requirements.txt

Commands:
check Checks for security vulnerabilities and against PEP 508 markers
provided in Pipfile.
Expand All @@ -199,6 +202,7 @@ Magic shell completions are now enabled!
Pipfile.
shell Spawns a shell within the virtualenv.
sync Installs all packages specified in Pipfile.lock.
reqs Generates a requirements.txt compatible output directly from Pipfile.lock
uninstall Un-installs a provided package and removes it from Pipfile.

Locate the project:
Expand Down
37 changes: 35 additions & 2 deletions docs/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -199,19 +199,45 @@ development dependencies::
py==1.4.34
pytest==3.2.3

Finally, if you wish to generate a requirements file with only the
If you wish to generate a requirements file with only the
development requirements you can do that too, using the ``--dev-only``
flag::

$ pipenv lock -r --dev-only
py==1.4.34
pytest==3.2.3

Often, you would want to generate a requirements file based on your current
ImreC marked this conversation as resolved.
Show resolved Hide resolved
environment. However, using pipenv lock -r will still do the locking process which
could update package versions. To keep the packages as is, use the ``--keep-outdated``
flag::

$ pipenv lock -r --keep-outdated
chardet==3.0.4
requests==2.18.4
certifi==2017.7.27.1
idna==2.6
urllib3==1.22

Note that using this approach, packages newly added to the Pipfile will still be
included in requirements.txt. If you really want to use Pipfile.lock and
Pipfile.lock only, you can generate the requirements using::
$ pipenv reqs
chardet==3.0.4
requests==2.18.4
certifi==2017.7.27.1
idna==2.6
urllib3==1.22

This will bypass the locking process completely. As with other commands,
passing ``--dev`` will include both the default and development dependencies

The locked requirements are written to stdout, with shell output redirection
used to write them to a file::

$ pipenv lock -r > requirements.txt
$ pipenv lock -r --dev-only > dev-requirements.txt
$ pipenv reqs --dev > all-requirements.txt
$ cat requirements.txt
chardet==3.0.4
requests==2.18.4
Expand All @@ -221,7 +247,14 @@ used to write them to a file::
$ cat dev-requirements.txt
py==1.4.34
pytest==3.2.3

$ cat all-requirements.txt
chardet==3.0.4
requests==2.18.4
certifi==2017.7.27.1
idna==2.6
urllib3==1.22
py==1.4.34
pytest==3.2.3

☤ Detection of Security Vulnerabilities
---------------------------------------
Expand Down
1 change: 1 addition & 0 deletions news/#4959.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implements a `pipenv reqs` command which generates a requirements.txt compatible output without locking.
19 changes: 19 additions & 0 deletions pipenv/cli/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -709,5 +709,24 @@ def verify(state):
sys.exit(0)


@cli.command(
short_help="Generate a requirements.txt from Pipfile.lock.",
context_settings=CONTEXT_SETTINGS,
)
@option("--dev", is_flag=True, default=False, help="Also add development requirements.")
ImreC marked this conversation as resolved.
Show resolved Hide resolved
@pass_state
def reqs(state, dev=False):
ImreC marked this conversation as resolved.
Show resolved Hide resolved
lockfile = state.project.lockfile_content
for i, package_index in enumerate(lockfile['_meta']['sources']):
prefix = '-i' if i == 0 else '--extra-index-url'
echo(crayons.normal(' '.join([prefix, package_index['url']])))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should output the index verbatim as specified in the Pipfile, without expanding variables. It is fairly common to specify credentials for a private PyPI this way, and writing those into the resulting requirements.txt file is counter-productive.

I'm not sure whether your existing code is already doing this or not, just wanted to mention this need.

for req_name, value in lockfile['default'].items():
echo(crayons.normal(f"{req_name}{value['version']}"))
if dev:
for req_name, value in lockfile['develop'].items():
echo(crayons.normal(f"{req_name}{value['version']}"))
sys.exit(0)


if __name__ == "__main__":
cli()
56 changes: 56 additions & 0 deletions tests/integration/test_reqs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import pytest


@pytest.mark.requirements
def test_reqs_generates_requirements_from_lockfile(PipenvInstance):
with PipenvInstance(chdir=True) as p:
packages = ('requests', '2.14.0')
dev_packages = ('flask', '0.12.2')
with open(p.pipfile_path, 'w') as f:
contents = f"""
[packages]
{packages[0]}= "=={packages[1]}"
[dev-packages]
{dev_packages[0]}= "=={dev_packages[1]}"
""".strip()
f.write(contents)
p.pipenv('lock')
c = p.pipenv('reqs')
d = p.pipenv('reqs --dev')
assert c.returncode == 0
assert d.returncode == 0

assert f'{packages[0]}=={packages[1]}' in c.stdout
assert f'{dev_packages[0]}=={dev_packages[1]}' not in c.stdout

assert f'{packages[0]}=={packages[1]}' in d.stdout
assert f'{dev_packages[0]}=={dev_packages[1]}' in d.stdout


@pytest.mark.requirements
def test_reqs_generates_requirements_from_lockfile_multiple_sources(PipenvInstance):
with PipenvInstance(chdir=True) as p:
packages = ('requests', '2.14.0')
dev_packages = ('flask', '0.12.2')
with open(p.pipfile_path, 'w') as f:
contents = f"""
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[[source]]
name = "other_source"
url = "https://some_other_source.org"
verify_ssl = true
[packages]
{packages[0]}= "=={packages[1]}"
[dev-packages]
{dev_packages[0]}= "=={dev_packages[1]}"
""".strip()
f.write(contents)
p.pipenv('lock')
c = p.pipenv('reqs')
assert c.returncode == 0

assert '-i https://pypi.org/simple' in c.stdout
ImreC marked this conversation as resolved.
Show resolved Hide resolved
assert '--extra-index-url https://some_other_source.org' in c.stdout