Skip to content

Commit

Permalink
Add --install option to pipx upgrade command
Browse files Browse the repository at this point in the history
  • Loading branch information
chrysle committed Feb 25, 2024
1 parent 6555533 commit 8a5ae4f
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 11 deletions.
3 changes: 3 additions & 0 deletions changelog.d/1262.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add `--install` option to `pipx upgrade` command.

This will install the package given as argument if it is not already installed.
50 changes: 39 additions & 11 deletions src/pipx/commands/upgrade.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import logging
import os
from pathlib import Path
from typing import List, Sequence
from typing import List, Optional, Sequence

from pipx import constants
from pipx import commands, constants
from pipx.colors import bold, red
from pipx.commands.common import expose_resources_globally
from pipx.constants import EXIT_CODE_OK, ExitCode
Expand Down Expand Up @@ -101,15 +102,38 @@ def _upgrade_venv(
include_injected: bool,
upgrading_all: bool,
force: bool,
install: bool = False,
python: Optional[str] = None,
) -> int:
"""Returns number of packages with changed versions."""
"""Return number of packages with changed versions."""
if not venv_dir.is_dir():
raise PipxError(
f"""
Package is not installed. Expected to find {str(venv_dir)}, but it
does not exist.
"""
)
if install:
commands.install(
venv_dir=None,
venv_args=[],
package_names=None,
package_specs=[str(venv_dir).split(os.path.sep)[-1]],
local_bin_dir=constants.LOCAL_BIN_DIR,
local_man_dir=constants.LOCAL_MAN_DIR,
python=python,
pip_args=pip_args,
verbose=verbose,
force=force,
reinstall=False,
include_dependencies=False,
preinstall_packages=None,
)
return 0
else:
raise PipxError(
f"""
Package is not installed. Expected to find {str(venv_dir)}, but it
does not exist.
"""
)

if python and not install:
logger.info("Ignoring --python as not combined with --install")

venv = Venv(venv_dir, verbose=verbose)

Expand Down Expand Up @@ -154,13 +178,15 @@ def _upgrade_venv(

def upgrade(
venv_dir: Path,
python: Optional[str],
pip_args: List[str],
verbose: bool,
*,
include_injected: bool,
force: bool,
install: bool,
) -> ExitCode:
"""Returns pipx exit code."""
"""Return pipx exit code."""

_ = _upgrade_venv(
venv_dir,
Expand All @@ -169,6 +195,8 @@ def upgrade(
include_injected=include_injected,
upgrading_all=False,
force=force,
install=install,
python=python,
)

# Any error in upgrade will raise PipxError (e.g. from venv.upgrade_package())
Expand All @@ -194,7 +222,7 @@ def upgrade_all(
venvs_upgraded += _upgrade_venv(
venv_dir,
venv.pipx_metadata.main_package.pip_args,
verbose,
verbose=verbose,
include_injected=include_injected,
upgrading_all=True,
force=force,
Expand Down
8 changes: 8 additions & 0 deletions src/pipx/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,12 @@ def run_pipx_command(args: argparse.Namespace) -> ExitCode: # noqa: C901
elif args.command == "upgrade":
return commands.upgrade(
venv_dir,
args.python,
pip_args,
verbose,
include_injected=args.include_injected,
force=args.force,
install=args.install,
)
elif args.command == "upgrade-all":
return commands.upgrade_all(
Expand Down Expand Up @@ -486,6 +488,12 @@ def _add_upgrade(subparsers, venv_completer: VenvCompleter, shared_parser: argpa
help="Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR",
)
add_pip_venv_args(p)
p.add_argument(
"--install",
action="store_true",
help="Install package spec if missing",
)
add_python_options(p)


def _add_upgrade_all(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:
Expand Down
6 changes: 6 additions & 0 deletions tests/test_upgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,9 @@ def test_upgrade_no_include_injected(pipx_temp_env, capsys):
captured = capsys.readouterr()
assert "upgraded package pylint" in captured.out
assert "upgraded package black" not in captured.out


def test_upgrade_install_missing(pipx_temp_env, capsys):
assert not run_pipx_cli(["upgrade", "pycowsay", "--install"])
captured = capsys.readouterr()
assert "installed package pycowsay" in captured.out

0 comments on commit 8a5ae4f

Please sign in to comment.