Skip to content
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
29 changes: 20 additions & 9 deletions src/fromager/commands/list_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,18 @@ def list_overrides(
variant_info: dict[str, str] = {}
for v in variants:
v_info = ps.variants.get(v)
if v_info:
if v_info.pre_built:
variant_info[str(v)] = "pre-built"
else:
variant_info[str(v)] = "yes"
else:
if v_info is None:
variant_info[str(v)] = ""
continue

is_prebuilt = v_info.pre_built
if v_info.source is not None:
# A variant `source:` block replaces the legacy settings,
# so its resolver decides whether wheels are prebuilt
# (e.g. provider: pypi-prebuilt).
is_prebuilt = v_info.source.resolves_prebuilt_wheel

variant_info[str(v)] = "pre-built" if is_prebuilt else "yes"

all_patches: PatchMap = pbi.get_all_patches()
global_patches: list[pathlib.Path] = all_patches.get(None, [])
Expand All @@ -86,7 +91,10 @@ def list_overrides(
[v for v in all_patches.keys() if v is not None]
)

min_release_age = ps.resolver_dist.min_release_age
# Show the package-level source so the column does not change with
# --variant; variant-level sources appear in the variant columns.
source = ps.source.provider if ps.source is not None else ""
min_release_age = pbi.resolver_min_release_age
min_release_age_str = (
str(min_release_age) if min_release_age is not None else ""
)
Expand All @@ -97,6 +105,7 @@ def list_overrides(
row_data = {
"package": name,
"version": "",
"source": source,
"patches": patches_str,
"plugin_hooks": plugin_hooks_str,
"min_release_age": min_release_age_str,
Expand All @@ -114,6 +123,7 @@ def list_overrides(
row_data = {
"package": name,
"version": str(version),
"source": source,
"patches": patches_str,
"plugin_hooks": plugin_hooks_str,
"min_release_age": min_release_age_str,
Expand Down Expand Up @@ -149,7 +159,7 @@ def _export_csv(
"""Export data as CSV."""
# Define field names in the order we want them
fieldnames = (
["package", "version", "patches", "min_release_age"]
["package", "version", "source", "patches", "min_release_age"]
+ variants
+ ["plugin_hooks"]
)
Expand All @@ -176,6 +186,7 @@ def _export_table(
table = Table(title="Package Overrides")
table.add_column("Package", justify="left", no_wrap=True)
table.add_column("Version", justify="left", no_wrap=True)
table.add_column("Source", justify="left", no_wrap=True)
table.add_column("Patches", justify="left", no_wrap=True)

table.add_column("Min Release Age (days)", justify="left", no_wrap=True)
Expand All @@ -186,7 +197,7 @@ def _export_table(
table.add_column("Plugin", justify="left")

column_keys = (
["package", "version", "patches", "min_release_age"]
["package", "version", "source", "patches", "min_release_age"]
+ variants
+ ["plugin_hooks"]
)
Expand Down
29 changes: 20 additions & 9 deletions src/fromager/commands/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
packagesettings,
request_session,
resolver,
sources,
)
from ..candidate import Candidate, Cooldown

Expand Down Expand Up @@ -151,15 +152,19 @@ def list_versions(
- "package_name>=1.0" (versions >= 1.0)
- "package_name==1.*" (versions matching 1.*)

This command uses the get_resolver_provider hook to retrieve a resolver
provider for the package in case there is a custom provider configured.
This command uses sources.get_source_provider() to retrieve a resolver
provider for the package, so both ``source:`` settings and the legacy
get_resolver_provider hook are honored.

Distribution types:
- "default": Use package settings for include_sdists/include_wheels
- "sdist": Only include source distributions
- "wheel": Only include wheels
- "both": Include both source distributions and wheels

An explicit distribution type cannot be combined with a ``source:``
resolver config; version resolution follows the configured resolver.

Output formats:
- "versions": one version per line (default)
- "requirements": name==version per line (pip-installable pins)
Expand All @@ -183,26 +188,32 @@ def list_versions(
logger.info(f"Filtering versions with specifier: {req.specifier}")

pbi = wkctx.package_build_info(req)
override_sdist_server_url = pbi.resolver_sdist_server_url(sdist_server_url)

dist_type = DistributionType(distribution_type)
if pbi.source_resolver is not None and dist_type is not DistributionType.DEFAULT:
raise click.ClickException(
f"--distribution-type {dist_type.value} cannot be combined with the "
f"'{pbi.source_resolver.provider}' source resolver of {req.name}: "
"version resolution follows the source config"
)

include_sdists, include_wheels = parse_distribution_option(
distribution_type,
pbi,
)

provider = overrides.find_and_invoke(
req.name,
"get_resolver_provider",
resolver.default_resolver_provider,
provider = sources.get_source_provider(
Comment thread
rd4398 marked this conversation as resolved.
Resolved
ctx=wkctx,
req=req,
sdist_server_url=sdist_server_url,
include_sdists=include_sdists,
include_wheels=include_wheels,
sdist_server_url=override_sdist_server_url,
)

# Get all available candidates from the provider (cooldown is NOT set on
# the provider so we receive every version that matches the specifier).
# the provider so we receive every version that matches the specifier;
# the detail view applies the cooldown separately).
provider.cooldown = None
candidates = list(
provider.find_matches(
identifier=req.name,
Expand Down
11 changes: 11 additions & 0 deletions src/fromager/packagesettings/_pbi.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,18 @@ def resolver_min_release_age(self) -> int | None:
Returns None (inherit global), 0 (disabled), or a positive integer
(override days). The caller is responsible for converting to a
:class:`~fromager.candidate.Cooldown` instance.

For packages with a ``source:`` resolver, the resolver's
``min_release_age`` takes precedence over the legacy
``resolver_dist`` setting.
"""
source_resolver = self.source_resolver
if source_resolver is not None:
source_min_release_age: int | None = getattr(
source_resolver, "min_release_age", None
)
if source_min_release_age is not None:
return source_min_release_age
return self._ps.resolver_dist.min_release_age

@property
Expand Down
16 changes: 14 additions & 2 deletions src/fromager/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,19 @@ def get_source_provider(
req: Requirement,
sdist_server_url: str,
req_type: RequirementType | None = None,
include_sdists: bool | None = None,
include_wheels: bool | None = None,
) -> resolver.BaseProvider:
"""Create a provider for resolving source distributions.

Returns a provider configured according to the package's resolver settings
(sdist/wheel inclusion and platform matching).

*include_sdists* and *include_wheels* override the legacy package
settings for packages without a ``source:`` resolver; they are ignored
for packages with one because the resolver defines its own inclusion
rules.

Packages with a ``source:`` resolver use that resolver's provider
configuration and ignore the caller's URL, with a warning unless it is the
default PyPI URL. Packages without one retain the
Expand Down Expand Up @@ -168,6 +175,11 @@ def get_source_provider(

override_sdist_server_url = pbi.resolver_sdist_server_url(sdist_server_url)

if include_sdists is None:
include_sdists = pbi.resolver_include_sdists
if include_wheels is None:
include_wheels = pbi.resolver_include_wheels

provider = typing.cast(
resolver.BaseProvider,
overrides.find_and_invoke(
Expand All @@ -176,8 +188,8 @@ def get_source_provider(
resolver.default_resolver_provider,
ctx=ctx,
req=req,
include_sdists=pbi.resolver_include_sdists,
include_wheels=pbi.resolver_include_wheels,
include_sdists=include_sdists,
include_wheels=include_wheels,
sdist_server_url=override_sdist_server_url,
req_type=req_type,
ignore_platform=pbi.resolver_ignore_platform,
Expand Down
86 changes: 86 additions & 0 deletions tests/test_list_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,89 @@ def test_list_overrides_min_release_age(
# test-other-pkg has no min_release_age override — should be empty
test_other_pkg = next(item for item in data if item["package"] == "test-other-pkg")
assert test_other_pkg["min_release_age"] == ""


def test_list_overrides_details_source(
testdata_path: pathlib.Path, cli_runner: CliRunner
) -> None:
"""--details includes the source provider for source-config packages."""
overrides_dir = testdata_path / "context" / "overrides"
settings_file = overrides_dir / "settings.yaml"
settings_dir = overrides_dir / "settings"
patches_dir = overrides_dir / "patches"

result = cli_runner.invoke(
fromager,
[
"--settings-file",
str(settings_file),
"--settings-dir",
str(settings_dir),
"--patches-dir",
str(patches_dir),
"list-overrides",
"--details",
"--format",
"json",
],
)
assert result.exit_code == 0

json_output = _extract_json_from_output(result.stdout)
data = json.loads(json_output)

# test-source-pkg has a source: block with a github-tag-git provider
test_source_pkg = next(
item for item in data if item["package"] == "test-source-pkg"
)
assert test_source_pkg["source"] == "github-tag-git"
# min_release_age is read from the source config
assert test_source_pkg["min_release_age"] == "14"
# the rocm variant resolves a prebuilt wheel through its variant source
assert test_source_pkg["rocm"] == "pre-built"

# legacy packages have no source config — the field is blank
test_other_pkg = next(item for item in data if item["package"] == "test-other-pkg")
assert test_other_pkg["source"] == ""


def test_list_overrides_details_source_variant(
testdata_path: pathlib.Path, cli_runner: CliRunner
) -> None:
"""--variant does not change the package-level source column."""
overrides_dir = testdata_path / "context" / "overrides"
settings_file = overrides_dir / "settings.yaml"
settings_dir = overrides_dir / "settings"
patches_dir = overrides_dir / "patches"

result = cli_runner.invoke(
fromager,
[
"--variant",
"rocm",
"--settings-file",
str(settings_file),
"--settings-dir",
str(settings_dir),
"--patches-dir",
str(patches_dir),
"list-overrides",
"--details",
"--format",
"json",
],
)
assert result.exit_code == 0

json_output = _extract_json_from_output(result.stdout)
data = json.loads(json_output)

test_source_pkg = next(
item for item in data if item["package"] == "test-source-pkg"
)
# the source column shows the package-level setting, not the rocm
# variant's pypi-prebuilt resolver
assert test_source_pkg["source"] == "github-tag-git"
# min_release_age comes from the variant's source resolver
assert test_source_pkg["min_release_age"] == "3"
assert test_source_pkg["rocm"] == "pre-built"
Loading
Loading