Skip to content

Support --dev flag for uninstall #6392

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,19 @@ The user can provide these additional parameters:

## uninstall

``$ pipenv uninstall`` supports all of the parameters in `pipenv install, as well as two additional options,
``--all`` and ``--all-dev``.
``$ pipenv uninstall`` supports all of the parameters in `pipenv install, as well as these additional options:

- --dev — This parameter will uninstall packages from the dev-packages section.

- --all — This parameter will purge all files from the virtual environment,
but leave the Pipfile untouched.

- --all-dev — This parameter will remove all of the development packages from
the virtual environment, and remove them from the Pipfile.

- --categories — This parameter allows you to specify which categories to uninstall from.
For example: ``pipenv uninstall ruff --categories dev-packages``


## lock

Expand Down
21 changes: 18 additions & 3 deletions pipenv/cli/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,24 @@ def lock_dev_option(f):


def uninstall_dev_option(f):
return _dev_option(
f, "Deprecated (as it has no effect). May be removed in a future release."
)
def callback(ctx, param, value):
state = ctx.ensure_object(State)
state.installstate.dev = value
if value:
state.installstate.categories.append("dev-packages")
return value

return option(
"--dev",
"-d",
is_flag=True,
default=False,
type=click_types.BOOL,
help="Uninstall packages from dev-packages.",
callback=callback,
expose_value=False,
show_envvar=True,
)(f)


def pre_option(f):
Expand Down
8 changes: 8 additions & 0 deletions pipenv/routines/uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ def do_uninstall(
if not categories:
categories = ["default"]

# If dev flag is set but no dev-packages in categories, add it
if (
"dev-packages" not in categories
and "develop" not in categories
and project.s.PIPENV_DEV
):
categories.append("dev-packages")

lockfile_content = project.lockfile_content

if all_dev:
Expand Down
103 changes: 103 additions & 0 deletions tests/integration/test_uninstall_dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import pytest


@pytest.mark.install
@pytest.mark.uninstall
def test_uninstall_dev_flag(pipenv_instance_private_pypi):
"""Ensure that running `pipenv uninstall --dev` properly removes packages from dev-packages"""
with pipenv_instance_private_pypi() as p:
with open(p.pipfile_path, "w") as f:
contents = """
[packages]
six = "*"

[dev-packages]
pytest = "*"
""".strip()
f.write(contents)

Check failure on line 18 in tests/integration/test_uninstall_dev.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

tests/integration/test_uninstall_dev.py:18:1: W293 Blank line contains whitespace
# Install both packages
c = p.pipenv("install --dev")
assert c.returncode == 0
assert "six" in p.pipfile["packages"]
assert "pytest" in p.pipfile["dev-packages"]
assert "six" in p.lockfile["default"]
assert "pytest" in p.lockfile["develop"]

Check failure on line 26 in tests/integration/test_uninstall_dev.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

tests/integration/test_uninstall_dev.py:26:1: W293 Blank line contains whitespace
# Verify both packages are installed
c = p.pipenv('run python -c "import six, pytest"')
assert c.returncode == 0

Check failure on line 30 in tests/integration/test_uninstall_dev.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

tests/integration/test_uninstall_dev.py:30:1: W293 Blank line contains whitespace
# Uninstall pytest with --dev flag
c = p.pipenv("uninstall pytest --dev")
assert c.returncode == 0

Check failure on line 34 in tests/integration/test_uninstall_dev.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

tests/integration/test_uninstall_dev.py:34:1: W293 Blank line contains whitespace
# Verify pytest was removed from dev-packages
assert "six" in p.pipfile["packages"]
assert "pytest" not in p.pipfile["dev-packages"]
assert "six" in p.lockfile["default"]
assert "pytest" not in p.lockfile["develop"]

Check failure on line 40 in tests/integration/test_uninstall_dev.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

tests/integration/test_uninstall_dev.py:40:1: W293 Blank line contains whitespace
# Verify pytest is no longer importable
c = p.pipenv('run python -c "import pytest"')
assert c.returncode != 0

Check failure on line 44 in tests/integration/test_uninstall_dev.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

tests/integration/test_uninstall_dev.py:44:1: W293 Blank line contains whitespace
# Verify six is still importable
c = p.pipenv('run python -c "import six"')
assert c.returncode == 0


@pytest.mark.install
@pytest.mark.uninstall
def test_uninstall_dev_flag_with_categories(pipenv_instance_private_pypi):
"""Ensure that running `pipenv uninstall --dev` works the same as `--categories dev-packages`"""
with pipenv_instance_private_pypi() as p:
with open(p.pipfile_path, "w") as f:
contents = """
[packages]
six = "*"

[dev-packages]
pytest = "*"
""".strip()
f.write(contents)

Check failure on line 64 in tests/integration/test_uninstall_dev.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

tests/integration/test_uninstall_dev.py:64:1: W293 Blank line contains whitespace
# Install both packages
c = p.pipenv("install --dev")
assert c.returncode == 0

Check failure on line 68 in tests/integration/test_uninstall_dev.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

tests/integration/test_uninstall_dev.py:68:1: W293 Blank line contains whitespace
# Create a second project to test with categories
with pipenv_instance_private_pypi() as p2:
with open(p2.pipfile_path, "w") as f:
contents = """
[packages]
six = "*"

[dev-packages]
pytest = "*"
""".strip()
f.write(contents)

Check failure on line 80 in tests/integration/test_uninstall_dev.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

tests/integration/test_uninstall_dev.py:80:1: W293 Blank line contains whitespace
# Install both packages
c = p2.pipenv("install --dev")
assert c.returncode == 0

Check failure on line 84 in tests/integration/test_uninstall_dev.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

tests/integration/test_uninstall_dev.py:84:1: W293 Blank line contains whitespace
# Uninstall pytest with --categories
c = p2.pipenv("uninstall pytest --categories dev-packages")
assert c.returncode == 0

# Verify pytest was removed from dev-packages
assert "six" in p2.pipfile["packages"]
assert "pytest" not in p2.pipfile["dev-packages"]
assert "six" in p2.lockfile["default"]
assert "pytest" not in p2.lockfile["develop"]

# Compare with first project
c = p.pipenv("uninstall pytest --dev")
assert c.returncode == 0

# Verify both approaches have the same result
assert p.pipfile["packages"] == p2.pipfile["packages"]
assert p.pipfile["dev-packages"] == p2.pipfile["dev-packages"]
assert p.lockfile["default"] == p2.lockfile["default"]
assert p.lockfile["develop"] == p2.lockfile["develop"]
Loading