mirror of
https://github.com/evennia/evennia.git
synced 2026-03-24 00:36:30 +01:00
Added unittests for nested inlinefuncs, and deprecation warning for the old version of {inlinefunc syntax.
This commit is contained in:
parent
641846e855
commit
0a4c217b3e
3 changed files with 90 additions and 19 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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: "<UKNOWN: %s>" % (args[0]),
|
||||
_INLINE_FUNCS = {"nomatch": lambda *args, **kwargs: "<UKNOWN>",
|
||||
"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"(?<!\\)\$(\w+)\{") # unescaped $funcname( (start of function call)
|
||||
|
||||
_RE_TOKEN = re.compile(r"""(?<!\\)''(?P<singlequote>.*?)(?<!\\)\''| # unescaped '' (escapes all inside them)
|
||||
_RE_TOKEN = re.compile(r"""(?<!\\)'''(?P<singlequote>.*?)(?<!\\)'''| # unescaped '' (escapes all inside them)
|
||||
(?<!\\)\"\"\"(?P<triplequote>.*?)(?<!\\)\"\"\"| # unescaped triple quote (escapes all inside them)
|
||||
(?P<comma>(?<!\\)\,)| # unescaped , (argument lists) - this is thrown away
|
||||
(?P<end>(?<!\\)\})| # unescaped ) (end of function call)
|
||||
|
|
@ -138,10 +176,11 @@ def parse_inlinefunc(string, strip=False, **kwargs):
|
|||
string (str): The incoming string to parse.
|
||||
strip (bool, optional): Whether to strip function calls rather than
|
||||
execute them.
|
||||
kwargs (any, optional): This will be passed on to all found inlinefuncs as
|
||||
part of their call signature. This is to be called by Evennia or the
|
||||
coder and is used to make various game states available, such as
|
||||
the session of the user triggering the parse, character etc.
|
||||
Kwargs:
|
||||
session (Session): This is sent to this function by Evennia when triggering
|
||||
it. It is passed to the inlinefunc.
|
||||
kwargs (any): All other kwargs are also passed on to the inlinefunc.
|
||||
|
||||
|
||||
"""
|
||||
global _PARSING_CACHE
|
||||
|
|
@ -159,7 +198,6 @@ def parse_inlinefunc(string, strip=False, **kwargs):
|
|||
ncallable = 0
|
||||
for match in _RE_TOKEN.finditer(string):
|
||||
gdict = match.groupdict()
|
||||
print "gdict:", gdict
|
||||
if gdict["singlequote"]:
|
||||
stack.append(gdict["singlequote"])
|
||||
elif gdict["triplequote"]:
|
||||
|
|
@ -204,6 +242,10 @@ def parse_inlinefunc(string, strip=False, **kwargs):
|
|||
# the rest
|
||||
stack.append(gdict["rest"])
|
||||
|
||||
if ncallable > 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])
|
||||
|
||||
|
|
|
|||
|
|
@ -309,3 +309,32 @@ class TestTextToHTMLparser(TestCase):
|
|||
self.assertEqual(self.parser.convert_urls('</span>http://example.com/<span class="red">'),
|
||||
'</span><a href="http://example.com/" target="_blank">http://example.com/</a><span class="red">')
|
||||
|
||||
|
||||
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. ")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue