@@ -70,7 +70,31 @@ impl CandidateSelector {
7070 pub ( crate ) fn select < ' a , InstalledPackages : InstalledPackagesProvider > (
7171 & ' a self ,
7272 package_name : & ' a PackageName ,
73- range : & ' a Range < Version > ,
73+ range : & Range < Version > ,
74+ version_maps : & ' a [ VersionMap ] ,
75+ preferences : & ' a Preferences ,
76+ installed_packages : & ' a InstalledPackages ,
77+ exclusions : & ' a Exclusions ,
78+ ) -> Option < Candidate < ' a > > {
79+ if let Some ( preferred) = Self :: get_preferred (
80+ package_name,
81+ range,
82+ version_maps,
83+ preferences,
84+ installed_packages,
85+ exclusions,
86+ ) {
87+ return Some ( preferred) ;
88+ }
89+
90+ self . select_no_preference ( package_name, range, version_maps)
91+ }
92+
93+ /// Get a preferred version if one exists. This is the preference from a lockfile or a locally
94+ /// installed version.
95+ fn get_preferred < ' a , InstalledPackages : InstalledPackagesProvider > (
96+ package_name : & ' a PackageName ,
97+ range : & Range < Version > ,
7498 version_maps : & ' a [ VersionMap ] ,
7599 preferences : & ' a Preferences ,
76100 installed_packages : & ' a InstalledPackages ,
@@ -141,8 +165,12 @@ impl CandidateSelector {
141165 }
142166 }
143167
144- // Determine the appropriate prerelease strategy for the current package.
145- let allow_prerelease = match & self . prerelease_strategy {
168+ None
169+ }
170+
171+ /// Determine the appropriate prerelease strategy for the current package.
172+ fn allow_prereleases ( & self , package_name : & PackageName ) -> AllowPreRelease {
173+ match & self . prerelease_strategy {
146174 PreReleaseStrategy :: Disallow => AllowPreRelease :: No ,
147175 PreReleaseStrategy :: Allow => AllowPreRelease :: Yes ,
148176 PreReleaseStrategy :: IfNecessary => AllowPreRelease :: IfNecessary ,
@@ -160,54 +188,58 @@ impl CandidateSelector {
160188 AllowPreRelease :: IfNecessary
161189 }
162190 }
163- } ;
191+ }
192+ }
164193
194+ /// Select a [`Candidate`] without checking for version preference such as an existing
195+ /// lockfile.
196+ pub ( crate ) fn select_no_preference < ' a > (
197+ & ' a self ,
198+ package_name : & ' a PackageName ,
199+ range : & Range < Version > ,
200+ version_maps : & ' a [ VersionMap ] ,
201+ ) -> Option < Candidate > {
165202 tracing:: trace!(
166- "selecting candidate for package {:? } with range {:?} with {} remote versions" ,
203+ "selecting candidate for package {} with range {:?} with {} remote versions" ,
167204 package_name,
168205 range,
169206 version_maps. iter( ) . map( VersionMap :: len) . sum:: <usize >( ) ,
170207 ) ;
171- match & self . resolution_strategy {
172- ResolutionStrategy :: Highest => version_maps. iter ( ) . find_map ( |version_map| {
208+ let highest = self . use_highest_version ( package_name) ;
209+ let allow_prerelease = self . allow_prereleases ( package_name) ;
210+
211+ if highest {
212+ version_maps. iter ( ) . find_map ( |version_map| {
173213 Self :: select_candidate (
174214 version_map. iter ( ) . rev ( ) ,
175215 package_name,
176216 range,
177217 allow_prerelease,
178218 )
179- } ) ,
180- ResolutionStrategy :: Lowest => version_maps. iter ( ) . find_map ( |version_map| {
219+ } )
220+ } else {
221+ version_maps. iter ( ) . find_map ( |version_map| {
181222 Self :: select_candidate ( version_map. iter ( ) , package_name, range, allow_prerelease)
182- } ) ,
223+ } )
224+ }
225+ }
226+
227+ /// By default, we select the latest version, but we also allow using the lowest version instead
228+ /// to check the lower bounds.
229+ pub ( crate ) fn use_highest_version ( & self , package_name : & PackageName ) -> bool {
230+ match & self . resolution_strategy {
231+ ResolutionStrategy :: Highest => true ,
232+ ResolutionStrategy :: Lowest => false ,
183233 ResolutionStrategy :: LowestDirect ( direct_dependencies) => {
184- if direct_dependencies. contains ( package_name) {
185- version_maps. iter ( ) . find_map ( |version_map| {
186- Self :: select_candidate (
187- version_map. iter ( ) ,
188- package_name,
189- range,
190- allow_prerelease,
191- )
192- } )
193- } else {
194- version_maps. iter ( ) . find_map ( |version_map| {
195- Self :: select_candidate (
196- version_map. iter ( ) . rev ( ) ,
197- package_name,
198- range,
199- allow_prerelease,
200- )
201- } )
202- }
234+ !direct_dependencies. contains ( package_name)
203235 }
204236 }
205237 }
206238
207239 /// Select the first-matching [`Candidate`] from a set of candidate versions and files,
208240 /// preferring wheels over source distributions.
209241 fn select_candidate < ' a > (
210- versions : impl Iterator < Item = ( & ' a Version , VersionMapDistHandle < ' a > ) > ,
242+ versions : impl Iterator < Item = ( & ' a Version , VersionMapDistHandle < ' a > ) > + ExactSizeIterator ,
211243 package_name : & ' a PackageName ,
212244 range : & Range < Version > ,
213245 allow_prerelease : AllowPreRelease ,
@@ -219,10 +251,8 @@ impl CandidateSelector {
219251 }
220252
221253 let mut prerelease = None ;
222- let mut steps = 0 ;
223- for ( version, maybe_dist) in versions {
224- steps += 1 ;
225-
254+ let versions_len = versions. len ( ) ;
255+ for ( step, ( version, maybe_dist) ) in versions. enumerate ( ) {
226256 let candidate = if version. any_prerelease ( ) {
227257 if range. contains ( version) {
228258 match allow_prerelease {
@@ -235,7 +265,7 @@ impl CandidateSelector {
235265 after {} steps: {:?} version",
236266 package_name,
237267 range,
238- steps ,
268+ step ,
239269 version,
240270 ) ;
241271 // If pre-releases are allowed, treat them equivalently
@@ -276,7 +306,7 @@ impl CandidateSelector {
276306 after {} steps: {:?} version",
277307 package_name,
278308 range,
279- steps ,
309+ step ,
280310 version,
281311 ) ;
282312 Candidate :: new ( package_name, version, dist)
@@ -308,7 +338,7 @@ impl CandidateSelector {
308338 after {} steps",
309339 package_name,
310340 range,
311- steps ,
341+ versions_len ,
312342 ) ;
313343 match prerelease {
314344 None => None ,
0 commit comments