Fix EvMenu failure to stop if returning options as an empty dict. Resolve #2981
This commit is contained in:
parent
804e44c3f0
commit
7c08f77aa0
4 changed files with 123 additions and 202 deletions
|
|
@ -21,14 +21,13 @@ of the screen is done by the unlogged-in "look" command.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from evennia import utils
|
from evennia import utils
|
||||||
|
|
||||||
CONNECTION_SCREEN = """
|
CONNECTION_SCREEN = """
|
||||||
|b==============================================================|n
|
|b==============================================================|n
|
||||||
Welcome to |g{}|n, version {}!
|
Welcome to |g{}|n, version {}!
|
||||||
|
|
||||||
Enter |wh|nelp for more info. |wlook|n will re-show this screen.
|
Enter |wh|nelp for more info.
|
||||||
|b==============================================================|n""".format(
|
|b==============================================================|n""".format(
|
||||||
settings.SERVERNAME, utils.get_evennia_version("short")
|
settings.SERVERNAME, utils.get_evennia_version("short")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ To install, add this line to the settings file (`mygame/server/conf/settings.py`
|
||||||
|
|
||||||
CMDSET_UNLOGGEDIN = "evennia.contrib.base_systems.menu_login.UnloggedinCmdSet"
|
CMDSET_UNLOGGEDIN = "evennia.contrib.base_systems.menu_login.UnloggedinCmdSet"
|
||||||
|
|
||||||
|
|
||||||
Reload the server and the new connection method will be active. Note that you must
|
Reload the server and the new connection method will be active. Note that you must
|
||||||
independently change the connection screen to match this login style, by editing
|
independently change the connection screen to match this login style, by editing
|
||||||
`mygame/server/conf/connection_screens.py`.
|
`mygame/server/conf/connection_screens.py`.
|
||||||
|
|
@ -20,7 +21,6 @@ called automatically when a new user connects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from evennia import CmdSet, Command, syscmdkeys
|
from evennia import CmdSet, Command, syscmdkeys
|
||||||
from evennia.utils.evmenu import EvMenu
|
from evennia.utils.evmenu import EvMenu
|
||||||
from evennia.utils.utils import callables_from_module, class_from_module, random_string_from_module
|
from evennia.utils.utils import callables_from_module, class_from_module, random_string_from_module
|
||||||
|
|
@ -57,9 +57,9 @@ def node_enter_username(caller, raw_text, **kwargs):
|
||||||
"""
|
"""
|
||||||
'Goto-callable', set up to be called from the _default option below.
|
'Goto-callable', set up to be called from the _default option below.
|
||||||
|
|
||||||
Called when user enters a username string. Check if this username already exists and set the flag
|
Called when user enters a username string. Check if this username already exists and set the
|
||||||
'new_user' if not. Will also directly login if the username is 'guest'
|
flag 'new_user' if not. Will also directly login if the username is 'guest' and
|
||||||
and GUEST_ENABLED is True.
|
GUEST_ENABLED is True.
|
||||||
|
|
||||||
The return from this goto-callable determines which node we go to next
|
The return from this goto-callable determines which node we go to next
|
||||||
and what kwarg it will be called with.
|
and what kwarg it will be called with.
|
||||||
|
|
@ -167,7 +167,7 @@ def node_enter_password(caller, raw_string, **kwargs):
|
||||||
# Attempting to fix password
|
# Attempting to fix password
|
||||||
text = "Enter a new password:"
|
text = "Enter a new password:"
|
||||||
else:
|
else:
|
||||||
text = "Creating a new account |c{}|n. " "Enter a password (empty to abort):".format(
|
text = "Creating a new account |c{}|n. Enter a password (empty to abort):".format(
|
||||||
username
|
username
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
@ -199,15 +199,24 @@ def node_quit_or_login(caller, raw_text, **kwargs):
|
||||||
# EvMenu helper function
|
# EvMenu helper function
|
||||||
|
|
||||||
|
|
||||||
def _node_formatter(nodetext, optionstext, caller=None):
|
class MenuLoginEvMenu(EvMenu):
|
||||||
"""Do not display the options, only the text.
|
"""
|
||||||
|
Version of EvMenu that does not display any of its options.
|
||||||
This function is used by EvMenu to format the text of nodes. The menu login
|
|
||||||
is just a series of prompts so we disable all automatic display decoration
|
|
||||||
and let the nodes handle everything on their own.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return nodetext
|
|
||||||
|
def node_formatter(self, nodetext, optionstext):
|
||||||
|
return nodetext
|
||||||
|
|
||||||
|
def options_formatter(self, optionlist):
|
||||||
|
"""Do not display the options, only the text.
|
||||||
|
|
||||||
|
This function is used by EvMenu to format the text of nodes. The menu login
|
||||||
|
is just a series of prompts so we disable all automatic display decoration
|
||||||
|
and let the nodes handle everything on their own.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
# Commands and CmdSets
|
# Commands and CmdSets
|
||||||
|
|
@ -240,12 +249,17 @@ class CmdUnloggedinLook(Command):
|
||||||
Run the menu using the nodes in this module.
|
Run the menu using the nodes in this module.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
EvMenu(
|
menu_nodes = {
|
||||||
|
"node_enter_username": node_enter_username,
|
||||||
|
"node_enter_password": node_enter_password,
|
||||||
|
"node_quit_or_login": node_quit_or_login,
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuLoginEvMenu(
|
||||||
self.caller,
|
self.caller,
|
||||||
"evennia.contrib.base_systems.menu_login.menu_login",
|
menu_nodes,
|
||||||
startnode="node_enter_username",
|
startnode="node_enter_username",
|
||||||
auto_look=False,
|
auto_look=False,
|
||||||
auto_quit=False,
|
auto_quit=False,
|
||||||
cmd_on_exit=None,
|
cmd_on_exit=None,
|
||||||
node_formatter=_node_formatter,
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -89,12 +89,6 @@ menu is immediately exited and the default "look" command is called.
|
||||||
string is the name of the next node to go to and the dict is the new,
|
string is the name of the next node to go to and the dict is the new,
|
||||||
(possibly modified) kwarg to pass into the next node. If the callable returns
|
(possibly modified) kwarg to pass into the next node. If the callable returns
|
||||||
None or the empty string, the current node will be revisited.
|
None or the empty string, the current node will be revisited.
|
||||||
- `exec` (str, callable or tuple, optional): This takes the same input as `goto` above
|
|
||||||
and runs before it. If given a node name, the node will be executed but will not
|
|
||||||
be considered the next node. If node/callback returns str or (str, dict), these will
|
|
||||||
replace the `goto` step (`goto` callbacks will not fire), with the string being the
|
|
||||||
next node name and the optional dict acting as the kwargs-input for the next node.
|
|
||||||
If an exec callable returns the empty string (only), the current node is re-run.
|
|
||||||
|
|
||||||
If `key` is not given, the option will automatically be identified by
|
If `key` is not given, the option will automatically be identified by
|
||||||
its number 1..N.
|
its number 1..N.
|
||||||
|
|
@ -111,7 +105,6 @@ Example:
|
||||||
options = ({"key": "testing",
|
options = ({"key": "testing",
|
||||||
"desc": "Select this to go to node 2",
|
"desc": "Select this to go to node 2",
|
||||||
"goto": ("node2", {"foo": "bar"}),
|
"goto": ("node2", {"foo": "bar"}),
|
||||||
"exec": "callback1"},
|
|
||||||
{"desc": "Go to node 3.",
|
{"desc": "Go to node 3.",
|
||||||
"goto": "node3"})
|
"goto": "node3"})
|
||||||
return text, options
|
return text, options
|
||||||
|
|
@ -280,7 +273,6 @@ from django.conf import settings
|
||||||
|
|
||||||
# i18n
|
# i18n
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from evennia import CmdSet, Command
|
from evennia import CmdSet, Command
|
||||||
from evennia.commands import cmdhandler
|
from evennia.commands import cmdhandler
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
|
|
@ -357,7 +349,8 @@ class EvMenuGotoAbortMessage(RuntimeError):
|
||||||
|
|
||||||
class CmdEvMenuNode(Command):
|
class CmdEvMenuNode(Command):
|
||||||
"""
|
"""
|
||||||
Menu options.
|
Command to handle all user input targeted at the menu while the menu is active.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = _CMD_NOINPUT
|
key = _CMD_NOINPUT
|
||||||
|
|
@ -638,7 +631,7 @@ class EvMenu:
|
||||||
# store ourself on the object
|
# store ourself on the object
|
||||||
self.caller.ndb._evmenu = self
|
self.caller.ndb._evmenu = self
|
||||||
|
|
||||||
# DEPRECATED - for backwards-compatibility
|
# DEPRECATED - for backwards-compatibility. Use `.ndb._evmenu` instead
|
||||||
self.caller.ndb._menutree = self
|
self.caller.ndb._menutree = self
|
||||||
|
|
||||||
if persistent:
|
if persistent:
|
||||||
|
|
@ -778,7 +771,7 @@ class EvMenu:
|
||||||
|
|
||||||
def _execute_node(self, nodename, raw_string, **kwargs):
|
def _execute_node(self, nodename, raw_string, **kwargs):
|
||||||
"""
|
"""
|
||||||
Execute a node.
|
Execute a node (-function) and get its returns.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
nodename (str): Name of node.
|
nodename (str): Name of node.
|
||||||
|
|
@ -814,87 +807,12 @@ class EvMenu:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# store options to make them easier to test
|
# store options to make them easier to test
|
||||||
self.test_options = options
|
|
||||||
self.test_nodetext = nodetext
|
self.test_nodetext = nodetext
|
||||||
|
self.test_options = options
|
||||||
|
|
||||||
return nodetext, options
|
return nodetext, options
|
||||||
|
|
||||||
def msg(self, txt):
|
def _extract_goto(self, nodename, option_dict):
|
||||||
"""
|
|
||||||
This is a central point for sending return texts to the caller. It
|
|
||||||
allows for a central point to add custom messaging when creating custom
|
|
||||||
EvMenu overrides.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
txt (str): The text to send.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
By default this will send to the same session provided to EvMenu
|
|
||||||
(if `session` kwarg was provided to `EvMenu.__init__`). It will
|
|
||||||
also send it with a `type=menu` for the benefit of OOB/webclient.
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.caller.msg(text=(txt, {"type": "menu"}), session=self._session)
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
nodename (callable or str): A callable to run as
|
|
||||||
`callable(caller, raw_string)`, or the Name of an existing
|
|
||||||
node to run as a callable. This may or may not return
|
|
||||||
a string.
|
|
||||||
raw_string (str): The raw default string entered on the
|
|
||||||
previous node (only used if the node accepts it as an
|
|
||||||
argument)
|
|
||||||
kwargs (any): These are optional kwargs passed into goto
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
new_goto (str or None): A replacement goto location string or
|
|
||||||
None (no replacement).
|
|
||||||
Notes:
|
|
||||||
Relying on exec callbacks to set the goto location is
|
|
||||||
very powerful but will easily lead to spaghetti structure and
|
|
||||||
hard-to-trace paths through the menu logic. So be careful with
|
|
||||||
relying on this.
|
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
if callable(nodename):
|
|
||||||
# this is a direct callable - execute it directly
|
|
||||||
ret = self._safe_call(nodename, raw_string, **kwargs)
|
|
||||||
if isinstance(ret, (tuple, list)):
|
|
||||||
if not len(ret) > 1 or not isinstance(ret[1], dict):
|
|
||||||
raise EvMenuError(
|
|
||||||
"exec callable must return either None, str or (str, dict)"
|
|
||||||
)
|
|
||||||
ret, kwargs = ret[:2]
|
|
||||||
else:
|
|
||||||
# nodename is a string; lookup as node and run as node in-place (don't goto it)
|
|
||||||
# execute the node
|
|
||||||
ret = self._execute_node(nodename, raw_string, **kwargs)
|
|
||||||
if isinstance(ret, (tuple, list)):
|
|
||||||
if not len(ret) > 1 and ret[1] and not isinstance(ret[1], dict):
|
|
||||||
raise EvMenuError("exec node must return either None, str or (str, dict)")
|
|
||||||
ret, kwargs = ret[:2]
|
|
||||||
except EvMenuError as err:
|
|
||||||
errmsg = "Error in exec '%s' (input: '%s'): %s" % (nodename, raw_string.rstrip(), err)
|
|
||||||
self.msg("|r%s|n" % errmsg)
|
|
||||||
logger.log_trace(errmsg)
|
|
||||||
return
|
|
||||||
|
|
||||||
if isinstance(ret, str):
|
|
||||||
# only return a value if a string (a goto target), ignore all other returns
|
|
||||||
if not ret:
|
|
||||||
# an empty string - rerun the same node
|
|
||||||
return self.nodename
|
|
||||||
return ret, kwargs
|
|
||||||
return None
|
|
||||||
|
|
||||||
def extract_goto_exec(self, nodename, option_dict):
|
|
||||||
"""
|
"""
|
||||||
Helper: Get callables and their eventual kwargs.
|
Helper: Get callables and their eventual kwargs.
|
||||||
|
|
||||||
|
|
@ -905,12 +823,10 @@ class EvMenu:
|
||||||
Returns:
|
Returns:
|
||||||
goto (str, callable or None): The goto directive in the option.
|
goto (str, callable or None): The goto directive in the option.
|
||||||
goto_kwargs (dict): Kwargs for `goto` if the former is callable, otherwise empty.
|
goto_kwargs (dict): Kwargs for `goto` if the former is callable, otherwise empty.
|
||||||
execute (callable or None): Executable given by the `exec` directive.
|
|
||||||
exec_kwargs (dict): Kwargs for `execute` if it's callable, otherwise empty.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
goto_kwargs, exec_kwargs = {}, {}
|
goto_kwargs = {}
|
||||||
goto, execute = option_dict.get("goto", None), option_dict.get("exec", None)
|
goto = option_dict.get("goto", None)
|
||||||
if goto and isinstance(goto, (tuple, list)):
|
if goto and isinstance(goto, (tuple, list)):
|
||||||
if len(goto) > 1:
|
if len(goto) > 1:
|
||||||
goto, goto_kwargs = goto[:2] # ignore any extra arguments
|
goto, goto_kwargs = goto[:2] # ignore any extra arguments
|
||||||
|
|
@ -923,29 +839,17 @@ class EvMenu:
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
goto = goto[0]
|
goto = goto[0]
|
||||||
if execute and isinstance(execute, (tuple, list)):
|
return goto, goto_kwargs
|
||||||
if len(execute) > 1:
|
|
||||||
execute, exec_kwargs = execute[:2] # ignore any extra arguments
|
|
||||||
if not hasattr(exec_kwargs, "__getitem__"):
|
|
||||||
# not a dict-like structure
|
|
||||||
raise EvMenuError(
|
|
||||||
"EvMenu node {}: exec kwargs is not a dict: {}".format(
|
|
||||||
nodename, goto_kwargs
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
execute = execute[0]
|
|
||||||
return goto, goto_kwargs, execute, exec_kwargs
|
|
||||||
|
|
||||||
def goto(self, nodename, raw_string, **kwargs):
|
def goto(self, nodename_or_callable, raw_string, **kwargs):
|
||||||
"""
|
"""
|
||||||
Run a node by name, optionally dynamically generating that name first.
|
Run a node by name, optionally dynamically generating that name first.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
nodename (str or callable): Name of node or a callable
|
nodename_or_callable (str or callable): Name of node or a callable
|
||||||
to be called as `function(caller, raw_string, **kwargs)` or
|
to be called as `function(caller, raw_string, **kwargs)` or
|
||||||
`function(caller, **kwargs)` to return the actual goto string or
|
`function(caller, **kwargs)`. This callable must return the node-name (str)
|
||||||
a ("nodename", kwargs) tuple.
|
pointing to the next node.
|
||||||
raw_string (str): The raw default string entered on the
|
raw_string (str): The raw default string entered on the
|
||||||
previous node (only used if the node accepts it as an
|
previous node (only used if the node accepts it as an
|
||||||
argument)
|
argument)
|
||||||
|
|
@ -953,10 +857,10 @@ class EvMenu:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if callable(nodename):
|
inp_nodename = nodename_or_callable
|
||||||
# run the "goto" callable, if possible
|
if callable(nodename_or_callable):
|
||||||
inp_nodename = nodename
|
# run the "goto" callable to get the next node to go to
|
||||||
nodename = self._safe_call(nodename, raw_string, **kwargs)
|
nodename = self._safe_call(nodename_or_callable, raw_string, **kwargs)
|
||||||
if isinstance(nodename, (tuple, list)):
|
if isinstance(nodename, (tuple, list)):
|
||||||
if not len(nodename) > 1 or not isinstance(nodename[1], dict):
|
if not len(nodename) > 1 or not isinstance(nodename[1], dict):
|
||||||
raise EvMenuError(
|
raise EvMenuError(
|
||||||
|
|
@ -966,8 +870,14 @@ class EvMenu:
|
||||||
if not nodename:
|
if not nodename:
|
||||||
# no nodename return. Re-run current node
|
# no nodename return. Re-run current node
|
||||||
nodename = self.nodename
|
nodename = self.nodename
|
||||||
|
else:
|
||||||
|
# the nodename given directly
|
||||||
|
nodename = nodename_or_callable
|
||||||
|
|
||||||
|
# one way or another, we have the nodename as a string now
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# execute the found node, make use of the returns.
|
# execute the found nodename, make use of the returns.
|
||||||
nodetext, options = self._execute_node(nodename, raw_string, **kwargs)
|
nodetext, options = self._execute_node(nodename, raw_string, **kwargs)
|
||||||
except EvMenuError:
|
except EvMenuError:
|
||||||
return
|
return
|
||||||
|
|
@ -978,47 +888,13 @@ class EvMenu:
|
||||||
)
|
)
|
||||||
|
|
||||||
# validation of the node return values
|
# validation of the node return values
|
||||||
|
|
||||||
|
# if the nodetext is a list/tuple, the second set is the help text.
|
||||||
helptext = ""
|
helptext = ""
|
||||||
if is_iter(nodetext):
|
if is_iter(nodetext):
|
||||||
if len(nodetext) > 1:
|
nodetext, *helptext = nodetext
|
||||||
nodetext, helptext = nodetext[:2]
|
helptext = helptext[0] if helptext else ""
|
||||||
else:
|
|
||||||
nodetext = nodetext[0]
|
|
||||||
nodetext = "" if nodetext is None else str(nodetext)
|
nodetext = "" if nodetext is None else str(nodetext)
|
||||||
options = [options] if isinstance(options, dict) else options
|
|
||||||
|
|
||||||
# this will be displayed in the given order
|
|
||||||
display_options = []
|
|
||||||
# this is used for lookup
|
|
||||||
self.options = {}
|
|
||||||
self.default = None
|
|
||||||
if options:
|
|
||||||
for inum, dic in enumerate(options):
|
|
||||||
# fix up the option dicts
|
|
||||||
keys = make_iter(dic.get("key"))
|
|
||||||
desc = dic.get("desc", dic.get("text", None))
|
|
||||||
if "_default" in keys:
|
|
||||||
keys = [key for key in keys if key != "_default"]
|
|
||||||
goto, goto_kwargs, execute, exec_kwargs = self.extract_goto_exec(nodename, dic)
|
|
||||||
self.default = (goto, goto_kwargs, execute, exec_kwargs)
|
|
||||||
else:
|
|
||||||
# use the key (only) if set, otherwise use the running number
|
|
||||||
keys = list(make_iter(dic.get("key", str(inum + 1).strip())))
|
|
||||||
goto, goto_kwargs, execute, exec_kwargs = self.extract_goto_exec(nodename, dic)
|
|
||||||
if keys:
|
|
||||||
display_options.append((keys[0], desc))
|
|
||||||
for key in keys:
|
|
||||||
if goto or execute:
|
|
||||||
self.options[strip_ansi(key).strip().lower()] = (
|
|
||||||
goto,
|
|
||||||
goto_kwargs,
|
|
||||||
execute,
|
|
||||||
exec_kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.nodetext = self._format_node(nodetext, display_options)
|
|
||||||
self.node_kwargs = kwargs
|
|
||||||
self.nodename = nodename
|
|
||||||
|
|
||||||
# handle the helptext
|
# handle the helptext
|
||||||
if helptext:
|
if helptext:
|
||||||
|
|
@ -1028,33 +904,46 @@ class EvMenu:
|
||||||
else:
|
else:
|
||||||
self.helptext = _HELP_NO_OPTIONS if self.auto_quit else _HELP_NO_OPTIONS_NO_QUIT
|
self.helptext = _HELP_NO_OPTIONS if self.auto_quit else _HELP_NO_OPTIONS_NO_QUIT
|
||||||
|
|
||||||
|
# store the current node's data in the menu state
|
||||||
|
|
||||||
|
self.nodename = nodename
|
||||||
|
self.node_kwargs = kwargs
|
||||||
|
self.options = {}
|
||||||
|
self.default = None
|
||||||
|
display_options = [] # options will be displayed in this order
|
||||||
|
|
||||||
|
if options:
|
||||||
|
options = [options] if isinstance(options, dict) else options
|
||||||
|
for inum, dic in enumerate(options):
|
||||||
|
|
||||||
|
# homogenize the options dict
|
||||||
|
keys = make_iter(dic.get("key"))
|
||||||
|
desc = dic.get("desc", dic.get("text", None))
|
||||||
|
|
||||||
|
if "_default" in keys:
|
||||||
|
keys = [key for key in keys if key != "_default"]
|
||||||
|
goto, goto_kwargs = self._extract_goto(nodename, dic)
|
||||||
|
self.default = (goto, goto_kwargs)
|
||||||
|
else:
|
||||||
|
# use the key (only) if set, otherwise use the running number
|
||||||
|
keys = list(make_iter(dic.get("key", str(inum + 1).strip())))
|
||||||
|
goto, goto_kwargs = self._extract_goto(nodename, dic)
|
||||||
|
|
||||||
|
if keys:
|
||||||
|
display_options.append((keys[0], desc))
|
||||||
|
for key in keys:
|
||||||
|
self.options[strip_ansi(key).strip().lower()] = (goto, goto_kwargs)
|
||||||
|
|
||||||
|
# format the text
|
||||||
|
self.nodetext = self._format_node(nodetext, display_options)
|
||||||
|
|
||||||
|
# display self.nodetext to the user
|
||||||
self.display_nodetext()
|
self.display_nodetext()
|
||||||
|
|
||||||
|
# close menu if we have no more options to process
|
||||||
if not options:
|
if not options:
|
||||||
self.close_menu()
|
self.close_menu()
|
||||||
|
|
||||||
def run_exec_then_goto(self, runexec, goto, raw_string, runexec_kwargs=None, goto_kwargs=None):
|
|
||||||
"""
|
|
||||||
Call 'exec' callback and goto (which may also be a callable) in sequence.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
runexec (callable or str): Callback to run before goto. If
|
|
||||||
the callback returns a string, this is used to replace
|
|
||||||
the `goto` string/callable before being passed into the goto handler.
|
|
||||||
goto (str): The target node to go to next (may be replaced
|
|
||||||
by `runexec`)..
|
|
||||||
raw_string (str): The original user input.
|
|
||||||
runexec_kwargs (dict, optional): Optional kwargs for runexec.
|
|
||||||
goto_kwargs (dict, optional): Optional kwargs for goto.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if runexec:
|
|
||||||
# replace goto only if callback returns
|
|
||||||
goto, goto_kwargs = self.run_exec(
|
|
||||||
runexec, raw_string, **(runexec_kwargs if runexec_kwargs else {})
|
|
||||||
) or (goto, goto_kwargs)
|
|
||||||
if goto:
|
|
||||||
self.goto(goto, raw_string, **(goto_kwargs if goto_kwargs else {}))
|
|
||||||
|
|
||||||
def close_menu(self):
|
def close_menu(self):
|
||||||
"""
|
"""
|
||||||
Shutdown menu; occurs when reaching the end node or using the quit command.
|
Shutdown menu; occurs when reaching the end node or using the quit command.
|
||||||
|
|
@ -1131,9 +1020,27 @@ class EvMenu:
|
||||||
)
|
)
|
||||||
self.msg(debugtxt)
|
self.msg(debugtxt)
|
||||||
|
|
||||||
|
def msg(self, txt):
|
||||||
|
"""
|
||||||
|
This is a central point for sending return texts to the caller. It
|
||||||
|
allows for a central point to add custom messaging when creating custom
|
||||||
|
EvMenu overrides.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
txt (str): The text to send.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
By default this will send to the same session provided to EvMenu
|
||||||
|
(if `session` kwarg was provided to `EvMenu.__init__`). It will
|
||||||
|
also send it with a `type=menu` for the benefit of OOB/webclient.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.caller.msg(text=(txt, {"type": "menu"}), session=self._session)
|
||||||
|
|
||||||
def parse_input(self, raw_string):
|
def parse_input(self, raw_string):
|
||||||
"""
|
"""
|
||||||
Parses the incoming string from the menu user.
|
Parses the incoming string from the menu user. This is the entry-point for all input
|
||||||
|
into the menu.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
raw_string (str): The incoming, unmodified string
|
raw_string (str): The incoming, unmodified string
|
||||||
|
|
@ -1144,14 +1051,15 @@ class EvMenu:
|
||||||
should also report errors directly to the user.
|
should also report errors directly to the user.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# this is the input cmd given to the menu
|
||||||
cmd = strip_ansi(raw_string.strip().lower())
|
cmd = strip_ansi(raw_string.strip().lower())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.options and cmd in self.options:
|
if self.options and cmd in self.options:
|
||||||
# this will take precedence over the default commands
|
# we chose one of the available options; this
|
||||||
# below
|
# will take precedence over the default commands
|
||||||
goto, goto_kwargs, execfunc, exec_kwargs = self.options[cmd]
|
goto_node, goto_kwargs = self.options[cmd]
|
||||||
self.run_exec_then_goto(execfunc, goto, raw_string, exec_kwargs, goto_kwargs)
|
self.goto(goto_node, raw_string, **(goto_kwargs or {}))
|
||||||
elif self.auto_look and cmd in ("look", "l"):
|
elif self.auto_look and cmd in ("look", "l"):
|
||||||
self.display_nodetext()
|
self.display_nodetext()
|
||||||
elif self.auto_help and cmd in ("help", "h"):
|
elif self.auto_help and cmd in ("help", "h"):
|
||||||
|
|
@ -1161,8 +1069,8 @@ class EvMenu:
|
||||||
elif self.debug_mode and cmd.startswith("menudebug"):
|
elif self.debug_mode and cmd.startswith("menudebug"):
|
||||||
self.print_debug_info(cmd[9:].strip())
|
self.print_debug_info(cmd[9:].strip())
|
||||||
elif self.default:
|
elif self.default:
|
||||||
goto, goto_kwargs, execfunc, exec_kwargs = self.default
|
goto_node, goto_kwargs = self.default
|
||||||
self.run_exec_then_goto(execfunc, goto, raw_string, exec_kwargs, goto_kwargs)
|
self.goto(goto_node, raw_string, **(goto_kwargs or {}))
|
||||||
else:
|
else:
|
||||||
self.msg(_HELP_NO_OPTION_MATCH)
|
self.msg(_HELP_NO_OPTION_MATCH)
|
||||||
except EvMenuGotoAbortMessage as err:
|
except EvMenuGotoAbortMessage as err:
|
||||||
|
|
@ -1354,7 +1262,7 @@ def list_node(option_generator, select=None, pagesize=10):
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
All normal `goto` or `exec` callables returned from the decorated nodes
|
All normal `goto` callables returned from the decorated nodes
|
||||||
will, if they accept `**kwargs`, get a new kwarg 'available_choices'
|
will, if they accept `**kwargs`, get a new kwarg 'available_choices'
|
||||||
injected. These are the ordered list of named options (descs) visible
|
injected. These are the ordered list of named options (descs) visible
|
||||||
on the current node page.
|
on the current node page.
|
||||||
|
|
@ -1486,7 +1394,7 @@ def list_node(option_generator, select=None, pagesize=10):
|
||||||
if isinstance(decorated_options, dict):
|
if isinstance(decorated_options, dict):
|
||||||
decorated_options = [decorated_options]
|
decorated_options = [decorated_options]
|
||||||
for eopt in decorated_options:
|
for eopt in decorated_options:
|
||||||
cback = ("goto" in eopt and "goto") or ("exec" in eopt and "exec") or None
|
cback = ("goto" in eopt and "goto") or None
|
||||||
if cback:
|
if cback:
|
||||||
signature = eopt[cback]
|
signature = eopt[cback]
|
||||||
if callable(signature):
|
if callable(signature):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue