1""" 2Special requirement and candidate classes to describe a requires-python constraint 3""" 4 5from typing import Iterable, Iterator, Mapping, cast 6 7from pdm import termui 8from pdm.models.candidates import Candidate 9from pdm.models.environment import Environment 10from pdm.models.requirements import NamedRequirement, Requirement 11from pdm.models.specifiers import PySpecSet 12 13 14class PythonCandidate(Candidate): 15 def format(self) -> str: 16 return ( 17 f"{termui.green(self.name, bold=True)} " 18 f"{termui.yellow(str(self.req.specifier))}" 19 ) 20 21 22class PythonRequirement(NamedRequirement): 23 @classmethod 24 def from_pyspec_set(cls, spec: PySpecSet) -> "PythonRequirement": 25 return cls(name="python", specifier=spec) 26 27 def as_candidate(self, environment: Environment) -> PythonCandidate: 28 return PythonCandidate(self, environment) 29 30 31def find_python_matches( 32 identifier: str, 33 requirements: Mapping[str, Iterator[Requirement]], 34 environment: Environment, 35) -> Iterable[Candidate]: 36 """All requires-python except for the first one(must come from the project) 37 must be superset of the first one. 38 """ 39 python_reqs = cast(Iterator[PythonRequirement], iter(requirements[identifier])) 40 project_req = next(python_reqs) 41 python_specs = cast(Iterator[PySpecSet], (req.specifier for req in python_reqs)) 42 if all(spec.is_superset(project_req.specifier or "") for spec in python_specs): 43 return [project_req.as_candidate(environment)] 44 else: 45 # There is a conflict, no match is found. 46 return [] 47 48 49def is_python_satisfied_by(requirement: Requirement, candidate: Candidate) -> bool: 50 return cast(PySpecSet, requirement.specifier).is_superset(candidate.req.specifier) 51