Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions enaml/core/compiler_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@
ast.DictComp,
ast.SetComp)

#: Opcode used to create a function
_MAKE_FUNC = ("MAKE_FUNCTION",)


def unhandled_pragma(name, filename, lineno):
""" Emit a warning for an unhandled pragma.
Expand Down Expand Up @@ -445,7 +442,11 @@ def run_in_dynamic_scope(code: bc.Bytecode, global_vars: set[str]) -> None:
fetch_helpers(cg)

# Scan all ops to detect function call after GET_ITER
for instr in instrs:
idx = 0
num_instrs = len(instrs)
while idx < num_instrs:
instr = instrs[idx]
idx += 1
if not isinstance(instr, bc.Instr):
cg.code_ops.append(instr)
continue
Expand All @@ -462,8 +463,17 @@ def run_in_dynamic_scope(code: bc.Bytecode, global_vars: set[str]) -> None:
run_in_dynamic_scope(inner, global_vars)
inner.update_flags()
i_arg = inner.to_code()
elif any(i_name == make_fun_op for make_fun_op in _MAKE_FUNC):
elif i_name == "MAKE_FUNCTION":
cg.code_ops.append(bc.Instr(i_name, i_arg)) # func

if PY313 and idx < num_instrs:
# If the next instruction is SET_FUNCTION_ATTRIBUTE it needs
# executed before wrapping the function in order to setup defaults on the stack.
i_next = instrs[idx]
if isinstance(i_next, bc.Instr) and i_next.name == "SET_FUNCTION_ATTRIBUTE":
cg.code_ops.append(bc.Instr(i_next.name, i_next.arg, location=instr.location))
idx += 1 # Skip the instruction

load_helper(cg, 'wrap_func') # func -> wrap
cg.rot_two() # wrap -> func
# Python 3.11 and 3.12 requires a NULL before a function that is not a method
Expand Down
32 changes: 32 additions & 0 deletions tests/core/test_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,35 @@ class A:


# XXX add test regarding handling of with statement


def test_compile_lamba_with_default():
source = dedent("""\
from enaml.widgets.api import Window

enamldef Main(Window):
attr f = lambda v="x": v
""")
Main = compile_source(source, 'Main')
window = Main()
assert window.f() == "x"
assert window.f("y") == "y"


def test_compile_lamba_with_default_in_dict_comp():
source = dedent("""\
from enaml.widgets.api import Window

enamldef Main(Window):
attr mode: str = "a"
attr keymap = {
k: lambda v=k: setattr(self, "mode", v)
for k in ("a", "b")
}
""")
Main = compile_source(source, 'Main')
window = Main()
assert window.mode == "a"
window.keymap['b']()
assert window.mode == "b"

Loading