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._evmenu. 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 holds either a string or a dict. If a string, this is the help text to show when auto_help is active for the menu and the user presses h. If a dict, this is a mapping of ‘help topic’: ‘help text’ to show in that menu. This can be used to show information without having to switch to another node.
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.
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"}),
{"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.
Menu generation from template string¶
In evmenu.py is a helper function parse_menu_template that parses a template-string and outputs a menu-tree dictionary suitable to pass into EvMenu:
menutree = evmenu.parse_menu_template(caller, menu_template, goto_callables)
EvMenu(caller, menutree)
For maximum flexibility you can inject normally-created nodes in the menu tree before passing it to EvMenu. If that’s not needed, you can also create a menu in one step with:
evmenu.template2menu(caller, menu_template, goto_callables)
The goto_callables is a mapping {“funcname”: callable, …}, where each callable must be a module-global function on the form funcname(caller, raw_string, **kwargs) (like any goto-callable). The menu_template is a multi-line string on the following form:
## node start
This is the text of the start node.
The text area can have multiple lines, line breaks etc.
Each option below is one of these forms
key: desc -> gotostr_or_func
key: gotostr_or_func
>: gotostr_or_func
> glob/regex: gotostr_or_func
## options
# comments are only allowed from beginning of line.
# Indenting is not necessary, but good for readability
1: Option number 1 -> node1
2: Option number 2 -> node2
next: This steps next -> go_back()
# the -> can be ignored if there is no desc
back: go_back(from_node=start)
abort: abort
## node node1
Text for Node1. Enter a message!
<return> to go back.
## options
# Beginning the option-line with >
# allows to perform different actions depending on
# what is inserted.
# this catches everything starting with foo
> foo*: handle_foo_message()
# regex are also allowed (this catches number inputs)
> [0-9]+?: handle_numbers()
# this catches the empty return
>: start
# this catches everything else
> *: handle_message(from_node=node1)
## node node2
Text for Node2. Just go back.
## options
>: start
## node abort
This exits the menu since there is no **## options** section.
Each menu node is defined by a # node <name> containing the text of the node, followed by ## options Also ## NODE and ## OPTIONS work. No python code logics is allowed in the template, this code is not evaluated but parsed. More advanced dynamic usage requires a full node-function (which can be added to the generated dict, as said).
Adding (..) to a goto treats it as a callable and it must then be included in the goto_callable mapping. Only named keywords (or no args at all) are allowed, these will be added to the **kwargs going into the callable. Quoting strings is only needed if wanting to pass strippable spaces, otherwise the key:values will be converted to strings/numbers with literal_eval before passed into the callable.
The > option takes a glob or regex to perform different actions depending on user input. Make sure to sort these in increasing order of generality since they will be tested in sequence.
-
exception
evennia.utils.evmenu.
EvMenuError
[source]¶ Bases:
RuntimeError
Error raised by menu when facing internal errors.
-
exception
evennia.utils.evmenu.
EvMenuGotoAbortMessage
[source]¶ Bases:
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.”)
-
class
evennia.utils.evmenu.
CmdEvMenuNode
(**kwargs)[source]¶ Bases:
evennia.commands.command.Command
Command to handle all user input targeted at the menu while the menu is active.
-
key
= '__noinput_command'¶
-
aliases
= ['__nomatch_command']¶
-
locks
= 'cmd:all()'¶
-
help_category
= 'menu'¶
-
auto_help_display_key
= '<menu commands>'¶
-
get_help
()[source]¶ Return the help message for this command and this caller.
By default, return self.__doc__ (the docstring just under the class definition). You can override this behavior, though, and even customize it depending on the caller, or other commands the caller can use.
- Parameters
caller (Object or Account) – the caller asking for help on the command.
cmdset (CmdSet) – the command set (if you need additional commands).
- Returns
docstring (str) – the help text to provide the caller for this command.
-
lock_storage
= 'cmd:all()'¶
-
search_index_entry
= {'aliases': '__nomatch_command', 'category': 'menu', 'key': '__noinput_command', 'no_prefix': ' __nomatch_command', 'tags': '', 'text': '\n Command to handle all user input targeted at the menu while the menu is active.\n\n '}¶
-
-
class
evennia.utils.evmenu.
EvMenuCmdSet
(cmdsetobj=None, key=None)[source]¶ Bases:
evennia.commands.cmdset.CmdSet
The 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:
object
This 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’ does merge the menu command, but note that the only command used in EvMenu has key/alias of NOINPUT/NOMATCH. So if you merge with ‘Union’ and a high cmdset_prio (below), you won’t replace individual normal commands as you may expect. Instead commands will work normally and you’ll only always fall back to menu commands when no other command is found. There is no way to partially replace normal commands with EvMenu actions - to do this, remove the normal command from the caller’s cmdset - if not found the menu’s version will kick in instead.
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._evmenu, 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._evmenu. Also the current Session (from the Command, so this is still valid in multisession environments) is available through caller.ndb._evmenu._session. The _evmenu 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.
-
goto
(nodename_or_callable, raw_string, **kwargs)[source]¶ Run a node by name, optionally dynamically generating that name first.
- Parameters
nodename_or_callable (str or callable) – Name of node or a callable to be called as function(caller, raw_string, **kwargs) or function(caller, **kwargs). This callable must return the node-name (str) pointing to the next node.
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.
-
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.
-
msg
(txt)[source]¶ 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.
- Parameters
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.
-
parse_input
(raw_string)[source]¶ Parses the incoming string from the menu user. This is the entry-point for all input into the menu.
- 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.
- 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
def select(caller, selection, available_choices=None, **kwargs): ''' This will be called by all auto-generated options except any 'extra_options' you return from the node (those you need to handle normally). Args: caller (Object or Account): User of the menu. selection (str): What caller chose in the menu available_choices (list): The keys of elements available on the *current listing page*. **kwargs: Kwargs passed on from the node. Returns: tuple, str or None: A tuple (nextnodename, **kwargs) or just nextnodename. Return **None** to go back to the listnode. ''' # (do something with **selection** here) return "nextnode", **kwargs @list_node(['foo', 'bar'], select) def node_index(caller): text = "describing the list" # optional extra options in addition to the list-options extra_options = [] return text, extra_options
Notes
All normal goto 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.Command
Enter 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', 'no_prefix': ' __noinput_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.CmdSet
This 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.
CmdYesNoQuestion
(**kwargs)[source]¶ Bases:
evennia.commands.command.Command
Handle a prompt for yes or no. Press [return] for the default choice.
-
key
= '__noinput_command'¶
-
aliases
= ['no', 'yes', 'y', 'a', '__nomatch_command', 'n', 'abort']¶
-
arg_regex
= re.compile('^$', re.IGNORECASE)¶
-
help_category
= 'general'¶
-
lock_storage
= 'cmd:all();'¶
-
search_index_entry
= {'aliases': 'no yes y a __nomatch_command n abort', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' no yes y a __nomatch_command n abort', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}¶
-
-
class
evennia.utils.evmenu.
YesNoQuestionCmdSet
(cmdsetobj=None, key=None)[source]¶ Bases:
evennia.commands.cmdset.CmdSet
This stores the input command
-
key
= 'yes_no_question_cmdset'¶
-
priority
= 1¶
-
mergetype
= 'Replace'¶
-
no_objs
= True¶
-
no_exits
= True¶
-
no_channels
= False¶
-
path
= 'evennia.utils.evmenu.YesNoQuestionCmdSet'¶
-
-
evennia.utils.evmenu.
ask_yes_no
(caller, prompt='Yes or No {options}?', yes_action='Yes', no_action='No', default=None, allow_abort=False, session=None, *args, **kwargs)[source]¶ A helper function for asking a simple yes/no question. This will cause the system to pause and wait for input from the player.
- Parameters
caller (Object) – The entity being asked.
prompt (str) – The yes/no question to ask. This takes an optional formatting marker {options} which will be filled with ‘Y/N’, ‘[Y]/N’ or ‘Y/[N]’ depending on the setting of default. If allow_abort is set, then the ‘A(bort)’ option will also be available.
yes_action (callable or str) – If a callable, this will be called with (caller, *args, **kwargs) when the Yes-choice is made. If a string, this string will be echoed back to the caller.
no_action (callable or str) – If a callable, this will be called with (caller, *args, **kwargs) when the No-choice is made. If a string, this string will be echoed back to the caller.
default (str optional) – This is what the user will get if they just press the return key without giving any input. One of ‘N’, ‘Y’, ‘A’ or None for no default (an explicit choice must be given). If ‘A’ (abort) is given, allow_abort kwarg is ignored and assumed set.
allow_abort (bool, optional) – If set, the ‘A(bort)’ option is available (a third option meaning neither yes or no but just exits the prompt).
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._yes_no_question.session.
*args – Additional arguments passed on into callables.
**kwargs – Additional keyword args passed on into callables.
- Raises
RuntimeError, FooError – If default and allow_abort clashes.
Example
# just returning strings ask_yes_no(caller, "Are you happy {options}?", "you answered yes", "you answered no") # trigger callables ask_yes_no(caller, "Are you sad {options}?", _callable_yes, _callable_no, allow_abort=True)
-
evennia.utils.evmenu.
parse_menu_template
(caller, menu_template, goto_callables=None)[source]¶ Parse menu-template string. The main function of the EvMenu templating system.
- Parameters
caller (Object or Account) – Entity using the menu.
menu_template (str) – Menu described using the templating format.
goto_callables (dict, optional) – Mapping between call-names and callables on the form callable(caller, raw_string, **kwargs). These are what is available to use in the menu_template string.
- Returns
dict – A {“node”: nodefunc} menutree suitable to pass into EvMenu.
-
evennia.utils.evmenu.
template2menu
(caller, menu_template, goto_callables=None, startnode='start', persistent=False, **kwargs)[source]¶ Helper function to generate and start an EvMenu based on a menu template string. This will internall call parse_menu_template and run a default EvMenu with its results.
- Parameters
caller (Object or Account) – The entity using the menu.
menu_template (str) – The menu-template string describing the content and structure of the menu. It can also be the python-path to, or a module containing a MENU_TEMPLATE global variable with the template.
goto_callables (dict, optional) – Mapping of callable-names to module-global objects to reference by name in the menu-template. Must be on the form callable(caller, raw_string, **kwargs).
startnode (str, optional) – The name of the startnode, if not ‘start’.
persistent (bool, optional) – If the generated menu should be persistent.
**kwargs – All kwargs will be passed into EvMenu.
- Returns
EvMenu – The generated EvMenu.