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

Add --install option to pipx upgrade command #1267

Merged
merged 2 commits into from
Feb 26, 2024
Merged
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
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 @@ -268,10 +268,12 @@ def run_pipx_command(args: argparse.Namespace, subparsers: Dict[str, argparse.Ar
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 @@ -497,6 +499,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(
Gitznik marked this conversation as resolved.
Show resolved Hide resolved
"--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