Skip to content

Commit 84b7ce7

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

File tree

3 files changed

+52
-8
lines changed

3 files changed

+52
-8
lines changed

mypyc/irbuild/util.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,19 @@ 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 (not is_trait_decorator(d)
137+
and not is_dataclass_decorator(d)
138+
and not mypyc_attr_call
139+
and not is_final_decorator(d)):
140+
return False
141+
137142
if cdef.info.typeddict_type:
138143
return False
139144
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: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2829,3 +2829,40 @@ 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+
2836+
@mypyc_attr(non_extension_class=True)
2837+
class AnnontatedNonExtensionClass:
2838+
pass
2839+
2840+
class DerivedClass(AnnontatedNonExtensionClass):
2841+
pass
2842+
2843+
def test_function():
2844+
setattr(AnnontatedNonExtensionClass, 'attr_class', 5)
2845+
assert(hasattr(AnnontatedNonExtensionClass, 'attr_class') == True)
2846+
assert(getattr(AnnontatedNonExtensionClass, 'attr_class') == 5)
2847+
delattr(AnnontatedNonExtensionClass, 'attr_class')
2848+
assert(hasattr(AnnontatedNonExtensionClass, 'attr_class') == False)
2849+
2850+
inst = AnnontatedNonExtensionClass()
2851+
setattr(inst, 'attr_instance', 6)
2852+
assert(hasattr(inst, 'attr_instance') == True)
2853+
assert(getattr(inst, 'attr_instance') == 6)
2854+
delattr(inst, 'attr_instance')
2855+
assert(hasattr(inst, 'attr_instance') == False)
2856+
2857+
setattr(DerivedClass, 'attr_class', 5)
2858+
assert(hasattr(DerivedClass, 'attr_class') == True)
2859+
assert(getattr(DerivedClass, 'attr_class') == 5)
2860+
delattr(DerivedClass, 'attr_class')
2861+
assert(hasattr(DerivedClass, 'attr_class') == False)
2862+
2863+
derived_inst = DerivedClass()
2864+
setattr(derived_inst, 'attr_instance', 6)
2865+
assert(hasattr(derived_inst, 'attr_instance') == True)
2866+
assert(getattr(derived_inst, 'attr_instance') == 6)
2867+
delattr(derived_inst, 'attr_instance')
2868+
assert(hasattr(derived_inst, 'attr_instance') == False)

0 commit comments

Comments
 (0)