mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-28 23:43:53 +00:00
[red-knot] Fallback for typing._NoDefaultType
(#14783)
## Summary `typing_extensions` has a `>=3.13` re-export for the `typing.NoDefault` singleton, but not for `typing._NoDefaultType`. This causes problems as soon as we understand `sys.version_info` branches, so we explicity switch to `typing._NoDefaultType` for Python 3.13 and later. This is a part of #14759 that I thought might make sense to break out and merge in isolation. ## Test Plan New test that will become more meaningful with #12700 --------- Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
bd27bfab5d
commit
2d3f557875
1 changed files with 34 additions and 11 deletions
|
@ -28,7 +28,7 @@ use crate::symbol::{Boundness, Symbol};
|
|||
use crate::types::diagnostic::TypeCheckDiagnosticsBuilder;
|
||||
use crate::types::mro::{ClassBase, Mro, MroError, MroIterator};
|
||||
use crate::types::narrow::narrowing_constraint;
|
||||
use crate::{Db, FxOrderSet, Module, Program};
|
||||
use crate::{Db, FxOrderSet, Module, Program, PythonVersion};
|
||||
|
||||
mod builder;
|
||||
mod diagnostic;
|
||||
|
@ -1897,13 +1897,13 @@ impl<'db> KnownClass {
|
|||
}
|
||||
|
||||
pub fn to_class_literal(self, db: &'db dyn Db) -> Type<'db> {
|
||||
core_module_symbol(db, self.canonical_module(), self.as_str())
|
||||
core_module_symbol(db, self.canonical_module(db), self.as_str())
|
||||
.ignore_possibly_unbound()
|
||||
.unwrap_or(Type::Unknown)
|
||||
}
|
||||
|
||||
/// Return the module in which we should look up the definition for this class
|
||||
pub(crate) const fn canonical_module(self) -> CoreStdlibModule {
|
||||
pub(crate) fn canonical_module(self, db: &'db dyn Db) -> CoreStdlibModule {
|
||||
match self {
|
||||
Self::Bool
|
||||
| Self::Object
|
||||
|
@ -1921,10 +1921,18 @@ impl<'db> KnownClass {
|
|||
Self::GenericAlias | Self::ModuleType | Self::FunctionType => CoreStdlibModule::Types,
|
||||
Self::NoneType => CoreStdlibModule::Typeshed,
|
||||
Self::SpecialForm | Self::TypeVar | Self::TypeAliasType => CoreStdlibModule::Typing,
|
||||
// TODO when we understand sys.version_info, we will need an explicit fallback here,
|
||||
// because typing_extensions has a 3.13+ re-export for the `typing.NoDefault`
|
||||
// singleton, but not for `typing._NoDefaultType`
|
||||
Self::NoDefaultType => CoreStdlibModule::TypingExtensions,
|
||||
Self::NoDefaultType => {
|
||||
let python_version = Program::get(db).target_version(db);
|
||||
|
||||
// typing_extensions has a 3.13+ re-export for the `typing.NoDefault`
|
||||
// singleton, but not for `typing._NoDefaultType`. So we need to switch
|
||||
// to `typing._NoDefaultType` for newer versions:
|
||||
if python_version >= PythonVersion::PY313 {
|
||||
CoreStdlibModule::Typing
|
||||
} else {
|
||||
CoreStdlibModule::TypingExtensions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1984,11 +1992,11 @@ impl<'db> KnownClass {
|
|||
};
|
||||
|
||||
let module = file_to_module(db, file)?;
|
||||
candidate.check_module(&module).then_some(candidate)
|
||||
candidate.check_module(db, &module).then_some(candidate)
|
||||
}
|
||||
|
||||
/// Return `true` if the module of `self` matches `module_name`
|
||||
fn check_module(self, module: &Module) -> bool {
|
||||
fn check_module(self, db: &'db dyn Db, module: &Module) -> bool {
|
||||
if !module.search_path().is_standard_library() {
|
||||
return false;
|
||||
}
|
||||
|
@ -2008,7 +2016,7 @@ impl<'db> KnownClass {
|
|||
| Self::GenericAlias
|
||||
| Self::ModuleType
|
||||
| Self::VersionInfo
|
||||
| Self::FunctionType => module.name() == self.canonical_module().as_str(),
|
||||
| Self::FunctionType => module.name() == self.canonical_module(db).as_str(),
|
||||
Self::NoneType => matches!(module.name().as_str(), "_typeshed" | "types"),
|
||||
Self::SpecialForm | Self::TypeVar | Self::TypeAliasType | Self::NoDefaultType => {
|
||||
matches!(module.name().as_str(), "typing" | "typing_extensions")
|
||||
|
@ -3683,13 +3691,28 @@ pub(crate) mod tests {
|
|||
#[test_case(Ty::None)]
|
||||
#[test_case(Ty::BooleanLiteral(true))]
|
||||
#[test_case(Ty::BooleanLiteral(false))]
|
||||
#[test_case(Ty::KnownClassInstance(KnownClass::NoDefaultType))]
|
||||
fn is_singleton(from: Ty) {
|
||||
let db = setup_db();
|
||||
|
||||
assert!(from.into_type(&db).is_singleton(&db));
|
||||
}
|
||||
|
||||
/// Explicitly test for Python version <3.13 and >=3.13, to ensure that
|
||||
/// the fallback to `typing_extensions` is working correctly.
|
||||
/// See [`KnownClass::canonical_module`] for more information.
|
||||
#[test_case(PythonVersion::PY312)]
|
||||
#[test_case(PythonVersion::PY313)]
|
||||
fn no_default_type_is_singleton(python_version: PythonVersion) {
|
||||
let db = TestDbBuilder::new()
|
||||
.with_python_version(python_version)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let no_default = Ty::KnownClassInstance(KnownClass::NoDefaultType).into_type(&db);
|
||||
|
||||
assert!(no_default.is_singleton(&db));
|
||||
}
|
||||
|
||||
#[test_case(Ty::None)]
|
||||
#[test_case(Ty::BooleanLiteral(true))]
|
||||
#[test_case(Ty::IntLiteral(1))]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue