From 710d3e2143512355875dba7de9b53098519ac698 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 18 Jul 2020 19:25:37 +0200 Subject: [PATCH 01/10] Add script.obj.dbref to scripts output. Resolves #2167 --- evennia/commands/default/system.py | 2 +- evennia/commands/default/tests.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index 2c67951f73..b6811cfb02 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -446,7 +446,7 @@ def format_script_list(scripts): table.add_row( script.id, - script.obj.key if (hasattr(script, "obj") and script.obj) else "", + f"{script.obj.key}({script.obj.dbref})" if (hasattr(script, "obj") and script.obj) else "", script.key, script.interval if script.interval > 0 else "--", nextrep, diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index f9f48ca699..e4978dd879 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -1123,7 +1123,7 @@ class TestBuilding(CommandTest): "= Obj", "To create a global script you need scripts/add .", ) - self.call(building.CmdScript(), "Obj = ", "dbref obj") + self.call(building.CmdScript(), "Obj = ", "dbref obj") self.call( building.CmdScript(), "/start Obj", "0 scripts started on Obj" From 006e8aeee83bf686ba2aa43395c72ea4730e7ecb Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 18 Jul 2020 19:37:49 +0200 Subject: [PATCH 02/10] Make CmdGet/Drop/Give give error if obj.move_to returns False. Resolves #2168. --- CHANGELOG.md | 17 +++++++------ evennia/commands/default/general.py | 39 ++++++++++++++++++----------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c41f391940..688cec7892 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,10 +37,10 @@ without arguments starts a full interactive Python console. - EvMore auto-justify now defaults to False since this works better with all types of texts (such as tables). New `justify` bool. Old `justify_kwargs` remains but is now only used to pass extra kwargs into the justify function. -- EvMore `text` argument can now also be a list or a queryset. Querysets will be - sliced to only return the required data per page. EvMore takes a new kwarg - `page_formatter` which will be called for each page. This allows to customize - the display of queryset data, build a new EvTable per page etc. +- EvMore `text` argument can now also be a list or a queryset. Querysets will be + sliced to only return the required data per page. EvMore takes a new kwarg + `page_formatter` which will be called for each page. This allows to customize + the display of queryset data, build a new EvTable per page etc. - Improve performance of `find` and `objects` commands on large data sets (strikaco) - New `CHANNEL_HANDLER_CLASS` setting allows for replacing the ChannelHandler entirely. - Made `py` interactive mode support regular quit() and more verbose. @@ -50,20 +50,21 @@ without arguments starts a full interactive Python console. `.get_command_info()` method for easier overloading and access. (Volund) - Removed unused `CYCLE_LOGFILES` setting. Added `SERVER_LOG_DAY_ROTATION` and `SERVER_LOG_MAX_SIZE` (and equivalent for PORTAL) to control log rotation. -- Addded `inside_rec` lockfunc - if room is locked, the normal `inside()` lockfunc will +- Addded `inside_rec` lockfunc - if room is locked, the normal `inside()` lockfunc will fail e.g. for your inventory objs (since their loc is you), whereas this will pass. - RPSystem contrib's CmdRecog will now list all recogs if no arg is given. Also multiple bugfixes. -- Remove `dummy@example.com` as a default account email when unset, a string is no longer +- Remove `dummy@example.com` as a default account email when unset, a string is no longer required by Django. - Fixes to `spawn`, make updating an existing prototype/object work better. Add `/raw` switch to `spawn` command to extract the raw prototype dict for manual editing. -- `list_to_string` is now `iter_to_string` (but old name still works as legacy alias). It will - now accept any input, including generators and single values. +- `list_to_string` is now `iter_to_string` (but old name still works as legacy alias). It will + now accept any input, including generators and single values. - EvTable should now correctly handle columns with wider asian-characters in them. - Update Twisted requirement to >=2.3.0 to close security vulnerability - Add `$random` inlinefunc, supports minval,maxval arguments that can be ints and floats. - Add `evennia.utils.inlinefuncs.raw()` as a helper to escape inlinefuncs in a string. +- Make CmdGet/Drop/Give give proper error if `obj.move_to` returns `False`. ## Evennia 0.9 (2018-2019) diff --git a/evennia/commands/default/general.py b/evennia/commands/default/general.py index 56dbf94231..cfe7b8e098 100644 --- a/evennia/commands/default/general.py +++ b/evennia/commands/default/general.py @@ -426,11 +426,14 @@ class CmdGet(COMMAND_DEFAULT_CLASS): if not obj.at_before_get(caller): return - obj.move_to(caller, quiet=True) - caller.msg("You pick up %s." % obj.name) - caller.location.msg_contents("%s picks up %s." % (caller.name, obj.name), exclude=caller) - # calling at_get hook method - obj.at_get(caller) + success = obj.move_to(caller, quiet=True) + if not success: + caller.msg("This can't be picked up.") + else: + caller.msg("You pick up %s." % obj.name) + caller.location.msg_contents("%s picks up %s." % (caller.name, obj.name), exclude=caller) + # calling at_get hook method + obj.at_get(caller) class CmdDrop(COMMAND_DEFAULT_CLASS): @@ -471,11 +474,14 @@ class CmdDrop(COMMAND_DEFAULT_CLASS): if not obj.at_before_drop(caller): return - obj.move_to(caller.location, quiet=True) - caller.msg("You drop %s." % (obj.name,)) - caller.location.msg_contents("%s drops %s." % (caller.name, obj.name), exclude=caller) - # Call the object script's at_drop() method. - obj.at_drop(caller) + success = obj.move_to(caller.location, quiet=True) + if not success: + caller.msg("This couldn't be dropped.") + else: + caller.msg("You drop %s." % (obj.name,)) + caller.location.msg_contents("%s drops %s." % (caller.name, obj.name), exclude=caller) + # Call the object script's at_drop() method. + obj.at_drop(caller) class CmdGive(COMMAND_DEFAULT_CLASS): @@ -522,11 +528,14 @@ class CmdGive(COMMAND_DEFAULT_CLASS): return # give object - caller.msg("You give %s to %s." % (to_give.key, target.key)) - to_give.move_to(target, quiet=True) - target.msg("%s gives you %s." % (caller.key, to_give.key)) - # Call the object script's at_give() method. - to_give.at_give(caller, target) + success = to_give.move_to(target, quiet=True) + if not success: + caller.msg("This could not be given.") + else: + caller.msg("You give %s to %s." % (to_give.key, target.key)) + target.msg("%s gives you %s." % (caller.key, to_give.key)) + # Call the object script's at_give() method. + to_give.at_give(caller, target) class CmdSetDesc(COMMAND_DEFAULT_CLASS): From 100fc7b5bf3636a90f38c4c040fac8717dc3b276 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 18 Jul 2020 19:40:02 +0200 Subject: [PATCH 03/10] Remove duplicate get_command_info in MuxCommand base. Resolves #2164 --- evennia/commands/default/muxcommand.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/evennia/commands/default/muxcommand.py b/evennia/commands/default/muxcommand.py index 5e0967a130..e7eee50992 100644 --- a/evennia/commands/default/muxcommand.py +++ b/evennia/commands/default/muxcommand.py @@ -202,12 +202,6 @@ class MuxCommand(Command): else: self.character = None - def get_command_info(self): - """ - Update of parent class's get_command_info() for MuxCommand. - """ - self.get_command_info() - def get_command_info(self): """ Update of parent class's get_command_info() for MuxCommand. From 2d61f36ff482d0521f3eaf00930a3b8b12ace292 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 18 Jul 2020 21:36:03 +0200 Subject: [PATCH 04/10] Add tags= keywords to all create functions, with unit tests. Resolves #2160 --- evennia/comms/comms.py | 2 + evennia/utils/create.py | 21 +++-- evennia/utils/tests/test_create_functions.py | 91 ++++++++++++++++++++ 3 files changed, 108 insertions(+), 6 deletions(-) diff --git a/evennia/comms/comms.py b/evennia/comms/comms.py index 430478828c..b9a308c350 100644 --- a/evennia/comms/comms.py +++ b/evennia/comms/comms.py @@ -51,6 +51,8 @@ class DefaultChannel(ChannelDB, metaclass=TypeclassBase): self.attributes.add("keep_log", cdict["keep_log"]) if cdict.get("desc"): self.attributes.add("desc", cdict["desc"]) + if cdict.get("tags"): + self.tags.batch_add(*cdict["tags"]) def basetype_setup(self): # delayed import of the channelhandler diff --git a/evennia/utils/create.py b/evennia/utils/create.py index 74fa49ffa2..3fbc6e2ea9 100644 --- a/evennia/utils/create.py +++ b/evennia/utils/create.py @@ -313,7 +313,7 @@ script = create_script # -def create_help_entry(key, entrytext, category="General", locks=None, aliases=None): +def create_help_entry(key, entrytext, category="General", locks=None, aliases=None, tags=None): """ Create a static help entry in the help database. Note that Command help entries are dynamic and directly taken from the __doc__ @@ -326,7 +326,8 @@ def create_help_entry(key, entrytext, category="General", locks=None, aliases=No entrytext (str): The body of te help entry category (str, optional): The help category of the entry. locks (str, optional): A lockstring to restrict access. - aliases (list of str): List of alternative (likely shorter) keynames. + aliases (list of str, optional): List of alternative (likely shorter) keynames. + tags (lst, optional): List of tags or tuples `(tag, category)`. Returns: help (HelpEntry): A newly created help entry. @@ -344,7 +345,9 @@ def create_help_entry(key, entrytext, category="General", locks=None, aliases=No if locks: new_help.locks.add(locks) if aliases: - new_help.aliases.add(aliases) + new_help.aliases.add(make_iter(aliases)) + if tags: + new_help.tags.batch_add(*tags) new_help.save() return new_help except IntegrityError: @@ -366,7 +369,8 @@ help_entry = create_help_entry # Comm system methods -def create_message(senderobj, message, channels=None, receivers=None, locks=None, header=None): +def create_message(senderobj, message, channels=None, receivers=None, + locks=None, tags=None, header=None): """ Create a new communication Msg. Msgs represent a unit of database-persistent communication between entites. @@ -382,6 +386,7 @@ def create_message(senderobj, message, channels=None, receivers=None, locks=None receivers (Object, Account, str or list): An Account/Object to send to, or a list of them. May be Account objects or accountnames. locks (str): Lock definition string. + tags (list): A list of tags or tuples `(tag, category)`. header (str): Mime-type or other optional information for the message Notes: @@ -408,6 +413,9 @@ def create_message(senderobj, message, channels=None, receivers=None, locks=None new_message.receivers = receiver if locks: new_message.locks.add(locks) + if tags: + new_message.tags.batch_add(*tags) + new_message.save() return new_message @@ -416,7 +424,7 @@ message = create_message create_msg = create_message -def create_channel(key, aliases=None, desc=None, locks=None, keep_log=True, typeclass=None): +def create_channel(key, aliases=None, desc=None, locks=None, keep_log=True, typeclass=None, tags=None): """ Create A communication Channel. A Channel serves as a central hub for distributing Msgs to groups of people without specifying the @@ -435,6 +443,7 @@ def create_channel(key, aliases=None, desc=None, locks=None, keep_log=True, type keep_log (bool): Log channel throughput. typeclass (str or class): The typeclass of the Channel (not often used). + tags (list): A list of tags or tuples `(tag, category)`. Returns: channel (Channel): A newly created channel. @@ -451,7 +460,7 @@ def create_channel(key, aliases=None, desc=None, locks=None, keep_log=True, type # store call signature for the signal new_channel._createdict = dict( - key=key, aliases=aliases, desc=desc, locks=locks, keep_log=keep_log + key=key, aliases=aliases, desc=desc, locks=locks, keep_log=keep_log, tags=tags ) # this will trigger the save signal which in turn calls the diff --git a/evennia/utils/tests/test_create_functions.py b/evennia/utils/tests/test_create_functions.py index aaa0b4927a..f996880af3 100644 --- a/evennia/utils/tests/test_create_functions.py +++ b/evennia/utils/tests/test_create_functions.py @@ -3,6 +3,7 @@ Tests of create functions """ +from django.test import TestCase from evennia.utils.test_resources import EvenniaTest from evennia.scripts.scripts import DefaultScript from evennia.utils import create @@ -76,3 +77,93 @@ class TestCreateScript(EvenniaTest): assert script.repeats == 1 assert script.key == "test_script" script.stop() + + +class TestCreateHelpEntry(TestCase): + + help_entry = """ + Qui laborum voluptas quis commodi ipsum quo temporibus eum. Facilis + assumenda facilis architecto in corrupti. Est placeat eum amet qui beatae + reiciendis. Accusamus vel aspernatur ab ex. Quam expedita sed expedita + consequuntur est dolorum non exercitationem. + + Ipsa vel ut dolorem voluptatem adipisci velit. Sit odit temporibus mollitia + illum ipsam placeat. Rem et ipsum dolor. Hic eum tempore excepturi qui veniam + magni. + + Excepturi quam repellendus inventore excepturi fugiat quo quasi molestias. + Nostrum ut assumenda enim a. Repellat quis omnis est officia accusantium. Fugit + facere qui aperiam. Perspiciatis commodi dolores ipsam nemo consequatur + quisquam qui non. Adipisci et molestias voluptatum est sed fugiat facere. + + """ + + def test_create_help_entry__simple(self): + entry = create.create_help_entry("testentry", self.help_entry, category="Testing") + self.assertEqual(entry.key, "testentry") + self.assertEqual(entry.entrytext, self.help_entry) + self.assertEqual(entry.help_category, "Testing") + + # creating same-named entry should not work (must edit existing) + self.assertFalse(create.create_help_entry("testentry", "testtext")) + + def test_create_help_entry__complex(self): + locks = "foo:false();bar:true()" + aliases = ['foo', 'bar', 'tst'] + tags = [("tag1", "help"), ("tag2", "help"), ("tag3", "help")] + + entry = create.create_help_entry("testentry", self.help_entry, category="Testing", + locks=locks, aliases=aliases, tags=tags) + self.assertTrue(all(lock in entry.locks.all() for lock in locks.split(";"))) + self.assertEqual(list(entry.aliases.all()).sort(), aliases.sort()) + self.assertEqual(entry.tags.all(return_key_and_category=True), tags) + + +class TestCreateMessage(EvenniaTest): + + msgtext = """ + Qui laborum voluptas quis commodi ipsum quo temporibus eum. Facilis + assumenda facilis architecto in corrupti. Est placeat eum amet qui beatae + reiciendis. Accusamus vel aspernatur ab ex. Quam expedita sed expedita + consequuntur est dolorum non exercitationem. + """ + + def test_create_msg__simple(self): + msg = create.create_message(self.char1, self.msgtext, header="TestHeader") + self.assertEqual(msg.message, self.msgtext) + self.assertEqual(msg.header, "TestHeader") + self.assertEqual(msg.senders, [self.char1]) + + def test_create_msg__channel(self): + chan1 = create.create_channel("DummyChannel1") + chan2 = create.create_channel("DummyChannel2") + msg = create.create_message(self.char1, self.msgtext, channels=[chan1, chan2], header="TestHeader") + self.assertEqual(list(msg.channels), [chan1, chan2]) + + def test_create_msg__custom(self): + locks = "foo:false();bar:true()" + tags = ["tag1", "tag2", "tag3"] + msg = create.create_message(self.char1, self.msgtext, header="TestHeader", + receivers=[self.char1, self.char2], locks=locks, tags=tags) + self.assertEqual(msg.receivers, [self.char1, self.char2]) + self.assertTrue(all(lock in msg.locks.all() for lock in locks.split(";"))) + self.assertEqual(msg.tags.all(), tags) + + +class TestCreateChannel(TestCase): + + def test_create_channel__simple(self): + chan = create.create_channel("TestChannel1", desc="Testing channel") + self.assertEqual(chan.key, "TestChannel1") + self.assertEqual(chan.db.desc, "Testing channel") + + def test_create_channel__complex(self): + locks = "foo:false();bar:true()" + tags = ["tag1", "tag2", "tag3"] + aliases = ['foo', 'bar', 'tst'] + + chan = create.create_channel("TestChannel2", desc="Testing channel", + aliases=aliases, locks=locks, tags=tags) + self.assertTrue(all(lock in chan.locks.all() for lock in locks.split(";"))) + self.assertEqual(chan.tags.all(), tags) + self.assertEqual(list(chan.aliases.all()).sort(), aliases.sort()) From d042b441ce46ac5cffff4a007d443f59ea38763a Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 18 Jul 2020 21:49:15 +0200 Subject: [PATCH 05/10] Black formatting artefact. Resolves #2158 --- evennia/objects/objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 54325846bb..0fcabca4a2 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -206,7 +206,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): # lockstring of newly created objects, for easy overloading. # Will be formatted with the appropriate attributes. - lockstring = "control:id({account_id}) or perm(Admin);" "delete:id({account_id}) or perm(Admin)" + lockstring = "control:id({account_id}) or perm(Admin);delete:id({account_id}) or perm(Admin)" objects = ObjectManager() From 5c3049a4384834d5583c2c3291680cb07b56ebc5 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 18 Jul 2020 21:56:34 +0200 Subject: [PATCH 06/10] Correct grammar for maxnum chars reached. Resolves #2151 --- evennia/commands/default/account.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evennia/commands/default/account.py b/evennia/commands/default/account.py index 33e8aed55b..6f199a8a30 100644 --- a/evennia/commands/default/account.py +++ b/evennia/commands/default/account.py @@ -154,7 +154,8 @@ class CmdCharCreate(COMMAND_DEFAULT_CLASS): if not account.is_superuser and ( account.db._playable_characters and len(account.db._playable_characters) >= charmax ): - self.msg("You may only create a maximum of %i characters." % charmax) + plural = "" if charmax == 1 else "s" + self.msg(f"You may only create a maximum of {charmax} character{plural}.") return from evennia.objects.models import ObjectDB From 2ed485b2b22f1cdc72acc73791ed412996ed7fd1 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 18 Jul 2020 21:59:31 +0200 Subject: [PATCH 07/10] Doc fix in tag docstring. Resolves #2155 --- evennia/typeclasses/tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/typeclasses/tags.py b/evennia/typeclasses/tags.py index 2cd4ae027d..23b3f0562f 100644 --- a/evennia/typeclasses/tags.py +++ b/evennia/typeclasses/tags.py @@ -36,7 +36,7 @@ class Tag(models.Model): indexed for efficient lookup in the database. Tags are shared between objects - a new tag is only created if the key+category combination did not previously exist, making them unsuitable for - storing object-related data (for this a full tag should be + storing object-related data (for this a regular Attribute should be used). The 'db_data' field is intended as a documentation field for the From 7b13553f25aad9f845bc5c0d5f519515170203fc Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 18 Jul 2020 22:37:43 +0200 Subject: [PATCH 08/10] Make DefaultObject/Room/Exit's argument optional. Also resolves #2162. --- CHANGELOG.md | 2 ++ evennia/objects/objects.py | 39 ++++++++++++++++++++++++++------------ evennia/objects/tests.py | 2 +- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 688cec7892..7450063360 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,8 @@ without arguments starts a full interactive Python console. - Add `$random` inlinefunc, supports minval,maxval arguments that can be ints and floats. - Add `evennia.utils.inlinefuncs.raw()` as a helper to escape inlinefuncs in a string. - Make CmdGet/Drop/Give give proper error if `obj.move_to` returns `False`. +- Make `Object/Room/Exit.create`'s `account` argument optional. If not given, will set perms + to that of the object itself (along with normal Admin/Dev permission). ## Evennia 0.9 (2018-2019) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 0fcabca4a2..d2bb4c2b2c 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -2029,10 +2029,11 @@ class DefaultCharacter(DefaultObject): # lockstring of newly created rooms, for easy overloading. # Will be formatted with the appropriate attributes. - lockstring = "puppet:id({character_id}) or pid({account_id}) or perm(Developer) or pperm(Developer);delete:id({account_id}) or perm(Admin)" + lockstring = ("puppet:id({character_id}) or pid({account_id}) or perm(Developer) or pperm(Developer);" + "delete:id({account_id}) or perm(Admin)") @classmethod - def create(cls, key, account, **kwargs): + def create(cls, key, account=None, **kwargs): """ Creates a basic Character with default parameters, unless otherwise specified or extended. @@ -2041,8 +2042,8 @@ class DefaultCharacter(DefaultObject): Args: key (str): Name of the new Character. - account (obj): Account to associate this Character with. Required as - an argument, but one can fake it out by supplying None-- it will + account (obj, optional): Account to associate this Character with. + If unset supplying None-- it will change the default lockset and skip creator attribution. Kwargs: @@ -2249,7 +2250,7 @@ class DefaultRoom(DefaultObject): ) @classmethod - def create(cls, key, account, **kwargs): + def create(cls, key, account=None, **kwargs): """ Creates a basic Room with default parameters, unless otherwise specified or extended. @@ -2258,7 +2259,9 @@ class DefaultRoom(DefaultObject): Args: key (str): Name of the new Room. - account (obj): Account to associate this Room with. + account (obj, optional): Account to associate this Room with. If + given, it will be given specific control/edit permissions to this + object (along with normal Admin perms). If not given, default Kwargs: description (str): Brief description for this object. @@ -2287,13 +2290,20 @@ class DefaultRoom(DefaultObject): # Get description, if provided description = kwargs.pop("description", "") + # get locks if provided + locks = kwargs.pop("locks", "") + try: # Create the Room obj = create.create_object(**kwargs) - # Set appropriate locks - lockstring = kwargs.get("locks", cls.lockstring.format(id=account.id)) - obj.locks.add(lockstring) + # Add locks + if not locks and account: + locks = cls.lockstring.format(**{"id": account.id}) + elif not locks and not account: + locks = cls.lockstring(**{"id": obj.id}) + + obj.locks.add(locks) # Record creator id and creation IP if ip: @@ -2442,7 +2452,7 @@ class DefaultExit(DefaultObject): # Command hooks @classmethod - def create(cls, key, account, source, dest, **kwargs): + def create(cls, key, source, dest, account=None, **kwargs): """ Creates a basic Exit with default parameters, unless otherwise specified or extended. @@ -2486,13 +2496,18 @@ class DefaultExit(DefaultObject): description = kwargs.pop("description", "") + locks = kwargs.get("locks", "") + try: # Create the Exit obj = create.create_object(**kwargs) # Set appropriate locks - lockstring = kwargs.get("locks", cls.lockstring.format(id=account.id)) - obj.locks.add(lockstring) + if not locks and account: + locks = cls.lockstring.format(**{"id": account.id}) + elif not locks and not account: + locks = cls.lockstring.format(**{"id": obj.id}) + obj.locks.add(locks) # Record creator id and creation IP if ip: diff --git a/evennia/objects/tests.py b/evennia/objects/tests.py index 75f91cba36..a63bc87079 100644 --- a/evennia/objects/tests.py +++ b/evennia/objects/tests.py @@ -50,7 +50,7 @@ class DefaultObjectTest(EvenniaTest): def test_exit_create(self): description = "The steaming depths of the dumpster, ripe with refuse in various states of decomposition." obj, errors = DefaultExit.create( - "in", self.account, self.room1, self.room2, description=description, ip=self.ip + "in", self.room1, self.room2, account=self.account, description=description, ip=self.ip ) self.assertTrue(obj, errors) self.assertFalse(errors, errors) From 6dc68acd1d48981a4b2fe83d76039197bfe61452 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 18 Jul 2020 22:47:40 +0200 Subject: [PATCH 09/10] Ensure uniqueness of Msg sender_strings list. Resolves #2127. --- evennia/comms/comms.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/evennia/comms/comms.py b/evennia/comms/comms.py index b9a308c350..e6ee5eef8c 100644 --- a/evennia/comms/comms.py +++ b/evennia/comms/comms.py @@ -396,7 +396,8 @@ class DefaultChannel(ChannelDB, metaclass=TypeclassBase): to build senders for the message. sender_strings (list, optional): Name strings of senders. Used for external connections where the sender is not an account or object. - When this is defined, external will be assumed. + When this is defined, external will be assumed. The list will be + filtered so each sender-string only occurs once. keep_log (bool or None, optional): This allows to temporarily change the logging status of this channel message. If `None`, the Channel's `keep_log` Attribute will be used. If `True` or `False`, that logging status will be used for this @@ -428,7 +429,7 @@ class DefaultChannel(ChannelDB, metaclass=TypeclassBase): if not msgobj: return False msgobj = self.message_transform( - msgobj, emit=emit, sender_strings=sender_strings, external=external + msgobj, emit=emit, sender_strings=list(set(sender_strings)), external=external ) self.distribute_message(msgobj, online=online) self.post_send_message(msgobj) From 8187f742abcdaf201fd06e46990b3dce77166fe0 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 18 Jul 2020 23:10:46 +0200 Subject: [PATCH 10/10] Add unittest for Attr/Tag.batch_add, clarify docstrings. Resolves #2125 --- evennia/comms/comms.py | 4 +++- evennia/typeclasses/attributes.py | 5 +++-- evennia/typeclasses/tags.py | 4 ++-- evennia/typeclasses/tests.py | 24 ++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/evennia/comms/comms.py b/evennia/comms/comms.py index e6ee5eef8c..a6a6330b32 100644 --- a/evennia/comms/comms.py +++ b/evennia/comms/comms.py @@ -428,8 +428,10 @@ class DefaultChannel(ChannelDB, metaclass=TypeclassBase): msgobj = self.pre_send_message(msgobj) if not msgobj: return False + if sender_strings: + sender_strings = list(set(make_iter(sender_strings))) msgobj = self.message_transform( - msgobj, emit=emit, sender_strings=list(set(sender_strings)), external=external + msgobj, emit=emit, sender_strings=sender_strings, external=external ) self.distribute_message(msgobj, online=online) self.post_send_message(msgobj) diff --git a/evennia/typeclasses/attributes.py b/evennia/typeclasses/attributes.py index 6fb4870dae..bc6b61dccf 100644 --- a/evennia/typeclasses/attributes.py +++ b/evennia/typeclasses/attributes.py @@ -593,8 +593,9 @@ class AttributeHandler(object): repeat-calling add when having many Attributes to add. Args: - indata (list): List of tuples of varying length representing the - Attribute to add to this object. Supported tuples are + *args (tuple): Each argument should be a tuples (can be of varying + length) representing the Attribute to add to this object. + Supported tuples are - `(key, value)` - `(key, value, category)` - `(key, value, category, lockstring)` diff --git a/evennia/typeclasses/tags.py b/evennia/typeclasses/tags.py index 23b3f0562f..c8f121d17d 100644 --- a/evennia/typeclasses/tags.py +++ b/evennia/typeclasses/tags.py @@ -449,8 +449,8 @@ class TagHandler(object): Batch-add tags from a list of tuples. Args: - tuples (tuple or str): Any number of `tagstr` keys, `(keystr, category)` or - `(keystr, category, data)` tuples. + *args (tuple or str): Each argument should be a `tagstr` keys or tuple `(keystr, category)` or + `(keystr, category, data)`. It's possible to mix input types. Notes: This will generate a mimimal number of self.add calls, diff --git a/evennia/typeclasses/tests.py b/evennia/typeclasses/tests.py index a49f1d2645..270a4f4525 100644 --- a/evennia/typeclasses/tests.py +++ b/evennia/typeclasses/tests.py @@ -42,6 +42,17 @@ class TestAttributes(EvenniaTest): self.obj1.attributes.add(key, value) self.assertEqual(self.obj1.attributes.get(key), value) + def test_batch_add(self): + attrs = [("key1", "value1"), + ("key2", "value2", "category2"), + ("key3", "value3"), + ("key4", "value4", "category4", "attrread:id(1)", False)] + new_attrs = self.obj1.attributes.batch_add(*attrs) + attrobj = self.obj1.attributes.get(key="key4", category="category4", return_obj=True) + self.assertEqual(attrobj.value, "value4") + self.assertEqual(attrobj.category, "category4") + self.assertEqual(attrobj.locks.all(), ["attrread:id(1)"]) + class TestTypedObjectManager(EvenniaTest): def _manager(self, methodname, *args, **kwargs): @@ -146,3 +157,16 @@ class TestTypedObjectManager(EvenniaTest): ), [], ) + + def test_batch_add(self): + tags = ["tag1", + ("tag2", "category2"), + "tag3", + ("tag4", "category4", "data4") + ] + self.obj1.tags.batch_add(*tags) + self.assertEqual(self.obj1.tags.get("tag1"), "tag1") + tagobj = self.obj1.tags.get("tag4", category="category4", return_tagobj=True) + self.assertEqual(tagobj.db_key, "tag4") + self.assertEqual(tagobj.db_category, "category4") + self.assertEqual(tagobj.db_data, "data4")