diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 8dd3e4f600..a4f2e897b1 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -656,8 +656,6 @@ class A(Generic[P]): ... P_default = ParamSpec('P_default', default=...) self.assertIs(P_default.__default__, ...) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_paramspec_none(self): U = ParamSpec('U') U_None = ParamSpec('U_None', default=None) @@ -756,8 +754,6 @@ class A(Generic[T, P, U]): ... self.assertEqual(A[float, [range]].__args__, (float, (range,), float)) self.assertEqual(A[float, [range], int].__args__, (float, (range,), int)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_typevartuple_none(self): U = TypeVarTuple('U') U_None = TypeVarTuple('U_None', default=None) @@ -3893,8 +3889,6 @@ def f(x: X): ... {'x': list[list[ForwardRef('X')]]} ) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_pep695_generic_class_with_future_annotations(self): original_globals = dict(ann_module695.__dict__) @@ -3913,8 +3907,6 @@ def test_pep695_generic_class_with_future_annotations_and_local_shadowing(self): hints_for_B = get_type_hints(ann_module695.B) self.assertEqual(hints_for_B, {"x": int, "y": str, "z": bytes}) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_pep695_generic_class_with_future_annotations_name_clash_with_global_vars(self): hints_for_C = get_type_hints(ann_module695.C) self.assertEqual( diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 13b90a612c..2bdc5de393 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -1212,6 +1212,7 @@ impl Compiler<'_> { /// Store each type parameter so it is accessible to the current scope, and leave a tuple of /// all the type parameters on the stack. fn compile_type_params(&mut self, type_params: &TypeParams) -> CompileResult<()> { + // First, compile each type parameter and store it for type_param in &type_params.type_params { match type_param { TypeParam::TypeVar(TypeParamTypeVar { name, bound, .. }) => { @@ -1664,8 +1665,12 @@ impl Compiler<'_> { let qualified_name = self.qualified_path.join("."); // If there are type params, we need to push a special symbol table just for them - if type_params.is_some() { + if let Some(type_params) = type_params { self.push_symbol_table(); + // Compile type parameters and store as .type_params + self.compile_type_params(type_params)?; + let dot_type_params = self.name(".type_params"); + emit!(self, Instruction::StoreLocal(dot_type_params)); } self.push_output(bytecode::CodeFlags::empty(), 0, 0, 0, name.to_owned()); @@ -1688,6 +1693,18 @@ impl Compiler<'_> { if Self::find_ann(body) { emit!(self, Instruction::SetupAnnotation); } + + // Set __type_params__ from .type_params if we have type parameters (PEP 695) + if type_params.is_some() { + // Load .type_params from enclosing scope + let dot_type_params = self.name(".type_params"); + emit!(self, Instruction::LoadNameAny(dot_type_params)); + + // Store as __type_params__ + let dunder_type_params = self.name("__type_params__"); + emit!(self, Instruction::StoreLocal(dunder_type_params)); + } + self.compile_statements(body)?; let classcell_idx = self @@ -1721,8 +1738,10 @@ impl Compiler<'_> { let mut func_flags = bytecode::MakeFunctionFlags::empty(); // Prepare generic type parameters: - if let Some(type_params) = type_params { - self.compile_type_params(type_params)?; + if type_params.is_some() { + // Load .type_params from the type params scope + let dot_type_params = self.name(".type_params"); + emit!(self, Instruction::LoadNameAny(dot_type_params)); func_flags |= bytecode::MakeFunctionFlags::TYPE_PARAMS; } diff --git a/compiler/codegen/src/symboltable.rs b/compiler/codegen/src/symboltable.rs index 4f42b3996f..385abfd72f 100644 --- a/compiler/codegen/src/symboltable.rs +++ b/compiler/codegen/src/symboltable.rs @@ -1267,6 +1267,7 @@ impl SymbolTableBuilder<'_> { } fn scan_type_params(&mut self, type_params: &TypeParams) -> SymbolTableResult { + // First register all type parameters for type_param in &type_params.type_params { match type_param { TypeParam::TypeVar(TypeParamTypeVar { diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 0a13c04dc8..6b899ef81f 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -572,7 +572,8 @@ impl PyByteArray { self.borrow_buf_mut().reverse(); } - #[pyclassmethod] + // TODO: Uncomment when Python adds __class_getitem__ to bytearray + // #[pyclassmethod] fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { PyGenericAlias::from_args(cls, args, vm) } diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 8045081dc7..67f910bef7 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -483,7 +483,8 @@ impl PyBytes { PyTuple::new_ref(param, &vm.ctx) } - #[pyclassmethod] + // TODO: Uncomment when Python adds __class_getitem__ to bytes + // #[pyclassmethod] fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { PyGenericAlias::from_args(cls, args, vm) } diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index d2c77d2652..9bf22c6b35 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -550,7 +550,8 @@ impl Py { Representable ))] impl PyMemoryView { - #[pyclassmethod] + // TODO: Uncomment when Python adds __class_getitem__ to memoryview + // #[pyclassmethod] fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { PyGenericAlias::from_args(cls, args, vm) } diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index 3f698de654..ac9c35a081 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -184,11 +184,6 @@ pub fn init(context: &Context) { Representable ))] impl PyRange { - #[pyclassmethod] - fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { - PyGenericAlias::from_args(cls, args, vm) - } - fn new(cls: PyTypeRef, stop: ArgIndex, vm: &VirtualMachine) -> PyResult> { PyRange { start: vm.ctx.new_pyref(0), @@ -328,6 +323,12 @@ impl PyRange { Ok(range.into()) } + + // TODO: Uncomment when Python adds __class_getitem__ to range + // #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } #[pyclass] diff --git a/vm/src/builtins/slice.rs b/vm/src/builtins/slice.rs index 02e066bbac..42607f4bb2 100644 --- a/vm/src/builtins/slice.rs +++ b/vm/src/builtins/slice.rs @@ -30,11 +30,6 @@ impl PyPayload for PySlice { #[pyclass(with(Comparable, Representable, Hashable))] impl PySlice { - #[pyclassmethod] - fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { - PyGenericAlias::from_args(cls, args, vm) - } - #[pygetset] fn start(&self, vm: &VirtualMachine) -> PyObjectRef { self.start.clone().to_pyobject(vm) @@ -200,6 +195,12 @@ impl PySlice { (zelf.start.clone(), zelf.stop.clone(), zelf.step.clone()), )) } + + // TODO: Uncomment when Python adds __class_getitem__ to slice + // #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } impl Hashable for PySlice { diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs index 7d3afe04d9..6988085d21 100644 --- a/vm/src/stdlib/builtins.rs +++ b/vm/src/stdlib/builtins.rs @@ -932,6 +932,23 @@ mod builtins { )) })?; + // For PEP 695 classes, set .type_params in namespace before calling the function + if let Ok(type_params) = function + .as_object() + .get_attr(identifier!(vm, __type_params__), vm) + { + if let Some(type_params_tuple) = type_params.downcast_ref::() { + if !type_params_tuple.is_empty() { + // Set .type_params in namespace so the compiler-generated code can use it + namespace.as_object().set_item( + vm.ctx.intern_str(".type_params"), + type_params, + vm, + )?; + } + } + } + let classcell = function.invoke_with_locals(().into(), Some(namespace.clone()), vm)?; let classcell = >::try_from_object(vm, classcell)?; @@ -943,9 +960,27 @@ mod builtins { )?; } + // Remove .type_params from namespace before creating the class + namespace + .as_object() + .del_item(vm.ctx.intern_str(".type_params"), vm) + .ok(); + let args = FuncArgs::new(vec![name_obj.into(), bases, namespace.into()], kwargs); let class = metaclass.call(args, vm)?; + // For PEP 695 classes, set __type_params__ on the class from the function + if let Ok(type_params) = function + .as_object() + .get_attr(identifier!(vm, __type_params__), vm) + { + if let Some(type_params_tuple) = type_params.downcast_ref::() { + if !type_params_tuple.is_empty() { + class.set_attr(identifier!(vm, __type_params__), type_params, vm)?; + } + } + } + if let Some(ref classcell) = classcell { let classcell = classcell.get().ok_or_else(|| { vm.new_type_error(format!(