More work on evmenu helper

This commit is contained in:
Griatch 2020-09-29 19:27:33 +02:00
parent b89d188c32
commit 84e26566ae
2 changed files with 73 additions and 61 deletions

View file

@ -170,5 +170,4 @@ GOTO_CALLABLES = {
def testmenu(caller): def testmenu(caller):
menutree = parse_menu_template(caller, MENU_TEMPLATE, GOTO_CALLABLES) menutree = parse_menu_template(caller, MENU_TEMPLATE, GOTO_CALLABLES)
# we'll use a custom EvMenu child later # we'll use a custom EvMenu child later
EvMenu(caller, menutree, auto_help=False) EvMenu(caller, menutree)

View file

@ -329,6 +329,19 @@ class EvMenuError(RuntimeError):
pass pass
class EvMenuGotoAbortMessage(RuntimeError):
"""
This can be raised by a goto-callable to abort the goto flow. The message
stored with the executable will be sent to the caller who will remain on
the current node. This can be used to pass single-line returns without
re-running the entire node with text and options.
Example:
raise EvMenuGotoMessage("That makes no sense.")
"""
# ------------------------------------------------------------- # -------------------------------------------------------------
# #
# Menu command and command set # Menu command and command set
@ -795,6 +808,8 @@ class EvMenu:
def run_exec(self, nodename, raw_string, **kwargs): def run_exec(self, nodename, raw_string, **kwargs):
""" """
NOTE: This is deprecated. Use `goto` directly instead.
Run a function or node as a callback (with the 'exec' option key). Run a function or node as a callback (with the 'exec' option key).
Args: Args:
@ -1100,24 +1115,29 @@ class EvMenu:
""" """
cmd = strip_ansi(raw_string.strip().lower()) cmd = strip_ansi(raw_string.strip().lower())
if self.options and cmd in self.options: try:
# this will take precedence over the default commands if self.options and cmd in self.options:
# below # this will take precedence over the default commands
goto, goto_kwargs, execfunc, exec_kwargs = self.options[cmd] # below
self.run_exec_then_goto(execfunc, goto, raw_string, exec_kwargs, goto_kwargs) goto, goto_kwargs, execfunc, exec_kwargs = self.options[cmd]
elif self.auto_look and cmd in ("look", "l"): self.run_exec_then_goto(execfunc, goto, raw_string, exec_kwargs, goto_kwargs)
self.display_nodetext() elif self.auto_look and cmd in ("look", "l"):
elif self.auto_help and cmd in ("help", "h"): self.display_nodetext()
self.display_helptext() elif self.auto_help and cmd in ("help", "h"):
elif self.auto_quit and cmd in ("quit", "q", "exit"): self.display_helptext()
self.close_menu() elif self.auto_quit and cmd in ("quit", "q", "exit"):
elif self.debug_mode and cmd.startswith("menudebug"): self.close_menu()
self.print_debug_info(cmd[9:].strip()) elif self.debug_mode and cmd.startswith("menudebug"):
elif self.default: self.print_debug_info(cmd[9:].strip())
goto, goto_kwargs, execfunc, exec_kwargs = self.default elif self.default:
self.run_exec_then_goto(execfunc, goto, raw_string, exec_kwargs, goto_kwargs) goto, goto_kwargs, execfunc, exec_kwargs = self.default
else: self.run_exec_then_goto(execfunc, goto, raw_string, exec_kwargs, goto_kwargs)
self.caller.msg(_HELP_NO_OPTION_MATCH, session=self._session) else:
self.caller.msg(_HELP_NO_OPTION_MATCH, session=self._session)
except EvMenuGotoAbortMessage as err:
# custom interrupt from inside a goto callable - print the message and
# stay on the current node.
self.caller.msg(str(err), session=self._session)
def display_nodetext(self): def display_nodetext(self):
self.caller.msg(self.nodetext, session=self._session) self.caller.msg(self.nodetext, session=self._session)
@ -1603,20 +1623,37 @@ _OPTION_COMMENT_START = "#"
# Input/option/goto handler functions that allows for dynamically generated # Input/option/goto handler functions that allows for dynamically generated
# nodes read from the menu template. # nodes read from the menu template.
def _process_callable(caller, goto, goto_callables, raw_string,
current_nodename, kwargs):
match = _RE_CALLABLE.match(goto)
if match:
gotofunc = match.group("funcname")
gotokwargs = match.group("kwargs") or ""
if gotofunc in goto_callables:
for kwarg in gotokwargs.split(","):
if kwarg and "=" in kwarg:
key, value = [part.strip() for part in kwarg.split("=", 1)]
try:
key = literal_eval(key)
except ValueError:
pass
try:
value = literal_eval(value)
except ValueError:
pass
kwargs[key] = value
goto = goto_callables[gotofunc](caller, raw_string, **kwargs)
if goto is None:
return goto, {"generated_nodename": current_nodename}
return goto, {"generated_nodename": goto}
def _generated_goto_func(caller, raw_string, **kwargs): def _generated_goto_func(caller, raw_string, **kwargs):
goto = kwargs["goto"] goto = kwargs["goto"]
goto_callables = kwargs["goto_callables"] goto_callables = kwargs["goto_callables"]
current_nodename = kwargs["current_nodename"] current_nodename = kwargs["current_nodename"]
return _process_callable(caller, goto, goto_callables, raw_string,
if _RE_CALLABLE.match(goto): current_nodename, kwargs)
gotofunc = goto.strip()[:-2]
if gotofunc in goto_callables:
goto = goto_callables[gotofunc](caller, raw_string, **kwargs)
if goto is None:
return goto, {"generated_nodename": current_nodename}
caller.msg(_HELP_NO_OPTION_MATCH)
return goto, {"generated_nodename": goto}
def _generated_input_goto_func(caller, raw_string, **kwargs): def _generated_input_goto_func(caller, raw_string, **kwargs):
@ -1627,40 +1664,16 @@ def _generated_input_goto_func(caller, raw_string, **kwargs):
# start with glob patterns # start with glob patterns
for pattern, goto in gotomap.items(): for pattern, goto in gotomap.items():
if fnmatch(raw_string.lower(), pattern): if fnmatch(raw_string.lower(), pattern):
match = _RE_CALLABLE.match(goto) return _process_callable(caller, goto, goto_callables, raw_string,
if match: current_nodename, kwargs)
gotofunc = match.group("funcname")
gotokwargs = match.group("kwargs") or ""
if gotofunc in goto_callables:
for kwarg in gotokwargs.split(","):
if kwarg and "=" in kwarg:
key, value = [part.strip() for part in kwarg.split("=", 1)]
try:
key = literal_eval(key)
except ValueError:
pass
try:
value = literal_eval(value)
except ValueError:
pass
kwargs[key] = value
goto = goto_callables[gotofunc](caller, raw_string, **kwargs)
if goto is None:
return goto, {"generated_nodename": current_nodename}
return goto, {"generated_nodename": goto}
# no glob pattern match; try regex # no glob pattern match; try regex
for pattern, goto in gotomap.items(): for pattern, goto in gotomap.items():
if pattern and re.match(pattern, raw_string.lower(), flags=re.I + re.M): if pattern and re.match(pattern, raw_string.lower(), flags=re.I + re.M):
if _RE_CALLABLE.match(goto): return _process_callable(caller, goto, goto_callables, raw_string,
gotofunc = goto.strip()[:-2] current_nodename, kwargs)
if gotofunc in goto_callables: # no match, show error
goto = goto_callables[gotofunc](caller, raw_string, **kwargs)
if goto is None: raise EvMenuGotoAbortMessage(_HELP_NO_OPTION_MATCH)
return goto, {"generated_nodename": current_nodename}
return goto, {"generated_nodename": goto}
# no match, rerun current node
caller.msg(_HELP_NO_OPTION_MATCH)
return None, {"generated_nodename": current_nodename}
def _generated_node(caller, raw_string, **kwargs): def _generated_node(caller, raw_string, **kwargs):
@ -1715,6 +1728,7 @@ def parse_menu_template(caller, menu_template, goto_callables=None):
# if we have a pattern, build the arguments for _default later # if we have a pattern, build the arguments for _default later
pattern = main_key[len(_OPTION_INPUT_MARKER):].strip() pattern = main_key[len(_OPTION_INPUT_MARKER):].strip()
inputparsemap[pattern] = goto inputparsemap[pattern] = goto
print(f"main_key {main_key} {pattern} {goto}")
else: else:
# a regular goto string/callable target # a regular goto string/callable target
option = { option = {
@ -1748,7 +1762,6 @@ def parse_menu_template(caller, menu_template, goto_callables=None):
} }
) )
print(f"nodename: {nodename}, options: {options}")
return options return options
def _parse(caller, menu_template, goto_callables): def _parse(caller, menu_template, goto_callables):