[ty] Infer the Python version from --python=<system installation> on Unix (#18550)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

This commit is contained in:
Alex Waygood 2025-06-11 15:32:33 +01:00 committed by GitHub
parent a863000cbc
commit e84406d8be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 334 additions and 137 deletions

View file

@ -29,6 +29,7 @@ rustc-hash = { workspace = true }
salsa = { workspace = true, optional = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
thiserror = { workspace = true }
[features]
schemars = ["dep:schemars"]

View file

@ -1,4 +1,4 @@
use std::fmt;
use std::{fmt, str::FromStr};
/// Representation of a Python version.
///
@ -97,18 +97,6 @@ impl Default for PythonVersion {
}
}
impl TryFrom<(&str, &str)> for PythonVersion {
type Error = std::num::ParseIntError;
fn try_from(value: (&str, &str)) -> Result<Self, Self::Error> {
let (major, minor) = value;
Ok(Self {
major: major.parse()?,
minor: minor.parse()?,
})
}
}
impl From<(u8, u8)> for PythonVersion {
fn from(value: (u8, u8)) -> Self {
let (major, minor) = value;
@ -123,6 +111,55 @@ impl fmt::Display for PythonVersion {
}
}
#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)]
pub enum PythonVersionDeserializationError {
#[error("Invalid python version `{0}`: expected `major.minor`")]
WrongPeriodNumber(Box<str>),
#[error("Invalid major version `{0}`: {1}")]
InvalidMajorVersion(Box<str>, #[source] std::num::ParseIntError),
#[error("Invalid minor version `{0}`: {1}")]
InvalidMinorVersion(Box<str>, #[source] std::num::ParseIntError),
}
impl TryFrom<(&str, &str)> for PythonVersion {
type Error = PythonVersionDeserializationError;
fn try_from(value: (&str, &str)) -> Result<Self, Self::Error> {
let (major, minor) = value;
Ok(Self {
major: major.parse().map_err(|err| {
PythonVersionDeserializationError::InvalidMajorVersion(Box::from(major), err)
})?,
minor: minor.parse().map_err(|err| {
PythonVersionDeserializationError::InvalidMinorVersion(Box::from(minor), err)
})?,
})
}
}
impl FromStr for PythonVersion {
type Err = PythonVersionDeserializationError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (major, minor) = s
.split_once('.')
.ok_or_else(|| PythonVersionDeserializationError::WrongPeriodNumber(Box::from(s)))?;
Self::try_from((major, minor)).map_err(|err| {
// Give a better error message for something like `3.8.5` or `3..8`
if matches!(
err,
PythonVersionDeserializationError::InvalidMinorVersion(_, _)
) && minor.contains('.')
{
PythonVersionDeserializationError::WrongPeriodNumber(Box::from(s))
} else {
err
}
})
}
}
#[cfg(feature = "serde")]
mod serde {
use super::PythonVersion;
@ -132,26 +169,9 @@ mod serde {
where
D: serde::Deserializer<'de>,
{
let as_str = String::deserialize(deserializer)?;
if let Some((major, minor)) = as_str.split_once('.') {
let major = major.parse().map_err(|err| {
serde::de::Error::custom(format!("invalid major version: {err}"))
})?;
let minor = minor.parse().map_err(|err| {
serde::de::Error::custom(format!("invalid minor version: {err}"))
})?;
Ok((major, minor).into())
} else {
let major = as_str.parse().map_err(|err| {
serde::de::Error::custom(format!(
"invalid python-version: {err}, expected: `major.minor`"
))
})?;
Ok((major, 0).into())
}
String::deserialize(deserializer)?
.parse()
.map_err(serde::de::Error::custom)
}
}