From ba23a466c88f2dbe545ec4b5a61fff5f08b3dcec Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 27 May 2025 16:08:43 +0100 Subject: [PATCH 1/2] [mypyc] Improve documentation of native and non-native classes Also discuss `mypyc_attr(native_class=<...>)`. --- mypyc/doc/native_classes.rst | 82 ++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/mypyc/doc/native_classes.rst b/mypyc/doc/native_classes.rst index 7f892de3e2396..e6546fa8a14ef 100644 --- a/mypyc/doc/native_classes.rst +++ b/mypyc/doc/native_classes.rst @@ -48,11 +48,13 @@ can be assigned to (similar to using ``__slots__``):: Inheritance ----------- -Only single inheritance is supported (except for :ref:`traits -`). Most non-native classes can't be used as base -classes. +Only single inheritance is supported from native classes (except for +:ref:`traits `). Most non-native extension classes can't +be used as base classes, but regular Python classes can be used as +base classes unless they use unsupported metaclasses (see below for +more about this). -These non-native classes can be used as base classes of native +These non-native extension classes can be used as base classes of native classes: * ``object`` @@ -63,8 +65,6 @@ classes: * ``IndexError`` * ``LookupError`` * ``UserWarning`` -* ``typing.NamedTuple`` -* ``enum.Enum`` By default, a non-native class can't inherit a native class, and you can't inherit from a native class outside the compilation unit that @@ -89,6 +89,15 @@ You need to install ``mypy-extensions`` to use ``@mypyc_attr``: pip install --upgrade mypy-extensions +Additionally, mypyc recognizes these base classes as special, and +undersands how they alter the behavior of classes (including native +classes) that subclass them: + +* ``typing.NamedTuple`` +* ``typing.Generic`` +* ``typing.Protocol`` +* ``enum.Enum`` + Class variables --------------- @@ -145,7 +154,8 @@ behavior is too dynamic. You can use these metaclasses, however: .. note:: If a class definition uses an unsupported metaclass, *mypyc - compiles the class into a regular Python class*. + compiles the class into a regular Python class* (non-native + class). Class decorators ---------------- @@ -165,7 +175,63 @@ efficient as pure native classes. .. note:: If a class definition uses an unsupported class decorator, *mypyc - compiles the class into a regular Python class*. + compiles the class into a regular Python class* (non-native class). + +Defining non-native classes +--------------------------- + +You can use the ``@mypy_extensions.mypyc_attr(...)`` class decorator +with an argument ``native_class=False`` to explicitly define normal +Python classes (non-native classes):: + + from mypy_extensions import mypyc_attr + + @mypyc_attr(native_class=False) + class NonNative: + def __init__(self) -> None: + self.attr = 1 + + setattr(NonNative, "extra", 1) # Ok + +This only has an effect in classes compiled using mypyc. Non-native +classes are significantly less efficient than native classes, but they +are sometimes necessary to work around the limitations of native classes. + +Non-native classes can use arbitrary metaclasses and class decorators, +and they support flexible multiple inheritance. Mypyc will still +generate a compile-time error if you try to assign to a method, or an +attribute that is not defined in a class body, since these are static +type errors detected by mypy:: + + o = NonNative() + o.extra = "x" # Static type error: "extra" not defined + +However, these operations still work at runtime, including in modules +that are not compiled using mypyc. You can also use ``setattr`` and +``getattr`` for dynamic access of arbitrary attributes. Expressions +with an ``Any`` type are also not type checked statically, allowing +access to arbitrary attributes:: + + a: Any = o + a.extra = "x" # Ok + + setattr(o, "extra", "y") # Also ok + +Implicit non-native classes +--------------------------- + +If a compiled class uses an unsupported metaclass or an unsupported +class decorator, it will implicitly be a non-native class, as +discussed above. You can still use ``@mypyc_attr(native_class=False)`` +to explicitly mark it as a non-native class. + +Explicit native classes +----------------------- + +You can use ``@mypyc_attr(native_class=True)`` to explicitly declare a +class as a native class. It will be a compile-time error if mypyc +can't compile the class as a native class. You can use this to avoid +accidentally defining implicit non-native classes. Deleting attributes ------------------- From 2b151320455f17f5dc3e1acf160653837f04c14e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 27 May 2025 16:27:31 +0100 Subject: [PATCH 2/2] Fix typo --- mypyc/doc/native_classes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/doc/native_classes.rst b/mypyc/doc/native_classes.rst index e6546fa8a14ef..dbcf238b78d57 100644 --- a/mypyc/doc/native_classes.rst +++ b/mypyc/doc/native_classes.rst @@ -90,7 +90,7 @@ You need to install ``mypy-extensions`` to use ``@mypyc_attr``: pip install --upgrade mypy-extensions Additionally, mypyc recognizes these base classes as special, and -undersands how they alter the behavior of classes (including native +understands how they alter the behavior of classes (including native classes) that subclass them: * ``typing.NamedTuple``