Skip to content

Commit

Permalink
core: improve method loading/calling
Browse files Browse the repository at this point in the history
  • Loading branch information
MatthieuDartiailh committed Sep 9, 2024
1 parent 6219e15 commit 2c01603
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 43 deletions.
5 changes: 3 additions & 2 deletions enaml/core/block_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from atom.api import Typed

from . import compiler_common as cmn
from .enaml_ast import TemplateInst, ChildDef
from ..compat import PY311, PY313


Expand Down Expand Up @@ -44,7 +45,7 @@ class FirstPassBlockCompiler(BaseBlockCompiler):
#: A mapping of auxiliary ast node -> compiler node index.
aux_index_map = Typed(dict, ())

def visit_ChildDef(self, node):
def visit_ChildDef(self, node: ChildDef):
# Claim the index for the compiler node.
index = len(self.index_map)
self.index_map[node] = index
Expand All @@ -66,7 +67,7 @@ def visit_ChildDef(self, node):
for item in node.body:
self.visit(item)

def visit_TemplateInst(self, node):
def visit_TemplateInst(self, node: TemplateInst):
# No pragmas are supported yet for template inst nodes.
cmn.warn_pragmas(node, self.filename)

Expand Down
30 changes: 21 additions & 9 deletions enaml/core/code_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,20 +161,26 @@ def load_const(self, const):
bc.Instr("LOAD_CONST", const), # TOS -> value
)

def load_attr(self, name, push_null_or_self: bool = False):
def load_attr(self, name):
"""Load an attribute from the object on TOS."""
if PY312:
args = (push_null_or_self, name)
elif PY311:
args = name
self.code_ops.append("DUP_TOP")
args = (False, name)
else:
args = name
self.code_ops.append( # TOS -> obj
self.code_ops.append( # TOS -> obj
bc.Instr("LOAD_ATTR", args), # TOS -> value
)
if not PY312 and PY311:
self.rot_two()

def load_method(self, name):
"""Load a method from an object on TOS."""
if PY312:
self.code_ops.append( # TOS -> obj
# on 3.12 the order is reversed
bc.Instr("LOAD_ATTR", (True, name)), # TOS -> method -> self
)
else:
# On 3.10 one has to use call_method next
self.code_ops.append(bc.Instr("LOAD_METHOD", name))

def store_global(self, name):
"""Store the TOS as a global."""
Expand Down Expand Up @@ -324,7 +330,7 @@ def push_null(self):
bc.Instr("PUSH_NULL"), # TOS -> NULL
)

def call_function(self, n_args=0, n_kwds=0):
def call_function(self, n_args=0, n_kwds=0, is_method: bool = False):
"""Call a function on the TOS with the given args and kwargs."""
if PY313:
# NOTE: In Python 3.13 the caller must push null
Expand All @@ -346,10 +352,16 @@ def call_function(self, n_args=0, n_kwds=0):
self.code_ops.extend(ops)
else:
if n_kwds:
if is_method:
raise ValueError(

Check warning on line 356 in enaml/core/code_generator.py

View check run for this annotation

Codecov / codecov/patch

enaml/core/code_generator.py#L356

Added line #L356 was not covered by tests
"Method calling convention cannot be used with keywords"
)
# kwargs_name should be a tuple listing the keyword
# arguments names
# TOS -> func -> args -> kwargs -> kwargs_names
op, arg = "CALL_FUNCTION_KW", n_args + n_kwds
elif is_method:
op, arg = "CALL_METHOD", n_args
else:
op, arg = "CALL_FUNCTION", n_args
self.code_ops.append(bc.Instr(op, arg)) # TOS -> retval
Expand Down
29 changes: 3 additions & 26 deletions enaml/core/compiler_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,29 +153,6 @@ def count_nodes(node: Template) -> int:
return node_count


# XXX
def has_list_comp(pyast):
""" Determine whether a Python expression has a list comprehension.
This function is only used under Python 2.
Parameters
----------
pyast : Expression
The Python Expression ast of interest.
Returns
-------
result : bool
True if the ast includes a list comprehension, False otherwise.
"""
for item in ast.walk(pyast):
if isinstance(item, ast.ListComp):
return True
return False


def fetch_helpers(cg: CodeGenerator) -> None:
""" Fetch the compiler helpers and store in fast locals.
Expand Down Expand Up @@ -361,9 +338,9 @@ def append_node(cg: CodeGenerator, parent: int, index: int):
"""
load_node(cg, parent)
cg.load_attr('children')
cg.load_attr('append', push_null_or_self=True)
cg.load_method('append')
load_node(cg, index)
cg.call_function(1)
cg.call_function(1, is_method=True)
cg.pop_top()


Expand Down Expand Up @@ -967,7 +944,7 @@ def _insert_decl_function(cg, funcdef):
if i.name == "MAKE_FUNCTION":
break
code_index = index + code_offset
assert code_index > 0
assert code_index >= 0

# extract the inner code object which represents the actual
# function code and update its flags
Expand Down
4 changes: 2 additions & 2 deletions enaml/core/enamldef_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ def visit_EnamlDef(self, node):

# Update the internal node ids for the hierarchy.
cmn.load_node(cg, 0)
cg.load_attr('update_id_nodes', push_null_or_self=True)
cg.call_function()
cg.load_method('update_id_nodes')
cg.call_function(is_method=True)
cg.pop_top()

# Load and return the compiler node list..
Expand Down
6 changes: 2 additions & 4 deletions enaml/core/template_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,9 @@ def visit_Template(self, node):
self.visit(item)

# Update the internal node ids for the hierarchy.
# Python 3.11 and 3.12 requires a NULL before a function that is not a method
# Python 3.13 one after
cmn.load_node(cg, 0)
cg.load_attr('update_id_nodes', push_null_or_self=True)
cg.call_function()
cg.load_method('update_id_nodes')
cg.call_function(is_method=True)
cg.pop_top()

# Load the compiler node list for returning.
Expand Down
44 changes: 44 additions & 0 deletions tests/test_stdlib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# ------------------------------------------------------------------------------
# Copyright (c) 2024, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# ------------------------------------------------------------------------------
"""Test importing modules from the stdlib."""


def test_dialog_buttons():
import enaml
with enaml.imports():
import enaml.stdlib.dialog_buttons


def test_dock_area_styles():
import enaml
with enaml.imports():
import enaml.stdlib.dock_area_styles


def test_fields():
import enaml
with enaml.imports():
import enaml.stdlib.fields


def test_mapped_view():
import enaml
with enaml.imports():
import enaml.stdlib.mapped_view


def test_task_dialog():
import enaml
with enaml.imports():
import enaml.stdlib.task_dialog


def test_message_box():
import enaml
with enaml.imports():
import enaml.stdlib.message_box

0 comments on commit 2c01603

Please sign in to comment.