From 644cf9451f7d28e98d05cde226db521dde86e9fe Mon Sep 17 00:00:00 2001 From: Griatch Date: Tue, 26 Apr 2016 00:19:57 +0200 Subject: [PATCH] Removed deprecated non-nested {inlinefuncs, only accepting (). Changed the default name of the mygame/server/conf/inlinefunc.py to mygame/server/conf/inlinefuncs.py. Added deprecationwarning for the old name. --- .../conf/{inlinefunc.py => inlinefuncs.py} | 12 +- evennia/server/evennia_launcher.py | 5 + evennia/server/sessionhandler.py | 6 +- evennia/settings_default.py | 13 +- evennia/utils/inlinefunc.py | 261 ------------------ .../{nested_inlinefuncs.py => inlinefuncs.py} | 0 evennia/utils/tests.py | 16 +- 7 files changed, 27 insertions(+), 286 deletions(-) rename evennia/game_template/server/conf/{inlinefunc.py => inlinefuncs.py} (80%) delete mode 100644 evennia/utils/inlinefunc.py rename evennia/utils/{nested_inlinefuncs.py => inlinefuncs.py} (100%) diff --git a/evennia/game_template/server/conf/inlinefunc.py b/evennia/game_template/server/conf/inlinefuncs.py similarity index 80% rename from evennia/game_template/server/conf/inlinefunc.py rename to evennia/game_template/server/conf/inlinefuncs.py index 2634e2fdee..c4edd83daf 100644 --- a/evennia/game_template/server/conf/inlinefunc.py +++ b/evennia/game_template/server/conf/inlinefuncs.py @@ -11,17 +11,17 @@ evennia.utils.inlinefunc. In text, usage is straightforward: -{funcname([arg1,arg2,...]) text {/funcname +$funcname([arg1,[arg2,...]]) Example 1 (using the "pad" inlinefunc): - "This is {pad(50,c,-) a center-padded text{/pad of width 50." + say This is $pad("a center-padded text", 50,c,-) of width 50. -> - "This is -------------- a center-padded text--------------- of width 50." + John says, "This is -------------- a center-padded text--------------- of width 50." -Example 2 (using "pad" and "time" inlinefuncs): - "The time is {pad(30){time(){/time{/padright now." +Example 2 (using nested "pad" and "time" inlinefuncs): + say The time is $pad($time(), 30)right now. -> - "The time is Oct 25, 11:09 right now." + John says, "The time is Oct 25, 11:09 right now." To add more inline functions, add them to this module, using the following call signature: diff --git a/evennia/server/evennia_launcher.py b/evennia/server/evennia_launcher.py index 2a5ee8b0a5..6ad1818284 100644 --- a/evennia/server/evennia_launcher.py +++ b/evennia/server/evennia_launcher.py @@ -805,6 +805,11 @@ def error_check_python_modules(): imp(settings.BASE_ROOM_TYPECLASS) imp(settings.BASE_EXIT_TYPECLASS) imp(settings.BASE_SCRIPT_TYPECLASS) + # changed game dir settings file names + if os.path.isfile(os.path.join("server", "conf", "inlinefunc.py")): + raise DeprecationWarning("Name change: mygame/server/conf/inlinefunc.py should " + "be renamed to mygame/server/conf/inlinefuncs.py (note the S at the end)") + def init_game_directory(path, check_db=True): diff --git a/evennia/server/sessionhandler.py b/evennia/server/sessionhandler.py index 2e2291f8e3..28dce77194 100644 --- a/evennia/server/sessionhandler.py +++ b/evennia/server/sessionhandler.py @@ -23,8 +23,7 @@ from evennia.utils.utils import (variable_from_module, is_iter, to_str, to_unicode, make_iter, callables_from_module) -from evennia.utils.inlinefunc import parse_inlinefunc -from evennia.utils.nested_inlinefuncs import parse_inlinefunc as parse_nested_inlinefunc +from evennia.utils.inlinefuncs import parse_inlinefunc try: import cPickle as pickle @@ -174,8 +173,7 @@ class SessionHandler(dict): session.protocol_flags["ENCODING"] = "utf-8" data = to_str(to_unicode(data), encoding=session.protocol_flags["ENCODING"]) if _INLINEFUNC_ENABLED and not raw: - data = parse_inlinefunc(data, strip=strip_inlinefunc, session=session) # deprecated! - data = parse_nested_inlinefunc(data, strip=strip_inlinefunc, session=session) + data = parse_inlinefunc(data, strip=strip_inlinefunc, session=session) return data elif hasattr(data, "id") and hasattr(data, "db_date_created") \ and hasattr(data, '__dbclass__'): diff --git a/evennia/settings_default.py b/evennia/settings_default.py index 076ebd9913..64414789b9 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -396,17 +396,16 @@ TIME_MONTH_PER_YEAR = 12 ###################################################################### # Inlinefunc ###################################################################### -# Evennia supports inline function preprocessing. This allows -# users to supply {func() ... {/func in text, performing dynamic -# text formatting and manipulation on the fly. If disabled, such -# inline functions will not be parsed. +# Evennia supports inline function preprocessing. This allows users +# to supply inline calls on the form $func(arg, arg, ...) to do +# session-aware text formatting and manipulation on the fly. If +# disabled, such inline functions will not be parsed. INLINEFUNC_ENABLED = False # Only functions defined globally (and not starting with '_') in # these modules will be considered valid inlinefuncs. The list # is loaded from left-to-right, same-named functions will overload -INLINEFUNC_MODULES = ["evennia.utils.inlinefunc", - "evennia.utils.nested_inlinefuncs", - "server.conf.inlinefunc"] +INLINEFUNC_MODULES = ["evennia.utils.inlinefuncs", + "server.conf.inlinefuncs"] ###################################################################### # Default Player setup and access diff --git a/evennia/utils/inlinefunc.py b/evennia/utils/inlinefunc.py deleted file mode 100644 index 547e41ebac..0000000000 --- a/evennia/utils/inlinefunc.py +++ /dev/null @@ -1,261 +0,0 @@ -""" -Inlinefunc - -**Note: This module is deprecated. Use evennia.utils.nested_inlinefuncs instead.** - -This is a simple inline text language for use to custom-format text in -Evennia. It is applied BEFORE ANSI/MUX parsing is applied. - -To activate Inlinefunc, settings.INLINEFUNC_ENABLED must be set. - -The format is straightforward: - - -{funcname([arg1,arg2,...]) text {/funcname - - -Example: - "This is {pad(50,c,-) a center-padded text{/pad of width 50." - -> - "This is -------------- a center-padded text--------------- of width 50." - -This can be inserted in any text, operated on by the parse_inlinefunc -function. funcname() (no space is allowed between the name and the -argument tuple) is picked from a selection of valid functions from -settings.INLINEFUNC_MODULES. - -Commands can be nested, and will applied inside-out. For correct -parsing their end-tags must match the starting tags in reverse order. - -Example: - "The time is {pad(30){time(){/time{/padright now." - -> - "The time is Oct 25, 11:09 right now." - -An inline function should have the following call signature: - - def funcname(text, *args, **kwargs) - -where the text is always the part between {funcname(args) and -{/funcname and the *args are taken from the appropriate part of the -call. It is important that the inline function properly clean the -incoming args, checking their type and replacing them with sane -defaults if needed. If impossible to resolve, the unmodified text -should be returned. The inlinefunc should never cause a traceback. - -""" - -import re -from django.conf import settings -from evennia.utils import utils, logger - -_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH - -# inline functions - -def pad(text, *args, **kwargs): - """ - Pad to width. pad(text, width=78, align='c', fillchar=' ') - - """ - width = _DEFAULT_WIDTH - align = 'c' - fillchar = ' ' - for iarg, arg in enumerate(args): - if iarg == 0: - 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: - fillchar = arg[0] - else: - break - return utils.pad(text, width=width, align=align, fillchar=fillchar) - - -def crop(text, *args, **kwargs): - """ - Crop to width. crop(text, width=78, suffix='[...]') - - """ - width = _DEFAULT_WIDTH - suffix = "[...]" - for iarg, arg in enumerate(args): - if iarg == 0: - width = int(arg) if arg.strip().isdigit() else width - elif iarg == 1: - suffix = arg - else: - break - return utils.crop(text, width=width, suffix=suffix) - - -def wrap(text, *args, **kwargs): - """ - Wrap/Fill text to width. fill(text, width=78, indent=0) - - """ - width = _DEFAULT_WIDTH - indent = 0 - for iarg, arg in enumerate(args): - if iarg == 0: - 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) - - -def time(text, *args, **kwargs): - """ - Inserts current time. - - """ - import time - strformat = "%h %d, %H:%M" - if args and args[0]: - strformat = str(args[0]) - return time.strftime(strformat) - - -def you(text, *args, **kwargs): - """ - Inserts your name. - - """ - name = "You" - sess = kwargs.get("session") - if sess and sess.puppet: - name = sess.puppet.key - return name - - -# load functions from module (including this one, if using default settings) -_INLINE_FUNCS = {} -for module in utils.make_iter(settings.INLINEFUNC_MODULES): - _INLINE_FUNCS.update(utils.callables_from_module(module)) -_INLINE_FUNCS.pop("inline_func_parse", None) - - -# dynamically build regexes for found functions -_RE_FUNCFULL = r"\{%s\((.*?)\)(.*?){/%s" -_RE_FUNCFULL_SINGLE = r"\{%s\((.*?)\)" -_RE_FUNCSTART = r"\{((?:%s))" -_RE_FUNCEND = r"\{/((?:%s))" -_RE_FUNCSPLIT = r"(\{/*(?:%s)(?:\(.*?\))*)" -_RE_FUNCCLEAN = r"\{%s\(.*?\)|\{/%s" - -_INLINE_FUNCS = dict((key, (func, re.compile(_RE_FUNCFULL % (key, key), re.DOTALL & re.MULTILINE), - re.compile(_RE_FUNCFULL_SINGLE % key, re.DOTALL & re.MULTILINE))) - for key, func in _INLINE_FUNCS.items() if callable(func)) -_FUNCSPLIT_REGEX = re.compile(_RE_FUNCSPLIT % r"|".join([key for key in _INLINE_FUNCS]), re.DOTALL & re.MULTILINE) -_FUNCSTART_REGEX = re.compile(_RE_FUNCSTART % r"|".join([key for key in _INLINE_FUNCS]), re.DOTALL & re.MULTILINE) -_FUNCEND_REGEX = re.compile(_RE_FUNCEND % r"|".join([key for key in _INLINE_FUNCS]), re.DOTALL & re.MULTILINE) -_FUNCCLEAN_REGEX = re.compile("|".join([_RE_FUNCCLEAN % (key, key) for key in _INLINE_FUNCS]), re.DOTALL & re.MULTILINE) - - -# inline parser functions - -def _execute_inline_function(funcname, text, session): - """ - Get the enclosed text between {funcname(...) and {/funcname - and execute the inline function to replace the whole block - with the result. - - Args: - funcname (str): Inlinefunction identifier. - text (str): Text to process. - session (Session): Session object. - - Notes: - This lookup is "dumb" - we just grab the first end tag we find. So - to work correctly this function must be called "inside out" on a - nested function tree, so each call only works on a "flat" tag. - - """ - def subfunc(match): - """ - replace the entire block with the result of the function call - - """ - args = [part.strip() for part in match.group(1).split(",")] - intext = match.group(2) - kwargs = {"session":session} - return _INLINE_FUNCS[funcname][0](intext, *args, **kwargs) - return _INLINE_FUNCS[funcname][1].sub(subfunc, text) - - -def _execute_inline_single_function(funcname, text, session): - """ - Get the arguments of a single function call (no matching end tag) - and execute it with an empty text input. - - Args: - funcname (str): Function identifier. - text (str): String to process. - session (Session): Session id. - - """ - def subfunc(match): - "replace the single call with the result of the function call" - args = [part.strip() for part in match.group(1).split(",")] - kwargs = {"session":session} - return _INLINE_FUNCS[funcname][0]("", *args, **kwargs) - return _INLINE_FUNCS[funcname][2].sub(subfunc, text) - - -def parse_inlinefunc(text, strip=False, session=None): - """ - Parse inline function-replacement. - - Args: - text (str): Text to parse. - strip (bool, optional): Remove all supported inlinefuncs from text. - session (bool): Session calling for the parsing. - - Returns: - text (str): Parsed text with processed results of - inlinefuncs. - - """ - - if strip: - # strip all functions - return _FUNCCLEAN_REGEX.sub("", text) - - stack = [] - for part in _FUNCSPLIT_REGEX.split(text): - endtag = _FUNCEND_REGEX.match(part) - if endtag: - # an end tag - endname = endtag.group(1) - while stack: - new_part = stack.pop() - part = new_part + part # add backwards -> fowards - starttag = _FUNCSTART_REGEX.match(new_part) - if starttag: - startname = starttag.group(1) - if startname == endname: - part = _execute_inline_function(startname, part, session) - break - stack.append(part) - # handle single functions without matching end tags; these are treated - # as being called with an empty string as text argument. - outstack = [] - 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) - - return "".join(outstack) - - -def _test(): - # this should all be handled - s = "This is a text with a{pad(78,c,-)text {pad(5)of{/pad {pad(30)nice{/pad size{/pad inside {pad(4,l)it{/pad." - s2 = "This is a text with a----------------text of nice size---------------- inside it ." - t = parse_inlinefunc(s) - assert(t == s2) - return t diff --git a/evennia/utils/nested_inlinefuncs.py b/evennia/utils/inlinefuncs.py similarity index 100% rename from evennia/utils/nested_inlinefuncs.py rename to evennia/utils/inlinefuncs.py diff --git a/evennia/utils/tests.py b/evennia/utils/tests.py index 8e79891efb..9f7afb9d4b 100644 --- a/evennia/utils/tests.py +++ b/evennia/utils/tests.py @@ -310,37 +310,37 @@ class TestTextToHTMLparser(TestCase): 'http://example.com/') -from evennia.utils import nested_inlinefuncs +from evennia.utils import inlinefuncs -class TestNestedInlineFuncs(TestCase): +class TestInlineFuncs(TestCase): "Test the nested inlinefunc module" def test_nofunc(self): - self.assertEqual(nested_inlinefuncs.parse_inlinefunc( + self.assertEqual(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( + self.assertEqual(inlinefuncs.parse_inlinefunc( "testing $blah{without an ending."), "testing $blah{without an ending.") def test_single_func(self): - self.assertEqual(nested_inlinefuncs.parse_inlinefunc( + self.assertEqual(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( + self.assertEqual(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( + self.assertEqual(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. ") def test_escaped2(self): - self.assertEqual(nested_inlinefuncs.parse_inlinefunc( + self.assertEqual(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. ")