diff --git a/crates/ty_python_semantic/src/module_resolver/module.rs b/crates/ty_python_semantic/src/module_resolver/module.rs index 7d61e65818..67a2f4eff5 100644 --- a/crates/ty_python_semantic/src/module_resolver/module.rs +++ b/crates/ty_python_semantic/src/module_resolver/module.rs @@ -330,6 +330,8 @@ pub enum KnownModule { UnittestMock, #[cfg(test)] Uuid, + #[cfg(test)] + UnderscoreSsl, Warnings, } @@ -355,6 +357,8 @@ impl KnownModule { Self::UnittestMock => "unittest.mock", #[cfg(test)] Self::Uuid => "uuid", + #[cfg(test)] + Self::UnderscoreSsl => "_ssl", Self::Templatelib => "string.templatelib", } } diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index ece4c38ab1..0029fe2fde 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -1460,11 +1460,6 @@ impl<'db> Type<'db> { .has_relation_to_impl(db, right, relation, visitor) } - (Type::TypedDict(_), _) | (_, Type::TypedDict(_)) => { - // TODO: Implement assignability and subtyping for TypedDict - C::from_bool(db, relation.is_assignability()) - } - // In general, a TypeVar `T` is not a subtype of a type `S` unless one of the two conditions is satisfied: // 1. `T` is a bound TypeVar and `T`'s upper bound is a subtype of `S`. // TypeVars without an explicit upper bound are treated as having an implicit upper bound of `object`. @@ -1816,6 +1811,16 @@ impl<'db> Type<'db> { .unwrap_or_else(|| KnownClass::Type.to_instance(db)) .has_relation_to_impl(db, target, relation, visitor), + (Type::TypedDict(left), Type::TypedDict(right)) if left == right => { + // TODO: Implement structural assignability and subtyping for TypedDict + C::always_satisfiable(db) + } + + (Type::TypedDict(_), _) | (_, Type::TypedDict(_)) => { + // TODO: Implement structural assignability and subtyping for TypedDict + C::from_bool(db, relation.is_assignability()) + } + // For example: `Type::SpecialForm(SpecialFormType::Type)` is a subtype of `Type::NominalInstance(_SpecialForm)`, // because `Type::SpecialForm(SpecialFormType::Type)` is a set with exactly one runtime value in it // (the symbol `typing.Type`), and that symbol is known to be an instance of `typing._SpecialForm` at runtime. diff --git a/crates/ty_python_semantic/src/types/property_tests/type_generation.rs b/crates/ty_python_semantic/src/types/property_tests/type_generation.rs index 0a296088ff..3da9ce703e 100644 --- a/crates/ty_python_semantic/src/types/property_tests/type_generation.rs +++ b/crates/ty_python_semantic/src/types/property_tests/type_generation.rs @@ -64,6 +64,8 @@ pub(crate) enum Ty { /// where the class has `Any` in its MRO UnittestMockInstance, UnittestMockLiteral, + /// A custom `TypedDict`, imported from the `_ssl` module + TypedDict(&'static str), } #[derive(Debug, Clone, PartialEq)] @@ -236,6 +238,15 @@ impl Ty { returns.map(|ty| ty.into_type(db)), ), ), + Ty::TypedDict(name) => { + let ty = known_module_symbol(db, KnownModule::UnderscoreSsl, name) + .place + .expect_type() + .to_instance(db) + .expect("Class type should be convertible to instance"); + assert!(ty.is_typed_dict()); + ty + } } } } @@ -314,6 +325,8 @@ fn arbitrary_core_type(g: &mut Gen, fully_static: bool) -> Ty { class: "int", method: "bit_length", }, + Ty::TypedDict("_Cipher"), + Ty::TypedDict("_CertInfo"), ]; let types = if fully_static { &types[fully_static_index..]