diff --git a/mypyc/irbuild/prepare.py b/mypyc/irbuild/prepare.py index 98ff348d8c30..65951999dcf9 100644 --- a/mypyc/irbuild/prepare.py +++ b/mypyc/irbuild/prepare.py @@ -298,6 +298,16 @@ def prepare_class_def( errors.error( "Inheriting from most builtin types is unimplemented", path, cdef.line ) + errors.note( + "Potential workaround: @mypy_extensions.mypyc_attr(native_class=False)", + path, + cdef.line, + ) + errors.note( + "https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes", + path, + cdef.line, + ) # Set up the parent class bases = [mapper.type_to_ir[base.type] for base in info.bases if base.type in mapper.type_to_ir] diff --git a/mypyc/test-data/commandline.test b/mypyc/test-data/commandline.test index ae0be03eb66b..77c2e08bcf34 100644 --- a/mypyc/test-data/commandline.test +++ b/mypyc/test-data/commandline.test @@ -138,7 +138,9 @@ Foo.lol = 50 # E: Only class variables defined as ClassVar can be assigned to def decorator(x: Any) -> Any: return x -class NeverMetaclass(type): # E: Inheriting from most builtin types is unimplemented +class NeverMetaclass(type): # E: Inheriting from most builtin types is unimplemented \ + # N: Potential workaround: @mypy_extensions.mypyc_attr(native_class=False) \ + # N: https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes pass class Concrete1: diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 9d564a552a05..fa4708f02e0b 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -1375,7 +1375,9 @@ class BadUse(): # E: native_class must be used with True or False only from mypy_extensions import mypyc_attr @mypyc_attr(native_class=True) -class M(type): # E: Inheriting from most builtin types is unimplemented +class M(type): # E: Inheriting from most builtin types is unimplemented \ + # N: Potential workaround: @mypy_extensions.mypyc_attr(native_class=False) \ + # N: https://mypyc.readthedocs.io/en/stable/native_classes.html#defining-non-native-classes pass @mypyc_attr(native_class=True) diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index b98f1989da51..fd486980ef16 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -934,6 +934,53 @@ def welp() -> int: from native import welp assert welp() == 35 +[case testSubclassUnsupportedException] +from mypy_extensions import mypyc_attr + +@mypyc_attr(native_class=False) +class MyError(ZeroDivisionError): + pass + +@mypyc_attr(native_class=False) +class MyError2(ZeroDivisionError): + def __init__(self, s: str) -> None: + super().__init__(s + "!") + self.x = s.upper() + +def f() -> None: + raise MyError("foobar") + +def test_non_native_exception_subclass_basics() -> None: + e = MyError() + assert isinstance(e, MyError) + assert isinstance(e, ZeroDivisionError) + assert isinstance(e, Exception) + + e = MyError("x") + assert repr(e) == "MyError('x')" + + e2 = MyError2("ab") + assert repr(e2) == "MyError2('ab!')", repr(e2) + assert e2.x == "AB" + +def test_raise_non_native_exception_subclass_1() -> None: + try: + f() + except MyError: + x = True + else: + assert False + assert x + +def test_raise_non_native_exception_subclass_2() -> None: + try: + f() + except ZeroDivisionError: + x = True + else: + assert False + assert x + [case testSubclassPy] from b import B, V class A(B):