Skip to content

Commit

Permalink
Show errors in Python editor; fix bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
bgribble committed Feb 26, 2025
1 parent f731fa5 commit 11dfb4d
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 19 deletions.
38 changes: 31 additions & 7 deletions mfp/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,37 @@ def lazyrecurse(evalstr):
return rv

def exec_str(self, pystr, global_vars=None):
if global_vars is not None:
for name, value in self.global_names.items():
if name not in global_vars:
global_vars[name] = value
exec(pystr, global_vars)
else:
exec(pystr, self.global_names)
try:
if global_vars is not None:
for name, value in self.global_names.items():
if name not in global_vars:
global_vars[name] = value
exec(pystr, global_vars)
else:
exec(pystr, self.global_names)
except SyntaxError as syn:
return dict(
lineno=syn.lineno,
offset=syn.offset,
code=syn.text,
message=str(syn)
)
except NameError as name_err:
import sys
_, _, exc_tb = sys.exc_info()
return dict(
lineno=exc_tb.tb_next.tb_lineno,
offset=0,
code=name_err.name,
message=str(name_err)
)
except Exception as e:
return dict(
lineno=1,
offset=0,
code="",
message=str(e)
)

def exec_file(self, filename):
import os.path
Expand Down
18 changes: 13 additions & 5 deletions mfp/gui/base_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,17 +393,24 @@ async def create(self, obj_type, init_args):
self.obj_type = obj_type
self.obj_args = init_args

self.tags = {}
self.update_badge()
objinfo = await MFPGUI().mfp.create(obj_type, init_args, patchname, scopename, name, self.synced_params())

if self.layer is not None and objinfo:
objinfo["layername"] = self.layer.name

if objinfo is None:
if not objinfo or "obj_id" not in objinfo:
self.app_window.hud_write("ERROR: Could not create, see log for details")

if objinfo and "code" in objinfo:
self.code = objinfo["code"]

self.connections_out = connections_out
self.connections_in = connections_in
self.tags['errorcount'] = 1
self.update_badge()
return None

if self.layer is not None and objinfo and isinstance(objinfo, dict):
objinfo["layername"] = self.layer.name
objinfo['obj_type'] = obj_type

# init state from objinfo
Expand Down Expand Up @@ -562,7 +569,7 @@ def port_alloc(self):
@mutates(
'num_inlets', 'num_outlets', 'dsp_inlets', 'dsp_outlets',
'obj_name', 'no_export', 'is_export', 'export_offset_x',
'export_offset_y', 'debug', 'layer'
'export_offset_y', 'debug', 'layer', 'code'
)
async def configure(self, params):
self.num_inlets = params.get("num_inlets", 0)
Expand All @@ -575,6 +582,7 @@ async def configure(self, params):
self.export_offset_x = params.get("export_offset_x", 0)
self.export_offset_y = params.get("export_offset_y", 0)
self.debug = params.get("debug", False)
self.code = params.get("code", None)

newscope = params.get("scope", "__patch__")
if (not self.scope) or newscope != self.scope:
Expand Down
18 changes: 18 additions & 0 deletions mfp/gui/imgui/app_window/info_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@
TAB_PADDING_Y = 4

open_code_editors = {}
focused_editor = None


def open_code_editor(app_window, param_name, param_type, param_value, target):
global focused_editor

def _ensure_editor():
editor = target.imgui_code_editor
if not editor:
Expand All @@ -51,9 +54,11 @@ def _ensure_editor():
return editor
editor = _ensure_editor()
open_code_editors[target] = (editor, param_name)
focused_editor = target


def render_code_editors(app_window):
global focused_editor
to_close = []
for target, info in open_code_editors.items():
editor, param_name = info
Expand All @@ -66,6 +71,10 @@ def render_code_editors(app_window):
(350, 400),
cond=imgui.Cond_.once
)
if focused_editor == target:
imgui.set_next_window_focus()
focused_editor = None

_, window_open = imgui.begin(
f"{label}##code_editor",
True,
Expand All @@ -83,6 +92,7 @@ def render_code_editors(app_window):
new_val = target.code or {}
new_val["body"] = editor.get_text()
new_val["lang"] = "python"
new_val["errorinfo"] = None

MFPGUI().async_task(target.dispatch_setter(param_name, new_val))
selected, _ = imgui.menu_item("Close", "", False)
Expand All @@ -100,6 +110,14 @@ def render_code_editors(app_window):
imgui.pop_style_var()
imgui.pop_style_var()

if target.code and "errorinfo" in target.code and target.code["errorinfo"]:
errinfo = target.code["errorinfo"]
editor.set_error_markers(
{ errinfo["lineno"]: errinfo["message"]}
)
else:
editor.set_error_markers({})

editor.render(label)
imgui.end()

Expand Down
2 changes: 1 addition & 1 deletion mfp/gui/modes/global_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ async def hover(self, details):

o = self.manager.pointer_obj
try:
if o is not None and o.obj_state == BaseElement.OBJ_COMPLETE:
if not self.snoop_conn and o is not None and o.obj_state == BaseElement.OBJ_COMPLETE:
await o.show_tip(self.manager.pointer_x, self.manager.pointer_y, details)
except Exception:
pass
Expand Down
2 changes: 1 addition & 1 deletion mfp/gui/processor_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ async def recreate_element(self, action, state_diff, previous):
)

@saga('code')
def params_changed(self, action, state_diff, previous):
async def params_changed(self, action, state_diff, previous):
self.send_params()

async def label_edit_finish(self, widget, text=None, aborted=False):
Expand Down
20 changes: 15 additions & 5 deletions mfp/mfp_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,9 @@ def load_extension(self, libname):
log.warning(f"mfp_app.load_extension: not implemented completely, path={fullpath}")

async def create(self, init_type, init_args, patch, scope, name, params=None):
create_params = {}
rval = None

# first try: is a factory registered?
ctor = self.registry.get(init_type)

Expand All @@ -407,7 +410,10 @@ async def create(self, init_type, init_args, patch, scope, name, params=None):
code = params.get("code")
if code and code.get("lang") == "python":
codestr = code.get("body")
patch.evaluator.exec_str(codestr, defs)
errinfo = patch.evaluator.exec_str(codestr, defs)
if errinfo:
create_params["code"] = dict(body=codestr, lang="python", errorinfo=errinfo)
rval = create_params

# second try: is there a .mfp patch file in the search path?
if ctor is None:
Expand All @@ -430,14 +436,14 @@ async def create(self, init_type, init_args, patch, scope, name, params=None):

if ctor is None:
log.error(f"[create] Could not find a build method for {init_type}")
return None
return rval

# create intervening scope if needed
if '.' in name:
parts = name.split('.')
if len(parts) > 2:
log.error("Cannot deep-create name {}".format(name))
return None
return rval

testobj = self.resolve(parts[0], patch, True)
if testobj:
Expand All @@ -447,12 +453,12 @@ async def create(self, init_type, init_args, patch, scope, name, params=None):
log.error(
"Cannot deep-create object {} in patch {}".format(name, testobj)
)
return None
return rval
elif not isinstance(scope, LexicalScope):
log.error(
"Cannot deep-create object {} in another object {}".format(name, testobj)
)
return None
return rval
else:
scope = testobj
else:
Expand All @@ -473,6 +479,10 @@ async def create(self, init_type, init_args, patch, scope, name, params=None):

if obj and obj.obj_id:
await obj.setup()

for attr, val in create_params.items():
obj.gui_params[attr] = val

obj.mark_ready()

return obj
Expand Down
3 changes: 3 additions & 0 deletions mfp/mfp_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ async def create(self, objtype, initargs, patch_name, scope_name, obj_name, para
if obj is None:
log.warning(f"Failed to create object {objtype} {initargs} {patch} {scope} {obj_name}")
return None
elif isinstance(obj, dict):
log.warning(f"Error in creating {objtype} {obj}")
return obj
return obj.gui_params

async def create_export_gui(self, obj_id):
Expand Down

0 comments on commit 11dfb4d

Please sign in to comment.