33import operator
44import os
55from collections import defaultdict
6+ from functools import reduce
67
78import packaging .markers
89import packaging .requirements
10+ import packaging .specifiers
911import packaging .utils
1012import packaging .version
1113import pytest
@@ -92,12 +94,23 @@ def _iter_matches(self, identifier, requirements, incompatibilities):
9294 name , _ , _ = identifier .partition ("[" )
9395 bad_versions = {c .version for c in incompatibilities [identifier ]}
9496 extras = {e for r in requirements [identifier ] for e in r .extras }
95- for key in self .index [name ]:
96- v = packaging .version .parse (key )
97- if any (v not in r .specifier for r in requirements [identifier ]):
98- continue
99- if v in bad_versions :
100- continue
97+
98+ # Get all versions from the index, excluding bad versions
99+ available_versions = (
100+ v
101+ for key in self .index [name ]
102+ if (v := packaging .version .parse (key )) not in bad_versions
103+ )
104+
105+ # Combine all requirement specifiers using & operator
106+ combined_specifier = reduce (
107+ operator .and_ ,
108+ map (operator .attrgetter ("specifier" ), requirements [identifier ]),
109+ packaging .specifiers .SpecifierSet (),
110+ )
111+
112+ # Filter versions using the combined specifier
113+ for v in combined_specifier .filter (available_versions ):
101114 yield Candidate (name = name , version = v , extras = extras )
102115
103116 def find_matches (self , identifier , requirements , incompatibilities ):
@@ -109,7 +122,9 @@ def find_matches(self, identifier, requirements, incompatibilities):
109122 return candidates
110123
111124 def is_satisfied_by (self , requirement , candidate ):
112- return candidate .version in requirement .specifier
125+ # Use prereleases=None to follow PEP 440: only include pre-releases
126+ # if the specifier explicitly mentions them
127+ return requirement .specifier .contains (candidate .version , prereleases = None )
113128
114129 def _iter_dependencies (self , candidate ):
115130 name = packaging .utils .canonicalize_name (candidate .name )
0 commit comments