Skip to content

Commit 908c537

Browse files
committed
[mypyc] Initial support for annotating classes to be non extension
1 parent bbd7a6c commit 908c537

File tree

3 files changed

+62
-8
lines changed

3 files changed

+62
-8
lines changed

mypyc/irbuild/util.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,21 @@ def get_mypyc_attrs(stmt: ClassDef | Decorator) -> dict[str, Any]:
126126

127127

128128
def is_extension_class(cdef: ClassDef) -> bool:
129-
if any(
130-
not is_trait_decorator(d)
131-
and not is_dataclass_decorator(d)
132-
and not get_mypyc_attr_call(d)
133-
and not is_final_decorator(d)
134-
for d in cdef.decorators
135-
):
136-
return False
129+
for d in cdef.decorators:
130+
mypyc_attr_call = get_mypyc_attr_call(d)
131+
# Classes decorated with "@mypyc_attr(non_extension_class=True)" are not extension classes
132+
if mypyc_attr_call and "non_extension_class" in mypyc_attr_call.arg_names:
133+
return False
134+
135+
# Classes that have any decorator other than supported decorators, are not extension classes
136+
if (
137+
not is_trait_decorator(d)
138+
and not is_dataclass_decorator(d)
139+
and not mypyc_attr_call
140+
and not is_final_decorator(d)
141+
):
142+
return False
143+
137144
if cdef.info.typeddict_type:
138145
return False
139146
if cdef.info.is_named_tuple:

mypyc/test-data/fixtures/ir.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,10 @@ def next(i: Iterator[_T]) -> _T: pass
350350
def next(i: Iterator[_T], default: _T) -> _T: pass
351351
def hash(o: object) -> int: ...
352352
def globals() -> Dict[str, Any]: ...
353+
def hasattr(obj: object, name: str) -> bool: ...
353354
def getattr(obj: object, name: str, default: Any = None) -> Any: ...
354355
def setattr(obj: object, name: str, value: Any) -> None: ...
356+
def delattr(obj: object, name: str) -> None: ...
355357
def enumerate(x: Iterable[_T]) -> Iterator[Tuple[int, _T]]: ...
356358
@overload
357359
def zip(x: Iterable[_T], y: Iterable[_S]) -> Iterator[Tuple[_T, _S]]: ...

mypyc/test-data/run-classes.test

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2829,3 +2829,48 @@ Traceback (most recent call last):
28292829
File "native.py", line 5, in __del__
28302830
raise Exception("e2")
28312831
Exception: e2
2832+
2833+
[case testNonExtensionClassAttr]
2834+
from mypy_extensions import mypyc_attr
2835+
from testutil import assertRaises
2836+
2837+
@mypyc_attr(non_extension_class=True)
2838+
class AnnontatedNonExtensionClass:
2839+
pass
2840+
2841+
class DerivedClass(AnnontatedNonExtensionClass):
2842+
pass
2843+
2844+
class ExtensionClass():
2845+
pass
2846+
2847+
def test_function():
2848+
setattr(AnnontatedNonExtensionClass, 'attr_class', 5)
2849+
assert(hasattr(AnnontatedNonExtensionClass, 'attr_class') == True)
2850+
assert(getattr(AnnontatedNonExtensionClass, 'attr_class') == 5)
2851+
delattr(AnnontatedNonExtensionClass, 'attr_class')
2852+
assert(hasattr(AnnontatedNonExtensionClass, 'attr_class') == False)
2853+
2854+
inst = AnnontatedNonExtensionClass()
2855+
setattr(inst, 'attr_instance', 6)
2856+
assert(hasattr(inst, 'attr_instance') == True)
2857+
assert(getattr(inst, 'attr_instance') == 6)
2858+
delattr(inst, 'attr_instance')
2859+
assert(hasattr(inst, 'attr_instance') == False)
2860+
2861+
setattr(DerivedClass, 'attr_class', 5)
2862+
assert(hasattr(DerivedClass, 'attr_class') == True)
2863+
assert(getattr(DerivedClass, 'attr_class') == 5)
2864+
delattr(DerivedClass, 'attr_class')
2865+
assert(hasattr(DerivedClass, 'attr_class') == False)
2866+
2867+
derived_inst = DerivedClass()
2868+
setattr(derived_inst, 'attr_instance', 6)
2869+
assert(hasattr(derived_inst, 'attr_instance') == True)
2870+
assert(getattr(derived_inst, 'attr_instance') == 6)
2871+
delattr(derived_inst, 'attr_instance')
2872+
assert(hasattr(derived_inst, 'attr_instance') == False)
2873+
2874+
ext_inst = ExtensionClass()
2875+
with assertRaises(AttributeError, "'ExtensionClass' object has no attribute 'attr_instance' and no __dict__ for setting new attributes"):
2876+
setattr(ext_inst, 'attr_instance', 6)

0 commit comments

Comments
 (0)