Skip to content

Commit 8c845ba

Browse files
committed
Prefer fast installer providers for cargo and gem
Signed-off-by: Nick Sweeting <git@sweeting.me>
1 parent 63b60e4 commit 8c845ba

5 files changed

Lines changed: 105 additions & 6 deletions

File tree

abxpkg/binprovider.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
from typing import (
1919
Optional,
20+
ClassVar,
2021
cast,
2122
final,
2223
Any,
@@ -359,6 +360,7 @@ class BinProvider(BaseModel):
359360
repr=False,
360361
) # e.g. '/opt/homebrew/bin:/opt/archivebox/bin'
361362
INSTALLER_BIN: BinName = "env"
363+
INSTALLER_BINPROVIDERS: ClassVar[tuple[BinProviderName, ...] | None] = None
362364

363365
euid: int | None = None
364366
install_root: Path | None = None
@@ -841,10 +843,16 @@ def INSTALLER_BINARY(self, no_cache: bool = False) -> ShallowBinary:
841843
if raw_provider_names
842844
else list(DEFAULT_PROVIDER_NAMES)
843845
)
846+
preferred_provider_names = (
847+
selected_provider_names
848+
if raw_provider_names or not self.INSTALLER_BINPROVIDERS
849+
else list(self.INSTALLER_BINPROVIDERS)
850+
)
844851
installer_provider_names = [
845852
provider_name
846-
for provider_name in selected_provider_names
853+
for provider_name in preferred_provider_names
847854
if provider_name
855+
and provider_name in selected_provider_names
848856
and provider_name in PROVIDER_CLASS_BY_NAME
849857
and provider_name != self.name
850858
]

abxpkg/binprovider_cargo.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pathlib import Path
77

88
from pydantic import Field, model_validator, computed_field
9-
from typing import Self, cast
9+
from typing import Self, cast, ClassVar
1010

1111
from .base_types import (
1212
BinProviderName,
@@ -28,6 +28,12 @@ class CargoProvider(BinProvider):
2828
name: BinProviderName = "cargo"
2929
_log_emoji = "🦀"
3030
INSTALLER_BIN: BinName = "cargo"
31+
INSTALLER_BINPROVIDERS: ClassVar[tuple[BinProviderName, ...] | None] = (
32+
"env",
33+
"apt",
34+
"brew",
35+
"nix",
36+
)
3137

3238
PATH: PATHStr = "" # Starts empty; setup_PATH() fills it with cargo_home/bin plus any install_root/bin override.
3339

@@ -111,13 +117,20 @@ def INSTALLER_BINARY(self, no_cache: bool = False):
111117
if raw_provider_names
112118
else list(DEFAULT_PROVIDER_NAMES)
113119
)
120+
if raw_provider_names:
121+
preferred_provider_names = selected_provider_names
122+
else:
123+
installer_binproviders = self.INSTALLER_BINPROVIDERS
124+
assert installer_binproviders is not None
125+
preferred_provider_names = list(installer_binproviders)
114126
env_provider = EnvProvider(install_root=None, bin_dir=None)
115127
installer_providers: list[BinProvider] = [
116128
env_provider
117129
if provider_name == "env"
118130
else PROVIDER_CLASS_BY_NAME[provider_name]()
119-
for provider_name in selected_provider_names
131+
for provider_name in preferred_provider_names
120132
if provider_name
133+
and provider_name in selected_provider_names
121134
and provider_name in PROVIDER_CLASS_BY_NAME
122135
and provider_name != self.name
123136
]

abxpkg/binprovider_gem.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pathlib import Path
77

88
from pydantic import Field, model_validator, computed_field
9-
from typing import Self
9+
from typing import Self, ClassVar
1010

1111
from .binary import Binary
1212
from .base_types import (
@@ -34,6 +34,12 @@ class GemProvider(BinProvider):
3434
name: BinProviderName = "gem"
3535
_log_emoji = "💎"
3636
INSTALLER_BIN: BinName = "gem"
37+
INSTALLER_BINPROVIDERS: ClassVar[tuple[BinProviderName, ...] | None] = (
38+
"env",
39+
"apt",
40+
"brew",
41+
"nix",
42+
)
3743

3844
PATH: PATHStr = DEFAULT_ENV_PATH # Starts with ambient system PATH; setup_PATH() prepends/appends gem bin_dir depending on whether install_root/bin_dir were overridden.
3945

@@ -95,12 +101,19 @@ def INSTALLER_BINARY(self, no_cache: bool = False):
95101
if raw_provider_names
96102
else list(DEFAULT_PROVIDER_NAMES)
97103
)
104+
if raw_provider_names:
105+
preferred_provider_names = selected_provider_names
106+
else:
107+
installer_binproviders = self.INSTALLER_BINPROVIDERS
108+
assert installer_binproviders is not None
109+
preferred_provider_names = list(installer_binproviders)
98110
dependency_providers = [
99111
EnvProvider(install_root=None, bin_dir=None)
100112
if provider_name == "env"
101113
else PROVIDER_CLASS_BY_NAME[provider_name]()
102-
for provider_name in selected_provider_names
114+
for provider_name in preferred_provider_names
103115
if provider_name
116+
and provider_name in selected_provider_names
104117
and provider_name in PROVIDER_CLASS_BY_NAME
105118
and provider_name != self.name
106119
]

abxpkg/cli.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,10 +371,28 @@ def merge_dicts(
371371
else:
372372
merged_overrides[provider_name] = provider_overrides
373373

374+
provider_names = options.provider_names
375+
if provider_names == list(DEFAULT_PROVIDER_NAMES):
376+
for provider_class in PROVIDER_CLASS_BY_NAME.values():
377+
if provider_class.model_fields["INSTALLER_BIN"].default != binary_name:
378+
continue
379+
preferred_provider_names = getattr(
380+
provider_class,
381+
"INSTALLER_BINPROVIDERS",
382+
None,
383+
)
384+
if preferred_provider_names:
385+
provider_names = [
386+
provider_name
387+
for provider_name in preferred_provider_names
388+
if provider_name in options.provider_names
389+
]
390+
break
391+
374392
binary_kwargs: dict[str, Any] = {
375393
"name": binary_name,
376394
"binproviders": build_providers(
377-
options.provider_names,
395+
provider_names,
378396
dry_run=dry_run,
379397
install_root=options.install_root,
380398
bin_dir=options.bin_dir,

tests/test_cli.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,6 +1663,53 @@ def test_build_binary_forwards_binary_level_fields(tmp_path):
16631663
assert binary.overrides == {"pip": {"install_args": ["custom==1.0"]}}
16641664

16651665

1666+
@pytest.mark.parametrize("binary_name", ["cargo", "gem"])
1667+
def test_build_binary_uses_installer_provider_preferences_for_default_provider_set(
1668+
tmp_path,
1669+
binary_name,
1670+
):
1671+
options = cli_module.CliOptions(
1672+
lib_dir=tmp_path,
1673+
provider_names=list(cli_module.DEFAULT_PROVIDER_NAMES),
1674+
dry_run=False,
1675+
debug=False,
1676+
no_cache=False,
1677+
)
1678+
1679+
binary = cli_module.build_binary(binary_name, options, dry_run=False)
1680+
provider_class = cli_module.PROVIDER_CLASS_BY_NAME[binary_name]
1681+
assert provider_class.INSTALLER_BINPROVIDERS is not None
1682+
expected_provider_names = [
1683+
provider_name
1684+
for provider_name in provider_class.INSTALLER_BINPROVIDERS
1685+
if provider_name in cli_module.DEFAULT_PROVIDER_NAMES
1686+
]
1687+
1688+
assert [
1689+
provider.name for provider in binary.binproviders
1690+
] == expected_provider_names
1691+
1692+
1693+
def test_build_binary_preserves_explicit_provider_order_for_installer_binaries(
1694+
tmp_path,
1695+
):
1696+
options = cli_module.CliOptions(
1697+
lib_dir=tmp_path,
1698+
provider_names=["env", "brew", "cargo"],
1699+
dry_run=False,
1700+
debug=False,
1701+
no_cache=False,
1702+
)
1703+
1704+
binary = cli_module.build_binary("cargo", options, dry_run=False)
1705+
1706+
assert [provider.name for provider in binary.binproviders] == [
1707+
"env",
1708+
"brew",
1709+
"cargo",
1710+
]
1711+
1712+
16661713
def test_build_binary_merges_cli_handler_overrides_into_all_selected_providers(
16671714
tmp_path,
16681715
):

0 commit comments

Comments
 (0)