@@ -50,139 +50,140 @@ def __init__(
5050 self .ctx = ctx
5151 self .prev_graph = prev_graph
5252 # Session-level resolution cache to avoid re-resolving same requirements
53- self ._resolved_requirements : dict [str , tuple [str , Version ]] = {}
53+ # Key: (requirement_string, pre_built) to distinguish source vs prebuilt
54+ self ._resolved_requirements : dict [tuple [str , bool ], tuple [str , Version ]] = {}
5455
55- def resolve_source (
56+ def resolve (
5657 self ,
5758 req : Requirement ,
5859 req_type : RequirementType ,
5960 parent_req : Requirement | None = None ,
61+ pre_built : bool | None = None ,
6062 ) -> tuple [str , Version ]:
61- """Resolve source package (sdist) .
63+ """Resolve package requirement .
6264
6365 Tries resolution strategies in order:
6466 1. Session cache (if previously resolved)
6567 2. Previous dependency graph
66- 3. PyPI source resolution
68+ 3. PyPI resolution ( source or prebuilt based on package build info)
6769
6870 Args:
69- req: Package requirement (must NOT have URL)
71+ req: Package requirement
7072 req_type: Type of requirement
7173 parent_req: Parent requirement from dependency chain
74+ pre_built: Optional override to force prebuilt (True) or source (False).
75+ If None (default), uses package build info to determine.
7276
7377 Returns:
74- Tuple of (source_url , resolved_version)
78+ Tuple of (url , resolved_version)
7579
7680 Raises:
77- ValueError: If req contains a URL (must use Bootstrapper for git URLs)
81+ ValueError: If req contains a git URL and pre_built is False
82+ (git URL source resolution must be handled by Bootstrapper)
7883 """
79- if req .url :
84+ # Determine pre_built if not specified (needed for cache key and URL guard)
85+ if pre_built is None :
86+ pbi = self .ctx .package_build_info (req )
87+ pre_built = pbi .pre_built
88+
89+ # Git URL source resolution must be handled by Bootstrapper.
90+ # But git URL prebuilt resolution is allowed - we look for wheels on PyPI
91+ # (test mode fallback uses this path).
92+ if req .url and not pre_built :
8093 raise ValueError (
8194 f"Git URL requirements must be handled by Bootstrapper: { req } "
8295 )
8396
84- # Check session cache first
85- cached_result = self .get_cached_resolution (req )
97+ # Check session cache (keyed by requirement + pre_built)
98+ cached_result = self .get_cached_resolution (req , pre_built )
8699 if cached_result is not None :
87100 logger .debug (f"resolved { req } from cache" )
88101 return cached_result
89102
90- # Try graph
91- cached_resolution = self ._resolve_from_graph (
92- req = req ,
93- req_type = req_type ,
94- pre_built = False ,
95- parent_req = parent_req ,
96- )
97- if cached_resolution :
98- source_url , resolved_version = cached_resolution
99- logger .debug (f"resolved from previous bootstrap to { resolved_version } " )
100- else :
101- # Fallback to PyPI
102- source_url , resolved_version = sources .resolve_source (
103- ctx = self .ctx ,
104- req = req ,
105- sdist_server_url = resolver .PYPI_SERVER_URL ,
106- req_type = req_type ,
107- )
103+ # Resolve using strategies
104+ url , resolved_version = self ._resolve (req , req_type , parent_req , pre_built )
108105
109106 # Cache the result
110- result = (source_url , resolved_version )
111- self .cache_resolution (req , result )
112- return source_url , resolved_version
107+ result = (url , resolved_version )
108+ self .cache_resolution (req , pre_built , result )
109+ return url , resolved_version
113110
114- def resolve_prebuilt (
111+ def _resolve (
115112 self ,
116113 req : Requirement ,
117114 req_type : RequirementType ,
118- parent_req : Requirement | None = None ,
115+ parent_req : Requirement | None ,
116+ pre_built : bool ,
119117 ) -> tuple [str , Version ]:
120- """Resolve pre-built package (wheels only) .
118+ """Internal resolution logic without caching .
121119
122120 Tries resolution strategies in order:
123- 1. Session cache (if previously resolved)
124- 2. Previous dependency graph
125- 3. PyPI wheel resolution
121+ 1. Previous dependency graph
122+ 2. PyPI resolution (source or prebuilt)
126123
127124 Args:
128125 req: Package requirement
129126 req_type: Type of requirement
130127 parent_req: Parent requirement from dependency chain
128+ pre_built: Whether to resolve prebuilt (True) or source (False)
131129
132130 Returns:
133- Tuple of (source_url, resolved_version)
134-
135- Raises:
136- ValueError: If unable to resolve
131+ Tuple of (url, resolved_version)
137132 """
138- # Check session cache first
139- cached_result = self .get_cached_resolution (req )
140- if cached_result is not None :
141- logger .debug (f"resolved { req } from cache" )
142- return cached_result
143-
144133 # Try graph
145134 cached_resolution = self ._resolve_from_graph (
146135 req = req ,
147136 req_type = req_type ,
148- pre_built = True ,
137+ pre_built = pre_built ,
149138 parent_req = parent_req ,
150139 )
151140
152141 if cached_resolution and not req .url :
153- wheel_url , resolved_version = cached_resolution
142+ url , resolved_version = cached_resolution
154143 logger .debug (f"resolved from previous bootstrap to { resolved_version } " )
155- else :
156- # Fallback to PyPI prebuilt resolution
144+ return url , resolved_version
145+
146+ # Fallback to PyPI
147+ if pre_built :
148+ # Resolve prebuilt wheel
157149 servers = wheels .get_wheel_server_urls (
158150 self .ctx , req , cache_wheel_server_url = resolver .PYPI_SERVER_URL
159151 )
160- wheel_url , resolved_version = wheels .resolve_prebuilt_wheel (
152+ url , resolved_version = wheels .resolve_prebuilt_wheel (
161153 ctx = self .ctx , req = req , wheel_server_urls = servers , req_type = req_type
162154 )
155+ else :
156+ # Resolve source (sdist)
157+ url , resolved_version = sources .resolve_source (
158+ ctx = self .ctx ,
159+ req = req ,
160+ sdist_server_url = resolver .PYPI_SERVER_URL ,
161+ req_type = req_type ,
162+ )
163163
164- # Cache the result
165- result = (wheel_url , resolved_version )
166- self .cache_resolution (req , result )
167- return wheel_url , resolved_version
164+ return url , resolved_version
168165
169166 def get_cached_resolution (
170167 self ,
171168 req : Requirement ,
169+ pre_built : bool ,
172170 ) -> tuple [str , Version ] | None :
173171 """Get a cached resolution result if it exists.
174172
175173 Args:
176174 req: Package requirement to look up in cache
175+ pre_built: Whether looking for prebuilt or source resolution
177176
178177 Returns:
179178 Tuple of (source_url, resolved_version) if cached, None otherwise
180179 """
181- return self ._resolved_requirements .get (str (req ))
180+ cache_key = (str (req ), pre_built )
181+ return self ._resolved_requirements .get (cache_key )
182182
183183 def cache_resolution (
184184 self ,
185185 req : Requirement ,
186+ pre_built : bool ,
186187 result : tuple [str , Version ],
187188 ) -> None :
188189 """Cache a resolution result.
@@ -192,9 +193,11 @@ def cache_resolution(
192193
193194 Args:
194195 req: Package requirement to cache
196+ pre_built: Whether this is a prebuilt or source resolution
195197 result: Tuple of (source_url, resolved_version)
196198 """
197- self ._resolved_requirements [str (req )] = result
199+ cache_key = (str (req ), pre_built )
200+ self ._resolved_requirements [cache_key ] = result
198201
199202 def _resolve_from_graph (
200203 self ,
0 commit comments