From a2b79a2ff122b17e334b510fc477a8a231f2fe88 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Sat, 27 Oct 2018 13:16:28 -0500 Subject: [PATCH 1/8] Demonstrate potential problem with --- evennia/prototypes/tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/evennia/prototypes/tests.py b/evennia/prototypes/tests.py index 411bd45c27..103a5d9404 100644 --- a/evennia/prototypes/tests.py +++ b/evennia/prototypes/tests.py @@ -313,6 +313,7 @@ class TestProtFuncs(EvenniaTest): "$eval({'test': '1', 2:3, 3: $toint(3.5)})"), {'test': '1', 2: 3, 3: 3}) self.assertEqual(protlib.protfunc_parser("$obj(#1)", session=self.session), '#1') + self.assertEqual(protlib.protfunc_parser("stone(#12345)", session=self.session), 'stone(#12345)') self.assertEqual(protlib.protfunc_parser("#1", session=self.session), '#1') self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6') self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6') From 5a8858aab9df94c4b2ab2117e911ca93bfda9bc0 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Sat, 27 Oct 2018 18:20:54 -0500 Subject: [PATCH 2/8] Tests for protfunc parser and _RE_DBREF --- evennia/prototypes/tests.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/evennia/prototypes/tests.py b/evennia/prototypes/tests.py index 103a5d9404..fe75ccf4ec 100644 --- a/evennia/prototypes/tests.py +++ b/evennia/prototypes/tests.py @@ -262,6 +262,25 @@ class TestProtFuncs(EvenniaTest): "prototype_desc": "testing prot", "key": "ExampleObj"} + def test_RE_DBREF(self): + def check_RE_DBREF(value, expected_value): + try: + result = ( + protlib._RE_DBREF.match(value), + protlib._RE_DBREF.search(value), + protlib._RE_DBREF.sub("$obj(\\1)", value) + ) + assert expected_value == result[2] + except Exception: + self.fail() + pass + + check_RE_DBREF('#1234', '#1234') + check_RE_DBREF('(#1234)', '(#1234)') + check_RE_DBREF('obj(#1234)', 'obj(#1234)') + check_RE_DBREF('$obj(#1234)', '$obj(#1234)') + check_RE_DBREF('obj($obj(#1234))', 'obj($obj(#1234))') + @mock.patch("evennia.prototypes.protfuncs.base_random", new=mock.MagicMock(return_value=0.5)) @mock.patch("evennia.prototypes.protfuncs.base_randint", new=mock.MagicMock(return_value=5)) def test_protfuncs(self): @@ -315,10 +334,15 @@ class TestProtFuncs(EvenniaTest): self.assertEqual(protlib.protfunc_parser("$obj(#1)", session=self.session), '#1') self.assertEqual(protlib.protfunc_parser("stone(#12345)", session=self.session), 'stone(#12345)') self.assertEqual(protlib.protfunc_parser("#1", session=self.session), '#1') + self.assertEqual(protlib.protfunc_parser("#12345", session=self.session), '#12345') + self.assertEqual(protlib.protfunc_parser("nothing(#1)", session=self.session), 'nothing(#1)') + self.assertEqual(protlib.protfunc_parser("(#12345)", session=self.session), '(#12345)') self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6') self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6') self.assertEqual(protlib.protfunc_parser("$objlist(#1)", session=self.session), ['#1']) + + self.assertEqual(protlib.value_to_obj( protlib.protfunc_parser("#6", session=self.session)), self.char1) self.assertEqual(protlib.value_to_obj_or_any( From c2ccc5d2975a96e75bcfc599353af589575e2794 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Sun, 28 Oct 2018 09:43:01 -0500 Subject: [PATCH 3/8] #1704: Update _RE_DBREF to match '(#Number)' so that it aliases to '(#Number)' --- evennia/prototypes/prototypes.py | 4 +-- evennia/prototypes/tests.py | 52 ++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/evennia/prototypes/prototypes.py b/evennia/prototypes/prototypes.py index 833316cf59..020c6c84cf 100644 --- a/evennia/prototypes/prototypes.py +++ b/evennia/prototypes/prototypes.py @@ -33,7 +33,7 @@ _PROTOTYPE_TAG_CATEGORY = "from_prototype" _PROTOTYPE_TAG_META_CATEGORY = "db_prototype" PROT_FUNCS = {} -_RE_DBREF = re.compile(r"(?') self.assertEqual(protlib.protfunc_parser("stone(#12345)", session=self.session), 'stone(#12345)') self.assertEqual(protlib.protfunc_parser("#1", session=self.session), '#1') self.assertEqual(protlib.protfunc_parser("#12345", session=self.session), '#12345') self.assertEqual(protlib.protfunc_parser("nothing(#1)", session=self.session), 'nothing(#1)') self.assertEqual(protlib.protfunc_parser("(#12345)", session=self.session), '(#12345)') self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6') - self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6') + self.assertEqual(protlib.protfunc_parser("obj(Char)", session=self.session), 'obj(Char)') self.assertEqual(protlib.protfunc_parser("$objlist(#1)", session=self.session), ['#1']) + self.assertEqual(protlib.protfunc_parser("objlist(#1)", session=self.session), 'objlist(#1)') + self.assertEqual(protlib.protfunc_parser("dbref(Char)", session=self.session), 'dbref(Char)') + self.assertEqual(protlib.protfunc_parser("$dbref(Char)", session=self.session), '') From bfeebc47ba759a35a835905741337328dcc94270 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Sun, 28 Oct 2018 20:43:14 -0500 Subject: [PATCH 4/8] Assert protfuncs._obj_search is called ONLY when (), () and () are called. Group tests according to protfuncs._obj_search() calls --- evennia/prototypes/tests.py | 86 ++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/evennia/prototypes/tests.py b/evennia/prototypes/tests.py index 6091c2a06f..ef09223cb1 100644 --- a/evennia/prototypes/tests.py +++ b/evennia/prototypes/tests.py @@ -11,6 +11,7 @@ from evennia.utils.test_resources import EvenniaTest from evennia.utils.tests.test_evmenu import TestEvMenu from evennia.prototypes import spawner, prototypes as protlib from evennia.prototypes import menus as olc_menus +from evennia.prototypes import protfuncs as protofuncs from evennia.prototypes.prototypes import _PROTOTYPE_TAG_META_CATEGORY @@ -344,23 +345,76 @@ class TestProtFuncs(EvenniaTest): self.assertEqual(protlib.protfunc_parser( "$eval({'test': '1', 2:3, 3: $toint(3.5)})"), {'test': '1', 2: 3, 3: 3}) - self.assertEqual(protlib.protfunc_parser("obj(#1)", session=self.session), 'obj(#1)') - self.assertEqual(protlib.protfunc_parser("$obj(#1)", session=self.session), '#1') - self.assertEqual(protlib.protfunc_parser("dbref(#1)", session=self.session), 'dbref(#1)') - self.assertEqual(protlib.protfunc_parser("$dbref(#1)", session=self.session), '#1') - self.assertEqual(protlib.protfunc_parser("$badfunc(#1)", session=self.session), '') - self.assertEqual(protlib.protfunc_parser("stone(#12345)", session=self.session), 'stone(#12345)') - self.assertEqual(protlib.protfunc_parser("#1", session=self.session), '#1') - self.assertEqual(protlib.protfunc_parser("#12345", session=self.session), '#12345') - self.assertEqual(protlib.protfunc_parser("nothing(#1)", session=self.session), 'nothing(#1)') - self.assertEqual(protlib.protfunc_parser("(#12345)", session=self.session), '(#12345)') - self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6') - self.assertEqual(protlib.protfunc_parser("obj(Char)", session=self.session), 'obj(Char)') - self.assertEqual(protlib.protfunc_parser("$objlist(#1)", session=self.session), ['#1']) - self.assertEqual(protlib.protfunc_parser("objlist(#1)", session=self.session), 'objlist(#1)') - self.assertEqual(protlib.protfunc_parser("dbref(Char)", session=self.session), 'dbref(Char)') - self.assertEqual(protlib.protfunc_parser("$dbref(Char)", session=self.session), '') + # no object search + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("obj(#1)", session=self.session), 'obj(#1)') + mocked__obj_search.assert_not_called() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("dbref(#1)", session=self.session), 'dbref(#1)') + mocked__obj_search.assert_not_called() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("stone(#12345)", session=self.session), 'stone(#12345)') + mocked__obj_search.assert_not_called() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("#1", session=self.session), '#1') + mocked__obj_search.assert_not_called() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("#12345", session=self.session), '#12345') + mocked__obj_search.assert_not_called() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("nothing(#1)", session=self.session), 'nothing(#1)') + mocked__obj_search.assert_not_called() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("(#12345)", session=self.session), '(#12345)') + mocked__obj_search.assert_not_called() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("obj(Char)", session=self.session), 'obj(Char)') + mocked__obj_search.assert_not_called() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("objlist(#1)", session=self.session), 'objlist(#1)') + mocked__obj_search.assert_not_called() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("dbref(Char)", session=self.session), 'dbref(Char)') + mocked__obj_search.assert_not_called() + + + # obj search happens + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("$objlist(#1)", session=self.session), ['#1']) + mocked__obj_search.assert_called_once() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("$obj(#1)", session=self.session), '#1') + mocked__obj_search.assert_called_once() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("$dbref(#1)", session=self.session), '#1') + mocked__obj_search.assert_called_once() + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6') + mocked__obj_search.assert_called_once() + + + # bad invocation + + self.assertEqual(protlib.protfunc_parser("$badfunc(#1)", session=self.session), '') + + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("$dbref(Char)", session=self.session), '') + mocked__obj_search.assert_not_called() self.assertEqual(protlib.value_to_obj( From f6d62e2f691541fb5a1b6ff0564a5cf450d60c56 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Sun, 28 Oct 2018 20:52:19 -0500 Subject: [PATCH 5/8] Harden mocked _obj_search() calls with passed in when applicable --- evennia/prototypes/tests.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/evennia/prototypes/tests.py b/evennia/prototypes/tests.py index ef09223cb1..38d577a408 100644 --- a/evennia/prototypes/tests.py +++ b/evennia/prototypes/tests.py @@ -394,23 +394,29 @@ class TestProtFuncs(EvenniaTest): with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: self.assertEqual(protlib.protfunc_parser("$objlist(#1)", session=self.session), ['#1']) mocked__obj_search.assert_called_once() + assert ('#1',) == mocked__obj_search.call_args[0] with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: self.assertEqual(protlib.protfunc_parser("$obj(#1)", session=self.session), '#1') mocked__obj_search.assert_called_once() + assert ('#1',) == mocked__obj_search.call_args[0] with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: self.assertEqual(protlib.protfunc_parser("$dbref(#1)", session=self.session), '#1') mocked__obj_search.assert_called_once() + assert ('#1',) == mocked__obj_search.call_args[0] with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: self.assertEqual(protlib.protfunc_parser("$obj(Char)", session=self.session), '#6') mocked__obj_search.assert_called_once() + assert ('Char',) == mocked__obj_search.call_args[0] # bad invocation - self.assertEqual(protlib.protfunc_parser("$badfunc(#1)", session=self.session), '') + with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: + self.assertEqual(protlib.protfunc_parser("$badfunc(#1)", session=self.session), '') + mocked__obj_search.assert_not_called() with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: self.assertEqual(protlib.protfunc_parser("$dbref(Char)", session=self.session), '') From 979d24a12feaf8e31eb9a1f9e7ee53dc98721f51 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Tue, 30 Oct 2018 19:50:40 -0500 Subject: [PATCH 6/8] Remove prototypes._RE_DBREF and add new dbref() to protfuncs --- evennia/prototypes/protfuncs.py | 13 ++++++++++++ evennia/prototypes/prototypes.py | 6 ------ evennia/prototypes/tests.py | 34 +------------------------------- 3 files changed, 14 insertions(+), 39 deletions(-) diff --git a/evennia/prototypes/protfuncs.py b/evennia/prototypes/protfuncs.py index a13aa7e532..1c7923c561 100644 --- a/evennia/prototypes/protfuncs.py +++ b/evennia/prototypes/protfuncs.py @@ -37,6 +37,7 @@ prototype key (this value must be possible to serialize in an Attribute). from ast import literal_eval from random import randint as base_randint, random as base_random, choice as base_choice +import re from evennia.utils import search from evennia.utils.utils import justify as base_justify, is_iter, to_str @@ -325,3 +326,15 @@ def objlist(*args, **kwargs): """ return ["#{}".format(obj.id) for obj in _obj_search(return_list=True, *args, **kwargs)] + + +def dbref(*args, **kwargs): + """ + Usage $dbref(<#dbref>) + Returns one Object searched globally by #dbref. Error if #dbref is invalid. + """ + _RE_DBREF = re.compile(r"\#[0-9]+") + if not args or len(args) < 1 or _RE_DBREF.match(args[0]) is None: + raise ValueError('$dbref requires a valid #dbref argument.') + + return obj(args[0]) diff --git a/evennia/prototypes/prototypes.py b/evennia/prototypes/prototypes.py index 020c6c84cf..a3281777e0 100644 --- a/evennia/prototypes/prototypes.py +++ b/evennia/prototypes/prototypes.py @@ -5,7 +5,6 @@ Handling storage of prototypes, both database-based ones (DBPrototypes) and thos """ -import re import hashlib import time from ast import literal_eval @@ -33,8 +32,6 @@ _PROTOTYPE_TAG_CATEGORY = "from_prototype" _PROTOTYPE_TAG_META_CATEGORY = "db_prototype" PROT_FUNCS = {} -_RE_DBREF = re.compile(r"\$dbref\((\#[0-9]+)\)") - class PermissionError(RuntimeError): pass @@ -576,9 +573,6 @@ def protfunc_parser(value, available_functions=None, testing=False, stacktrace=F available_functions = PROT_FUNCS if available_functions is None else available_functions - # $dbref(#Number) becomes $obj(#Number) - value = _RE_DBREF.sub("$obj(\\1)", value) - result = inlinefuncs.parse_inlinefunc( value, available_funcs=available_functions, stacktrace=stacktrace, testing=testing, **kwargs) diff --git a/evennia/prototypes/tests.py b/evennia/prototypes/tests.py index 38d577a408..59fb69ca3e 100644 --- a/evennia/prototypes/tests.py +++ b/evennia/prototypes/tests.py @@ -263,38 +263,6 @@ class TestProtFuncs(EvenniaTest): "prototype_desc": "testing prot", "key": "ExampleObj"} - def test_RE_DBREF(self): - def check_RE_DBREF(value, expected_value, - exp_is_match, exp_is_search): - try: - mm = protlib._RE_DBREF.match(value) - ms = protlib._RE_DBREF.search(value) - sub1 = protlib._RE_DBREF.sub("$obj(\\1)", value) - assert expected_value == sub1 - assert (exp_is_match and mm) or (not exp_is_match and mm is None) - assert (exp_is_search and ms) or (not exp_is_search and ms is None) - except Exception as e: - self.fail("%r" % ((value, mm, ms, expected_value, exp_is_match, exp_is_search, e),)) - - check_RE_DBREF('1234', '1234', False, False) - check_RE_DBREF('#1234', '#1234', False, False) - check_RE_DBREF('(#1234)', '(#1234)', False, False) - check_RE_DBREF('obj(#1234)', 'obj(#1234)', False, False) - check_RE_DBREF('dbref(#1234)', 'dbref(#1234)', False, False) - check_RE_DBREF('$obj(#1234)', '$obj(#1234)', False, False) - check_RE_DBREF('$dbref(#1234)', '$obj(#1234)', True, True) - check_RE_DBREF('obj($obj(#1234))', 'obj($obj(#1234))', False, False) - check_RE_DBREF('obj($dbref(#1234))', 'obj($obj(#1234))', False, True) - check_RE_DBREF('some #1234 value', 'some #1234 value', False, False) - check_RE_DBREF('some (#1234) value', 'some (#1234) value', False, False) - check_RE_DBREF('some obj(#1234) value', 'some obj(#1234) value', False, False) - check_RE_DBREF('some dbref(#1234) value', 'some dbref(#1234) value', False, False) - check_RE_DBREF('some $dbref(#1234) value', 'some $obj(#1234) value', False, True) - check_RE_DBREF('some obj($obj(#1234) value)', 'some obj($obj(#1234) value)', False, False) - check_RE_DBREF('some obj($dbref(#1234) value)', 'some obj($obj(#1234) value)', False, True) - check_RE_DBREF('some dbref($obj(#1234) value)', 'some dbref($obj(#1234) value)', False, False) - check_RE_DBREF('some dbref($dbref(#1234) value)', 'some dbref($obj(#1234) value)', False, True) - @mock.patch("evennia.prototypes.protfuncs.base_random", new=mock.MagicMock(return_value=0.5)) @mock.patch("evennia.prototypes.protfuncs.base_randint", new=mock.MagicMock(return_value=5)) def test_protfuncs(self): @@ -419,7 +387,7 @@ class TestProtFuncs(EvenniaTest): mocked__obj_search.assert_not_called() with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: - self.assertEqual(protlib.protfunc_parser("$dbref(Char)", session=self.session), '') + # self.assertEqual(protlib.protfunc_parser("$dbref(Char)", session=self.session), '') mocked__obj_search.assert_not_called() From cd9a91c377f3a9cb9a8cb55a96f2223134fac0f2 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Tue, 30 Oct 2018 20:10:36 -0500 Subject: [PATCH 7/8] Update test --- evennia/prototypes/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/prototypes/tests.py b/evennia/prototypes/tests.py index 59fb69ca3e..e8ef8a4e6b 100644 --- a/evennia/prototypes/tests.py +++ b/evennia/prototypes/tests.py @@ -387,7 +387,7 @@ class TestProtFuncs(EvenniaTest): mocked__obj_search.assert_not_called() with mock.patch("evennia.prototypes.protfuncs._obj_search", wraps=protofuncs._obj_search) as mocked__obj_search: - # self.assertEqual(protlib.protfunc_parser("$dbref(Char)", session=self.session), '') + self.assertRaises(ValueError, protlib.protfunc_parser, "$dbref(Char)") mocked__obj_search.assert_not_called() From 6014d5eba498b9e52b49f6f12feaaaf36d133b78 Mon Sep 17 00:00:00 2001 From: Henddher Pedroza Date: Mon, 5 Nov 2018 18:44:12 -0600 Subject: [PATCH 8/8] Move regex out of protofunc to prevent recompilation on each call. --- evennia/prototypes/protfuncs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evennia/prototypes/protfuncs.py b/evennia/prototypes/protfuncs.py index 1c7923c561..e222876221 100644 --- a/evennia/prototypes/protfuncs.py +++ b/evennia/prototypes/protfuncs.py @@ -44,6 +44,8 @@ from evennia.utils.utils import justify as base_justify, is_iter, to_str _PROTLIB = None +_RE_DBREF = re.compile(r"\#[0-9]+") + # default protfuncs @@ -333,7 +335,6 @@ def dbref(*args, **kwargs): Usage $dbref(<#dbref>) Returns one Object searched globally by #dbref. Error if #dbref is invalid. """ - _RE_DBREF = re.compile(r"\#[0-9]+") if not args or len(args) < 1 or _RE_DBREF.match(args[0]) is None: raise ValueError('$dbref requires a valid #dbref argument.')