.. This document was generated by tools/gen-cpydiff.py

Core language
=============
Generated Sat 07 Oct 2023 07:35:47 UTC

.. _cpydiff_core_fstring_concat:

f-strings don't support concatenation with adjacent literals if the adjacent literals contain braces or are f-strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Cause:** MicroPython is optimised for code space.

**Workaround:** Use the + operator between literal strings when either or both are 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
    print(f"{x}" f"{y}")  # fails

+-------------+----------------------------------------+
| CPy output: | uPy output:                            |
+-------------+----------------------------------------+
| ::          | ::                                     |
|             |                                        |
|     aa1     |     Traceback (most recent call last): |
|     1ab     |       File "<stdin>", line 13          |
|     a{}a1   |     SyntaxError: invalid syntax        |
|     1a{}b   |                                        |
|     12      |                                        |
+-------------+----------------------------------------+

.. _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 "<stdin>", line 9           |
|                   |     SyntaxError: invalid syntax        |
+-------------------+----------------------------------------+

.. _cpydiff_core_fstring_raw:

Raw f-strings are not supported
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Cause:** MicroPython is optimised for code space.

Sample code::

    
    rf"hello"

+-------------+--------------------------------------------------+
| CPy output: | uPy output:                                      |
+-------------+--------------------------------------------------+
|             | ::                                               |
|             |                                                  |
|             |     Traceback (most recent call last):           |
|             |       File "<stdin>", line 8                     |
|             |     SyntaxError: raw f-strings are not supported |
+-------------+--------------------------------------------------+

.. _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 "<stdin>", line 8           |
|             |     SyntaxError: invalid syntax        |
+-------------+----------------------------------------+

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_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_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} |     <property> |
+---------------+----------------+

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 "<stdin>", line 13, in <module>                          |
|              |     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 "<stdin>", line 13, in <module>                 |
|             |     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': <function test at 0x7ff2865ed260>, '__name__': '__main__', '__file__': '<stdin>'} |
+----------------+------------------------------------------------------------------------------------------------+

.. _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       |
+-------------+-------------+

import
------

.. _cpydiff_core_import_all:

__all__ is unsupported in __init__.py in MicroPython.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Cause:** Not implemented.

**Workaround:** Manually import the sub-modules directly in __init__.py using ``from . import foo, bar``.

Sample code::

    from modules3 import *
    
    foo.hello()

+-------------+-------------------------------------------+
| CPy output: | uPy output:                               |
+-------------+-------------------------------------------+
| ::          | ::                                        |
|             |                                           |
|     hello   |     Traceback (most recent call last):    |
|             |       File "<stdin>", line 9, in <module> |
|             |     NameError: name 'foo' isn't defined   |
+-------------+-------------------------------------------+

.. _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 "<stdin>", line 13, in <module>    |
|                                                       |     ImportError: no module named 'subpkg.bar' |
+-------------------------------------------------------+-----------------------------------------------+