-
Notifications
You must be signed in to change notification settings - Fork 807
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0510842
commit 5470b5b
Showing
18 changed files
with
282 additions
and
290 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,114 +1,102 @@ | ||
use pep440_rs::{Version, VersionSpecifiers}; | ||
use pep440_rs::Version; | ||
use uv_python::{Interpreter, PythonVersion}; | ||
|
||
use crate::{RequiresPython, RequiresPythonRange}; | ||
|
||
#[derive(Debug, Clone, Eq, PartialEq)] | ||
pub struct PythonRequirement { | ||
source: PythonRequirementSource, | ||
/// The exact installed version of Python. | ||
exact: Version, | ||
/// The installed version of Python. | ||
installed: Version, | ||
installed: RequiresPython, | ||
/// The target version of Python; that is, the version of Python for which we are resolving | ||
/// dependencies. This is typically the same as the installed version, but may be different | ||
/// when specifying an alternate Python version for the resolution. | ||
/// | ||
/// If `None`, the target version is the same as the installed version. | ||
target: Option<PythonTarget>, | ||
target: RequiresPython, | ||
} | ||
|
||
impl PythonRequirement { | ||
/// Create a [`PythonRequirement`] to resolve against both an [`Interpreter`] and a | ||
/// [`PythonVersion`]. | ||
pub fn from_python_version(interpreter: &Interpreter, python_version: &PythonVersion) -> Self { | ||
let exact = interpreter.python_full_version().version.clone(); | ||
let installed = interpreter.python_full_version().version.only_release(); | ||
let target = python_version.python_full_version().only_release(); | ||
Self { | ||
installed: interpreter.python_full_version().version.only_release(), | ||
target: Some(PythonTarget::Version( | ||
python_version.python_full_version().only_release(), | ||
)), | ||
exact, | ||
installed: RequiresPython::greater_than_equal_version(&installed), | ||
target: RequiresPython::greater_than_equal_version(&target), | ||
source: PythonRequirementSource::PythonVersion, | ||
} | ||
} | ||
|
||
/// Create a [`PythonRequirement`] to resolve against both an [`Interpreter`] and a | ||
/// [`MarkerEnvironment`]. | ||
pub fn from_requires_python( | ||
interpreter: &Interpreter, | ||
requires_python: &RequiresPython, | ||
requires_python: RequiresPython, | ||
) -> Self { | ||
let exact = interpreter.python_full_version().version.clone(); | ||
let installed = interpreter.python_full_version().version.only_release(); | ||
Self { | ||
installed: interpreter.python_full_version().version.only_release(), | ||
target: Some(PythonTarget::RequiresPython(requires_python.clone())), | ||
exact, | ||
installed: RequiresPython::greater_than_equal_version(&installed), | ||
target: requires_python, | ||
source: PythonRequirementSource::RequiresPython, | ||
} | ||
} | ||
|
||
/// Create a [`PythonRequirement`] to resolve against an [`Interpreter`]. | ||
pub fn from_interpreter(interpreter: &Interpreter) -> Self { | ||
let exact = interpreter.python_full_version().version.clone(); | ||
let installed = interpreter.python_full_version().version.only_release(); | ||
Self { | ||
installed: interpreter.python_full_version().version.only_release(), | ||
target: None, | ||
exact, | ||
installed: RequiresPython::greater_than_equal_version(&installed), | ||
target: RequiresPython::greater_than_equal_version(&installed), | ||
source: PythonRequirementSource::Interpreter, | ||
} | ||
} | ||
|
||
/// Narrow the [`PythonRequirement`] to the given version, if it's stricter (i.e., greater) | ||
/// than the current `Requires-Python` minimum. | ||
pub fn narrow(&self, target: &RequiresPythonRange) -> Option<Self> { | ||
let Some(PythonTarget::RequiresPython(requires_python)) = self.target.as_ref() else { | ||
return None; | ||
}; | ||
let requires_python = requires_python.narrow(target)?; | ||
Some(Self { | ||
exact: self.exact.clone(), | ||
installed: self.installed.clone(), | ||
target: Some(PythonTarget::RequiresPython(requires_python)), | ||
target: self.target.narrow(target)?, | ||
source: self.source, | ||
}) | ||
} | ||
|
||
/// Return the exact version of Python. | ||
pub fn exact(&self) -> &Version { | ||
&self.exact | ||
} | ||
|
||
/// Return the installed version of Python. | ||
pub fn installed(&self) -> &Version { | ||
pub fn installed(&self) -> &RequiresPython { | ||
&self.installed | ||
} | ||
|
||
/// Return the target version of Python. | ||
pub fn target(&self) -> Option<&PythonTarget> { | ||
self.target.as_ref() | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Eq, PartialEq)] | ||
pub enum PythonTarget { | ||
/// The [`PythonTarget`] specifier is a single version specifier, as provided via | ||
/// `--python-version` on the command line. | ||
/// | ||
/// The use of a separate enum variant allows us to use a verbatim representation when reporting | ||
/// back to the user. | ||
Version(Version), | ||
/// The [`PythonTarget`] specifier is a set of version specifiers, as extracted from the | ||
/// `Requires-Python` field in a `pyproject.toml` or `METADATA` file. | ||
RequiresPython(RequiresPython), | ||
} | ||
|
||
impl PythonTarget { | ||
/// Returns `true` if the target Python is compatible with the [`VersionSpecifiers`]. | ||
pub fn is_compatible_with(&self, target: &VersionSpecifiers) -> bool { | ||
match self { | ||
PythonTarget::Version(version) => target.contains(version), | ||
PythonTarget::RequiresPython(requires_python) => { | ||
requires_python.is_contained_by(target) | ||
} | ||
} | ||
pub fn target(&self) -> &RequiresPython { | ||
&self.target | ||
} | ||
|
||
/// Returns the [`RequiresPython`] for the [`PythonTarget`] specifier. | ||
pub fn as_requires_python(&self) -> Option<&RequiresPython> { | ||
match self { | ||
PythonTarget::Version(_) => None, | ||
PythonTarget::RequiresPython(requires_python) => Some(requires_python), | ||
} | ||
/// Return the source of the [`PythonRequirement`]. | ||
pub fn source(&self) -> PythonRequirementSource { | ||
self.source | ||
} | ||
} | ||
|
||
impl std::fmt::Display for PythonTarget { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
PythonTarget::Version(specifier) => std::fmt::Display::fmt(specifier, f), | ||
PythonTarget::RequiresPython(specifiers) => std::fmt::Display::fmt(specifiers, f), | ||
} | ||
} | ||
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash, Ord)] | ||
pub enum PythonRequirementSource { | ||
/// `--python-version` | ||
PythonVersion, | ||
/// `Requires-Python` | ||
RequiresPython, | ||
/// The discovered Python interpreter. | ||
Interpreter, | ||
} |
Oops, something went wrong.