Skip to content

Commit 7b874a4

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 <noreply@anthropic.com> Signed-off-by: Rohan Devasthale <rdevasth@redhat.com>
1 parent 07c1396 commit 7b874a4

9 files changed

Lines changed: 57 additions & 38 deletions

File tree

docs/reference/hooks.rst

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -191,19 +191,6 @@ 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

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: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
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

@@ -175,10 +177,20 @@ def _resolve(
175177
req_type=req_type,
176178
ignore_platform=False,
177179
)
178-
results = resolver.resolve_from_provider(provider, req)
180+
results = resolver.find_all_matching_from_provider(provider, req)
179181
if results:
180182
return results
181-
except Exception:
183+
else:
184+
logger.debug(
185+
f"{req.name}: no prebuilt wheel found on {url}, trying next server"
186+
)
187+
except (
188+
resolvelib.resolvers.ResolverException,
189+
pypi_simple.errors.NoSuchProjectError,
190+
) as e:
191+
# Only catch expected "not found" exceptions - try next server
192+
# Let auth failures, network errors, and plugin bugs propagate
193+
logger.debug(f"{req.name}: no matching wheel on {url}: {e}")
182194
continue
183195
# If we get here, no wheel server succeeded
184196
raise ValueError(
@@ -201,7 +213,7 @@ def _resolve(
201213
req_type=req_type,
202214
ignore_platform=pbi.resolver_ignore_platform,
203215
)
204-
return resolver.resolve_from_provider(provider, req)
216+
return resolver.find_all_matching_from_provider(provider, req)
205217

206218
def get_cached_resolution(
207219
self,
@@ -369,8 +381,8 @@ def _resolve_from_version_source(
369381
constraints=self.ctx.constraints,
370382
use_resolver_cache=False,
371383
)
372-
# resolve_from_provider now returns all matching candidates
373-
return resolver.resolve_from_provider(provider, req)
384+
# find_all_matching_from_provider now returns all matching candidates
385+
return resolver.find_all_matching_from_provider(provider, req)
374386
except Exception as err:
375387
logger.debug(f"could not resolve {req} from {version_source}: {err}")
376388
return None

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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def resolve_source(
160160
)
161161

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

165165
# Return highest version (first in sorted list)
166166
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

tests/test_sources.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ def test_invalid_tarfile(mock_download_url: typing.Any, tmp_path: pathlib.Path)
2323
sources._download_source_check(req=req, destination_dir=fake_dir, url=fake_url)
2424

2525

26-
@patch("fromager.resolver.resolve_from_provider")
26+
@patch("fromager.resolver.find_all_matching_from_provider")
2727
@patch("fromager.sources._download_source_check")
2828
def test_resolve_source_from_settings(
2929
download_source_check: Mock,
30-
resolve_from_provider: Mock,
30+
find_all_matching_from_provider: Mock,
3131
testdata_context: context.WorkContext,
3232
) -> None:
33-
resolve_from_provider.return_value = [("url", Version("42.1.2"))]
33+
find_all_matching_from_provider.return_value = [("url", Version("42.1.2"))]
3434
download_source_check.return_value = pathlib.Path("filename.zip")
3535
req = Requirement("test_pkg==42.1.2")
3636
sdist_server_url = "https://sdist.test/egg"
@@ -59,7 +59,7 @@ def test_resolve_source_from_settings(
5959
)
6060

6161

62-
@patch("fromager.resolver.resolve_from_provider")
62+
@patch("fromager.resolver.find_all_matching_from_provider")
6363
@patch("fromager.sources._download_source_check")
6464
@patch.multiple(
6565
packagesettings.PackageBuildInfo,
@@ -69,10 +69,10 @@ def test_resolve_source_from_settings(
6969
)
7070
def test_resolve_source_with_predefined_resolve_dist(
7171
download_source_check: Mock,
72-
resolve_from_provider: Mock,
72+
find_all_matching_from_provider: Mock,
7373
tmp_context: context.WorkContext,
7474
) -> None:
75-
resolve_from_provider.return_value = [("url", Version("1.0"))]
75+
find_all_matching_from_provider.return_value = [("url", Version("1.0"))]
7676
download_source_check.return_value = pathlib.Path("filename")
7777
req = Requirement("foo==1.0")
7878

0 commit comments

Comments
 (0)