Files
micropython/tests/extmod/marshal_fun_nested.py
Damien George 93692caefa extmod/modmarshal: Support marshal.dumps of functions with children.
This commit adds support to the `marshal` module to be able to dump
functions that have child functions.  For example:

    import marshal

    def f():
        def child():
            return 1
        return child

    marshal.dumps(f.__code__)

It also covers the case of marshalling functions that use list
comprehensions, because a list comprehension uses a child function.

This is made possible by the newly enhanced
`mp_raw_code_save_fun_to_bytes()` that can now handle nested functions.

Unmarshalling via `marshal.loads()` already supports nested functions
because it uses the standard `mp_raw_code_load_mem()` function which is
used to import mpy files (and hence can handle all possibilities).

Signed-off-by: Damien George <damien@micropython.org>
2026-01-27 01:20:36 +11:00

80 lines
1.7 KiB
Python

# Test the marshal module, with functions that have children.
try:
import marshal
(lambda: 0).__code__
except (AttributeError, ImportError):
print("SKIP")
raise SystemExit
def f_with_child():
def child():
return a
return child
def f_with_child_defargs():
def child(a="default"):
return a
return child
def f_with_child_closure():
a = "closure 1"
def child():
return a
a = "closure 2"
return child
def f_with_child_closure_defargs():
a = "closure defargs 1"
def child(b="defargs default"):
return (a, b)
a = "closure defargs 1"
return child
def f_with_list_comprehension(a):
return [i + a for i in range(4)]
ftype = type(lambda: 0)
# Test function with a child.
f = ftype(marshal.loads(marshal.dumps(f_with_child.__code__)), {"a": "global"})
print(f()())
# Test function with a child that has default arguments.
f = ftype(marshal.loads(marshal.dumps(f_with_child_defargs.__code__)), {})
print(f()())
print(f()("non-default"))
# Test function with a child that is a closure.
f = ftype(marshal.loads(marshal.dumps(f_with_child_closure.__code__)), {})
print(f()())
# Test function with a child that is a closure and has default arguments.
f = ftype(marshal.loads(marshal.dumps(f_with_child_closure_defargs.__code__)), {})
print(f()())
print(f()("defargs non-default"))
# Test function with a list comprehension (which will be an anonymous child).
f = ftype(marshal.loads(marshal.dumps(f_with_list_comprehension.__code__)), {})
print(f(10))
# Test child within a module (the outer scope).
code = compile("def child(a): return a", "", "exec")
f = marshal.loads(marshal.dumps(code))
ctx = {}
exec(f, ctx)
print(ctx["child"]("arg"))