Skip to content

Commit 2d3f557

Browse files
sharkdpMichaReiser
andauthored
[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 <[email protected]>
1 parent bd27bfa commit 2d3f557

File tree

1 file changed

+34
-11
lines changed
  • crates/red_knot_python_semantic/src

1 file changed

+34
-11
lines changed

crates/red_knot_python_semantic/src/types.rs

+34-11
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::symbol::{Boundness, Symbol};
2828
use crate::types::diagnostic::TypeCheckDiagnosticsBuilder;
2929
use crate::types::mro::{ClassBase, Mro, MroError, MroIterator};
3030
use crate::types::narrow::narrowing_constraint;
31-
use crate::{Db, FxOrderSet, Module, Program};
31+
use crate::{Db, FxOrderSet, Module, Program, PythonVersion};
3232

3333
mod builder;
3434
mod diagnostic;
@@ -1897,13 +1897,13 @@ impl<'db> KnownClass {
18971897
}
18981898

18991899
pub fn to_class_literal(self, db: &'db dyn Db) -> Type<'db> {
1900-
core_module_symbol(db, self.canonical_module(), self.as_str())
1900+
core_module_symbol(db, self.canonical_module(db), self.as_str())
19011901
.ignore_possibly_unbound()
19021902
.unwrap_or(Type::Unknown)
19031903
}
19041904

19051905
/// Return the module in which we should look up the definition for this class
1906-
pub(crate) const fn canonical_module(self) -> CoreStdlibModule {
1906+
pub(crate) fn canonical_module(self, db: &'db dyn Db) -> CoreStdlibModule {
19071907
match self {
19081908
Self::Bool
19091909
| Self::Object
@@ -1921,10 +1921,18 @@ impl<'db> KnownClass {
19211921
Self::GenericAlias | Self::ModuleType | Self::FunctionType => CoreStdlibModule::Types,
19221922
Self::NoneType => CoreStdlibModule::Typeshed,
19231923
Self::SpecialForm | Self::TypeVar | Self::TypeAliasType => CoreStdlibModule::Typing,
1924-
// TODO when we understand sys.version_info, we will need an explicit fallback here,
1925-
// because typing_extensions has a 3.13+ re-export for the `typing.NoDefault`
1926-
// singleton, but not for `typing._NoDefaultType`
1927-
Self::NoDefaultType => CoreStdlibModule::TypingExtensions,
1924+
Self::NoDefaultType => {
1925+
let python_version = Program::get(db).target_version(db);
1926+
1927+
// typing_extensions has a 3.13+ re-export for the `typing.NoDefault`
1928+
// singleton, but not for `typing._NoDefaultType`. So we need to switch
1929+
// to `typing._NoDefaultType` for newer versions:
1930+
if python_version >= PythonVersion::PY313 {
1931+
CoreStdlibModule::Typing
1932+
} else {
1933+
CoreStdlibModule::TypingExtensions
1934+
}
1935+
}
19281936
}
19291937
}
19301938

@@ -1984,11 +1992,11 @@ impl<'db> KnownClass {
19841992
};
19851993

19861994
let module = file_to_module(db, file)?;
1987-
candidate.check_module(&module).then_some(candidate)
1995+
candidate.check_module(db, &module).then_some(candidate)
19881996
}
19891997

19901998
/// Return `true` if the module of `self` matches `module_name`
1991-
fn check_module(self, module: &Module) -> bool {
1999+
fn check_module(self, db: &'db dyn Db, module: &Module) -> bool {
19922000
if !module.search_path().is_standard_library() {
19932001
return false;
19942002
}
@@ -2008,7 +2016,7 @@ impl<'db> KnownClass {
20082016
| Self::GenericAlias
20092017
| Self::ModuleType
20102018
| Self::VersionInfo
2011-
| Self::FunctionType => module.name() == self.canonical_module().as_str(),
2019+
| Self::FunctionType => module.name() == self.canonical_module(db).as_str(),
20122020
Self::NoneType => matches!(module.name().as_str(), "_typeshed" | "types"),
20132021
Self::SpecialForm | Self::TypeVar | Self::TypeAliasType | Self::NoDefaultType => {
20142022
matches!(module.name().as_str(), "typing" | "typing_extensions")
@@ -3683,13 +3691,28 @@ pub(crate) mod tests {
36833691
#[test_case(Ty::None)]
36843692
#[test_case(Ty::BooleanLiteral(true))]
36853693
#[test_case(Ty::BooleanLiteral(false))]
3686-
#[test_case(Ty::KnownClassInstance(KnownClass::NoDefaultType))]
36873694
fn is_singleton(from: Ty) {
36883695
let db = setup_db();
36893696

36903697
assert!(from.into_type(&db).is_singleton(&db));
36913698
}
36923699

3700+
/// Explicitly test for Python version <3.13 and >=3.13, to ensure that
3701+
/// the fallback to `typing_extensions` is working correctly.
3702+
/// See [`KnownClass::canonical_module`] for more information.
3703+
#[test_case(PythonVersion::PY312)]
3704+
#[test_case(PythonVersion::PY313)]
3705+
fn no_default_type_is_singleton(python_version: PythonVersion) {
3706+
let db = TestDbBuilder::new()
3707+
.with_python_version(python_version)
3708+
.build()
3709+
.unwrap();
3710+
3711+
let no_default = Ty::KnownClassInstance(KnownClass::NoDefaultType).into_type(&db);
3712+
3713+
assert!(no_default.is_singleton(&db));
3714+
}
3715+
36933716
#[test_case(Ty::None)]
36943717
#[test_case(Ty::BooleanLiteral(true))]
36953718
#[test_case(Ty::IntLiteral(1))]

0 commit comments

Comments
 (0)