From bb2528276b183cdef68160bc7b414cef01a55c45 Mon Sep 17 00:00:00 2001 From: Cal Date: Tue, 28 May 2024 12:06:51 -0600 Subject: [PATCH 1/5] avoid erroneous global searches --- evennia/contrib/rpg/rpsystem/rpsystem.py | 42 +++++++++++------------- evennia/contrib/rpg/rpsystem/tests.py | 28 ++++++++++++++++ 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/evennia/contrib/rpg/rpsystem/rpsystem.py b/evennia/contrib/rpg/rpsystem/rpsystem.py index 7cb9de81ab..7c8549b58e 100644 --- a/evennia/contrib/rpg/rpsystem/rpsystem.py +++ b/evennia/contrib/rpg/rpsystem/rpsystem.py @@ -392,7 +392,7 @@ def parse_sdescs_and_recogs( # if no sdesc, include key plus aliases instead else: candidate_map.append((obj, obj.key)) - candidate_map.extend([(obj, alias) for alias in obj.aliases.all()]) + candidate_map.extend([(obj, alias) for alias in obj.aliases.all()]) # escape mapping syntax on the form {#id} if it exists already in emote, # if so it is replaced with just "id". @@ -439,7 +439,7 @@ def parse_sdescs_and_recogs( (re.search(rquery, text, _RE_FLAGS), obj, text) for obj, text in candidate_map ) # filter out any non-matching candidates - bestmatches = [(obj, match.group()) for match, obj, text in matches if match] + bestmatches = [(obj, m.group()) for m, obj, text in matches if m] else: # to find the longest match, we start from the marker and lengthen the @@ -1333,30 +1333,28 @@ class ContribRPObject(DefaultObject): """ # we also want to use the default search method search_obj = super().get_search_result - is_builder = self.locks.check_lockstring(self, "perm(Builder)") + is_builder = self.permissions.check("Builder") + results = [] - if candidates: - candidates = parse_sdescs_and_recogs( + if candidates is not None: + searched_results = parse_sdescs_and_recogs( self, candidates, _PREFIX + searchdata, search_mode=True ) - results = [] - for candidate in candidates: - # we search by candidate keys here; this allows full error - # management and use of all kwargs - we will use searchdata - # in eventual error reporting later (not their keys). Doing - # it like this e.g. allows for use of the typeclass kwarg - # limiter. - results.extend( - [obj for obj in search_obj(candidate.key, **kwargs) if obj not in results] - ) - - if not results and is_builder: - # builders get to do a global search by key+alias - results = search_obj(searchdata, **kwargs) + if not searched_results and is_builder: + # builders get to do a search by key + results = search_obj(searchdata, candidates=candidates, **kwargs) + else: + # we do a default search on each result by key, here, to apply extra filtering kwargs + for searched_obj in searched_results: + results.extend( + [ + obj + for obj in search_obj(searched_obj.key, candidates=candidates, **kwargs) + if obj not in results + ] + ) else: - # global searches with #drefs end up here. Global searches are - # only done in code, so is controlled, #dbrefs are turned off - # for non-Builders. + # no candidates means it's a global search, so we pass it back to the default results = search_obj(searchdata, **kwargs) return results diff --git a/evennia/contrib/rpg/rpsystem/tests.py b/evennia/contrib/rpg/rpsystem/tests.py index df40eb367b..abef83a4ce 100644 --- a/evennia/contrib/rpg/rpsystem/tests.py +++ b/evennia/contrib/rpg/rpsystem/tests.py @@ -346,6 +346,34 @@ class TestRPSystem(BaseEvenniaTest): self.assertEqual(self.speaker.search("receiver of emotes"), self.receiver1) self.assertEqual(self.speaker.search("colliding"), self.receiver2) + def test_get_search_result(self): + self.obj1 = create_object(rpsystem.ContribRPObject, key="Obj1", location=self.room) + self.obj1.sdesc.add("something") + self.obj2 = create_object(rpsystem.ContribRPCharacter, key="Obj2", location=self.room) + self.obj2.sdesc.add("something") + candidates = [self.obj1, self.obj2] + # search candidates by sdesc: both objects should be found + result = self.speaker.get_search_result("something", candidates) + self.assertIn(self.obj1, result) + self.assertIn(self.obj2, result) + # search empty candidates: no objects should be found + result = self.speaker.get_search_result("something", candidates=[]) + self.assertNotIn(self.obj1, result) + self.assertNotIn(self.obj2, result) + # typeclass was given: only matching object should be found + result = self.speaker.get_search_result( + "something", candidates=candidates, typeclass=rpsystem.ContribRPCharacter + ) + self.assertNotIn(self.obj1, result) + self.assertIn(self.obj2, result) + # search by key with player permissions: no objects should be found + result = self.speaker.get_search_result("obj1", candidates) + self.assertNotIn(self.obj1, result) + # search by key with builder permissions: object should be found + self.speaker.permissions.add("builder") + result = self.speaker.get_search_result("obj1", candidates) + self.assertIn(self.obj1, result) + class TestRPSystemCommands(BaseEvenniaCommandTest): def setUp(self): From 0fa30953591d8561e85153edff341cd8f3b0925c Mon Sep 17 00:00:00 2001 From: Cal Date: Tue, 28 May 2024 12:19:43 -0600 Subject: [PATCH 2/5] remove disambiguator from rpsystem search string --- evennia/contrib/rpg/rpsystem/rpsystem.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/evennia/contrib/rpg/rpsystem/rpsystem.py b/evennia/contrib/rpg/rpsystem/rpsystem.py index 7c8549b58e..b85237e2ad 100644 --- a/evennia/contrib/rpg/rpsystem/rpsystem.py +++ b/evennia/contrib/rpg/rpsystem/rpsystem.py @@ -422,7 +422,10 @@ def parse_sdescs_and_recogs( # first see if there is a number given (e.g. 1-tall) num_identifier, _ = marker_match.groups("") # return "" if no match, rather than None + # get the beginning of the actual text, minus the numeric identifier match_index = marker_match.start() + if num_identifier: + match_index += len(num_identifier) + 1 # split the emote string at the reference marker, to process everything after it head = string[:match_index] tail = string[match_index + 1 :] From f8de9a484e7e6d2820acd7d1b45077a7e7f6a9b0 Mon Sep 17 00:00:00 2001 From: Cal Date: Tue, 28 May 2024 12:20:54 -0600 Subject: [PATCH 3/5] avoid erroneous multimatch --- evennia/contrib/rpg/rpsystem/rpsystem.py | 2 +- evennia/contrib/rpg/rpsystem/tests.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/evennia/contrib/rpg/rpsystem/rpsystem.py b/evennia/contrib/rpg/rpsystem/rpsystem.py index b85237e2ad..ed044f0eca 100644 --- a/evennia/contrib/rpg/rpsystem/rpsystem.py +++ b/evennia/contrib/rpg/rpsystem/rpsystem.py @@ -1352,7 +1352,7 @@ class ContribRPObject(DefaultObject): results.extend( [ obj - for obj in search_obj(searched_obj.key, candidates=candidates, **kwargs) + for obj in search_obj(searched_obj.key, candidates=[searched_obj], **kwargs) if obj not in results ] ) diff --git a/evennia/contrib/rpg/rpsystem/tests.py b/evennia/contrib/rpg/rpsystem/tests.py index abef83a4ce..964ab4d3c7 100644 --- a/evennia/contrib/rpg/rpsystem/tests.py +++ b/evennia/contrib/rpg/rpsystem/tests.py @@ -356,6 +356,10 @@ class TestRPSystem(BaseEvenniaTest): result = self.speaker.get_search_result("something", candidates) self.assertIn(self.obj1, result) self.assertIn(self.obj2, result) + # search by sdesc with 2-disambiguator: only second object should be found + result = self.speaker.get_search_result("2-something", candidates) + self.assertNotIn(self.obj1, result) + self.assertIn(self.obj2, result) # search empty candidates: no objects should be found result = self.speaker.get_search_result("something", candidates=[]) self.assertNotIn(self.obj1, result) From c41f5d9ebd7580dafbedc6e7f002d781b3c68d41 Mon Sep 17 00:00:00 2001 From: Cal Date: Tue, 28 May 2024 12:30:14 -0600 Subject: [PATCH 4/5] rewrite new test for clarity --- evennia/contrib/rpg/rpsystem/rpsystem.py | 4 +++- evennia/contrib/rpg/rpsystem/tests.py | 26 +++++++++++++++--------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/evennia/contrib/rpg/rpsystem/rpsystem.py b/evennia/contrib/rpg/rpsystem/rpsystem.py index ed044f0eca..ef53bfa793 100644 --- a/evennia/contrib/rpg/rpsystem/rpsystem.py +++ b/evennia/contrib/rpg/rpsystem/rpsystem.py @@ -1352,7 +1352,9 @@ class ContribRPObject(DefaultObject): results.extend( [ obj - for obj in search_obj(searched_obj.key, candidates=[searched_obj], **kwargs) + for obj in search_obj( + searched_obj.key, candidates=[searched_obj], **kwargs + ) if obj not in results ] ) diff --git a/evennia/contrib/rpg/rpsystem/tests.py b/evennia/contrib/rpg/rpsystem/tests.py index 964ab4d3c7..7c01ae9ca1 100644 --- a/evennia/contrib/rpg/rpsystem/tests.py +++ b/evennia/contrib/rpg/rpsystem/tests.py @@ -352,31 +352,37 @@ class TestRPSystem(BaseEvenniaTest): self.obj2 = create_object(rpsystem.ContribRPCharacter, key="Obj2", location=self.room) self.obj2.sdesc.add("something") candidates = [self.obj1, self.obj2] + # search candidates by sdesc: both objects should be found result = self.speaker.get_search_result("something", candidates) - self.assertIn(self.obj1, result) - self.assertIn(self.obj2, result) + self.assertEqual(list(result), candidates) + # search by sdesc with 2-disambiguator: only second object should be found result = self.speaker.get_search_result("2-something", candidates) - self.assertNotIn(self.obj1, result) - self.assertIn(self.obj2, result) + self.assertEqual(list(result), [self.obj2]) + # search empty candidates: no objects should be found result = self.speaker.get_search_result("something", candidates=[]) - self.assertNotIn(self.obj1, result) - self.assertNotIn(self.obj2, result) + self.assertEqual(list(result), []) + # typeclass was given: only matching object should be found result = self.speaker.get_search_result( "something", candidates=candidates, typeclass=rpsystem.ContribRPCharacter ) - self.assertNotIn(self.obj1, result) - self.assertIn(self.obj2, result) + self.assertEqual(list(result), [self.obj2]) + # search by key with player permissions: no objects should be found result = self.speaker.get_search_result("obj1", candidates) - self.assertNotIn(self.obj1, result) + self.assertEqual(list(result), []) + # search by key with builder permissions: object should be found self.speaker.permissions.add("builder") result = self.speaker.get_search_result("obj1", candidates) - self.assertIn(self.obj1, result) + self.assertEqual(list(result), [self.obj1]) + + # search by key with builder permissions when NOT IN candidates: object should NOT be found + result = self.speaker.get_search_result("obj1", [self.obj2]) + self.assertEqual(list(result), []) class TestRPSystemCommands(BaseEvenniaCommandTest): From 3efeda7e6f4d4c469ef579170e69fff96fb69e68 Mon Sep 17 00:00:00 2001 From: Cal Date: Fri, 28 Jun 2024 16:22:15 -0600 Subject: [PATCH 5/5] fix to fit PEP8 standards --- evennia/contrib/rpg/rpsystem/rpsystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/contrib/rpg/rpsystem/rpsystem.py b/evennia/contrib/rpg/rpsystem/rpsystem.py index ef53bfa793..b4938e8d3a 100644 --- a/evennia/contrib/rpg/rpsystem/rpsystem.py +++ b/evennia/contrib/rpg/rpsystem/rpsystem.py @@ -442,7 +442,7 @@ def parse_sdescs_and_recogs( (re.search(rquery, text, _RE_FLAGS), obj, text) for obj, text in candidate_map ) # filter out any non-matching candidates - bestmatches = [(obj, m.group()) for m, obj, text in matches if m] + bestmatches = [(obj, mtch.group()) for mtch, obj, text in matches if mtch] else: # to find the longest match, we start from the marker and lengthen the