Skip to content

Commit 18f8b30

Browse files
committed
DRY common package managers install logic, extract install_command method
(a preparation for #6684 and good organization regardless)
1 parent 8736ffc commit 18f8b30

File tree

3 files changed

+64
-85
lines changed

3 files changed

+64
-85
lines changed

marimo/_runtime/packages/conda_package_manager.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
# Copyright 2024 Marimo. All rights reserved.
22
from __future__ import annotations
33

4-
from typing import Optional
5-
64
from marimo._runtime.packages.module_name_to_conda_name import (
75
module_name_to_conda_name,
86
)
97
from marimo._runtime.packages.package_manager import (
108
CanonicalizingPackageManager,
11-
LogCallback,
129
PackageDescription,
1310
)
1411
from marimo._runtime.packages.utils import split_packages
@@ -25,23 +22,12 @@ def _construct_module_name_mapping(self) -> dict[str, str]:
2522
class PixiPackageManager(CondaPackageManager):
2623
name = "pixi"
2724

28-
async def _install(
29-
self,
30-
package: str,
31-
*,
32-
upgrade: bool,
33-
log_callback: Optional[LogCallback] = None,
34-
) -> bool:
35-
if upgrade:
36-
return self.run(
37-
["pixi", "upgrade", *split_packages(package)],
38-
log_callback=log_callback,
39-
)
40-
else:
41-
return self.run(
42-
["pixi", "add", *split_packages(package)],
43-
log_callback=log_callback,
44-
)
25+
def install_command(self, package: str, *, upgrade: bool) -> list[str]:
26+
return [
27+
"pixi",
28+
"upgrade" if upgrade else "add",
29+
*split_packages(package),
30+
]
4531

4632
async def uninstall(self, package: str) -> bool:
4733
return self.run(

marimo/_runtime/packages/package_manager.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,16 @@ def is_manager_installed(self) -> bool:
5656
)
5757
return False
5858

59-
@abc.abstractmethod
59+
def install_command(self, package: str, *, upgrade: bool) -> list[str]:
60+
"""
61+
Get the shell command to install a package (where applicable).
62+
63+
Used by the _install method. If not applicable (for example, with micropip),
64+
override the _install method instead.
65+
"""
66+
# PackageManager's may not implement this method if they override _install
67+
raise NotImplementedError
68+
6069
async def _install(
6170
self,
6271
package: str,
@@ -65,7 +74,10 @@ async def _install(
6574
log_callback: Optional[LogCallback] = None,
6675
) -> bool:
6776
"""Installation logic."""
68-
...
77+
return self.run(
78+
self.install_command(package, upgrade=upgrade),
79+
log_callback=log_callback,
80+
)
6981

7082
async def install(
7183
self,

marimo/_runtime/packages/pypi_package_manager.py

Lines changed: 44 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,15 @@ class PipPackageManager(PypiPackageManager):
5656
name = "pip"
5757
docs_url = "https://pip.pypa.io/"
5858

59-
async def _install(
60-
self,
61-
package: str,
62-
*,
63-
upgrade: bool,
64-
log_callback: Optional[LogCallback] = None,
65-
) -> bool:
66-
LOGGER.info(f"Installing {package} with pip")
67-
cmd = ["pip", "--python", PY_EXE, "install"]
68-
if upgrade:
69-
cmd.append("--upgrade")
70-
cmd.extend(split_packages(package))
71-
return self.run(cmd, log_callback=log_callback)
59+
def install_command(self, package: str, *, upgrade: bool) -> list[str]:
60+
return [
61+
"pip",
62+
"--python",
63+
PY_EXE,
64+
"install",
65+
*(["--upgrade"] if upgrade else []),
66+
*split_packages(package),
67+
]
7268

7369
async def uninstall(self, package: str) -> bool:
7470
LOGGER.info(f"Uninstalling {package} with pip")
@@ -165,20 +161,11 @@ def _uv_bin(self) -> str:
165161
def is_manager_installed(self) -> bool:
166162
return self._uv_bin != "uv" or super().is_manager_installed()
167163

168-
async def _install(
169-
self,
170-
package: str,
171-
*,
172-
upgrade: bool,
173-
log_callback: Optional[LogCallback] = None,
174-
) -> bool:
164+
def install_command(self, package: str, *, upgrade: bool) -> list[str]:
175165
install_cmd: list[str]
176166
if self.is_in_uv_project:
177-
LOGGER.info(f"Installing in {package} with 'uv add'")
178167
install_cmd = [self._uv_bin, "add"]
179168
else:
180-
LOGGER.info(f"Installing in {package} with 'uv pip install'")
181-
182169
install_cmd = [self._uv_bin, "pip", "install"]
183170

184171
# Allow for explicit site directory location if needed
@@ -189,10 +176,28 @@ async def _install(
189176
if upgrade:
190177
install_cmd.append("--upgrade")
191178

192-
return self.run(
179+
return install_cmd + [
193180
# trade installation time for faster start time
194-
install_cmd
195-
+ ["--compile", *split_packages(package), "-p", PY_EXE],
181+
"--compile",
182+
*split_packages(package),
183+
"-p",
184+
PY_EXE,
185+
]
186+
187+
async def _install(
188+
self,
189+
package: str,
190+
*,
191+
upgrade: bool,
192+
log_callback: Optional[LogCallback] = None,
193+
) -> bool:
194+
"""Installation logic."""
195+
LOGGER.info(
196+
f"Installing in {package} with 'uv {'add' if self.is_in_uv_project else 'pip install'}'"
197+
)
198+
return await super()._install(
199+
package,
200+
upgrade=upgrade,
196201
log_callback=log_callback,
197202
)
198203

@@ -474,21 +479,12 @@ class RyePackageManager(PypiPackageManager):
474479
name = "rye"
475480
docs_url = "https://rye.astral.sh/"
476481

477-
async def _install(
478-
self,
479-
package: str,
480-
*,
481-
upgrade: bool,
482-
log_callback: Optional[LogCallback] = None,
483-
) -> bool:
484-
if upgrade:
485-
return self.run(
486-
["rye", "sync", "--update", *split_packages(package)],
487-
log_callback=log_callback,
488-
)
489-
return self.run(
490-
["rye", "add", *split_packages(package)], log_callback=log_callback
491-
)
482+
def install_command(self, package: str, *, upgrade: bool) -> list[str]:
483+
return [
484+
"rye",
485+
*(["sync", "--update"] if upgrade else ["add"]),
486+
*split_packages(package),
487+
]
492488

493489
async def uninstall(self, package: str) -> bool:
494490
return self.run(
@@ -504,28 +500,13 @@ class PoetryPackageManager(PypiPackageManager):
504500
name = "poetry"
505501
docs_url = "https://python-poetry.org/docs/"
506502

507-
async def _install(
508-
self,
509-
package: str,
510-
*,
511-
upgrade: bool,
512-
log_callback: Optional[LogCallback] = None,
513-
) -> bool:
514-
if upgrade:
515-
return self.run(
516-
[
517-
"poetry",
518-
"update",
519-
"--no-interaction",
520-
*split_packages(package),
521-
],
522-
log_callback=log_callback,
523-
)
524-
525-
return self.run(
526-
["poetry", "add", "--no-interaction", *split_packages(package)],
527-
log_callback=log_callback,
528-
)
503+
def install_command(self, package: str, *, upgrade: bool) -> list[str]:
504+
return [
505+
"poetry",
506+
"update" if upgrade else "add",
507+
"--no-interaction",
508+
*split_packages(package),
509+
]
529510

530511
async def uninstall(self, package: str) -> bool:
531512
return self.run(

0 commit comments

Comments
 (0)