diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 53636f0230..2ce20c75ae 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -84,7 +84,11 @@ cd extra_tests pytest -v # Run the Python test module -cargo run --release -- -m test +cargo run --release -- -m test ${TEST_MODULE} +cargo run --release -- -m test test_unicode # to test test_unicode.py + +# Run the Python test module with specific function +cargo run --release -- -m test test_unicode -k test_unicode_escape ``` ### Determining What to Implement diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index 4d630ed166..633893fec6 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -275,8 +275,6 @@ class MyType(type): with self.assertRaises(TypeError): MyType[int] - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_pickle(self): alias = GenericAlias(list, T) for proto in range(pickle.HIGHEST_PROTOCOL + 1): @@ -286,8 +284,6 @@ def test_pickle(self): self.assertEqual(loaded.__args__, alias.__args__) self.assertEqual(loaded.__parameters__, alias.__parameters__) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_copy(self): class X(list): def __copy__(self): diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index c62bf61181..2d6f15e9d9 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -877,8 +877,6 @@ def eq(actual, expected, typed=True): eq(x[NT], int | NT | bytes) eq(x[S], int | S | bytes) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_union_pickle(self): orig = list[T] | int for proto in range(pickle.HIGHEST_PROTOCOL + 1): @@ -888,8 +886,6 @@ def test_union_pickle(self): self.assertEqual(loaded.__args__, orig.__args__) self.assertEqual(loaded.__parameters__, orig.__parameters__) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_union_copy(self): orig = list[T] | int for copied in (copy.copy(orig), copy.deepcopy(orig)): diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 70397e2649..1bf1fd0da5 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -374,7 +374,6 @@ def test_alias(self): class TypeVarTests(BaseTestCase): # TODO: RUSTPYTHON - @unittest.expectedFailure def test_basic_plain(self): T = TypeVar('T') # T equals itself. @@ -405,7 +404,6 @@ def test_basic_with_exec(self): self.assertIs(T.__module__, None) # TODO: RUSTPYTHON - @unittest.expectedFailure def test_attributes(self): T_bound = TypeVar('T_bound', bound=int) self.assertEqual(T_bound.__name__, 'T_bound') @@ -448,14 +446,11 @@ def test_typevar_subclass_type_error(self): issubclass(T, int) # TODO: RUSTPYTHON - @unittest.expectedFailure def test_constrained_error(self): with self.assertRaises(TypeError): X = TypeVar('X', int) X - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_union_unique(self): X = TypeVar('X') Y = TypeVar('Y') @@ -486,7 +481,6 @@ def test_union_constrained(self): self.assertNotEqual(Union[A, str], Union[A]) # TODO: RUSTPYTHON - @unittest.expectedFailure def test_repr(self): self.assertEqual(repr(T), '~T') self.assertEqual(repr(KT), '~KT') @@ -502,7 +496,6 @@ def test_no_redefinition(self): self.assertNotEqual(TypeVar('T', int, str), TypeVar('T', int, str)) # TODO: RUSTPYTHON - @unittest.expectedFailure def test_cannot_subclass(self): with self.assertRaisesRegex(TypeError, NOT_A_BASE_TYPE % 'TypeVar'): class V(TypeVar): pass @@ -515,8 +508,6 @@ def test_cannot_instantiate_vars(self): with self.assertRaises(TypeError): TypeVar('A')() - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_bound_errors(self): with self.assertRaises(TypeError): TypeVar('X', bound=Union) @@ -533,22 +524,16 @@ def test_missing__name__(self): ) exec(code, {}) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_no_bivariant(self): with self.assertRaises(ValueError): TypeVar('T', covariant=True, contravariant=True) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_cannot_combine_explicit_and_infer(self): with self.assertRaises(ValueError): TypeVar('T', covariant=True, infer_variance=True) with self.assertRaises(ValueError): TypeVar('T', contravariant=True, infer_variance=True) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_var_substitution(self): T = TypeVar('T') subst = T.__typing_subst__ @@ -591,7 +576,6 @@ def test_many_weakrefs(self): del vals # TODO: RUSTPYTHON - @unittest.expectedFailure def test_constructor(self): T = TypeVar(name="T") self.assertEqual(T.__name__, "T") @@ -648,8 +632,6 @@ def test_constructor(self): self.assertIs(T.__infer_variance__, True) class TypeParameterDefaultsTests(BaseTestCase): - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_typevar(self): T = TypeVar('T', default=int) self.assertEqual(T.__default__, int) @@ -844,8 +826,6 @@ class A(Generic[T, U, DefaultStrT]): ... ): Test = A[int] - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_pickle(self): global U, U_co, U_contra, U_default # pickle wants to reference the class by name U = TypeVar('U') @@ -3695,8 +3675,6 @@ def test_repr(self): self.assertEqual(repr(MySimpleMapping), f"") - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_chain_repr(self): T = TypeVar('T') S = TypeVar('S') @@ -3721,8 +3699,6 @@ class C(Generic[T]): self.assertTrue(str(Z).endswith( '.C[typing.Tuple[str, int]]')) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_new_repr(self): T = TypeVar('T') U = TypeVar('U', covariant=True) @@ -3734,8 +3710,6 @@ def test_new_repr(self): self.assertEqual(repr(List[S][T][int]), 'typing.List[int]') self.assertEqual(repr(List[int]), 'typing.List[int]') - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_new_repr_complex(self): T = TypeVar('T') TS = TypeVar('TS') @@ -3862,8 +3836,6 @@ def test_orig_bases(self): class C(typing.Dict[str, T]): ... self.assertEqual(C.__orig_bases__, (typing.Dict[str, T],)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_naive_runtime_checks(self): def naive_dict_check(obj, tp): # Check if a dictionary conforms to Dict type @@ -3919,8 +3891,6 @@ class D(C, List[T][U][V]): ... self.assertEqual(C.__orig_bases__, (List[T][U][V],)) self.assertEqual(D.__orig_bases__, (C, List[T][U][V])) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_subscript_meta(self): T = TypeVar('T') class Meta(type): ... @@ -3972,8 +3942,6 @@ class A(Generic[T]): self.assertTrue(repr(Tuple[mod_generics_cache.B.A[str]]) .endswith('mod_generics_cache.B.A[str]]')) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_extended_generic_rules_eq(self): T = TypeVar('T') U = TypeVar('U') @@ -3990,8 +3958,6 @@ class Derived(Base): ... self.assertEqual(Callable[[T], T][KT], Callable[[KT], KT]) self.assertEqual(Callable[..., List[T]][int], Callable[..., List[int]]) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_extended_generic_rules_repr(self): T = TypeVar('T') self.assertEqual(repr(Union[Tuple, Callable]).replace('typing.', ''), @@ -4298,8 +4264,6 @@ class C(B[int]): ) del PP - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_copy_and_deepcopy(self): T = TypeVar('T') class Node(Generic[T]): ... @@ -8419,8 +8383,6 @@ def test_order_in_union(self): with self.subTest(args=args): self.assertEqual(expr2, Union[args]) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_specialize(self): L = Annotated[List[T], "my decoration"] LI = Annotated[List[int], "my decoration"] @@ -8471,8 +8433,6 @@ def __eq__(self, other): self.assertEqual(a.x, c.x) self.assertEqual(a.classvar, c.classvar) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_instantiate_generic(self): MyCount = Annotated[typing.Counter[T], "my decoration"] self.assertEqual(MyCount([4, 4, 5]), {4: 2, 5: 1}) @@ -8601,8 +8561,6 @@ class _Annotated_test_G(Generic[T]): self.assertEqual(x.bar, 'abc') self.assertEqual(x.x, 1) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_subst(self): dec = "a decoration" dec2 = "another decoration" @@ -8748,8 +8706,6 @@ def test_typevar_subst(self): with self.assertRaises(TypeError): J[int] - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_annotated_in_other_types(self): X = List[Annotated[T, 5]] self.assertEqual(X[int], List[Annotated[int, 5]]) @@ -9750,14 +9706,14 @@ class CustomerModel(ModelBase, init=False): class NoDefaultTests(BaseTestCase): + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_pickling(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): s = pickle.dumps(NoDefault, proto) loaded = pickle.loads(s) self.assertIs(NoDefault, loaded) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_constructor(self): self.assertIs(NoDefault, type(NoDefault)()) with self.assertRaises(TypeError): @@ -9775,8 +9731,6 @@ def test_doc(self): def test_class(self): self.assertIs(NoDefault.__class__, type(NoDefault)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_no_call(self): with self.assertRaises(TypeError): NoDefault() diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index 490134829e..45df7b5f64 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -1498,7 +1498,7 @@ fn best_base<'a>(bases: &'a [PyTypeRef], vm: &VirtualMachine) -> PyResult<&'a Py if !base_i.slots.flags.has_feature(PyTypeFlags::BASETYPE) { return Err(vm.new_type_error(format!( "type '{}' is not an acceptable base type", - base_i.name() + base_i.slot_name() ))); } diff --git a/vm/src/stdlib/typing.rs b/vm/src/stdlib/typing.rs index d7008ee739..0564d22b2f 100644 --- a/vm/src/stdlib/typing.rs +++ b/vm/src/stdlib/typing.rs @@ -1,23 +1,41 @@ -pub(crate) use _typing::make_module; +use crate::{PyRef, VirtualMachine, stdlib::PyModule}; + +pub(crate) use _typing::NoDefault; + +pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef { + let module = _typing::make_module(vm); + extend_module!(vm, &module, { + "NoDefault" => vm.ctx.typing_no_default.clone(), + }); + module +} #[pymodule] pub(crate) mod _typing { use crate::{ - PyObjectRef, PyPayload, PyResult, VirtualMachine, + AsObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, builtins::{PyGenericAlias, PyTupleRef, PyTypeRef, pystr::AsPyStr}, function::{FuncArgs, IntoFuncArgs}, + types::{Constructor, Representable}, }; pub(crate) fn _call_typing_func_object<'a>( - _vm: &VirtualMachine, - _func_name: impl AsPyStr<'a>, - _args: impl IntoFuncArgs, + vm: &VirtualMachine, + func_name: impl AsPyStr<'a>, + args: impl IntoFuncArgs, ) -> PyResult { - todo!("does this work????"); - // let module = vm.import("typing", 0)?; - // let module = vm.import("_pycodecs", None, 0)?; - // let func = module.get_attr(func_name, vm)?; - // func.call(args, vm) + let module = vm.import("typing", 0)?; + let func = module.get_attr(func_name.as_pystr(&vm.ctx), vm)?; + func.call(args, vm) + } + + fn type_check(arg: PyObjectRef, msg: &str, vm: &VirtualMachine) -> PyResult { + // Calling typing.py here leads to bootstrapping problems + if vm.is_none(&arg) { + return Ok(arg.class().to_owned().into()); + } + let message_str: PyObjectRef = vm.ctx.new_str(msg).into(); + _call_typing_func_object(vm, "_type_check", (arg, message_str)) } #[pyfunction] @@ -26,7 +44,7 @@ pub(crate) mod _typing { } #[pyattr] - #[pyclass(name = "TypeVar")] + #[pyclass(name = "TypeVar", module = "typing")] #[derive(Debug, PyPayload)] #[allow(dead_code)] pub(crate) struct TypeVar { @@ -35,29 +53,255 @@ pub(crate) mod _typing { evaluate_bound: PyObjectRef, constraints: parking_lot::Mutex, evaluate_constraints: PyObjectRef, + default_value: parking_lot::Mutex, + evaluate_default: PyObjectRef, covariant: bool, contravariant: bool, infer_variance: bool, } - #[pyclass(flags(BASETYPE))] + #[pyclass(flags(HAS_DICT), with(Constructor, Representable))] impl TypeVar { - pub(crate) fn _bound(&self, vm: &VirtualMachine) -> PyResult { + #[pymethod(magic)] + fn mro_entries(&self, _bases: PyObjectRef, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error("Cannot subclass an instance of TypeVar".to_owned())) + } + + #[pygetset(magic)] + fn name(&self) -> PyObjectRef { + self.name.clone() + } + + #[pygetset(magic)] + fn constraints(&self, vm: &VirtualMachine) -> PyResult { + let mut constraints = self.constraints.lock(); + if !vm.is_none(&constraints) { + return Ok(constraints.clone()); + } + let r = if !vm.is_none(&self.evaluate_constraints) { + *constraints = self.evaluate_constraints.call((), vm)?; + constraints.clone() + } else { + vm.ctx.empty_tuple.clone().into() + }; + Ok(r) + } + + #[pygetset(magic)] + fn bound(&self, vm: &VirtualMachine) -> PyResult { let mut bound = self.bound.lock(); if !vm.is_none(&bound) { return Ok(bound.clone()); } - if !vm.is_none(&self.evaluate_bound) { + let r = if !vm.is_none(&self.evaluate_bound) { *bound = self.evaluate_bound.call((), vm)?; - Ok(bound.clone()) + bound.clone() } else { - Ok(vm.ctx.none()) - } + vm.ctx.none() + }; + Ok(r) } #[pygetset(magic)] - fn name(&self) -> PyObjectRef { + fn covariant(&self) -> bool { + self.covariant + } + + #[pygetset(magic)] + fn contravariant(&self) -> bool { + self.contravariant + } + + #[pygetset(magic)] + fn infer_variance(&self) -> bool { + self.infer_variance + } + + #[pygetset(magic)] + fn default(&self, vm: &VirtualMachine) -> PyResult { + let mut default_value = self.default_value.lock(); + // Check if default_value is NoDefault (not just None) + if !default_value.is(&vm.ctx.typing_no_default) { + return Ok(default_value.clone()); + } + if !vm.is_none(&self.evaluate_default) { + *default_value = self.evaluate_default.call((), vm)?; + Ok(default_value.clone()) + } else { + // Return NoDefault singleton + Ok(vm.ctx.typing_no_default.clone().into()) + } + } + + #[pymethod(magic)] + fn typing_subst( + zelf: crate::PyRef, + arg: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult { + let self_obj: PyObjectRef = zelf.into(); + _call_typing_func_object(vm, "_typevar_subst", (self_obj, arg)) + } + + #[pymethod(magic)] + fn reduce(&self) -> PyObjectRef { self.name.clone() } + + #[pymethod] + fn has_default(&self, vm: &VirtualMachine) -> bool { + if !vm.is_none(&self.evaluate_default) { + return true; + } + let default_value = self.default_value.lock(); + // Check if default_value is not NoDefault + !default_value.is(&vm.ctx.typing_no_default) + } + } + + impl Representable for TypeVar { + #[inline(always)] + fn repr_str(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let name = zelf.name.str(vm)?; + let repr = if zelf.covariant { + format!("+{}", name) + } else if zelf.contravariant { + format!("-{}", name) + } else { + format!("~{}", name) + }; + Ok(repr) + } + } + + impl Constructor for TypeVar { + type Args = FuncArgs; + + fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { + let mut kwargs = args.kwargs; + // Parse arguments manually + let (name, constraints) = if args.args.is_empty() { + // Check if name is provided as keyword argument + if let Some(name) = kwargs.swap_remove("name") { + (name, vec![]) + } else { + return Err(vm.new_type_error( + "TypeVar() missing required argument: 'name' (pos 1)".to_owned(), + )); + } + } else if args.args.len() == 1 { + (args.args[0].clone(), vec![]) + } else { + let name = args.args[0].clone(); + let constraints = args.args[1..].to_vec(); + (name, constraints) + }; + + let bound = kwargs.swap_remove("bound"); + let covariant = kwargs + .swap_remove("covariant") + .map(|v| v.try_to_bool(vm)) + .transpose()? + .unwrap_or(false); + let contravariant = kwargs + .swap_remove("contravariant") + .map(|v| v.try_to_bool(vm)) + .transpose()? + .unwrap_or(false); + let infer_variance = kwargs + .swap_remove("infer_variance") + .map(|v| v.try_to_bool(vm)) + .transpose()? + .unwrap_or(false); + let default = kwargs.swap_remove("default"); + + // Check for unexpected keyword arguments + if !kwargs.is_empty() { + let unexpected_keys: Vec = kwargs.keys().map(|s| s.to_string()).collect(); + return Err(vm.new_type_error(format!( + "TypeVar() got unexpected keyword argument(s): {}", + unexpected_keys.join(", ") + ))); + } + + // Check for invalid combinations + if covariant && contravariant { + return Err( + vm.new_value_error("Bivariant type variables are not supported.".to_owned()) + ); + } + + if infer_variance && (covariant || contravariant) { + return Err(vm.new_value_error( + "Variance cannot be specified with infer_variance".to_owned(), + )); + } + + // Handle constraints and bound + let (constraints_obj, evaluate_constraints) = if !constraints.is_empty() { + // Check for single constraint + if constraints.len() == 1 { + return Err(vm.new_type_error("A single constraint is not allowed".to_owned())); + } + if bound.is_some() { + return Err( + vm.new_type_error("Constraints cannot be used with bound".to_owned()) + ); + } + let constraints_tuple = vm.ctx.new_tuple(constraints); + (constraints_tuple.clone().into(), constraints_tuple.into()) + } else { + (vm.ctx.none(), vm.ctx.none()) + }; + + // Handle bound + let (bound_obj, evaluate_bound) = if let Some(bound) = bound { + if vm.is_none(&bound) { + (vm.ctx.none(), vm.ctx.none()) + } else { + // Type check the bound + let bound = type_check(bound, "Bound must be a type.", vm)?; + (bound, vm.ctx.none()) + } + } else { + (vm.ctx.none(), vm.ctx.none()) + }; + + // Handle default value + let (default_value, evaluate_default) = if let Some(default) = default { + (default, vm.ctx.none()) + } else { + // If no default provided, use NoDefault singleton + (vm.ctx.typing_no_default.clone().into(), vm.ctx.none()) + }; + + let typevar = TypeVar { + name, + bound: parking_lot::Mutex::new(bound_obj), + evaluate_bound, + constraints: parking_lot::Mutex::new(constraints_obj), + evaluate_constraints, + default_value: parking_lot::Mutex::new(default_value), + evaluate_default, + covariant, + contravariant, + infer_variance, + }; + + let obj = typevar.into_ref_with_type(vm, cls)?; + let obj_ref: PyObjectRef = obj.into(); + + // Set __module__ from the current frame + if let Some(frame) = vm.current_frame() { + if let Ok(module_name) = frame.globals.get_item("__name__", vm) { + // Don't set __module__ if it's None + if !vm.is_none(&module_name) { + obj_ref.set_attr("__module__", module_name, vm)?; + } + } + } + + Ok(obj_ref) + } } pub(crate) fn make_typevar( @@ -72,9 +316,11 @@ pub(crate) mod _typing { evaluate_bound, constraints: parking_lot::Mutex::new(vm.ctx.none()), evaluate_constraints, + default_value: parking_lot::Mutex::new(vm.ctx.none()), + evaluate_default: vm.ctx.none(), covariant: false, contravariant: false, - infer_variance: true, + infer_variance: false, } } @@ -169,16 +415,37 @@ pub(crate) mod _typing { } } - #[pyattr] - #[pyclass(name = "NoDefault")] + #[pyclass(no_attr, name = "NoDefaultType", module = "typing")] #[derive(Debug, PyPayload)] - #[allow(dead_code)] - pub(crate) struct NoDefault { - name: PyObjectRef, + pub struct NoDefault; + + #[pyclass(with(Constructor), flags(BASETYPE))] + impl NoDefault { + #[pymethod(magic)] + fn reduce(&self, _vm: &VirtualMachine) -> String { + "NoDefault".to_owned() + } } - #[pyclass(flags(BASETYPE))] - impl NoDefault {} + impl Constructor for NoDefault { + type Args = FuncArgs; + + fn py_new(_cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { + if !args.args.is_empty() || !args.kwargs.is_empty() { + return Err(vm.new_type_error("NoDefaultType takes no arguments".to_owned())); + } + + // Return singleton instance from context + Ok(vm.ctx.typing_no_default.clone().into()) + } + } + + impl Representable for NoDefault { + #[inline(always)] + fn repr_str(_zelf: &crate::Py, _vm: &VirtualMachine) -> PyResult { + Ok("typing.NoDefault".to_owned()) + } + } #[pyattr] #[pyclass(name = "TypeVarTuple")] diff --git a/vm/src/types/zoo.rs b/vm/src/types/zoo.rs index 0d39648514..383732cf69 100644 --- a/vm/src/types/zoo.rs +++ b/vm/src/types/zoo.rs @@ -88,6 +88,7 @@ pub struct TypeZoo { pub object_type: &'static Py, pub ellipsis_type: &'static Py, pub none_type: &'static Py, + pub typing_no_default_type: &'static Py, pub not_implemented_type: &'static Py, pub generic_alias_type: &'static Py, pub union_type: &'static Py, @@ -179,6 +180,7 @@ impl TypeZoo { weakproxy_type: weakproxy::PyWeakProxy::init_builtin_type(), method_descriptor_type: descriptor::PyMethodDescriptor::init_builtin_type(), none_type: singletons::PyNone::init_builtin_type(), + typing_no_default_type: crate::stdlib::typing::NoDefault::init_builtin_type(), not_implemented_type: singletons::PyNotImplemented::init_builtin_type(), generic_alias_type: genericalias::PyGenericAlias::init_builtin_type(), union_type: union_::PyUnion::init_builtin_type(), diff --git a/vm/src/vm/context.rs b/vm/src/vm/context.rs index a61484e6bc..a89bc6cd74 100644 --- a/vm/src/vm/context.rs +++ b/vm/src/vm/context.rs @@ -41,6 +41,8 @@ pub struct Context { pub ellipsis: PyRef, pub not_implemented: PyRef, + pub typing_no_default: PyRef, + pub types: TypeZoo, pub exceptions: exceptions::ExceptionZoo, pub int_cache_pool: Vec, @@ -278,6 +280,11 @@ impl Context { let ellipsis = create_object(PyEllipsis, PyEllipsis::static_type()); let not_implemented = create_object(PyNotImplemented, PyNotImplemented::static_type()); + let typing_no_default = create_object( + crate::stdlib::typing::NoDefault, + crate::stdlib::typing::NoDefault::static_type(), + ); + let int_cache_pool = Self::INT_CACHE_POOL_RANGE .map(|v| { PyRef::new_ref( @@ -317,6 +324,7 @@ impl Context { true_value, false_value, none, + typing_no_default, empty_tuple, empty_frozenset, empty_str,