Skip to content

Commit fa7af0e

Browse files
committed
type.__type_params__
1 parent e25c285 commit fa7af0e

File tree

5 files changed

+57
-38
lines changed

5 files changed

+57
-38
lines changed

Lib/test/test_dataclasses.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3246,8 +3246,6 @@ def test_classvar_module_level_import(self):
32463246
# won't exist on the instance.
32473247
self.assertNotIn('not_iv4', c.__dict__)
32483248

3249-
# TODO: RUSTPYTHON
3250-
@unittest.expectedFailure
32513249
def test_text_annotations(self):
32523250
from test import dataclass_textanno
32533251

Lib/test/test_typing.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,8 +1605,6 @@ class D(Generic[Unpack[Ts]]): pass
16051605
self.assertIs(D[T].__origin__, D)
16061606
self.assertIs(D[Unpack[Ts]].__origin__, D)
16071607

1608-
# TODO: RUSTPYTHON
1609-
@unittest.expectedFailure
16101608
def test_get_type_hints_on_unpack_args(self):
16111609
Ts = TypeVarTuple('Ts')
16121610

@@ -1809,8 +1807,6 @@ class F(Generic[Unpack[Ts], T1, T2]): pass
18091807
F[int, str, float]
18101808
F[int, str, float, bool]
18111809

1812-
# TODO: RUSTPYTHON
1813-
@unittest.expectedFailure
18141810
def test_variadic_args_annotations_are_correct(self):
18151811
Ts = TypeVarTuple('Ts')
18161812

@@ -2494,7 +2490,6 @@ def test_var_substitution(self):
24942490
self.assertEqual(C5[int, str, float],
24952491
Callable[[typing.List[int], tuple[str, int], float], int])
24962492

2497-
@unittest.skip("TODO: RUSTPYTHON")
24982493
def test_type_subst_error(self):
24992494
Callable = self.Callable
25002495
P = ParamSpec('P')
@@ -3859,8 +3854,6 @@ def barfoo(x: AT): ...
38593854
def barfoo2(x: CT): ...
38603855
self.assertIs(get_type_hints(barfoo2, globals(), locals())['x'], CT)
38613856

3862-
# TODO: RUSTPYTHON
3863-
@unittest.expectedFailure
38643857
def test_generic_pep585_forward_ref(self):
38653858
# See https://bugs.python.org/issue41370
38663859

@@ -5189,8 +5182,6 @@ def cmp(o1, o2):
51895182
self.assertIsNot(r1, r2)
51905183
self.assertRaises(RecursionError, cmp, r1, r2)
51915184

5192-
# TODO: RUSTPYTHON
5193-
@unittest.expectedFailure
51945185
def test_union_forward_recursion(self):
51955186
ValueList = List['Value']
51965187
Value = Union[str, ValueList]
@@ -5239,8 +5230,6 @@ def foo(a: 'Callable[..., T]'):
52395230
self.assertEqual(get_type_hints(foo, globals(), locals()),
52405231
{'a': Callable[..., T]})
52415232

5242-
# TODO: RUSTPYTHON
5243-
@unittest.expectedFailure
52445233
def test_special_forms_forward(self):
52455234

52465235
class C:
@@ -5323,8 +5312,6 @@ def foo(self, x: int): ...
53235312

53245313
self.assertEqual(get_type_hints(Child.foo), {'x': int})
53255314

5326-
# TODO: RUSTPYTHON
5327-
@unittest.expectedFailure
53285315
def test_no_type_check_nested_types(self):
53295316
# See https://bugs.python.org/issue46571
53305317
class Other:
@@ -5409,8 +5396,6 @@ def test_no_type_check_TypeError(self):
54095396
# `TypeError: can't set attributes of built-in/extension type 'dict'`
54105397
no_type_check(dict)
54115398

5412-
# TODO: RUSTPYTHON
5413-
@unittest.expectedFailure
54145399
def test_no_type_check_forward_ref_as_string(self):
54155400
class C:
54165401
foo: typing.ClassVar[int] = 7
@@ -5465,8 +5450,6 @@ def test_default_globals(self):
54655450
hints = get_type_hints(ns['C'].foo)
54665451
self.assertEqual(hints, {'a': ns['C'], 'return': ns['D']})
54675452

5468-
# TODO: RUSTPYTHON
5469-
@unittest.expectedFailure
54705453
def test_final_forward_ref(self):
54715454
self.assertEqual(gth(Loop, globals())['attr'], Final[Loop])
54725455
self.assertNotEqual(gth(Loop, globals())['attr'], Final[int])
@@ -5832,8 +5815,6 @@ def test_get_type_hints_classes(self):
58325815
'my_inner_a2': mod_generics_cache.B.A,
58335816
'my_outer_a': mod_generics_cache.A})
58345817

5835-
# TODO: RUSTPYTHON
5836-
@unittest.expectedFailure
58375818
def test_get_type_hints_classes_no_implicit_optional(self):
58385819
class WithNoneDefault:
58395820
field: int = None # most type-checkers won't be happy with it
@@ -5878,8 +5859,6 @@ class B: ...
58785859
b.__annotations__ = {'x': 'A'}
58795860
self.assertEqual(gth(b, locals()), {'x': A})
58805861

5881-
# TODO: RUSTPYTHON
5882-
@unittest.expectedFailure
58835862
def test_get_type_hints_ClassVar(self):
58845863
self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__),
58855864
{'var': typing.ClassVar[ann_module2.CV]})
@@ -6006,8 +5985,6 @@ def annotated_with_none_default(x: Annotated[int, 'data'] = None): ...
60065985
{'x': Annotated[int, 'data']},
60075986
)
60085987

6009-
# TODO: RUSTPYTHON
6010-
@unittest.expectedFailure
60115988
def test_get_type_hints_classes_str_annotations(self):
60125989
class Foo:
60135990
y = str
@@ -6023,8 +6000,6 @@ class BadModule:
60236000
self.assertNotIn('bad', sys.modules)
60246001
self.assertEqual(get_type_hints(BadModule), {})
60256002

6026-
# TODO: RUSTPYTHON
6027-
@unittest.expectedFailure
60286003
def test_get_type_hints_annotated_bad_module(self):
60296004
# See https://bugs.python.org/issue44468
60306005
class BadBase:
@@ -6035,8 +6010,6 @@ class BadType(BadBase):
60356010
self.assertNotIn('bad', sys.modules)
60366011
self.assertEqual(get_type_hints(BadType), {'foo': tuple, 'bar': list})
60376012

6038-
# TODO: RUSTPYTHON
6039-
@unittest.expectedFailure
60406013
def test_forward_ref_and_final(self):
60416014
# https://bugs.python.org/issue45166
60426015
hints = get_type_hints(ann_module5)
@@ -8274,8 +8247,6 @@ class C:
82748247
A.x = 5
82758248
self.assertEqual(C.x, 5)
82768249

8277-
# TODO: RUSTPYTHON
8278-
@unittest.expectedFailure
82798250
def test_special_form_containment(self):
82808251
class C:
82818252
classvar: Annotated[ClassVar[int], "a decoration"] = 4
@@ -8284,8 +8255,6 @@ class C:
82848255
self.assertEqual(get_type_hints(C, globals())['classvar'], ClassVar[int])
82858256
self.assertEqual(get_type_hints(C, globals())['const'], Final[int])
82868257

8287-
# TODO: RUSTPYTHON
8288-
@unittest.expectedFailure
82898258
def test_special_forms_nesting(self):
82908259
# These are uncommon types and are to ensure runtime
82918260
# is lax on validation. See gh-89547 for more context.
@@ -8563,8 +8532,6 @@ def test_no_isinstance(self):
85638532
with self.assertRaises(TypeError):
85648533
isinstance(42, TypeAlias)
85658534

8566-
# TODO: RUSTPYTHON
8567-
@unittest.expectedFailure
85688535
def test_stringized_usage(self):
85698536
class A:
85708537
a: "TypeAlias"
@@ -8652,8 +8619,6 @@ def test_args_kwargs(self):
86528619
self.assertEqual(repr(P.kwargs), "P.kwargs")
86538620

86548621

8655-
# TODO: RUSTPYTHON
8656-
@unittest.expectedFailure
86578622
def test_stringized(self):
86588623
P = ParamSpec('P')
86598624
class C(Generic[P]):

compiler/codegen/src/compile.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2901,7 +2901,20 @@ impl Compiler<'_> {
29012901
} else {
29022902
let was_in_annotation = self.in_annotation;
29032903
self.in_annotation = true;
2904-
let result = self.compile_expression(annotation);
2904+
2905+
// Special handling for starred annotations (*Ts -> Unpack[Ts])
2906+
let result = match annotation {
2907+
Expr::Starred(ExprStarred { value, .. }) => {
2908+
// Following CPython's approach:
2909+
// *args: *Ts (where Ts is a TypeVarTuple).
2910+
// Do [annotation_value] = [*Ts].
2911+
self.compile_expression(value)?;
2912+
emit!(self, Instruction::UnpackSequence { size: 1 });
2913+
Ok(())
2914+
}
2915+
_ => self.compile_expression(annotation),
2916+
};
2917+
29052918
self.in_annotation = was_in_annotation;
29062919
result?;
29072920
}

vm/src/builtins/type.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,48 @@ impl PyType {
849849
.and_then(|doc| get_text_signature_from_internal_doc(&self.name(), doc))
850850
.map(|signature| signature.to_string())
851851
}
852+
853+
#[pygetset]
854+
fn __type_params__(&self, vm: &VirtualMachine) -> PyTupleRef {
855+
let attrs = self.attributes.read();
856+
let key = identifier!(vm, __type_params__);
857+
if let Some(params) = attrs.get(&key) {
858+
if let Ok(tuple) = params.clone().downcast::<PyTuple>() {
859+
return tuple;
860+
}
861+
}
862+
// Return empty tuple if not found or not a tuple
863+
vm.ctx.empty_tuple.clone()
864+
}
865+
866+
#[pygetset(setter)]
867+
fn set___type_params__(
868+
&self,
869+
value: PySetterValue<PyTupleRef>,
870+
vm: &VirtualMachine,
871+
) -> PyResult<()> {
872+
match value {
873+
PySetterValue::Assign(ref val) => {
874+
let key = identifier!(vm, __type_params__);
875+
self.check_set_special_type_attr(val.as_ref(), key, vm)?;
876+
let mut attrs = self.attributes.write();
877+
attrs.insert(key, val.clone().into());
878+
}
879+
PySetterValue::Delete => {
880+
// For delete, we still need to check if the type is immutable
881+
if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
882+
return Err(vm.new_type_error(format!(
883+
"cannot delete '__type_params__' attribute of immutable type '{}'",
884+
self.slot_name()
885+
)));
886+
}
887+
let mut attrs = self.attributes.write();
888+
let key = identifier!(vm, __type_params__);
889+
attrs.shift_remove(&key);
890+
}
891+
}
892+
Ok(())
893+
}
852894
}
853895

854896
impl Constructor for PyType {

vm/src/vm/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ declare_const_name! {
223223
__sizeof__,
224224
__truediv__,
225225
__trunc__,
226+
__type_params__,
226227
__typing_subst__,
227228
__typing_is_unpacked_typevartuple__,
228229
__typing_prepare_subst__,

0 commit comments

Comments
 (0)