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

Core language
=============
Generated Wed 19 Jul 2023 21:24:18 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     |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
|     1ab     |                                                                     |
|     a{}a1   |                                                                     |
|     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 |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
|     hello ] world |                                                                     |
+-------------------+---------------------------------------------------------------------+

.. _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:                                                         |
+-------------+---------------------------------------------------------------------+
|             | ::                                                                  |
|             |                                                                     |
|             |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+-------------+---------------------------------------------------------------------+

.. _cpydiff_core_fstring_repr:

f-strings don't support the !r, !s, and !a conversions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

**Workaround:** Use repr(), str(), and ascii() explictly.

Sample code::

    
    
    class X:
        def __repr__(self):
            return "repr"
    
        def __str__(self):
            return "str"
    
    
    print(f"{X()!r}")
    print(f"{X()!s}")

+-------------+---------------------------------------------------------------------+
| CPy output: | uPy output:                                                         |
+-------------+---------------------------------------------------------------------+
| ::          | ::                                                                  |
|             |                                                                     |
|     repr    |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
|     str     |                                                                     |
+-------------+---------------------------------------------------------------------+

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__ |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+-------------+---------------------------------------------------------------------+

.. _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     |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+-------------+---------------------------------------------------------------------+

.. _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__ |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
|     B.__init__ |                                                                     |
|     C.__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} |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+---------------+---------------------------------------------------------------------+

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:                                                         |
+---------------------------------------------------+---------------------------------------------------------------------+
| ::                                                | ::                                                                  |
|                                                   |                                                                     |
|     append() takes exactly one argument (0 given) |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+---------------------------------------------------+---------------------------------------------------------------------+

.. _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__ |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+--------------+---------------------------------------------------------------------+

.. _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       |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+-------------+---------------------------------------------------------------------+

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   |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
|     1       |                                                                     |
|     2       |                                                                     |
|     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} |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+----------------+---------------------------------------------------------------------+

.. _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       |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
|     2       |                                                                     |
+-------------+---------------------------------------------------------------------+

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   |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+-------------+---------------------------------------------------------------------+

.. _cpydiff_core_import_path:

__path__ attribute of a package has a different type (single string instead of list of strings) in MicroPython
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Cause:** MicroPython does'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:                                                         |
+-------------------------------------------------------------+---------------------------------------------------------------------+
| ::                                                          | ::                                                                  |
|                                                             |                                                                     |
|     ['/github/workspace/micropython/tests/cpydiff/modules'] |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+-------------------------------------------------------------+---------------------------------------------------------------------+

.. _cpydiff_core_import_prereg:

Failed to load modules are still registered as loaded
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Cause:** To make module handling more efficient, it's not wrapped with exception handling.

**Workaround:** Test modules before production use; during development, use ``del sys.modules["name"]``, or just soft or hard reset the board.

Sample code::

    import sys
    
    try:
        from modules import foo
    except NameError as e:
        print(e)
    try:
        from modules import foo
    
        print("Should not get here")
    except NameError as e:
        print(e)

+-------------------------------+---------------------------------------------------------------------+
| CPy output:                   | uPy output:                                                         |
+-------------------------------+---------------------------------------------------------------------+
| ::                            | ::                                                                  |
|                               |                                                                     |
|     foo                       |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
|     name 'xxx' is not defined |                                                                     |
|     foo                       |                                                                     |
|     name 'xxx' is not defined |                                                                     |
+-------------------------------+---------------------------------------------------------------------+

.. _cpydiff_core_import_split_ns_pkgs:

MicroPython does'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 |     /bin/sh: 1: ../ports/unix/build-standard/micropython: not found |
+-------------------------------------------------------+---------------------------------------------------------------------+

