diff --git a/.cspell.json b/.cspell.json index 62824d2d52f..6112c35d590 100644 --- a/.cspell.json +++ b/.cspell.json @@ -42,6 +42,7 @@ ], "ignorePaths": [ "**/__pycache__/**", + "target/**", "Lib/**" ], // words - list of words to be always considered correct diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index 954229edeb3..46430d32310 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -3246,8 +3246,6 @@ def test_classvar_module_level_import(self): # won't exist on the instance. self.assertNotIn('not_iv4', c.__dict__) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_text_annotations(self): from test import dataclass_textanno diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 050a1635b96..8dd3e4f6007 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -1605,8 +1605,6 @@ class D(Generic[Unpack[Ts]]): pass self.assertIs(D[T].__origin__, D) self.assertIs(D[Unpack[Ts]].__origin__, D) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_get_type_hints_on_unpack_args(self): Ts = TypeVarTuple('Ts') @@ -1809,8 +1807,6 @@ class F(Generic[Unpack[Ts], T1, T2]): pass F[int, str, float] F[int, str, float, bool] - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_variadic_args_annotations_are_correct(self): Ts = TypeVarTuple('Ts') @@ -2494,7 +2490,6 @@ def test_var_substitution(self): self.assertEqual(C5[int, str, float], Callable[[typing.List[int], tuple[str, int], float], int]) - @unittest.skip("TODO: RUSTPYTHON") def test_type_subst_error(self): Callable = self.Callable P = ParamSpec('P') @@ -3859,8 +3854,6 @@ def barfoo(x: AT): ... def barfoo2(x: CT): ... self.assertIs(get_type_hints(barfoo2, globals(), locals())['x'], CT) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_generic_pep585_forward_ref(self): # See https://bugs.python.org/issue41370 @@ -5189,8 +5182,6 @@ def cmp(o1, o2): self.assertIsNot(r1, r2) self.assertRaises(RecursionError, cmp, r1, r2) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_union_forward_recursion(self): ValueList = List['Value'] Value = Union[str, ValueList] @@ -5239,8 +5230,6 @@ def foo(a: 'Callable[..., T]'): self.assertEqual(get_type_hints(foo, globals(), locals()), {'a': Callable[..., T]}) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_special_forms_forward(self): class C: @@ -5323,8 +5312,6 @@ def foo(self, x: int): ... self.assertEqual(get_type_hints(Child.foo), {'x': int}) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_no_type_check_nested_types(self): # See https://bugs.python.org/issue46571 class Other: @@ -5409,8 +5396,6 @@ def test_no_type_check_TypeError(self): # `TypeError: can't set attributes of built-in/extension type 'dict'` no_type_check(dict) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_no_type_check_forward_ref_as_string(self): class C: foo: typing.ClassVar[int] = 7 @@ -5465,8 +5450,6 @@ def test_default_globals(self): hints = get_type_hints(ns['C'].foo) self.assertEqual(hints, {'a': ns['C'], 'return': ns['D']}) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_final_forward_ref(self): self.assertEqual(gth(Loop, globals())['attr'], Final[Loop]) self.assertNotEqual(gth(Loop, globals())['attr'], Final[int]) @@ -5832,8 +5815,6 @@ def test_get_type_hints_classes(self): 'my_inner_a2': mod_generics_cache.B.A, 'my_outer_a': mod_generics_cache.A}) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_get_type_hints_classes_no_implicit_optional(self): class WithNoneDefault: field: int = None # most type-checkers won't be happy with it @@ -5878,8 +5859,6 @@ class B: ... b.__annotations__ = {'x': 'A'} self.assertEqual(gth(b, locals()), {'x': A}) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_get_type_hints_ClassVar(self): self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__), {'var': typing.ClassVar[ann_module2.CV]}) @@ -6006,8 +5985,6 @@ def annotated_with_none_default(x: Annotated[int, 'data'] = None): ... {'x': Annotated[int, 'data']}, ) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_get_type_hints_classes_str_annotations(self): class Foo: y = str @@ -6023,8 +6000,6 @@ class BadModule: self.assertNotIn('bad', sys.modules) self.assertEqual(get_type_hints(BadModule), {}) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_get_type_hints_annotated_bad_module(self): # See https://bugs.python.org/issue44468 class BadBase: @@ -6035,8 +6010,6 @@ class BadType(BadBase): self.assertNotIn('bad', sys.modules) self.assertEqual(get_type_hints(BadType), {'foo': tuple, 'bar': list}) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_forward_ref_and_final(self): # https://bugs.python.org/issue45166 hints = get_type_hints(ann_module5) @@ -8274,8 +8247,6 @@ class C: A.x = 5 self.assertEqual(C.x, 5) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_special_form_containment(self): class C: classvar: Annotated[ClassVar[int], "a decoration"] = 4 @@ -8284,8 +8255,6 @@ class C: self.assertEqual(get_type_hints(C, globals())['classvar'], ClassVar[int]) self.assertEqual(get_type_hints(C, globals())['const'], Final[int]) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_special_forms_nesting(self): # These are uncommon types and are to ensure runtime # is lax on validation. See gh-89547 for more context. @@ -8563,8 +8532,6 @@ def test_no_isinstance(self): with self.assertRaises(TypeError): isinstance(42, TypeAlias) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_stringized_usage(self): class A: a: "TypeAlias" @@ -8652,8 +8619,6 @@ def test_args_kwargs(self): self.assertEqual(repr(P.kwargs), "P.kwargs") - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_stringized(self): P = ParamSpec('P') class C(Generic[P]): diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 10c1c84d913..13b90a612ce 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -2901,7 +2901,20 @@ impl Compiler<'_> { } else { let was_in_annotation = self.in_annotation; self.in_annotation = true; - let result = self.compile_expression(annotation); + + // Special handling for starred annotations (*Ts -> Unpack[Ts]) + let result = match annotation { + Expr::Starred(ExprStarred { value, .. }) => { + // Following CPython's approach: + // *args: *Ts (where Ts is a TypeVarTuple). + // Do [annotation_value] = [*Ts]. + self.compile_expression(value)?; + emit!(self, Instruction::UnpackSequence { size: 1 }); + Ok(()) + } + _ => self.compile_expression(annotation), + }; + self.in_annotation = was_in_annotation; result?; } diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index 0dc6f816b6c..558653366e3 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -849,6 +849,48 @@ impl PyType { .and_then(|doc| get_text_signature_from_internal_doc(&self.name(), doc)) .map(|signature| signature.to_string()) } + + #[pygetset] + fn __type_params__(&self, vm: &VirtualMachine) -> PyTupleRef { + let attrs = self.attributes.read(); + let key = identifier!(vm, __type_params__); + if let Some(params) = attrs.get(&key) { + if let Ok(tuple) = params.clone().downcast::() { + return tuple; + } + } + // Return empty tuple if not found or not a tuple + vm.ctx.empty_tuple.clone() + } + + #[pygetset(setter)] + fn set___type_params__( + &self, + value: PySetterValue, + vm: &VirtualMachine, + ) -> PyResult<()> { + match value { + PySetterValue::Assign(ref val) => { + let key = identifier!(vm, __type_params__); + self.check_set_special_type_attr(val.as_ref(), key, vm)?; + let mut attrs = self.attributes.write(); + attrs.insert(key, val.clone().into()); + } + PySetterValue::Delete => { + // For delete, we still need to check if the type is immutable + if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) { + return Err(vm.new_type_error(format!( + "cannot delete '__type_params__' attribute of immutable type '{}'", + self.slot_name() + ))); + } + let mut attrs = self.attributes.write(); + let key = identifier!(vm, __type_params__); + attrs.shift_remove(&key); + } + } + Ok(()) + } } impl Constructor for PyType { diff --git a/vm/src/vm/context.rs b/vm/src/vm/context.rs index f77abc5fa6d..efa50b238e3 100644 --- a/vm/src/vm/context.rs +++ b/vm/src/vm/context.rs @@ -223,6 +223,7 @@ declare_const_name! { __sizeof__, __truediv__, __trunc__, + __type_params__, __typing_subst__, __typing_is_unpacked_typevartuple__, __typing_prepare_subst__,