Skip to content

Commit e18c44f

Browse files
committed
feat: add --upgrade and --upgrade-strategy options
- Adds the `--upgrade` option to `inject` and `install`, mirroring the pip install option - Adds `--upgrade-strategy` to `upgrade` as well as all subcommands where `--upgrade` has been added
1 parent 255a83f commit e18c44f

File tree

7 files changed

+100
-2
lines changed

7 files changed

+100
-2
lines changed

src/pipx/commands/inject.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ def inject_dep(
2828
include_apps: bool,
2929
include_dependencies: bool,
3030
force: bool,
31+
upgrade: bool,
32+
upgrade_strategy: Optional[str],
3133
suffix: bool = False,
3234
) -> bool:
3335
logger.debug("Injecting package %s", package_spec)
@@ -64,7 +66,11 @@ def inject_dep(
6466
verbose=verbose,
6567
)
6668

67-
if not force and venv.has_package(package_name):
69+
if upgrade:
70+
pip_args = ["--upgrade"] + pip_args
71+
if upgrade_strategy is not None:
72+
pip_args += ["--upgrade-strategy", upgrade_strategy]
73+
elif not force and venv.has_package(package_name):
6874
logger.info("Package %s has already been injected", package_name)
6975
print(
7076
pipx_wrap(
@@ -119,6 +125,8 @@ def inject(
119125
include_apps: bool,
120126
include_dependencies: bool,
121127
force: bool,
128+
upgrade: bool,
129+
upgrade_strategy: Optional[str],
122130
suffix: bool = False,
123131
) -> ExitCode:
124132
"""Returns pipx exit code."""
@@ -148,6 +156,8 @@ def inject(
148156
include_apps=include_apps,
149157
include_dependencies=include_dependencies,
150158
force=force,
159+
upgrade=upgrade,
160+
upgrade_strategy=upgrade_strategy,
151161
suffix=suffix,
152162
)
153163

src/pipx/commands/install.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ def install(
2929
verbose: bool,
3030
*,
3131
force: bool,
32+
upgrade: bool,
33+
upgrade_strategy: Optional[str],
3234
reinstall: bool,
3335
include_dependencies: bool,
3436
preinstall_packages: Optional[List[str]],
@@ -60,7 +62,11 @@ def install(
6062

6163
venv = Venv(venv_dir, python=python, verbose=verbose)
6264
venv.check_upgrade_shared_libs(pip_args=pip_args, verbose=verbose)
63-
if exists:
65+
if upgrade:
66+
pip_args = ["--upgrade"] + pip_args
67+
if upgrade_strategy is not None:
68+
pip_args += ["--upgrade-strategy", upgrade_strategy]
69+
elif exists:
6470
if not reinstall and force and python_flag_passed:
6571
print(
6672
pipx_wrap(
@@ -103,6 +109,8 @@ def install(
103109
include_dependencies=include_dependencies,
104110
include_apps=True,
105111
is_main_package=True,
112+
upgrade=upgrade,
113+
upgrade_strategy=upgrade_strategy,
106114
suffix=suffix,
107115
)
108116
run_post_install_actions(
@@ -213,6 +221,8 @@ def install_all(
213221
reinstall=False,
214222
include_dependencies=main_package.include_dependencies,
215223
preinstall_packages=[],
224+
upgrade=False,
225+
upgrade_strategy=None,
216226
suffix=main_package.suffix,
217227
)
218228

@@ -228,6 +238,8 @@ def install_all(
228238
include_apps=inject_package.include_apps,
229239
include_dependencies=inject_package.include_dependencies,
230240
force=force,
241+
upgrade=False,
242+
upgrade_strategy=None,
231243
suffix=inject_package.suffix == main_package.suffix,
232244
)
233245
except PipxError as e:

src/pipx/commands/reinstall.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ def reinstall(
9595
verbose=verbose,
9696
include_apps=injected_package.include_apps,
9797
include_dependencies=injected_package.include_dependencies,
98+
upgrade=False,
99+
upgrade_strategy=None,
98100
force=True,
99101
)
100102

src/pipx/commands/run.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ def _download_and_run(
245245
pip_args=pip_args,
246246
include_dependencies=False,
247247
include_apps=True,
248+
upgrade=False,
249+
upgrade_strategy=None,
248250
is_main_package=True,
249251
)
250252

src/pipx/commands/upgrade.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def _upgrade_package(
2222
pip_args: List[str],
2323
is_main_package: bool,
2424
force: bool,
25+
upgrade_strategy: Optional[str],
2526
upgrading_all: bool,
2627
) -> int:
2728
"""Returns 1 if package version changed, 0 if same version"""
@@ -49,6 +50,7 @@ def _upgrade_package(
4950
include_dependencies=package_metadata.include_dependencies,
5051
include_apps=package_metadata.include_apps,
5152
is_main_package=is_main_package,
53+
upgrade_strategy=upgrade_strategy,
5254
suffix=package_metadata.suffix,
5355
)
5456

@@ -111,6 +113,7 @@ def _upgrade_venv(
111113
*,
112114
include_injected: bool,
113115
upgrading_all: bool,
116+
upgrade_strategy: Optional[str],
114117
force: bool,
115118
install: bool = False,
116119
venv_args: Optional[List[str]] = None,
@@ -177,6 +180,7 @@ def _upgrade_venv(
177180
is_main_package=True,
178181
force=force,
179182
upgrading_all=upgrading_all,
183+
upgrade_strategy=upgrade_strategy,
180184
)
181185

182186
if include_injected:
@@ -190,6 +194,7 @@ def _upgrade_venv(
190194
is_main_package=False,
191195
force=force,
192196
upgrading_all=upgrading_all,
197+
upgrade_strategy=upgrade_strategy,
193198
)
194199

195200
return versions_updated
@@ -205,6 +210,7 @@ def upgrade(
205210
include_injected: bool,
206211
force: bool,
207212
install: bool,
213+
upgrade_strategy: Optional[str],
208214
python_flag_passed: bool = False,
209215
) -> ExitCode:
210216
"""Return pipx exit code."""
@@ -218,6 +224,7 @@ def upgrade(
218224
upgrading_all=False,
219225
force=force,
220226
install=install,
227+
upgrade_strategy=upgrade_strategy,
221228
venv_args=venv_args,
222229
python=python,
223230
python_flag_passed=python_flag_passed,
@@ -236,6 +243,7 @@ def upgrade_all(
236243
skip: Sequence[str],
237244
force: bool,
238245
python_flag_passed: bool = False,
246+
upgrade_strategy: Optional[str],
239247
) -> ExitCode:
240248
"""Return pipx exit code."""
241249
failed: List[str] = []
@@ -253,6 +261,7 @@ def upgrade_all(
253261
verbose=verbose,
254262
include_injected=include_injected,
255263
upgrading_all=True,
264+
upgrade_strategy=upgrade_strategy,
256265
force=force,
257266
python_flag_passed=python_flag_passed,
258267
)

src/pipx/main.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ def run_pipx_command(args: argparse.Namespace, subparsers: Dict[str, argparse.Ar
290290
venv_args,
291291
verbose,
292292
force=args.force,
293+
upgrade=args.upgrade,
294+
upgrade_strategy=args.upgrade_strategy,
293295
reinstall=False,
294296
include_dependencies=args.include_deps,
295297
preinstall_packages=args.preinstall,
@@ -318,6 +320,8 @@ def run_pipx_command(args: argparse.Namespace, subparsers: Dict[str, argparse.Ar
318320
include_apps=args.include_apps,
319321
include_dependencies=args.include_deps,
320322
force=args.force,
323+
upgrade=args.upgrade,
324+
upgrade_strategy=args.upgrade_strategy,
321325
suffix=args.with_suffix,
322326
)
323327
elif args.command == "uninject":
@@ -340,6 +344,7 @@ def run_pipx_command(args: argparse.Namespace, subparsers: Dict[str, argparse.Ar
340344
force=args.force,
341345
install=args.install,
342346
python_flag_passed=python_flag_passed,
347+
upgrade_strategy=args.upgrade_strategy,
343348
)
344349
elif args.command == "upgrade-all":
345350
return commands.upgrade_all(
@@ -350,6 +355,7 @@ def run_pipx_command(args: argparse.Namespace, subparsers: Dict[str, argparse.Ar
350355
force=args.force,
351356
pip_args=pip_args,
352357
python_flag_passed=python_flag_passed,
358+
upgrade_strategy=args.upgrade_strategy,
353359
)
354360
elif args.command == "upgrade-shared":
355361
return commands.upgrade_shared(
@@ -501,6 +507,22 @@ def _add_install(subparsers: argparse._SubParsersAction, shared_parser: argparse
501507
"installing the main package. Use this flag multiple times if you want to preinstall multiple packages."
502508
),
503509
)
510+
p.add_argument(
511+
"--upgrade",
512+
action="store_true",
513+
help="Upgrade packages if already installed with `pip install --upgrade`",
514+
)
515+
p.add_argument(
516+
"--upgrade-strategy",
517+
nargs=1,
518+
choices=["eager", "only-if-needed"],
519+
help=(
520+
"Determines how dependency upgrading is handled. "
521+
'"eager" upgrades all dependencies regardless of whether version requirements are satisfied by the main package. '
522+
'"only-if-needed" upgrades dependencies only when they do not satisfy requirements. '
523+
'Default "only-if-needed" if --upgrade is provided. Ignored if --upgrade is not specified.'
524+
),
525+
)
504526
add_pip_venv_args(p)
505527

506528

@@ -569,6 +591,22 @@ def _add_inject(subparsers, venv_completer: VenvCompleter, shared_parser: argpar
569591
action="store_true",
570592
help="Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR",
571593
)
594+
p.add_argument(
595+
"--upgrade",
596+
action="store_true",
597+
help="Upgrade packages if already installed with `pip install --upgrade`",
598+
)
599+
p.add_argument(
600+
"--upgrade-strategy",
601+
nargs=1,
602+
choices=["eager", "only-if-needed"],
603+
help=(
604+
"Determines how dependency upgrading is handled. "
605+
'"eager" upgrades all dependencies regardless of whether version requirements are satisfied by the main package. '
606+
'"only-if-needed" upgrades dependencies only when they do not satisfy requirements. '
607+
'Default "only-if-needed" if --upgrade is provided. Ignored if --upgrade is not specified.'
608+
),
609+
)
572610
p.add_argument(
573611
"--with-suffix",
574612
action="store_true",
@@ -658,6 +696,17 @@ def _add_upgrade(subparsers, venv_completer: VenvCompleter, shared_parser: argpa
658696
action="store_true",
659697
help="Install package spec if missing",
660698
)
699+
p.add_argument(
700+
"--upgrade-strategy",
701+
nargs=1,
702+
choices=["eager", "only-if-needed"],
703+
help=(
704+
"Determines how dependency upgrading is handled. "
705+
'"eager" upgrades all dependencies regardless of whether version requirements are satisfied by the main package. '
706+
'"only-if-needed" upgrades dependencies only when they do not satisfy requirements. '
707+
'Default "only-if-needed" if --upgrade is provided. Ignored if --upgrade is not specified.'
708+
),
709+
)
661710
add_python_options(p)
662711

663712

@@ -680,6 +729,17 @@ def _add_upgrade_all(subparsers: argparse._SubParsersAction, shared_parser: argp
680729
action="store_true",
681730
help="Modify existing virtual environment and files in PIPX_BIN_DIR and PIPX_MAN_DIR",
682731
)
732+
p.add_argument(
733+
"--upgrade-strategy",
734+
nargs=1,
735+
choices=["eager", "only-if-needed"],
736+
help=(
737+
"Determines how dependency upgrading is handled. "
738+
'"eager" upgrades all dependencies regardless of whether version requirements are satisfied by the main package. '
739+
'"only-if-needed" upgrades dependencies only when they do not satisfy requirements. '
740+
'Default "only-if-needed" if --upgrade is provided. Ignored if --upgrade is not specified.'
741+
),
742+
)
683743

684744

685745
def _add_upgrade_shared(subparsers: argparse._SubParsersAction, shared_parser: argparse.ArgumentParser) -> None:

src/pipx/venv.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,9 +452,12 @@ def upgrade_package(
452452
include_dependencies: bool,
453453
include_apps: bool,
454454
is_main_package: bool,
455+
upgrade_strategy: Optional[str],
455456
suffix: str = "",
456457
) -> None:
457458
logger.info("Upgrading %s", package_descr := full_package_description(package_name, package_or_url))
459+
if upgrade_strategy is not None:
460+
pip_args += ["--upgrade-strategy", upgrade_strategy]
458461
with animate(f"upgrading {package_descr}", self.do_animation):
459462
pip_process = self._run_pip(["--no-input", "install", "--upgrade"] + pip_args + [package_or_url])
460463
subprocess_post_check(pip_process)

0 commit comments

Comments
 (0)