Skip to content

Commit cb016ce

Browse files
Merge pull request #906 from tiran/resolve-cli
feat: add 'fromager resolve package' subcommand
2 parents 6889433 + d5db6ad commit cb016ce

6 files changed

Lines changed: 475 additions & 136 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ list-overrides = "fromager.commands.list_overrides:list_overrides"
118118
list-versions = "fromager.commands.list_versions:list_versions"
119119
migrate-config = "fromager.commands.migrate_config:migrate_config"
120120
minimize = "fromager.commands.minimize:minimize"
121+
package = "fromager.commands.package:package"
121122
stats = "fromager.commands.stats:stats"
122123
step = "fromager.commands.step:step"
123124
canonicalize = "fromager.commands.canonicalize:canonicalize"

src/fromager/commands/find_updates.py

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
from packaging.requirements import Requirement
1111
from packaging.version import Version
1212

13-
from fromager import constraints, context, overrides, resolver
14-
from fromager.commands.list_versions import DistributionType
13+
from .. import constraints, context, overrides, resolver
14+
from . import package
1515

1616
logger = logging.getLogger(__name__)
1717

@@ -32,17 +32,8 @@ class OutputFormat(Enum):
3232
default=OutputFormat.REQUIREMENTS.value,
3333
help="Output format (requirements: name==version, json: JSON array, csv: CSV with name,version columns)",
3434
)
35-
@click.option(
36-
"--distribution-type",
37-
type=click.Choice(DistributionType, case_sensitive=False),
38-
default=DistributionType.DEFAULT.value,
39-
help="Distribution type to include in version lookup (default: use package settings, sdist: source only, wheel: wheels only, both: include both sdists and wheels)",
40-
)
41-
@click.option(
42-
"--sdist-server-url",
43-
default=resolver.PYPI_SERVER_URL,
44-
help="URL to the Python package index to use for version lookup",
45-
)
35+
@package.distribution_type_option
36+
@package.sdist_server_url_option
4637
@click.option(
4738
"-o",
4839
"--output",
@@ -197,22 +188,9 @@ def _find_newer_versions(
197188
override_sdist_server_url = pbi.resolver_sdist_server_url(sdist_server_url)
198189

199190
# Determine include flags based on distribution type
200-
dist_type = DistributionType(distribution_type)
201-
match dist_type:
202-
case DistributionType.SDIST:
203-
include_sdists = True
204-
include_wheels = False
205-
case DistributionType.WHEEL:
206-
include_sdists = False
207-
include_wheels = True
208-
case DistributionType.BOTH:
209-
include_sdists = True
210-
include_wheels = True
211-
case _: # DEFAULT
212-
# Use package settings defaults
213-
package_settings = wkctx.settings.package_setting(constraint.name)
214-
include_sdists = package_settings.resolver_dist.include_sdists
215-
include_wheels = package_settings.resolver_dist.include_wheels
191+
include_sdists, include_wheels = package.parse_distribution_option(
192+
distribution_type, pbi
193+
)
216194

217195
# Get resolver provider
218196
provider = overrides.find_and_invoke(
Lines changed: 16 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,14 @@
1-
import logging
2-
from enum import Enum
3-
41
import click
5-
from packaging.requirements import Requirement
6-
from packaging.version import Version
7-
8-
from fromager import context, overrides, resolver
9-
10-
logger = logging.getLogger(__name__)
11-
12-
13-
class DistributionType(Enum):
14-
"""Distribution type for version lookup"""
152

16-
DEFAULT = "default"
17-
SDIST = "sdist"
18-
WHEEL = "wheel"
19-
BOTH = "both"
3+
from .. import resolver
4+
from . import package
205

216

22-
@click.command()
7+
@click.command(deprecated=True)
238
@click.option(
249
"--distribution-type",
25-
type=click.Choice(DistributionType, case_sensitive=False),
26-
default=DistributionType.DEFAULT.value,
10+
type=click.Choice(package.DistributionType, case_sensitive=False),
11+
default=package.DistributionType.DEFAULT.value,
2712
help="Distribution type to include in version lookup (default: use package settings, sdist: source only, wheel: wheels only, both: include both sdists and wheels)",
2813
)
2914
@click.option(
@@ -42,98 +27,22 @@ class DistributionType(Enum):
4227
help="Format output as requirement specifiers (name==version) instead of just version numbers",
4328
)
4429
@click.argument("requirement_spec", required=True)
45-
@click.pass_obj
30+
@click.pass_context
4631
def list_versions(
47-
wkctx: context.WorkContext,
32+
ctx: click.Context,
4833
requirement_spec: str,
4934
distribution_type: str,
5035
sdist_server_url: str,
5136
ignore_no_versions: bool,
5237
format_as_requirements: bool,
5338
) -> None:
54-
"""List all available versions for a package requirement specifier.
55-
56-
The REQUIREMENT_SPEC should be a package requirement specification like:
57-
- "package_name" (any version)
58-
- "package_name>=1.0" (versions >= 1.0)
59-
- "package_name==1.*" (versions matching 1.*)
60-
61-
This command uses the get_resolver_provider hook to retrieve a resolver
62-
provider for the package in case there is a custom provider configured.
63-
64-
Distribution types:
65-
- "default": Use package settings for include_sdists/include_wheels
66-
- "sdist": Only include source distributions
67-
- "wheel": Only include wheels
68-
- "both": Include both source distributions and wheels
69-
"""
70-
try:
71-
req = Requirement(requirement_spec)
72-
except Exception as e:
73-
raise click.ClickException(
74-
f"Invalid requirement specification '{requirement_spec}': {e}"
75-
) from e
76-
77-
pbi = wkctx.package_build_info(req)
78-
override_sdist_server_url = pbi.resolver_sdist_server_url(sdist_server_url)
79-
80-
# Determine include flags based on distribution type
81-
dist_type = DistributionType(distribution_type)
82-
match dist_type:
83-
case DistributionType.SDIST:
84-
include_sdists = True
85-
include_wheels = False
86-
case DistributionType.WHEEL:
87-
include_sdists = False
88-
include_wheels = True
89-
case DistributionType.BOTH:
90-
include_sdists = True
91-
include_wheels = True
92-
case _: # DEFAULT
93-
# Use package settings defaults
94-
package_settings = wkctx.settings.package_setting(req.name)
95-
include_sdists = package_settings.resolver_dist.include_sdists
96-
include_wheels = package_settings.resolver_dist.include_wheels
97-
98-
logger.info(f"Looking up versions for {req.name}")
99-
if req.specifier:
100-
logger.info(f"Filtering versions with specifier: {req.specifier}")
101-
logger.info(
102-
f"Using distribution type: {dist_type.value} (sdists: {include_sdists}, wheels: {include_wheels})"
39+
"""List all available versions for a package requirement specifier."""
40+
click.secho("use 'fromager package list-versions'", bold=True)
41+
ctx.invoke(
42+
package.list_versions,
43+
requirement_spec=requirement_spec,
44+
distribution_type=distribution_type,
45+
sdist_server_url=sdist_server_url,
46+
ignore_no_versions=ignore_no_versions,
47+
format_as_requirements=format_as_requirements,
10348
)
104-
105-
provider = overrides.find_and_invoke(
106-
req.name,
107-
"get_resolver_provider",
108-
resolver.default_resolver_provider,
109-
ctx=wkctx,
110-
req=req,
111-
include_sdists=include_sdists,
112-
include_wheels=include_wheels,
113-
sdist_server_url=override_sdist_server_url,
114-
)
115-
116-
# Get all available candidates from the provider
117-
candidates = list(
118-
provider.find_matches(
119-
identifier=req.name,
120-
requirements={req.name: [req]},
121-
incompatibilities={req.name: []},
122-
)
123-
)
124-
125-
if not candidates:
126-
if ignore_no_versions:
127-
logger.warning(f"No versions found for {req.name}")
128-
return
129-
else:
130-
raise click.ClickException(f"No versions found for {req.name}")
131-
132-
versions: list[Version] = sorted(set(candidate.version for candidate in candidates))
133-
logger.info(f"Found {len(versions)} version(s)")
134-
135-
for version in versions:
136-
if format_as_requirements:
137-
print(f"{req.name}=={version}")
138-
else:
139-
print(version)

0 commit comments

Comments
 (0)