mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-01 09:22:19 +00:00
[red-knot] Move, rename and make public the PyVersion
type (#12782)
This commit is contained in:
parent
b595346213
commit
c4e651921b
8 changed files with 167 additions and 128 deletions
|
@ -5,7 +5,8 @@ use rustc_hash::FxHasher;
|
||||||
pub use db::Db;
|
pub use db::Db;
|
||||||
pub use module_name::ModuleName;
|
pub use module_name::ModuleName;
|
||||||
pub use module_resolver::{resolve_module, system_module_search_paths, vendored_typeshed_stubs};
|
pub use module_resolver::{resolve_module, system_module_search_paths, vendored_typeshed_stubs};
|
||||||
pub use program::{Program, ProgramSettings, SearchPathSettings, TargetVersion};
|
pub use program::{Program, ProgramSettings, SearchPathSettings};
|
||||||
|
pub use python_version::{PythonVersion, TargetVersion, UnsupportedPythonVersion};
|
||||||
pub use semantic_model::{HasTy, SemanticModel};
|
pub use semantic_model::{HasTy, SemanticModel};
|
||||||
|
|
||||||
pub mod ast_node_ref;
|
pub mod ast_node_ref;
|
||||||
|
@ -15,6 +16,7 @@ mod module_name;
|
||||||
mod module_resolver;
|
mod module_resolver;
|
||||||
mod node_key;
|
mod node_key;
|
||||||
mod program;
|
mod program;
|
||||||
|
mod python_version;
|
||||||
pub mod semantic_index;
|
pub mod semantic_index;
|
||||||
mod semantic_model;
|
mod semantic_model;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
@ -499,9 +499,8 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, File, Mod
|
||||||
let resolver_settings = module_resolution_settings(db);
|
let resolver_settings = module_resolution_settings(db);
|
||||||
let target_version = resolver_settings.target_version();
|
let target_version = resolver_settings.target_version();
|
||||||
let resolver_state = ResolverState::new(db, target_version);
|
let resolver_state = ResolverState::new(db, target_version);
|
||||||
let (_, minor_version) = target_version.as_tuple();
|
|
||||||
let is_builtin_module =
|
let is_builtin_module =
|
||||||
ruff_python_stdlib::sys::is_builtin_module(minor_version, name.as_str());
|
ruff_python_stdlib::sys::is_builtin_module(target_version.minor_version(), name.as_str());
|
||||||
|
|
||||||
for search_path in resolver_settings.search_paths(db) {
|
for search_path in resolver_settings.search_paths(db) {
|
||||||
// When a builtin module is imported, standard module resolution is bypassed:
|
// When a builtin module is imported, standard module resolution is bypassed:
|
||||||
|
|
|
@ -2,7 +2,8 @@ use ruff_db::system::{DbWithTestSystem, SystemPath, SystemPathBuf};
|
||||||
use ruff_db::vendored::VendoredPathBuf;
|
use ruff_db::vendored::VendoredPathBuf;
|
||||||
|
|
||||||
use crate::db::tests::TestDb;
|
use crate::db::tests::TestDb;
|
||||||
use crate::program::{Program, SearchPathSettings, TargetVersion};
|
use crate::program::{Program, SearchPathSettings};
|
||||||
|
use crate::python_version::TargetVersion;
|
||||||
|
|
||||||
/// A test case for the module resolver.
|
/// A test case for the module resolver.
|
||||||
///
|
///
|
||||||
|
|
|
@ -14,7 +14,7 @@ use ruff_db::files::{system_path_to_file, File};
|
||||||
use super::vendored::vendored_typeshed_stubs;
|
use super::vendored::vendored_typeshed_stubs;
|
||||||
use crate::db::Db;
|
use crate::db::Db;
|
||||||
use crate::module_name::ModuleName;
|
use crate::module_name::ModuleName;
|
||||||
use crate::TargetVersion;
|
use crate::python_version::{PythonVersion, TargetVersion};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct LazyTypeshedVersions<'db>(OnceCell<&'db TypeshedVersions>);
|
pub(crate) struct LazyTypeshedVersions<'db>(OnceCell<&'db TypeshedVersions>);
|
||||||
|
@ -63,7 +63,7 @@ impl<'db> LazyTypeshedVersions<'db> {
|
||||||
// Unwrapping here is not correct...
|
// Unwrapping here is not correct...
|
||||||
parse_typeshed_versions(db, versions_file).as_ref().unwrap()
|
parse_typeshed_versions(db, versions_file).as_ref().unwrap()
|
||||||
});
|
});
|
||||||
versions.query_module(module, PyVersion::from(target_version))
|
versions.query_module(module, PythonVersion::from(target_version))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ impl TypeshedVersions {
|
||||||
fn query_module(
|
fn query_module(
|
||||||
&self,
|
&self,
|
||||||
module: &ModuleName,
|
module: &ModuleName,
|
||||||
target_version: PyVersion,
|
target_version: PythonVersion,
|
||||||
) -> TypeshedVersionsQueryResult {
|
) -> TypeshedVersionsQueryResult {
|
||||||
if let Some(range) = self.exact(module) {
|
if let Some(range) = self.exact(module) {
|
||||||
if range.contains(target_version) {
|
if range.contains(target_version) {
|
||||||
|
@ -322,13 +322,13 @@ impl fmt::Display for TypeshedVersions {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
enum PyVersionRange {
|
enum PyVersionRange {
|
||||||
AvailableFrom(RangeFrom<PyVersion>),
|
AvailableFrom(RangeFrom<PythonVersion>),
|
||||||
AvailableWithin(RangeInclusive<PyVersion>),
|
AvailableWithin(RangeInclusive<PythonVersion>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PyVersionRange {
|
impl PyVersionRange {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn contains(&self, version: PyVersion) -> bool {
|
fn contains(&self, version: PythonVersion) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::AvailableFrom(inner) => inner.contains(&version),
|
Self::AvailableFrom(inner) => inner.contains(&version),
|
||||||
Self::AvailableWithin(inner) => inner.contains(&version),
|
Self::AvailableWithin(inner) => inner.contains(&version),
|
||||||
|
@ -342,9 +342,14 @@ impl FromStr for PyVersionRange {
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let mut parts = s.split('-').map(str::trim);
|
let mut parts = s.split('-').map(str::trim);
|
||||||
match (parts.next(), parts.next(), parts.next()) {
|
match (parts.next(), parts.next(), parts.next()) {
|
||||||
(Some(lower), Some(""), None) => Ok(Self::AvailableFrom((lower.parse()?)..)),
|
(Some(lower), Some(""), None) => {
|
||||||
|
let lower = PythonVersion::from_versions_file_string(lower)?;
|
||||||
|
Ok(Self::AvailableFrom(lower..))
|
||||||
|
}
|
||||||
(Some(lower), Some(upper), None) => {
|
(Some(lower), Some(upper), None) => {
|
||||||
Ok(Self::AvailableWithin((lower.parse()?)..=(upper.parse()?)))
|
let lower = PythonVersion::from_versions_file_string(lower)?;
|
||||||
|
let upper = PythonVersion::from_versions_file_string(upper)?;
|
||||||
|
Ok(Self::AvailableWithin(lower..=upper))
|
||||||
}
|
}
|
||||||
_ => Err(TypeshedVersionsParseErrorKind::UnexpectedNumberOfHyphens),
|
_ => Err(TypeshedVersionsParseErrorKind::UnexpectedNumberOfHyphens),
|
||||||
}
|
}
|
||||||
|
@ -362,75 +367,21 @@ impl fmt::Display for PyVersionRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
impl PythonVersion {
|
||||||
struct PyVersion {
|
fn from_versions_file_string(s: &str) -> Result<Self, TypeshedVersionsParseErrorKind> {
|
||||||
major: u8,
|
|
||||||
minor: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for PyVersion {
|
|
||||||
type Err = TypeshedVersionsParseErrorKind;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let mut parts = s.split('.').map(str::trim);
|
let mut parts = s.split('.').map(str::trim);
|
||||||
let (Some(major), Some(minor), None) = (parts.next(), parts.next(), parts.next()) else {
|
let (Some(major), Some(minor), None) = (parts.next(), parts.next(), parts.next()) else {
|
||||||
return Err(TypeshedVersionsParseErrorKind::UnexpectedNumberOfPeriods(
|
return Err(TypeshedVersionsParseErrorKind::UnexpectedNumberOfPeriods(
|
||||||
s.to_string(),
|
s.to_string(),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let major = match u8::from_str(major) {
|
PythonVersion::try_from((major, minor)).map_err(|int_parse_error| {
|
||||||
Ok(major) => major,
|
TypeshedVersionsParseErrorKind::IntegerParsingFailure {
|
||||||
Err(err) => {
|
|
||||||
return Err(TypeshedVersionsParseErrorKind::IntegerParsingFailure {
|
|
||||||
version: s.to_string(),
|
version: s.to_string(),
|
||||||
err,
|
err: int_parse_error,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
|
||||||
let minor = match u8::from_str(minor) {
|
|
||||||
Ok(minor) => minor,
|
|
||||||
Err(err) => {
|
|
||||||
return Err(TypeshedVersionsParseErrorKind::IntegerParsingFailure {
|
|
||||||
version: s.to_string(),
|
|
||||||
err,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(Self { major, minor })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for PyVersion {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let PyVersion { major, minor } = self;
|
|
||||||
write!(f, "{major}.{minor}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TargetVersion> for PyVersion {
|
|
||||||
fn from(value: TargetVersion) -> Self {
|
|
||||||
match value {
|
|
||||||
TargetVersion::Py37 => PyVersion { major: 3, minor: 7 },
|
|
||||||
TargetVersion::Py38 => PyVersion { major: 3, minor: 8 },
|
|
||||||
TargetVersion::Py39 => PyVersion { major: 3, minor: 9 },
|
|
||||||
TargetVersion::Py310 => PyVersion {
|
|
||||||
major: 3,
|
|
||||||
minor: 10,
|
|
||||||
},
|
|
||||||
TargetVersion::Py311 => PyVersion {
|
|
||||||
major: 3,
|
|
||||||
minor: 11,
|
|
||||||
},
|
|
||||||
TargetVersion::Py312 => PyVersion {
|
|
||||||
major: 3,
|
|
||||||
minor: 12,
|
|
||||||
},
|
|
||||||
TargetVersion::Py313 => PyVersion {
|
|
||||||
major: 3,
|
|
||||||
minor: 13,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::python_version::TargetVersion;
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use ruff_db::system::SystemPathBuf;
|
use ruff_db::system::SystemPathBuf;
|
||||||
use salsa::Durability;
|
use salsa::Durability;
|
||||||
|
@ -24,59 +25,6 @@ pub struct ProgramSettings {
|
||||||
pub search_paths: SearchPathSettings,
|
pub search_paths: SearchPathSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumeration of all supported Python versions
|
|
||||||
///
|
|
||||||
/// TODO: unify with the `PythonVersion` enum in the linter/formatter crates?
|
|
||||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
|
||||||
pub enum TargetVersion {
|
|
||||||
Py37,
|
|
||||||
#[default]
|
|
||||||
Py38,
|
|
||||||
Py39,
|
|
||||||
Py310,
|
|
||||||
Py311,
|
|
||||||
Py312,
|
|
||||||
Py313,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TargetVersion {
|
|
||||||
pub const fn as_tuple(self) -> (u8, u8) {
|
|
||||||
match self {
|
|
||||||
Self::Py37 => (3, 7),
|
|
||||||
Self::Py38 => (3, 8),
|
|
||||||
Self::Py39 => (3, 9),
|
|
||||||
Self::Py310 => (3, 10),
|
|
||||||
Self::Py311 => (3, 11),
|
|
||||||
Self::Py312 => (3, 12),
|
|
||||||
Self::Py313 => (3, 13),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Self::Py37 => "py37",
|
|
||||||
Self::Py38 => "py38",
|
|
||||||
Self::Py39 => "py39",
|
|
||||||
Self::Py310 => "py310",
|
|
||||||
Self::Py311 => "py311",
|
|
||||||
Self::Py312 => "py312",
|
|
||||||
Self::Py313 => "py313",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for TargetVersion {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for TargetVersion {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
std::fmt::Display::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configures the search paths for module resolution.
|
/// Configures the search paths for module resolution.
|
||||||
#[derive(Eq, PartialEq, Debug, Clone, Default)]
|
#[derive(Eq, PartialEq, Debug, Clone, Default)]
|
||||||
pub struct SearchPathSettings {
|
pub struct SearchPathSettings {
|
||||||
|
|
136
crates/red_knot_python_semantic/src/python_version.rs
Normal file
136
crates/red_knot_python_semantic/src/python_version.rs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// Enumeration of all supported Python versions
|
||||||
|
///
|
||||||
|
/// TODO: unify with the `PythonVersion` enum in the linter/formatter crates?
|
||||||
|
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||||
|
pub enum TargetVersion {
|
||||||
|
Py37,
|
||||||
|
#[default]
|
||||||
|
Py38,
|
||||||
|
Py39,
|
||||||
|
Py310,
|
||||||
|
Py311,
|
||||||
|
Py312,
|
||||||
|
Py313,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TargetVersion {
|
||||||
|
pub fn major_version(self) -> u8 {
|
||||||
|
PythonVersion::from(self).major
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn minor_version(self) -> u8 {
|
||||||
|
PythonVersion::from(self).minor
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn as_display_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Py37 => "py37",
|
||||||
|
Self::Py38 => "py38",
|
||||||
|
Self::Py39 => "py39",
|
||||||
|
Self::Py310 => "py310",
|
||||||
|
Self::Py311 => "py311",
|
||||||
|
Self::Py312 => "py312",
|
||||||
|
Self::Py313 => "py313",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TargetVersion {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(self.as_display_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for TargetVersion {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic representation for a Python version.
|
||||||
|
///
|
||||||
|
/// Unlike [`TargetVersion`], this does not necessarily represent
|
||||||
|
/// a Python version that we actually support.
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
pub struct PythonVersion {
|
||||||
|
pub major: u8,
|
||||||
|
pub minor: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 fmt::Display for PythonVersion {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let PythonVersion { major, minor } = self;
|
||||||
|
write!(f, "{major}.{minor}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TargetVersion> for PythonVersion {
|
||||||
|
fn from(value: TargetVersion) -> Self {
|
||||||
|
match value {
|
||||||
|
TargetVersion::Py37 => PythonVersion { major: 3, minor: 7 },
|
||||||
|
TargetVersion::Py38 => PythonVersion { major: 3, minor: 8 },
|
||||||
|
TargetVersion::Py39 => PythonVersion { major: 3, minor: 9 },
|
||||||
|
TargetVersion::Py310 => PythonVersion {
|
||||||
|
major: 3,
|
||||||
|
minor: 10,
|
||||||
|
},
|
||||||
|
TargetVersion::Py311 => PythonVersion {
|
||||||
|
major: 3,
|
||||||
|
minor: 11,
|
||||||
|
},
|
||||||
|
TargetVersion::Py312 => PythonVersion {
|
||||||
|
major: 3,
|
||||||
|
minor: 12,
|
||||||
|
},
|
||||||
|
TargetVersion::Py313 => PythonVersion {
|
||||||
|
major: 3,
|
||||||
|
minor: 13,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct UnsupportedPythonVersion(PythonVersion);
|
||||||
|
|
||||||
|
impl fmt::Display for UnsupportedPythonVersion {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Python version {} is unsupported", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for UnsupportedPythonVersion {}
|
||||||
|
|
||||||
|
impl TryFrom<PythonVersion> for TargetVersion {
|
||||||
|
type Error = UnsupportedPythonVersion;
|
||||||
|
|
||||||
|
fn try_from(value: PythonVersion) -> Result<Self, Self::Error> {
|
||||||
|
let PythonVersion { major: 3, minor } = value else {
|
||||||
|
return Err(UnsupportedPythonVersion(value));
|
||||||
|
};
|
||||||
|
match minor {
|
||||||
|
7 => Ok(TargetVersion::Py37),
|
||||||
|
8 => Ok(TargetVersion::Py38),
|
||||||
|
9 => Ok(TargetVersion::Py39),
|
||||||
|
10 => Ok(TargetVersion::Py310),
|
||||||
|
11 => Ok(TargetVersion::Py311),
|
||||||
|
12 => Ok(TargetVersion::Py312),
|
||||||
|
13 => Ok(TargetVersion::Py313),
|
||||||
|
_ => Err(UnsupportedPythonVersion(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -168,7 +168,8 @@ mod tests {
|
||||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||||
|
|
||||||
use crate::db::tests::TestDb;
|
use crate::db::tests::TestDb;
|
||||||
use crate::program::{Program, SearchPathSettings, TargetVersion};
|
use crate::program::{Program, SearchPathSettings};
|
||||||
|
use crate::python_version::TargetVersion;
|
||||||
use crate::types::Type;
|
use crate::types::Type;
|
||||||
use crate::{HasTy, SemanticModel};
|
use crate::{HasTy, SemanticModel};
|
||||||
|
|
||||||
|
|
|
@ -1502,7 +1502,8 @@ mod tests {
|
||||||
|
|
||||||
use crate::builtins::builtins_scope;
|
use crate::builtins::builtins_scope;
|
||||||
use crate::db::tests::TestDb;
|
use crate::db::tests::TestDb;
|
||||||
use crate::program::{Program, SearchPathSettings, TargetVersion};
|
use crate::program::{Program, SearchPathSettings};
|
||||||
|
use crate::python_version::TargetVersion;
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::semantic_index::symbol::FileScopeId;
|
use crate::semantic_index::symbol::FileScopeId;
|
||||||
use crate::semantic_index::{global_scope, semantic_index, symbol_table, use_def_map};
|
use crate::semantic_index::{global_scope, semantic_index, symbol_table, use_def_map};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue