diff --git a/changelog.d/1611.bugfix.md b/changelog.d/1611.bugfix.md new file mode 100644 index 0000000000..c49f2493c9 --- /dev/null +++ b/changelog.d/1611.bugfix.md @@ -0,0 +1 @@ +`pipx reinstall`: An exception will now be raised if package is pinned. `--unpin` option is introduced to allow reinstalling pinned package diff --git a/src/pipx/commands/reinstall.py b/src/pipx/commands/reinstall.py index 72011c5deb..145c8ba765 100644 --- a/src/pipx/commands/reinstall.py +++ b/src/pipx/commands/reinstall.py @@ -27,6 +27,7 @@ def reinstall( verbose: bool, force_reinstall_shared_libs: bool = False, python_flag_passed: bool = False, + unpin: bool = False, ) -> ExitCode: """Returns pipx exit code.""" if not venv_dir.exists(): @@ -54,6 +55,9 @@ def reinstall( else: package_or_url = venv.main_package_name + if venv.pipx_metadata.main_package.pinned and not unpin: + raise PipxError(f"{error} Package {venv_dir} is pinned. Pass --unpin to unpin and reinstall it.") + uninstall(venv_dir, local_bin_dir, local_man_dir, verbose) # in case legacy original dir name diff --git a/src/pipx/main.py b/src/pipx/main.py index f1b9569cc6..f0d433196a 100644 --- a/src/pipx/main.py +++ b/src/pipx/main.py @@ -397,6 +397,7 @@ def run_pipx_command(args: argparse.Namespace, subparsers: Dict[str, argparse.Ar python=args.python, verbose=verbose, python_flag_passed=python_flag_passed, + unpin=args.unpin, ) elif args.command == "reinstall-all": return commands.reinstall_all( @@ -731,6 +732,7 @@ def _add_reinstall(subparsers, venv_completer: VenvCompleter, shared_parser: arg parents=[shared_parser], ) p.add_argument("package").completer = venv_completer + p.add_argument("--unpin", action="store_true", help="Unpin and reinstall the package if it is pinned.") add_python_options(p) diff --git a/tests/test_reinstall.py b/tests/test_reinstall.py index 6c7886680f..1183a14f0b 100644 --- a/tests/test_reinstall.py +++ b/tests/test_reinstall.py @@ -68,3 +68,15 @@ def test_reinstall_with_path(pipx_temp_env, capsys, tmp_path): captured = capsys.readouterr() assert "Expected the name of an installed package" in captured.err.replace("\n", " ") + + +def test_reinstall_pinned_package(pipx_temp_env, capsys): + assert not run_pipx_cli(["install", "black"]) + assert not run_pipx_cli(["pin", "black"]) + assert run_pipx_cli(["reinstall", "black"]) + captured = capsys.readouterr() + assert "--unpin to unpin" in captured.err + + assert not run_pipx_cli(["reinstall", "--unpin", "black"]) + captured = capsys.readouterr() + assert "installed package black" in captured.out