Core Language

Generated Sun 08 Jul 2018 04:25:25 UTC

Classes

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__
 

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)

When inheriting from multiple classes super() only calls one class

Cause: See Method Resolution Order (MRO) is not compliant with CPython

Workaround: See Method Resolution Order (MRO) is not compliant with CPython

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__
B.__init__
C.__init__
A.__init__
D.__init__
B.__init__
A.__init__

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

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)
function takes 2 positional arguments but 1 were given

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 10, in <module>
AttributeError: 'function' object has no attribute 'x'

Generator

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
1
2
3
Exit
Enter
1
2
3

Runtime

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 0x7f2762f81560>, '__name__': '__main__', '__file__': '<stdin>'}

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

__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:
['/home/kwagyeman/Documents/GitHub/openmv/src/micropython/tests/cpydiff/modules']
../tests/cpydiff//modules

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
name 'xxx' is not defined
foo
name 'xxx' is not defined
foo
name 'xxx' is not defined
Should not get here

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
Traceback (most recent call last):
  File "<stdin>", line 12, in <module>
ImportError: no module named 'subpkg.bar'