.. This document was generated by tools/gen-cpydiff.py Core language ============= Generated Thu 11 Sep 2025 09:13:26 UTC Classes ------- .. _cpydiff_core_class_delnotimpl: Special method __del__ not implemented for user-defined classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sample code:: import gc class Foo: def __del__(self): print("__del__") f = Foo() del f gc.collect() +-------------+-------------+ | CPy output: | uPy output: | +-------------+-------------+ | :: | | | | | | __del__ | | +-------------+-------------+ .. _cpydiff_core_class_initsubclass: ``__init_subclass__`` isn't automatically called. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython does not currently implement PEP 487. **Workaround:** Manually call ``__init_subclass__`` after class creation if needed. e.g.:: class A(Base): pass A.__init_subclass__() Sample code:: class Base: @classmethod def __init_subclass__(cls): print(f"Base.__init_subclass__({cls.__name__})") class A(Base): pass +-------------------------------+-------------+ | CPy output: | uPy output: | +-------------------------------+-------------+ | :: | | | | | | Base.__init_subclass__(A) | | +-------------------------------+-------------+ .. _cpydiff_core_class_initsubclass_autoclassmethod: ``__init_subclass__`` isn't an implicit classmethod. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython does not currently implement PEP 487. ``__init_subclass__`` is not currently in the list of special-cased class/static methods. **Workaround:** Decorate declarations of ``__init_subclass__`` with ``@classmethod``. Sample code:: def regularize_spelling(text, prefix="bound_"): # for regularizing across the CPython "method" vs MicroPython "bound_method" spelling for the type of a bound classmethod if text.startswith(prefix): return text[len(prefix) :] return text class A: def __init_subclass__(cls): pass @classmethod def manual_decorated(cls): pass a = type(A.__init_subclass__).__name__ b = type(A.manual_decorated).__name__ print(regularize_spelling(a)) print(regularize_spelling(b)) if a != b: print("FAIL") +-------------+--------------+ | CPy output: | uPy output: | +-------------+--------------+ | :: | :: | | | | | method | function | | method | method | | | FAIL | +-------------+--------------+ .. _cpydiff_core_class_initsubclass_kwargs: MicroPython doesn't support parameterized ``__init_subclass__`` class customization. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython does not currently implement PEP 487. The MicroPython syntax tree does not include a kwargs node after the class inheritance list. **Workaround:** Use class variables or another mechanism to specify base-class customizations. Sample code:: class Base: @classmethod def __init_subclass__(cls, arg=None, **kwargs): cls.init_subclass_was_called = True print(f"Base.__init_subclass__({cls.__name__}, {arg=!r}, {kwargs=!r})") class A(Base, arg="arg"): pass # Regularize across MicroPython not automatically calling __init_subclass__ either. if not getattr(A, "init_subclass_was_called", False): A.__init_subclass__() +-----------------------------------------------------+--------------------------------------------------------+ | CPy output: | uPy output: | +-----------------------------------------------------+--------------------------------------------------------+ | :: | :: | | | | | Base.__init_subclass__(A, arg='arg', kwargs={}) | Traceback (most recent call last): | | | File "", line 16, in | | | TypeError: function doesn't take keyword arguments | +-----------------------------------------------------+--------------------------------------------------------+ .. _cpydiff_core_class_initsubclass_super: ``__init_subclass__`` can't be defined a cooperatively-recursive way. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython does not currently implement PEP 487. The base object type does not have an ``__init_subclass__`` implementation. **Workaround:** Omit the recursive ``__init_subclass__`` call unless it's known that the grandparent also defines it. Sample code:: class Base: @classmethod def __init_subclass__(cls, **kwargs): cls.init_subclass_was_called = True super().__init_subclass__(**kwargs) class A(Base): pass # Regularize across MicroPython not automatically calling __init_subclass__ either. if not getattr(A, "init_subclass_was_called", False): A.__init_subclass__() +-------------+-------------------------------------------------------------------------+ | CPy output: | uPy output: | +-------------+-------------------------------------------------------------------------+ | | :: | | | | | | Traceback (most recent call last): | | | File "", line 22, in | | | File "", line 13, in __init_subclass__ | | | AttributeError: 'super' object has no attribute '__init_subclass__' | +-------------+-------------------------------------------------------------------------+ .. _cpydiff_core_class_mro: Method Resolution Order (MRO) is not compliant with CPython ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** Depth first non-exhaustive method resolution order **Workaround:** Avoid complex class hierarchies with multiple inheritance and complex method overrides. Keep in mind that many languages don't support multiple inheritance at all. Sample code:: class Foo: def __str__(self): return "Foo" class C(tuple, Foo): pass t = C((1, 2, 3)) print(t) +-------------+---------------+ | CPy output: | uPy output: | +-------------+---------------+ | :: | :: | | | | | Foo | (1, 2, 3) | +-------------+---------------+ .. _cpydiff_core_class_name_mangling: Private Class Members name mangling is not implemented ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** The MicroPython compiler does not implement name mangling for private class members. **Workaround:** Avoid using or having a collision with global names, by adding a unique prefix to the private class member name manually. Sample code:: def __print_string(string): print(string) class Foo: def __init__(self, string): self.string = string def do_print(self): __print_string(self.string) example_string = "Example String to print." class_item = Foo(example_string) print(class_item.string) class_item.do_print() +------------------------------------------------------------------------------------------+------------------------------+ | CPy output: | uPy output: | +------------------------------------------------------------------------------------------+------------------------------+ | :: | :: | | | | | Example String to print. | Example String to print. | | Traceback (most recent call last): | Example String to print. | | File "", line 26, in | | | File "", line 18, in do_print | | | NameError: name '_Foo__print_string' is not defined. Did you mean: '__print_string'? | | +------------------------------------------------------------------------------------------+------------------------------+ .. _cpydiff_core_class_super_init: When inheriting native types, calling a method in ``__init__(self, ...)`` before ``super().__init__()`` raises an ``AttributeError`` (or segfaults if ``MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG`` is not enabled). ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython does not have separate ``__new__`` and ``__init__`` methods in native types. **Workaround:** Call ``super().__init__()`` first. Sample code:: class L1(list): def __init__(self, a): self.append(a) try: L1(1) print("OK") except AttributeError: print("AttributeError") class L2(list): def __init__(self, a): super().__init__() self.append(a) try: L2(1) print("OK") except AttributeError: print("AttributeError") +-------------+--------------------+ | CPy output: | uPy output: | +-------------+--------------------+ | :: | :: | | | | | OK | AttributeError | | OK | OK | +-------------+--------------------+ .. _cpydiff_core_class_supermultiple: When inheriting from multiple classes super() only calls one class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** See :ref:`cpydiff_core_class_mro` **Workaround:** See :ref:`cpydiff_core_class_mro` Sample code:: class A: def __init__(self): print("A.__init__") class B(A): def __init__(self): print("B.__init__") super().__init__() class C(A): def __init__(self): print("C.__init__") super().__init__() class D(B, C): def __init__(self): print("D.__init__") super().__init__() D() +----------------+----------------+ | CPy output: | uPy output: | +----------------+----------------+ | :: | :: | | | | | D.__init__ | D.__init__ | | B.__init__ | B.__init__ | | C.__init__ | A.__init__ | | A.__init__ | | +----------------+----------------+ .. _cpydiff_core_class_superproperty: Calling super() getter property in subclass will return a property object, not the value ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sample code:: class A: @property def p(self): return {"a": 10} class AA(A): @property def p(self): return super().p a = AA() print(a.p) +---------------+----------------+ | CPy output: | uPy output: | +---------------+----------------+ | :: | :: | | | | | {'a': 10} | | +---------------+----------------+ Functions --------- .. _cpydiff_core_function_argcount: Error messages for methods may display unexpected argument counts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython counts "self" as an argument. **Workaround:** Interpret error messages with the information above in mind. Sample code:: try: [].append() except Exception as e: print(e) +--------------------------------------------------------+------------------------------------------------------------+ | CPy output: | uPy output: | +--------------------------------------------------------+------------------------------------------------------------+ | :: | :: | | | | | list.append() takes exactly one argument (0 given) | function takes 2 positional arguments but 1 were given | +--------------------------------------------------------+------------------------------------------------------------+ .. _cpydiff_core_function_moduleattr: Function objects do not have the ``__module__`` attribute ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython is optimized for reduced code size and RAM usage. **Workaround:** Use ``sys.modules[function.__globals__['__name__']]`` for non-builtin modules. Sample code:: def f(): pass print(f.__module__) +--------------+---------------------------------------------------------------------+ | CPy output: | uPy output: | +--------------+---------------------------------------------------------------------+ | :: | :: | | | | | __main__ | Traceback (most recent call last): | | | File "", line 13, in | | | AttributeError: 'function' object has no attribute '__module__' | +--------------+---------------------------------------------------------------------+ .. _cpydiff_core_function_userattr: User-defined attributes for functions are not supported ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython is highly optimized for memory usage. **Workaround:** Use external dictionary, e.g. ``FUNC_X[f] = 0``. Sample code:: def f(): pass f.x = 0 print(f.x) +-------------+------------------------------------------------------------+ | CPy output: | uPy output: | +-------------+------------------------------------------------------------+ | :: | :: | | | | | 0 | Traceback (most recent call last): | | | File "", line 13, in | | | AttributeError: 'function' object has no attribute 'x' | +-------------+------------------------------------------------------------+ Generator --------- .. _cpydiff_core_generator_noexit: Context manager __exit__() not called in a generator which does not run to completion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sample code:: class foo(object): def __enter__(self): print("Enter") def __exit__(self, *args): print("Exit") def bar(x): with foo(): while True: x += 1 yield x def func(): g = bar(0) for _ in range(3): print(next(g)) func() +-------------+-------------+ | CPy output: | uPy output: | +-------------+-------------+ | :: | :: | | | | | Enter | Enter | | 1 | 1 | | 2 | 2 | | 3 | 3 | | Exit | | +-------------+-------------+ Runtime ------- .. _cpydiff_core_locals: Local variables aren't included in locals() result ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython doesn't maintain symbolic local environment, it is optimized to an array of slots. Thus, local variables can't be accessed by a name. Sample code:: def test(): val = 2 print(locals()) test() +----------------+------------------------------------------------------------------------------------------------+ | CPy output: | uPy output: | +----------------+------------------------------------------------------------------------------------------------+ | :: | :: | | | | | {'val': 2} | {'test': , '__name__': '__main__', '__file__': ''} | +----------------+------------------------------------------------------------------------------------------------+ .. _cpydiff_core_locals_eval: Code running in eval() function doesn't have access to local variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython doesn't maintain symbolic local environment, it is optimized to an array of slots. Thus, local variables can't be accessed by a name. Effectively, ``eval(expr)`` in MicroPython is equivalent to ``eval(expr, globals(), globals())``. Sample code:: val = 1 def test(): val = 2 print(val) eval("print(val)") test() +-------------+-------------+ | CPy output: | uPy output: | +-------------+-------------+ | :: | :: | | | | | 2 | 2 | | 2 | 1 | +-------------+-------------+ f-strings --------- .. _cpydiff_core_fstring_concat: f-strings don't support concatenation with adjacent literals if the adjacent literals contain braces ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython is optimised for code space. **Workaround:** Use the + operator between literal strings when they are not both f-strings Sample code:: x, y = 1, 2 print("aa" f"{x}") # works print(f"{x}" "ab") # works print("a{}a" f"{x}") # fails print(f"{x}" "a{}b") # fails +-------------+--------------------------------------------+ | CPy output: | uPy output: | +-------------+--------------------------------------------+ | :: | :: | | | | | aa1 | aa1 | | 1ab | 1ab | | a{}a1 | Traceback (most recent call last): | | 1a{}b | File "", line 12, in | | | IndexError: tuple index out of range | +-------------+--------------------------------------------+ .. _cpydiff_core_fstring_parser: f-strings cannot support expressions that require parsing to resolve unbalanced nested braces and brackets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython is optimised for code space. **Workaround:** Always use balanced braces and brackets in expressions inside f-strings Sample code:: print(f"{'hello { world'}") print(f"{'hello ] world'}") +-------------------+----------------------------------------+ | CPy output: | uPy output: | +-------------------+----------------------------------------+ | :: | :: | | | | | hello { world | Traceback (most recent call last): | | hello ] world | File "", line 9 | | | SyntaxError: invalid syntax | +-------------------+----------------------------------------+ .. _cpydiff_core_fstring_repr: f-strings don't support !a conversions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicropPython does not implement ascii() **Workaround:** None Sample code:: f"{'unicode text'!a}" +-------------+----------------------------------------+ | CPy output: | uPy output: | +-------------+----------------------------------------+ | | :: | | | | | | Traceback (most recent call last): | | | File "", line 8 | | | SyntaxError: invalid syntax | +-------------+----------------------------------------+ import ------ .. _cpydiff_core_import_path: __path__ attribute of a package has a different type (single string instead of list of strings) in MicroPython ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython doesn't support namespace packages split across filesystem. Beyond that, MicroPython's import system is highly optimized for minimal memory usage. **Workaround:** Details of import handling is inherently implementation dependent. Don't rely on such details in portable applications. Sample code:: import modules print(modules.__path__) +----------------------------------------------------------------------+------------------------------+ | CPy output: | uPy output: | +----------------------------------------------------------------------+------------------------------+ | :: | :: | | | | | ['/home/micropython/micropython-autodocs/tests/cpydiff/modules'] | ../tests/cpydiff/modules | +----------------------------------------------------------------------+------------------------------+ .. _cpydiff_core_import_split_ns_pkgs: MicroPython doesn't support namespace packages split across filesystem. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Cause:** MicroPython's import system is highly optimized for simplicity, minimal memory usage, and minimal filesystem search overhead. **Workaround:** Don't install modules belonging to the same namespace package in different directories. For MicroPython, it's recommended to have at most 3-component module search paths: for your current application, per-user (writable), system-wide (non-writable). Sample code:: import sys sys.path.append(sys.path[1] + "/modules") sys.path.append(sys.path[1] + "/modules2") import subpkg.foo import subpkg.bar print("Two modules of a split namespace package imported") +-------------------------------------------------------+-----------------------------------------------+ | CPy output: | uPy output: | +-------------------------------------------------------+-----------------------------------------------+ | :: | :: | | | | | Two modules of a split namespace package imported | Traceback (most recent call last): | | | File "", line 14, in | | | ImportError: no module named 'subpkg.bar' | +-------------------------------------------------------+-----------------------------------------------+