evennia.utils.evmenu¶
EvMenu
This implements a full menu system for Evennia.
To start the menu, just import the EvMenu class from this module. Example usage:
from evennia.utils.evmenu import EvMenu
EvMenu(caller, menu_module_path,
startnode="node1",
cmdset_mergetype="Replace", cmdset_priority=1,
auto_quit=True, cmd_on_exit="look", persistent=True)
Where caller is the Object to use the menu on - it will get a new cmdset while using the Menu. The menu_module_path is the python path to a python module containing function definitions. By adjusting the keyword options of the Menu() initialization call you can start the menu at different places in the menu definition file, adjust if the menu command should overload the normal commands or not, etc.
The persistent keyword will make the menu survive a server reboot. It is False by default. Note that if using persistent mode, every node and callback in the menu must be possible to be pickled, this excludes e.g. callables that are class methods or functions defined dynamically or as part of another function. In non-persistent mode no such restrictions exist.
The menu is defined in a module (this can be the same module as the command definition too) with function definitions:
def node1(caller):
# (this is the start node if called like above)
# code
return text, options
def node_with_other_name(caller, input_string):
# code
return text, options
def another_node(caller, input_string, **kwargs):
# code
return text, options
Where caller is the object using the menu and input_string is the command entered by the user on the previous node (the command entered to get to this node). The node function code will only be executed once per node-visit and the system will accept nodes with both one or two arguments interchangeably. It also accepts nodes that takes **kwargs.
The menu tree itself is available on the caller as caller.ndb._menutree. This makes it a convenient place to store temporary state variables between nodes, since this NAttribute is deleted when the menu is exited.
The return values must be given in the above order, but each can be returned as None as well. If the options are returned as None, the menu is immediately exited and the default “look” command is called.
- text (str, tuple or None): Text shown at this node. If a tuple, the
second element in the tuple is a help text to display at this node when the user enters the menu help command there.
options (tuple, dict or None): If None, this exits the menu. If a single dict, this is a single-option node. If a tuple, it should be a tuple of option dictionaries. Option dicts have the following keys:
key (str or tuple, optional): What to enter to choose this option. If a tuple, it must be a tuple of strings, where the first string is the key which will be shown to the user and the others are aliases. If unset, the options’ number will be used. The special key _default marks this option as the default fallback when no other option matches the user input. There can only be one _default option per node. It will not be displayed in the list.
desc (str, optional): This describes what choosing the option will do.
goto (str, tuple or callable): If string, should be the name of node to go to when this option is selected. If a callable, it has the signature callable(caller[,raw_input][,**kwargs]). If a tuple, the first element is the callable and the second is a dict with the **kwargs to pass to the callable. Those kwargs will also be passed into the next node if possible. Such a callable should return either a str or a (str, dict), where the 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 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 its number 1..N.
Example:
# in menu_module.py
def node1(caller):
text = ("This is a node text",
"This is help text for this node")
options = ({"key": "testing",
"desc": "Select this to go to node 2",
"goto": ("node2", {"foo": "bar"}),
"exec": "callback1"},
{"desc": "Go to node 3.",
"goto": "node3"})
return text, options
def callback1(caller):
# this is called when choosing the "testing" option in node1
# (before going to node2). If it returned a string, say 'node3',
# then the next node would be node3 instead of node2 as specified
# by the normal 'goto' option key above.
caller.msg("Callback called!")
def node2(caller, **kwargs):
text = '''
This is node 2. It only allows you to go back
to the original node1. This extra indent will
be stripped. We don't include a help text but
here are the variables passed to us: {}
'''.format(kwargs)
options = {"goto": "node1"}
return text, options
def node3(caller):
text = "This ends the menu since there are no options."
return text, None
When starting this menu with Menu(caller, “path.to.menu_module”), the first node will look something like this:
This is a node text
______________________________________
testing: Select this to go to node 2
2: Go to node 3
Where you can both enter “testing” and “1” to select the first option. If the client supports MXP, they may also mouse-click on “testing” to do the same. When making this selection, a function “callback1” in the same Using help will show the help text, otherwise a list of available commands while in menu mode.
The menu tree is exited either by using the in-menu quit command or by reaching a node without any options.
For a menu demo, import CmdTestMenu from this module and add it to your default cmdset. Run it with this module, like testmenu evennia.utils.evmenu.
-
exception
evennia.utils.evmenu.EvMenuError[source]¶ Bases:
RuntimeErrorError raised by menu when facing internal errors.
-
class
evennia.utils.evmenu.CmdEvMenuNode(**kwargs)[source]¶ Bases:
evennia.commands.command.CommandMenu options.
-
key= '__noinput_command'¶
-
aliases= ['__nomatch_command']¶
-
locks= 'cmd:all()'¶
-
help_category= 'menu'¶
-
lock_storage= 'cmd:all()'¶
-
search_index_entry= {'aliases': '__nomatch_command', 'category': 'menu', 'key': '__noinput_command', 'tags': '', 'text': '\n Menu options.\n '}¶
-
-
class
evennia.utils.evmenu.EvMenuCmdSet(cmdsetobj=None, key=None)[source]¶ Bases:
evennia.commands.cmdset.CmdSetThe Menu cmdset replaces the current cmdset.
-
key= 'menu_cmdset'¶
-
priority= 1¶
-
mergetype= 'Replace'¶
-
no_objs= True¶
-
no_exits= True¶
-
no_channels= False¶
-
path= 'evennia.utils.evmenu.EvMenuCmdSet'¶
-
-
class
evennia.utils.evmenu.EvMenu(caller, menudata, startnode='start', cmdset_mergetype='Replace', cmdset_priority=1, auto_quit=True, auto_look=True, auto_help=True, cmd_on_exit='look', persistent=False, startnode_input='', session=None, debug=False, **kwargs)[source]¶ Bases:
objectThis object represents an operational menu. It is initialized from a menufile.py instruction.
-
node_border_char= '_'¶
-
__init__(caller, menudata, startnode='start', cmdset_mergetype='Replace', cmdset_priority=1, auto_quit=True, auto_look=True, auto_help=True, cmd_on_exit='look', persistent=False, startnode_input='', session=None, debug=False, **kwargs)[source]¶ Initialize the menu tree and start the caller onto the first node.
- Parameters
caller (Object, Account or Session) – The user of the menu.
menudata (str, module or dict) – The full or relative path to the module holding the menu tree data. All global functions in this module whose name doesn’t start with ‘_ ‘ will be parsed as menu nodes. Also the module itself is accepted as input. Finally, a dictionary menu tree can be given directly. This must then be a mapping {“nodekey”:callable,…} where callable must be called as and return the data expected of a menu node. This allows for dynamic menu creation.
startnode (str, optional) – The starting node name in the menufile.
cmdset_mergetype (str, optional) – ‘Replace’ (default) means the menu commands will be exclusive - no other normal commands will be usable while the user is in the menu. ‘Union’ means the menu commands will be integrated with the existing commands (it will merge with merge_priority), if so, make sure that the menu’s command names don’t collide with existing commands in an unexpected way. Also the CMD_NOMATCH and CMD_NOINPUT will be overloaded by the menu cmdset. Other cmdser mergetypes has little purpose for the menu.
cmdset_priority (int, optional) – The merge priority for the menu command set. The default (1) is usually enough for most types of menus.
auto_quit (bool, optional) – Allow user to use “q”, “quit” or “exit” to leave the menu at any point. Recommended during development!
auto_look (bool, optional) – Automatically make “looK” or “l” to re-show the last node. Turning this off means you have to handle re-showing nodes yourself, but may be useful if you need to use “l” for some other purpose.
auto_help (bool, optional) – Automatically make “help” or “h” show the current help entry for the node. If turned off, eventual help must be handled manually, but it may be useful if you need ‘h’ for some other purpose, for example.
cmd_on_exit (callable, str or None, optional) – When exiting the menu (either by reaching a node with no options or by using the in-built quit command (activated with allow_quit), this callback function or command string will be executed. The callback function takes two parameters, the caller then the EvMenu object. This is called after cleanup is complete. Set to None to not call any command.
persistent (bool, optional) – Make the Menu persistent (i.e. it will survive a reload. This will make the Menu cmdset persistent. Use with caution - if your menu is buggy you may end up in a state you can’t get out of! Also note that persistent mode requires that all formatters, menu nodes and callables are possible to pickle. When the server is reloaded, the latest node shown will be completely re-run with the same input arguments - so be careful if you are counting up some persistent counter or similar - the counter may be run twice if reload happens on the node that does that. Note that if debug is True, this setting is ignored and assumed to be False.
startnode_input (str or (str, dict), optional) – Send an input text to startnode as if a user input text from a fictional previous node. If including the dict, this will be passed as kwargs to that node. When the server reloads, the latest visited node will be re-run as **node(caller, raw_string, **kwargs).
session (Session, optional) – This is useful when calling EvMenu from an account in multisession mode > 2. Note that this session only really relevant for the very first display of the first node - after that, EvMenu itself will keep the session updated from the command input. So a persistent menu will not be using this same session anymore after a reload.
debug (bool, optional) – If set, the ‘menudebug’ command will be made available by default in all nodes of the menu. This will print out the current state of the menu. Deactivate for production use! When the debug flag is active, the persistent flag is deactivated.
**kwargs – All kwargs will become initialization variables on caller.ndb._menutree, to be available at run.
- Raises
EvMenuError – If the start/end node is not found in menu tree.
Notes
While running, the menu is stored on the caller as caller.ndb._menutree. Also the current Session (from the Command, so this is still valid in multisession environments) is available through caller.ndb._menutree._session. The _menutree property is a good one for storing intermediary data on between nodes since it will be automatically deleted when the menu closes.
In persistent mode, all nodes, formatters and callbacks in the menu must be possible to be pickled, this excludes e.g. callables that are class methods or functions defined dynamically or as part of another function. In non-persistent mode no such restrictions exist.
-
run_exec(nodename, raw_string, **kwargs)[source]¶ Run a function or node as a callback (with the ‘exec’ option key).
- Parameters
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.
-
extract_goto_exec(nodename, option_dict)[source]¶ Helper: Get callables and their eventual kwargs.
- Parameters
nodename (str) – The current node name (used for error reporting).
option_dict (dict) – The seleted option’s dict.
- Returns
goto (str, callable or None) – The goto directive in the option. 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(nodename, raw_string, **kwargs)[source]¶ Run a node by name, optionally dynamically generating that name first.
- Parameters
nodename (str or callable) – Name of node or a callable to be called as function(caller, raw_string, **kwargs) or function(caller, **kwargs) to return the actual goto string or a (“nodename”, kwargs) tuple.
raw_string (str) – The raw default string entered on the previous node (only used if the node accepts it as an argument)
**kwargs – Extra arguments to goto callables.
-
run_exec_then_goto(runexec, goto, raw_string, runexec_kwargs=None, goto_kwargs=None)[source]¶ Call ‘exec’ callback and goto (which may also be a callable) in sequence.
- Parameters
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.
-
print_debug_info(arg)[source]¶ Messages the caller with the current menu state, for debug purposes.
- Parameters
arg (str) – Arg to debug instruction, either nothing, ‘full’ or the name of a property to inspect.
-
parse_input(raw_string)[source]¶ Parses the incoming string from the menu user.
- Parameters
raw_string (str) – The incoming, unmodified string from the user.
Notes
This method is expected to parse input and use the result to relay execution to the relevant methods of the menu. It should also report errors directly to the user.
-
nodetext_formatter(nodetext)[source]¶ Format the node text itself.
- Parameters
nodetext (str) – The full node text (the text describing the node).
- Returns
nodetext (str) – The formatted node text.
-
helptext_formatter(helptext)[source]¶ Format the node’s help text
- Parameters
helptext (str) – The unformatted help text for the node.
- Returns
helptext (str) – The formatted help text.
-
options_formatter(optionlist)[source]¶ Formats the option block.
- Parameters
optionlist (list) – List of (key, description) tuples for every option related to this node.
caller (Object, Account or None, optional) – The caller of the node.
- Returns
options (str) – The formatted option display.
-
node_formatter(nodetext, optionstext)[source]¶ Formats the entirety of the node.
- Parameters
nodetext (str) – The node text as returned by self.nodetext_formatter.
optionstext (str) – The options display as returned by self.options_formatter.
caller (Object, Account or None, optional) – The caller of the node.
- Returns
node (str) – The formatted node to display.
-
-
evennia.utils.evmenu.list_node(option_generator, select=None, pagesize=10)[source]¶ Decorator for making an EvMenu node into a multi-page list node. Will add new options, prepending those options added in the node.
- Parameters
option_generator (callable or list) – A list of strings indicating the options, or a callable that is called as option_generator(caller) to produce such a list.
select (callable or str, optional) – Node to redirect a selection to. Its **kwargs will contain the available_choices list and selection will hold one of the elements in that list. If a callable, it will be called as select(caller, menuchoice, **kwargs) where menuchoice is the chosen option as a string and available_choices is a kwarg mapping the option keys to the choices offered by the option_generator. The callable whould return the name of the target node to goto after this selection (or None to repeat the list-node). Note that if this is not given, the decorated node must itself provide a way to continue from the node!
pagesize (int) – How many options to show per page.
Example
list_node(['foo', 'bar'], select) def node_index(caller): text = "describing the list" return text, []
Notes
All normal goto or exec callables returned from the decorated nodes will, if they accept **kwargs, get a new kwarg ‘available_choices’ injected. These are the ordered list of named options (descs) visible on the current node page.
-
class
evennia.utils.evmenu.CmdGetInput(**kwargs)[source]¶ Bases:
evennia.commands.command.CommandEnter your data and press return.
-
key= '__nomatch_command'¶
-
aliases= ['__noinput_command']¶
-
help_category= 'general'¶
-
lock_storage= 'cmd:all();'¶
-
search_index_entry= {'aliases': '__noinput_command', 'category': 'general', 'key': '__nomatch_command', 'tags': '', 'text': '\n Enter your data and press return.\n '}¶
-
-
class
evennia.utils.evmenu.InputCmdSet(cmdsetobj=None, key=None)[source]¶ Bases:
evennia.commands.cmdset.CmdSetThis stores the input command
-
key= 'input_cmdset'¶
-
priority= 1¶
-
mergetype= 'Replace'¶
-
no_objs= True¶
-
no_exits= True¶
-
no_channels= False¶
-
path= 'evennia.utils.evmenu.InputCmdSet'¶
-
-
evennia.utils.evmenu.get_input(caller, prompt, callback, session=None, *args, **kwargs)[source]¶ This is a helper function for easily request input from the caller.
- Parameters
caller (Account or Object) – The entity being asked the question. This should usually be an object controlled by a user.
prompt (str) – This text will be shown to the user, in order to let them know their input is needed.
callback (callable) – A function that will be called when the user enters a reply. It must take three arguments: the caller, the prompt text and the result of the input given by the user. If the callback doesn’t return anything or return False, the input prompt will be cleaned up and exited. If returning True, the prompt will remain and continue to accept input.
session (Session, optional) – This allows to specify the session to send the prompt to. It’s usually only needed if caller is an Account in multisession modes greater than 2. The session is then updated by the command and is available (for example in callbacks) through caller.ndb.getinput._session.
*args (any) – Extra arguments to pass to callback. To utilise *args (and **kwargs), a value for the session argument must also be provided.
**kwargs (any) – Extra kwargs to pass to callback.
- Raises
RuntimeError – If the given callback is not callable.
Notes
The result value sent to the callback is raw and not processed in any way. This means that you will get the ending line return character from most types of client inputs. So make sure to strip that before doing a comparison.
When the prompt is running, a temporary object caller.ndb._getinput is stored; this will be removed when the prompt finishes.
If you need the specific Session of the caller (which may not be easy to get if caller is an account in higher multisession modes), then it is available in the callback through caller.ndb._getinput._session. This is why the session is required as input.
It’s not recommended to ‘chain’ get_input into a sequence of questions. This will result in the caller stacking ever more instances of InputCmdSets. While they will all be cleared on concluding the get_input chain, EvMenu should be considered for anything beyond a single question.
-
class
evennia.utils.evmenu.CmdTestMenu(**kwargs)[source]¶ Bases:
evennia.commands.command.CommandTest menu
- Usage:
testmenu <menumodule>
Starts a demo menu from a menu node definition module.
-
aliases= []¶
-
help_category= 'general'¶
-
key= 'testmenu'¶
-
lock_storage= 'cmd:all();'¶
-
search_index_entry= {'aliases': '', 'category': 'general', 'key': 'testmenu', 'tags': '', 'text': '\n Test menu\n\n Usage:\n testmenu <menumodule>\n\n Starts a demo menu from a menu node definition module.\n\n '}¶