diff --git a/type-enum-plugin/type_enum_plugin/core.py b/type-enum-plugin/type_enum_plugin/core.py index 5c61f75..b85654b 100644 --- a/type-enum-plugin/type_enum_plugin/core.py +++ b/type-enum-plugin/type_enum_plugin/core.py @@ -39,6 +39,7 @@ class TypeEnumTransform: def transform(self) -> None: """Transform the attributes in a TypeEnum.""" cls = self.cls + cls.info.is_final = True for stmt in cls.defs.body: # Any assignment that doesn't use the new type declaration # syntax can be ignored out of hand. @@ -134,6 +135,11 @@ def transform(self) -> None: # info=cls.info, # ) + # == attempts to make the namespace class abstract == + # n = cls.info.names["__init__"].node + # assert isinstance(n, FuncDef) + # n.abstract_status = 2 + def get_type_from_expression(self, type_node: Expression) -> Type | None: try: type = expr_to_unanalyzed_type( @@ -164,6 +170,7 @@ def create_namedtuple( line=line, existing_info=None, ) + info.is_final = True # add the surrounding class as a base class info.mro.append(self.cls.info) diff --git a/type-enum/tests/test_instantiation.py b/type-enum/tests/test_instantiation.py index 022c902..f568a2d 100644 --- a/type-enum/tests/test_instantiation.py +++ b/type-enum/tests/test_instantiation.py @@ -82,7 +82,7 @@ class T(TypeEnum): with self.assertRaises(TypeError): - class U(T): + class U(T): # type: ignore[misc] B = () def test_instantiate_type_enum(self) -> None: @@ -93,3 +93,10 @@ class T(TypeEnum): T() with self.assertRaises(TypeError): TypeEnum() + + def test_name_clash(self) -> None: + with self.assertRaises(TypeError): + + class T(TypeEnum): + A = {"B": int} + B = (int, str) diff --git a/type-enum/tests/test_matching.py b/type-enum/tests/test_matching.py index 186cee9..d5bfeab 100644 --- a/type-enum/tests/test_matching.py +++ b/type-enum/tests/test_matching.py @@ -1,3 +1,5 @@ +from typing_extensions import assert_type + from type_enum import TypeEnum from .common import CustomTestCase @@ -19,9 +21,9 @@ class T(TypeEnum): b = T.B(0, "foo") match b: - case T.B(x, y): - self.assertEqual(x, 0) - self.assertEqual(y, "foo") + case T.B(y, z): + self.assertEqual(y, 0) + self.assertEqual(z, "foo") case _: self.fail(f"not matched: {b}") @@ -40,9 +42,11 @@ class T(TypeEnum): a = T.A(x=3, y="foo") match a: - case T.A(x, y): - self.assertEqual(x, 3) - self.assertEqual(y, "foo") + case T.A(x=x_, y=y_): + self.assertEqual(x_, 3) + self.assertEqual(y_, "foo") + assert_type(x_, int) + assert_type(y_, str) case _: self.fail(f"not matched: {a}") diff --git a/type-enum/type_enum/_core.py b/type-enum/type_enum/_core.py index dfdd5c0..401d62a 100644 --- a/type-enum/type_enum/_core.py +++ b/type-enum/type_enum/_core.py @@ -32,6 +32,11 @@ def __new__(cls, name: str, bases: tuple[type, ...], ns: dict[str, Any]): if isinstance(types, tuple): subtype = _create_tuple_class(name, attr_name, types) elif isinstance(types, dict): + for k in types: + if k in ns: + raise TypeError( + f"'{k}' appears in namespace and in '{attr_name}'" + ) subtype = NamedTuple(attr_name, [(k, v) for k, v in types.items()]) else: raise TypeError(