From 0a4c217b3e08ac0ec2e1576ebee82128f006445e Mon Sep 17 00:00:00 2001 From: Griatch Date: Tue, 17 Nov 2015 22:39:26 +0100 Subject: [PATCH] Added unittests for nested inlinefuncs, and deprecation warning for the old version of {inlinefunc syntax. --- evennia/utils/inlinefunc.py | 9 ++-- evennia/utils/nested_inlinefuncs.py | 71 +++++++++++++++++++++++------ evennia/utils/tests.py | 29 ++++++++++++ 3 files changed, 90 insertions(+), 19 deletions(-) diff --git a/evennia/utils/inlinefunc.py b/evennia/utils/inlinefunc.py index e6a3087b81..e89481997c 100644 --- a/evennia/utils/inlinefunc.py +++ b/evennia/utils/inlinefunc.py @@ -45,7 +45,7 @@ should be returned. The inlinefunc should never cause a traceback. import re from django.conf import settings -from evennia.utils import utils +from evennia.utils import utils, logger _DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH @@ -61,7 +61,7 @@ def pad(text, *args, **kwargs): fillchar = ' ' for iarg, arg in enumerate(args): if iarg == 0: - width = int(arg) if arg.isdigit() else width + width = int(arg) if arg.strip().isdigit() else width elif iarg == 1: align = arg if arg in ('c', 'l', 'r') else align elif iarg == 2: @@ -80,7 +80,7 @@ def crop(text, *args, **kwargs): suffix = "[...]" for iarg, arg in enumerate(args): if iarg == 0: - width = int(arg) if arg.isdigit() else width + width = int(arg) if arg.strip().isdigit() else width elif iarg == 1: suffix = arg else: @@ -97,7 +97,7 @@ def wrap(text, *args, **kwargs): indent = 0 for iarg, arg in enumerate(args): if iarg == 0: - width = int(arg) if arg.isdigit() else width + width = int(arg) if arg.strip().isdigit() else width elif iarg == 1: indent = int(arg) if arg.isdigit() else indent return utils.wrap(text, width=width, indent=indent) @@ -242,6 +242,7 @@ def parse_inlinefunc(text, strip=False, session=None): for part in _FUNCSPLIT_REGEX.split("".join(stack)): starttag = _FUNCSTART_REGEX.match(part) if starttag: + logger.log_dep("The {func()-style inlinefunc is deprecated. Use the $func{} form instead.") startname = starttag.group(1) part = _execute_inline_single_function(startname, part, session) outstack.append(part) diff --git a/evennia/utils/nested_inlinefuncs.py b/evennia/utils/nested_inlinefuncs.py index 5cda53c973..2473163878 100644 --- a/evennia/utils/nested_inlinefuncs.py +++ b/evennia/utils/nested_inlinefuncs.py @@ -4,9 +4,9 @@ Inline functions (nested form). This parser accepts nested inlinefunctions on the form ``` -$funcname(arg, arg, ...) +$funcname{arg, arg, ...} ``` -where any arg can be another $funcname() call. +where any arg can be another $funcname{} call. Each token starts with "$funcname(" where there must be no space between the $funcname and (. It ends with a matched ending parentesis. @@ -50,18 +50,56 @@ import re from django.conf import settings from evennia.utils import utils + +# example/testing inline functions + +def pad(*args, **kwargs): + """ + Pad to width. pad(text, width, align, fillchar) + + """ + text, width, align, fillchar = "", 78, 'c', ' ' + nargs = len(args) + if nargs > 0: + text = args[0] + if nargs > 1: + width = int(args[1]) if args[1].strip().isdigit() else 78 + if nargs > 2: + align = args[2] if args[2] in ('c', 'l', 'r') else 'c' + if nargs > 3: + fillchar = args[3] + return utils.pad(text, width=width, align=align, fillchar=fillchar) + + +def crop(text, *args, **kwargs): + """ + Crop to width. crop(text, width=78, suffix='[...]') + + """ + text = width, suffix = "", 78, "[...]" + nargs = len(args) + if nargs > 0: + text = args[0] + if nargs > 1: + width = int(args[1]) if args[1].strip().isdigit() else 78 + if nargs > 2: + suffix = args[2] + return utils.crop(text, width=width, suffix=suffix) + + # we specify a default nomatch function to use if no matching func was # found. This will be overloaded by any nomatch function defined in # the imported modules. -_INLINE_FUNCS = {"nomatch": lambda *args, **kwargs: "" % (args[0]), +_INLINE_FUNCS = {"nomatch": lambda *args, **kwargs: "", "stackfull": lambda *args, **kwargs: "\n (not parsed: inlinefunc stack size exceeded.)"} # load custom inline func modules. for module in utils.make_iter(settings.INLINEFUNC_MODULES): _INLINE_FUNCS.update(utils.all_from_module(module)) -# remove -_INLINE_FUNCS.pop("inline_func_parse", None) + +# remove the core function if we include examples in this module itself +#_INLINE_FUNCS.pop("inline_func_parse", None) # The stack size is a security measure. Set to <=0 to disable. @@ -74,7 +112,7 @@ except AttributeError: _RE_STARTTOKEN = re.compile(r"(?.*?)(?.*?)(?.*?)(?(?(? 0: + # this means not all inlinefuncs were complete + return string + if _STACK_MAXSIZE > 0 and _STACK_MAXSIZE < len(stack): # if stack is larger than limit, throw away parsing return string + gdict["stackfull"](*args, **kwargs) @@ -213,6 +255,7 @@ def parse_inlinefunc(string, strip=False, **kwargs): # run the stack recursively def _run_stack(item): + retval = item if isinstance(item, tuple): if strip: return "" @@ -227,11 +270,9 @@ def parse_inlinefunc(string, strip=False, **kwargs): # all other args should merge into one string args[-1] += _run_stack(arg) # execute the inlinefunc at this point or strip it. - return "" if strip else utils.to_str(func(*args, **kwargs)) - else: - return item + retval = "" if strip else func(*args, **kwargs) + return utils.to_str(retval, force_string=True) # execute the stack from the cache - print "_PARSING_CACHE[string]:", _PARSING_CACHE[string] return "".join(_run_stack(item) for item in _PARSING_CACHE[string]) diff --git a/evennia/utils/tests.py b/evennia/utils/tests.py index 4cf0481ac6..f8b05209da 100644 --- a/evennia/utils/tests.py +++ b/evennia/utils/tests.py @@ -309,3 +309,32 @@ class TestTextToHTMLparser(TestCase): self.assertEqual(self.parser.convert_urls('http://example.com/'), 'http://example.com/') + +from evennia.utils import nested_inlinefuncs + +class TestNestedInlineFuncs(TestCase): + "Test the nested inlinefunc module" + def test_nofunc(self): + self.assertEqual(nested_inlinefuncs.parse_inlinefunc( + "as$382ewrw w we w werw,|44943}"), + "as$382ewrw w we w werw,|44943}") + + def test_incomplete(self): + self.assertEqual(nested_inlinefuncs.parse_inlinefunc( + "testing $blah{without an ending."), + "testing $blah{without an ending.") + + def test_single_func(self): + self.assertEqual(nested_inlinefuncs.parse_inlinefunc( + "this is a test with $pad{centered, 20} text in it."), + "this is a test with centered text in it.") + + def test_nested(self): + self.assertEqual(nested_inlinefuncs.parse_inlinefunc( + "this $crop{is a test with $pad{padded, 20} text in $pad{pad2, 10} a crop, 80}"), + "this is a test with padded text in pad2 a crop") + + def test_escaped(self): + self.assertEqual(nested_inlinefuncs.parse_inlinefunc( + "this should be $pad{'''escaped,''' and \"\"\"instead,\"\"\" cropped $crop{with a long,5} text., 80}"), + "this should be escaped, and instead, cropped with text. ")