From 1746aaf06b36955acf144638f5669654e59fdd7b Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 4 Oct 2020 21:26:07 +0200 Subject: [PATCH] Final unit tests for evmenu templating --- evennia/contrib/tutorial_world/intro_menu.py | 33 +++++++---- evennia/utils/evmenu.py | 61 ++++++++++++++------ evennia/utils/tests/test_evmenu.py | 13 +++++ 3 files changed, 77 insertions(+), 30 deletions(-) diff --git a/evennia/contrib/tutorial_world/intro_menu.py b/evennia/contrib/tutorial_world/intro_menu.py index 4dcb68281..c36e2ce57 100644 --- a/evennia/contrib/tutorial_world/intro_menu.py +++ b/evennia/contrib/tutorial_world/intro_menu.py @@ -290,10 +290,20 @@ def goto_command_demo_room(caller, raw_string, **kwargs): _maintain_demo_room(caller) caller.cmdset.remove(DemoCommandSetHelp) caller.cmdset.remove(DemoCommandSetComms) - caller.cmdset.add(DemoCommandSetRoom) # TODO - make persistent + caller.cmdset.add(DemoCommandSetRoom) return "command_demo_room" +def goto_cleanup_cmdsets(caller, raw_strings, **kwargs): + """ + Cleanup all cmdsets. + """ + caller.cmdset.remove(DemoCommandSetHelp) + caller.cmdset.remove(DemoCommandSetComms) + caller.cmdset.remove(DemoCommandSetRoom) + return kwargs.get("gotonode") + + # register all callables that can be used in the menu template GOTO_CALLABLES = { @@ -302,6 +312,7 @@ GOTO_CALLABLES = { "goto_command_demo_help": goto_command_demo_help, "goto_command_demo_comms": goto_command_demo_comms, "goto_command_demo_room": goto_command_demo_room, + "goto_cleanup_cmdsets": goto_cleanup_cmdsets, } @@ -365,7 +376,7 @@ gaming style you like and possibly any new ones you can come up with! ## OPTIONS next;n: About Evennia -> about_evennia - back to start;start;t: start + back to start;back;start;t: start >: about_evennia # --------------------------------------------------------------------------------- @@ -498,7 +509,7 @@ those channels ... ## OPTIONS next;n: Talk on Channels -> talk on channels - back;b: Using the webclient -> using webclient + back;b: Using the webclient -> goto_cleanup_cmdsets(gotonode='using webclient') back to start;start: start >: talk on channels @@ -556,7 +567,7 @@ include other people/objects in the emote, reference things by a short-descripti ## OPTIONS next;n: Paging people -> paging_people - back;b: Talk on Channels -> talk on channels + back;b: Talk on Channels -> goto_command_demo_help(gotonode='talk on channels') back to start;start: start >: paging_people @@ -627,7 +638,7 @@ color codes printed, try ## OPTIONS next;n: Moving and Exploring -> goto_command_demo_room() - back;b: Paging people -> paging_people + back;b: Paging people -> goto_command_demo_comms(gotonode='paging_people') back to start;start: start >: goto_command_demo_room() @@ -650,7 +661,7 @@ around in. Explore a little and use |ynext|n when you are done. ## OPTIONS next;n: Conclusions -> conclusions - back;b: Channel commands -> testing_colors + back;b: Channel commands -> goto_command_demo_comms(gotonode='testing_colors') back to start;start: start >: conclusions @@ -667,13 +678,11 @@ if you get stuck! Write |ynext|n to end this wizard and continue to the tutorial-world quest! If you want there is also some |wextra|n info for where to go beyond that. -Good luck! - ## OPTIONS - extra: Some more help on where to go next -> post scriptum - next;next;n: end - back;b: goto_command_demo_room() + extra: Where to go next -> post scriptum + next;next;n: End -> end + back;b: Moving and Exploring -> goto_command_demo_room() back to start;start: start >: end @@ -718,7 +727,7 @@ back: conclusions ## NODE end -Thanks for trying out the tutorial! +|gGood luck!|n """ diff --git a/evennia/utils/evmenu.py b/evennia/utils/evmenu.py index 73feafee6..cbdce8241 100644 --- a/evennia/utils/evmenu.py +++ b/evennia/utils/evmenu.py @@ -1634,7 +1634,7 @@ _RE_NODE = re.compile(r"##\s*?NODE\s+?(?P\S[\S\s]*?)$", re.I + re.M) _RE_OPTIONS_SEP = re.compile(r"##\s*?OPTIONS\s*?$", re.I + re.M) _RE_CALLABLE = re.compile(r"\S+?\(\)", re.I + re.M) _RE_CALLABLE = re.compile( - r"(?P\S+?)(?:\((?P[\S\s]+?=[\S\s]+?)\)|\(\))", re.I + re.M + r"(?P\S+?)(?:\((?P[\S\s]+?)\)|\(\))", re.I + re.M ) _HELP_NO_OPTION_MATCH = _("Choose an option or try 'help'.") @@ -1664,23 +1664,23 @@ def _process_callable(caller, goto, goto_callables, raw_string, 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)] - if key in ("evmenu_goto", "evmenu_gotomap", "_current_nodename", - "evmenu_current_nodename", "evmenu_goto_callables"): - raise RuntimeError( - f"EvMenu template error: goto-callable '{goto}' uses a " - f"kwarg ({key}) that is reserved for the EvMenu templating " - "system. Rename the kwarg.") - try: - key = literal_eval(key) - except ValueError: - pass - try: - value = literal_eval(value) - except ValueError: - pass - kwargs[key] = value + key, value = [part.strip() for part in kwarg.split("=", 1)] + if key in ("evmenu_goto", "evmenu_gotomap", "_current_nodename", + "evmenu_current_nodename", "evmenu_goto_callables"): + raise RuntimeError( + f"EvMenu template error: goto-callable '{goto}' uses a " + f"kwarg ({kwarg}) that is reserved for the EvMenu templating " + "system. Rename the kwarg.") + 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} @@ -1755,6 +1755,23 @@ def parse_menu_template(caller, menu_template, goto_callables=None): dict: A `{"node": nodefunc}` menutree suitable to pass into EvMenu. """ + def _validate_kwarg(goto, kwarg): + """ + Validate goto-callable kwarg is on correct form. + """ + if not "=" in kwarg: + raise RuntimeError( + f"EvMenu template error: goto-callable '{goto}' has a " + f"non-kwarg argument ({kwarg}). All callables in the " + "template must have only keyword-arguments, or no " + "args at all.") + key, _ = [part.strip() for part in kwarg.split("=", 1)] + if key in ("evmenu_goto", "evmenu_gotomap", "_current_nodename", + "evmenu_current_nodename", "evmenu_goto_callables"): + raise RuntimeError( + f"EvMenu template error: goto-callable '{goto}' uses a " + f"kwarg ({kwarg}) that is reserved for the EvMenu templating " + "system. Rename the kwarg.") def _parse_options(nodename, optiontxt, goto_callables): """ @@ -1779,6 +1796,14 @@ def parse_menu_template(caller, menu_template, goto_callables=None): if _OPTION_CALL_MARKER in goto: desc, goto = [part.strip() for part in goto.split(_OPTION_CALL_MARKER, 1)] + # validate callable + match = _RE_CALLABLE.match(goto) + if match: + kwargs = match.group("kwargs") + if kwargs: + for kwarg in kwargs.split(','): + _validate_kwarg(goto, kwarg) + # parse key [;aliases|pattern] key = [part.strip() for part in key.split(_OPTION_ALIAS_MARKER)] if not key: diff --git a/evennia/utils/tests/test_evmenu.py b/evennia/utils/tests/test_evmenu.py index 718ccacba..190afce56 100644 --- a/evennia/utils/tests/test_evmenu.py +++ b/evennia/utils/tests/test_evmenu.py @@ -327,3 +327,16 @@ class TestMenuTemplateParse(EvenniaTest): def test_template2menu(self): evmenu.template2menu(self.char1, self.menu_template, self.goto_callables) + + def test_parse_menu_fail(self): + template = """ + ## NODE + + Text + + ## OPTIONS + + next: callnode2(invalid) + """ + with self.assertRaises(RuntimeError): + evmenu.parse_menu_template(self.char1, template, self.goto_callables)