From a1b0b15b57ae4aa4ffb1ac0dcf112445d8859124 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Tue, 29 Apr 2025 15:45:59 +0000 Subject: [PATCH 1/3] Add a few more tests for mypyc_attr native_class Testing dunder methods and metaclasses --- mypyc/test-data/irbuild-classes.test | 12 ++++++ mypyc/test-data/run-classes.test | 63 ++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 972146bcb0b4..57e53caca69b 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -1370,3 +1370,15 @@ class NonNativeClassContradiction(): # E: Class is marked as native_class=True @mypyc_attr(native_class="yes") class BadUse(): # E: native_class must be used with True or False only pass + +[case testMypycAttrNativeClassMetaError] +from mypy_extensions import mypyc_attr + +@mypyc_attr(native_class=False) +class M(type): + pass + +@mypyc_attr(native_class=True) +class A(metaclass=M): # E: Class is marked as native_class=True but it can't be a native class + pass + diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index f8720383d7fb..d68d683bd389 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2887,3 +2887,66 @@ def test_function(): explicit_ext_inst = AnnotatedExtensionClass() with assertRaises(AttributeError): setattr(explicit_ext_inst, 'attr_instance', 6) + +[case testMypycAttrNativeClassDunder] +from mypy_extensions import mypyc_attr +from typing import Generic, TypeVar + +_T = TypeVar("_T") + +@mypyc_attr(native_class=False) +class Bar(Generic[_T]): + # Note the lack of __deletable__ + def __init__(self) -> None: + self.value: str = 'start' + def __get__(self, instance: _T, owner: type[_T] | None = None) -> str: + return self.value + def __set__(self, instance: _T, value: str) -> None: + self.value = value + def __delete__(self, instance: _T) -> None: + del self.value + +@mypyc_attr(native_class=False) +class Foo(object): + bar: Bar = Bar() + +[file driver.py] +import native + +f = native.Foo() +assert(hasattr(f, 'bar')) +assert(f.bar == 'start') +f.bar = 'test' +assert(f.bar == 'test') +del f.bar +assert(not hasattr(f, 'bar')) + +[case testMypycAttrNativeClassMeta] +from mypy_extensions import mypyc_attr +from typing import ClassVar, TypeVar + +_T = TypeVar("_T") + +@mypyc_attr(native_class=False) +class M(type): + count: ClassVar[int] = 0 + def make(cls: type[_T]) -> _T: + M.count += 1 + return cls() + +# implicit native_class=False +# see testMypycAttrNativeClassMetaError for when trying to set it True +class A(metaclass=M): + pass + +[file driver.py] +import native + +a: native.A = native.A.make() +assert(native.A.count == 1) + +class B(native.A): + pass + +b: B = B.make() +assert(B.count == 2) From 7df6ab17f28a872071c6cc73994336f6594ac830 Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Tue, 29 Apr 2025 16:00:05 +0000 Subject: [PATCH 2/3] Add error for metaclass too --- mypyc/test-data/irbuild-classes.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 57e53caca69b..7a3be738b87e 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -1374,8 +1374,8 @@ class BadUse(): # E: native_class must be used with True or False only [case testMypycAttrNativeClassMetaError] from mypy_extensions import mypyc_attr -@mypyc_attr(native_class=False) -class M(type): +@mypyc_attr(native_class=True) +class M(type): # E: Inheriting from most builtin types is unimplemented pass @mypyc_attr(native_class=True) From 7059bbe720dcfd8386a7e3d3c4ab4cbfe85f439f Mon Sep 17 00:00:00 2001 From: Valentin Stanciu <250871+svalentin@users.noreply.github.com> Date: Fri, 2 May 2025 14:53:37 +0000 Subject: [PATCH 3/3] Use 3.9 syntax + count dunder calls --- mypyc/test-data/irbuild-classes.test | 1 - mypyc/test-data/run-classes.test | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 7a3be738b87e..94971640a094 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -1381,4 +1381,3 @@ class M(type): # E: Inheriting from most builtin types is unimplemented @mypyc_attr(native_class=True) class A(metaclass=M): # E: Class is marked as native_class=True but it can't be a native class pass - diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index d68d683bd389..97bc063dd8ea 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2890,20 +2890,28 @@ def test_function(): [case testMypycAttrNativeClassDunder] from mypy_extensions import mypyc_attr -from typing import Generic, TypeVar +from typing import Generic, Optional, TypeVar _T = TypeVar("_T") +get_count = set_count = del_count = 0 + @mypyc_attr(native_class=False) class Bar(Generic[_T]): # Note the lack of __deletable__ def __init__(self) -> None: self.value: str = 'start' - def __get__(self, instance: _T, owner: type[_T] | None = None) -> str: + def __get__(self, instance: _T, owner: Optional[type[_T]] = None) -> str: + global get_count + get_count += 1 return self.value def __set__(self, instance: _T, value: str) -> None: + global set_count + set_count += 1 self.value = value def __delete__(self, instance: _T) -> None: + global del_count + del_count += 1 del self.value @mypyc_attr(native_class=False) @@ -2915,11 +2923,15 @@ import native f = native.Foo() assert(hasattr(f, 'bar')) +assert(native.get_count == 1) assert(f.bar == 'start') +assert(native.get_count == 2) f.bar = 'test' assert(f.bar == 'test') +assert(native.set_count == 1) del f.bar assert(not hasattr(f, 'bar')) +assert(native.del_count == 1) [case testMypycAttrNativeClassMeta] from mypy_extensions import mypyc_attr