Skip to content

Commit e696bc8

Browse files
rd4398claude
andcommitted
docs: remove deprecated resolve_source hook
Remove the resolve_source plugin entry point and its documentation: - Removed entry point from pyproject.toml - Removed default_resolve_source from hooks.rst This hook was replaced by the get_resolver_provider hook during the resolver refactoring. Resolution now uses the provider pattern directly instead of calling resolve_source as a plugin hook. Fixes ReadTheDocs CI warning about missing default_resolve_source. Co-Authored-By: Claude Sonnet 4.5 <[email protected]> Signed-off-by: Rohan Devasthale <[email protected]>
1 parent 07c1396 commit e696bc8

11 files changed

Lines changed: 64 additions & 52 deletions

docs/customization.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ source url can be provided directly in settings.yaml. Optionally the
9090
downloaded sdist can be renamed. Both the url and the destination filename
9191
support templating. The only supported template variable are:
9292

93-
- `version` - it is replaced by the version returned by the `resolve_source`
93+
- `version` - it is replaced by the resolved version of the package
9494
- `canonicalized_name` - it is replaced by the canonicalized name of the
9595
package specified in the requirement, specifically it applies `canonicalize_name(req.nam)`
9696

docs/reference/hooks.rst

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -191,27 +191,13 @@ Source hooks
191191

192192
.. currentmodule:: fromager.sources
193193

194-
.. autofromagerhook:: default_resolve_source
195-
196-
The ``resolve_source()`` function is responsible for resolving a
197-
requirement and acquiring the source for that version of a
198-
package. The default is to use pypi.org to resolve the requirement.
199-
200-
The arguments are the ``WorkContext``, the ``Requirement`` being
201-
evaluated, and the URL to the sdist index.
202-
203-
The return value is ``Tuple[str, Version]`` where the first member is
204-
the url from which the source can be downloaded and the second member
205-
is the version of the resolved package.
206-
207194
.. autofromagerhook:: default_download_source
208195

209196
The ``download_source()`` function is responsible for downloading the
210197
source from a URL.
211198

212199
The arguments are the ``WorkContext``, the ``Requirement`` being
213-
evaluated, version of the package being downloaded, the URL
214-
from which the source can be downloaded as returned by ``resolve_source``,
200+
evaluated, version of the package being downloaded, the download URL,
215201
and the output directory in which the source should be downloaded.
216202

217203
The return value should be a ``pathlib.Path`` file path to the downloaded source.

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ get_build_backend_dependencies = "fromager.dependencies:default_get_build_backen
101101
get_build_sdist_dependencies = "fromager.dependencies:default_get_build_sdist_dependencies"
102102
resolver_provider = "fromager.resolver:default_resolver_provider"
103103
download_source = "fromager.sources:default_download_source"
104-
resolve_source = "fromager.sources:default_resolve_source"
105104
build_sdist = "fromager.sources:default_build_sdist"
106105
build_wheel = "fromager.wheels:default_build_wheel"
107106

src/fromager/bootstrap_requirement_resolver.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
import logging
1010
import typing
1111

12+
import pypi_simple.errors
13+
import resolvelib.resolvers
1214
from packaging.requirements import Requirement
1315
from packaging.version import Version
1416

15-
from . import overrides, resolver
17+
from . import overrides, resolver, wheels
1618
from .dependency_graph import DependencyGraph
1719
from .requirements_file import RequirementType
1820

@@ -151,14 +153,10 @@ def _resolve(
151153

152154
if pre_built:
153155
# Resolve prebuilt wheel
154-
# Get wheel server URLs
155-
wheel_server_urls: list[str] = []
156-
if pbi.wheel_server_url:
157-
wheel_server_urls.append(pbi.wheel_server_url)
158-
else:
159-
if self.ctx.wheel_server_url:
160-
wheel_server_urls.append(self.ctx.wheel_server_url)
161-
wheel_server_urls.append(resolver.PYPI_SERVER_URL)
156+
# Get wheel server URLs (use PyPI as cache/fallback server)
157+
wheel_server_urls = wheels.get_wheel_server_urls(
158+
self.ctx, req, cache_wheel_server_url=resolver.PYPI_SERVER_URL
159+
)
162160

163161
# Try each wheel server until one succeeds
164162
for url in wheel_server_urls:
@@ -175,10 +173,20 @@ def _resolve(
175173
req_type=req_type,
176174
ignore_platform=False,
177175
)
178-
results = resolver.resolve_from_provider(provider, req)
176+
results = resolver.find_all_matching_from_provider(provider, req)
179177
if results:
180178
return results
181-
except Exception:
179+
else:
180+
logger.debug(
181+
f"{req.name}: no prebuilt wheel found on {url}, trying next server"
182+
)
183+
except (
184+
resolvelib.resolvers.ResolverException,
185+
pypi_simple.errors.NoSuchProjectError,
186+
) as e:
187+
# Only catch expected "not found" exceptions - try next server
188+
# Let auth failures, network errors, and plugin bugs propagate
189+
logger.debug(f"{req.name}: no matching wheel on {url}: {e}")
182190
continue
183191
# If we get here, no wheel server succeeded
184192
raise ValueError(
@@ -201,7 +209,7 @@ def _resolve(
201209
req_type=req_type,
202210
ignore_platform=pbi.resolver_ignore_platform,
203211
)
204-
return resolver.resolve_from_provider(provider, req)
212+
return resolver.find_all_matching_from_provider(provider, req)
205213

206214
def get_cached_resolution(
207215
self,
@@ -369,8 +377,8 @@ def _resolve_from_version_source(
369377
constraints=self.ctx.constraints,
370378
use_resolver_cache=False,
371379
)
372-
# resolve_from_provider now returns all matching candidates
373-
return resolver.resolve_from_provider(provider, req)
380+
# find_all_matching_from_provider now returns all matching candidates
381+
return resolver.find_all_matching_from_provider(provider, req)
374382
except Exception as err:
375383
logger.debug(f"could not resolve {req} from {version_source}: {err}")
376384
return None

src/fromager/commands/list_overrides.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ def list_overrides(
7777
"prebuilt_wheel",
7878
# from overrides.py, found by searching for find_override_method
7979
"download_source",
80-
"resolve_source",
8180
"get_resolver_provider",
8281
"prepare_source",
8382
"build_sdist",

src/fromager/resolver.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def resolve(
103103
req_type=req_type,
104104
ignore_platform=ignore_platform,
105105
)
106-
results = resolve_from_provider(provider, req)
106+
results = find_all_matching_from_provider(provider, req)
107107
return results[0]
108108

109109

@@ -168,21 +168,37 @@ def ending(self, state: typing.Any) -> None:
168168
self._report("successfully resolved %r", self.req)
169169

170170

171-
def resolve_from_provider(
171+
def find_all_matching_from_provider(
172172
provider: BaseProvider, req: Requirement
173173
) -> list[tuple[str, Version]]:
174-
"""Resolve requirement and return all matching candidates.
174+
"""Find all matching candidates from provider without full dependency resolution.
175+
176+
This function collects ALL candidates that match the requirement, rather than
177+
performing full dependency resolution to find a single best candidate.
175178
176179
Returns list of (url, version) tuples sorted by version (highest first).
180+
181+
IMPORTANT: This bypasses resolvelib's full resolver to collect all matching
182+
candidates. This is safe ONLY because BaseProvider.get_dependencies() returns
183+
an empty list (no transitive dependencies to resolve). The empty incompatibilities
184+
dict means no version is ever excluded based on conflicts.
185+
186+
If get_dependencies() is ever extended to return actual dependencies, this
187+
function must be revisited to use resolvelib's full resolution algorithm
188+
(Resolver.resolve()) to properly handle dependency conflicts and backtracking.
177189
"""
178190
# Get all matching candidates directly from provider
179191
# instead of using resolvelib's resolver which picks just one
180192
identifier = provider.identify(req)
181193
try:
194+
# Bypass resolvelib's resolver to collect all matching candidates rather than
195+
# just the single best one. This is safe because get_dependencies() returns []
196+
# (no transitive deps to resolve). If get_dependencies() is ever extended,
197+
# this must be revisited to use resolvelib's full resolution.
182198
candidates = provider.find_matches(
183199
identifier=identifier,
184200
requirements={identifier: [req]},
185-
incompatibilities={},
201+
incompatibilities={}, # Empty - safe only because no transitive deps
186202
)
187203
except resolvelib.resolvers.ResolverException as err:
188204
constraint = provider.constraints.get_constraint(req.name)
@@ -193,7 +209,8 @@ def resolve_from_provider(
193209
) from err
194210

195211
# Convert candidates to list of (url, version) tuples
196-
# Candidates are already sorted by version (highest first)
212+
# Candidates are sorted by version (highest first) by BaseProvider.find_matches()
213+
# which calls sorted(candidates, key=attrgetter("version", "build_tag"), reverse=True)
197214
return [(candidate.url, candidate.version) for candidate in candidates]
198215

199216

src/fromager/sources.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ def get_source_type(ctx: context.WorkContext, req: Requirement) -> SourceType:
4949
pbi = ctx.package_build_info(req)
5050
if (
5151
overrides.find_override_method(req.name, "download_source")
52-
or overrides.find_override_method(req.name, "resolve_source")
5352
or overrides.find_override_method(req.name, "get_resolver_provider")
5453
or pbi.download_source_url(resolve_template=False)
5554
):
@@ -160,7 +159,7 @@ def resolve_source(
160159
)
161160

162161
# Get all matching candidates from provider
163-
results = resolver.resolve_from_provider(provider, req)
162+
results = resolver.find_all_matching_from_provider(provider, req)
164163

165164
# Return highest version (first in sorted list)
166165
url, version = results[0]

src/fromager/wheels.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ def resolve_prebuilt_wheel(
487487
)
488488

489489
# Get all matching candidates from provider
490-
results = resolver.resolve_from_provider(provider, req)
490+
results = resolver.find_all_matching_from_provider(provider, req)
491491

492492
if results:
493493
# Return highest version (first in sorted list)

tests/test_bootstrap_requirement_resolver.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ def test_resolve_rejects_git_urls_for_source(tmp_context: WorkContext) -> None:
448448
)
449449

450450

451-
@patch("fromager.resolver.resolve_from_provider")
451+
@patch("fromager.resolver.find_all_matching_from_provider")
452452
def test_resolve_allows_git_urls_for_prebuilt(
453453
mock_resolve: MagicMock,
454454
tmp_context: WorkContext,
@@ -476,7 +476,7 @@ def test_resolve_allows_git_urls_for_prebuilt(
476476
assert version == Version("1.0")
477477

478478

479-
@patch("fromager.resolver.resolve_from_provider")
479+
@patch("fromager.resolver.find_all_matching_from_provider")
480480
def test_resolve_auto_routes_to_prebuilt(
481481
mock_resolve: MagicMock,
482482
tmp_context: WorkContext,
@@ -514,7 +514,7 @@ def test_resolve_auto_routes_to_prebuilt(
514514
assert version == Version("1.0")
515515

516516

517-
@patch("fromager.resolver.resolve_from_provider")
517+
@patch("fromager.resolver.find_all_matching_from_provider")
518518
def test_resolve_auto_routes_to_source(
519519
mock_resolve: MagicMock,
520520
tmp_context: WorkContext,
@@ -554,7 +554,7 @@ def test_resolve_auto_routes_to_source(
554554
assert version == Version("2.0")
555555

556556

557-
@patch("fromager.resolver.resolve_from_provider")
557+
@patch("fromager.resolver.find_all_matching_from_provider")
558558
def test_resolve_prebuilt_after_source_uses_separate_cache(
559559
mock_resolve: MagicMock,
560560
tmp_context: WorkContext,

tests/test_resolver.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,7 +1213,9 @@ def test_custom_resolver_error_message_missing_tag() -> None:
12131213
provider = resolver.GitHubTagProvider(organization="test-org", repo="test-repo")
12141214

12151215
with pytest.raises(resolvelib.resolvers.ResolverException) as exc_info:
1216-
resolver.resolve_from_provider(provider, Requirement("test-package==1.0.0"))
1216+
resolver.find_all_matching_from_provider(
1217+
provider, Requirement("test-package==1.0.0")
1218+
)
12171219

12181220
error_message = str(exc_info.value)
12191221
assert (
@@ -1249,7 +1251,9 @@ def custom_resolver_provider(
12491251
provider = custom_resolver_provider()
12501252

12511253
with pytest.raises(resolvelib.resolvers.ResolverException) as exc_info:
1252-
resolver.resolve_from_provider(provider, Requirement("test-package==1.0.0"))
1254+
resolver.find_all_matching_from_provider(
1255+
provider, Requirement("test-package==1.0.0")
1256+
)
12531257

12541258
error_message = str(exc_info.value)
12551259
# After fix for issue #858, the error message should indicate that a GitHub resolver was used

0 commit comments

Comments
 (0)