From 0baf7c567e8b06a840a58c948971dd9413e1c8ad Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:14:57 -0500 Subject: [PATCH 01/31] Mark print line as DEBUG This debug line spams unit tests for comms. Be sure to note for easy location for later removal. --- evennia/comms/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/comms/models.py b/evennia/comms/models.py index c456a6e72c..e66f00e813 100644 --- a/evennia/comms/models.py +++ b/evennia/comms/models.py @@ -527,7 +527,7 @@ class SubscriptionHandler(object): for subscriber in make_iter(entity): if subscriber: clsname = subscriber.__dbclass__.__name__ - print("subscriber:", subscriber, clsname) + print("subscriber:", subscriber, clsname) # DEBUG # chooses the right type if clsname == "ObjectDB": self.obj.db_object_subscriptions.add(subscriber) From 91a6ced885a53de66269108a6924f0cd19c4c4d5 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:18:18 -0500 Subject: [PATCH 02/31] MUX command add optional self.option, self.split These optional variables aid in parsing, if set. self.options = (tuple of valid /switches expected by this command self.split = Alternate string delimiter to separate left/right side. --- evennia/commands/default/muxcommand.py | 48 +++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/evennia/commands/default/muxcommand.py b/evennia/commands/default/muxcommand.py index 5d8d4b2890..6d731ea14f 100644 --- a/evennia/commands/default/muxcommand.py +++ b/evennia/commands/default/muxcommand.py @@ -79,6 +79,11 @@ class MuxCommand(Command): it here). The rest of the command is stored in self.args, which can start with the switch indicator /. + Optional variables to aid in parsing, if set: + self.options = (tuple of valid /switches expected + by this command (without the /)) + self.split = Alternate string delimiter to separate left/right hand side. + This parser breaks self.args into its constituents and stores them in the following variables: self.switches = [list of /switches (without the /)] @@ -109,15 +114,44 @@ class MuxCommand(Command): else: args = "" switches = switches[0].split('/') + # Parse mux options, comparing them against user-provided switches, expanding abbreviations. + if hasattr(self, "options") and self.options and switches: + # If specific options are known, test them against given switches. + valid_switches, unused_switches, extra_switches = [], [], [] + for element in switches: + option_check = [each for each in self.options if each.lower().startswith(element.lower())] + if len(option_check) > 1: + extra_switches += option_check # Either the option provided is ambiguous, + elif len(option_check) == 1: + valid_switches += option_check # or it is a valid option abbreviation, + elif len(option_check) == 0: + unused_switches += [element] # or an extraneous option to be ignored. + if extra_switches: # User provided switches + self.msg('|g%s|n: |wAmbiguous switch supplied: Did you mean /|C%s|w?' % + (self.cmdstring, ' |nor /|C'.join(extra_switches))) + if unused_switches: + plural = '' if len(unused_switches) == 1 else 'es' + self.msg('|g%s|n: |wExtra switch%s "/|C%s|w" ignored.' % + (self.cmdstring, plural, '|n, /|C'.join(unused_switches))) + switches = valid_switches # Only include valid_switches in command function call arglist = [arg.strip() for arg in args.split()] # check for arg1, arg2, ... = argA, argB, ... constructs - lhs, rhs = args, None + lhs, rhs = args.strip(), None lhslist, rhslist = [arg.strip() for arg in args.split(',')], [] - if args and '=' in args: - lhs, rhs = [arg.strip() for arg in args.split('=', 1)] - lhslist = [arg.strip() for arg in lhs.split(',')] - rhslist = [arg.strip() for arg in rhs.split(',')] + if lhs: + if '=' in lhs: # Default delimiter has priority + # Parse to separate left into left/right sides using default delimiter + lhs, rhs = lhs.split('=', 1) + elif hasattr(self, "split") and self.split and self.split in lhs: + # Parse to separate left into left/right sides using a custom delimiter, if provided. + lhs, rhs = lhs.split(self.split, 1) # At most, split once, into left and right parts. + # Trim user-injected whitespace + rhs = rhs.strip() if rhs is not None else None + lhs = lhs.strip() + # Further split left/right sides by comma delimiter + lhslist = [arg.strip() for arg in lhs.split(',')] if lhs is not None else "" + rhslist = [arg.strip() for arg in rhs.split(',')] if rhs is not None else "" # save to object properties: self.raw = raw @@ -169,6 +203,10 @@ class MuxCommand(Command): string += "\nraw argument (self.raw): |w%s|n \n" % self.raw string += "cmd args (self.args): |w%s|n\n" % self.args string += "cmd switches (self.switches): |w%s|n\n" % self.switches + if hasattr(self, "options"): # Optional + string += "cmd options (self.options): |w%s|n\n" % self.options + if hasattr(self, "split"): # Optional + string += "cmd parse left/right using (self.split): |w%s|n\n" % self.split string += "space-separated arg list (self.arglist): |w%s|n\n" % self.arglist string += "lhs, left-hand side of '=' (self.lhs): |w%s|n\n" % self.lhs string += "lhs, comma separated (self.lhslist): |w%s|n\n" % self.lhslist From e1881fb8809921769a9b1d687b2876221280cc27 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:22:26 -0500 Subject: [PATCH 03/31] Add tests for MUX command added feature Shortened long lines, added tests with different parses for MUX command added class vars: options and split --- evennia/commands/default/tests.py | 84 ++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 38829f0cf5..f581c32589 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -72,7 +72,6 @@ class CommandTest(EvenniaTest): cmdobj.obj = obj or (caller if caller else self.char1) # test old_msg = receiver.msg - returned_msg = "" try: receiver.msg = Mock() cmdobj.at_pre_cmd() @@ -126,9 +125,12 @@ class TestGeneral(CommandTest): self.call(general.CmdPose(), "looks around", "Char looks around") def test_nick(self): - self.call(general.CmdNick(), "testalias = testaliasedstring1", "Inputlinenick 'testalias' mapped to 'testaliasedstring1'.") - self.call(general.CmdNick(), "/account testalias = testaliasedstring2", "Accountnick 'testalias' mapped to 'testaliasedstring2'.") - self.call(general.CmdNick(), "/object testalias = testaliasedstring3", "Objectnick 'testalias' mapped to 'testaliasedstring3'.") + self.call(general.CmdNick(), "testalias = testaliasedstring1", + "Inputlinenick 'testalias' mapped to 'testaliasedstring1'.") + self.call(general.CmdNick(), "/account testalias = testaliasedstring2", + "Accountnick 'testalias' mapped to 'testaliasedstring2'.") + self.call(general.CmdNick(), "/object testalias = testaliasedstring3", + "Objectnick 'testalias' mapped to 'testaliasedstring3'.") self.assertEqual(u"testaliasedstring1", self.char1.nicks.get("testalias")) self.assertEqual(None, self.char1.nicks.get("testalias", category="account")) self.assertEqual(u"testaliasedstring2", self.char1.account.nicks.get("testalias", category="account")) @@ -225,7 +227,8 @@ class TestAccount(CommandTest): self.call(account.CmdColorTest(), "ansi", "ANSI colors:", caller=self.account) def test_char_create(self): - self.call(account.CmdCharCreate(), "Test1=Test char", "Created new character Test1. Use @ic Test1 to enter the game", caller=self.account) + self.call(account.CmdCharCreate(), "Test1=Test char", + "Created new character Test1. Use @ic Test1 to enter the game", caller=self.account) def test_quell(self): self.call(account.CmdQuell(), "", "Quelling to current puppet's permissions (developer).", caller=self.account) @@ -234,7 +237,8 @@ class TestAccount(CommandTest): class TestBuilding(CommandTest): def test_create(self): name = settings.BASE_OBJECT_TYPECLASS.rsplit('.', 1)[1] - self.call(building.CmdCreate(), "/drop TestObj1", "You create a new %s: TestObj1." % name) + self.call(building.CmdCreate(), "/d TestObj1", # /d switch is abbreviated form of /drop + "You create a new %s: TestObj1." % name) def test_examine(self): self.call(building.CmdExamine(), "Obj", "Name/key: Obj") @@ -244,7 +248,8 @@ class TestBuilding(CommandTest): self.call(building.CmdSetObjAlias(), "Obj = TestObj1b", "Alias(es) for 'Obj(#4)' set to 'testobj1b'.") def test_copy(self): - self.call(building.CmdCopy(), "Obj = TestObj2;TestObj2b, TestObj3;TestObj3b", "Copied Obj to 'TestObj3' (aliases: ['TestObj3b']") + self.call(building.CmdCopy(), "Obj = TestObj2;TestObj2b, TestObj3;TestObj3b", + "Copied Obj to 'TestObj3' (aliases: ['TestObj3b']") def test_attribute_commands(self): self.call(building.CmdSetAttribute(), "Obj/test1=\"value1\"", "Created attribute Obj/test1 = 'value1'") @@ -287,7 +292,8 @@ class TestBuilding(CommandTest): def test_typeclass(self): self.call(building.CmdTypeclass(), "Obj = evennia.objects.objects.DefaultExit", - "Obj changed typeclass from evennia.objects.objects.DefaultObject to evennia.objects.objects.DefaultExit.") + "Obj changed typeclass from evennia.objects.objects.DefaultObject " + "to evennia.objects.objects.DefaultExit.") def test_lock(self): self.call(building.CmdLock(), "Obj = test:perm(Developer)", "Added lock 'test:perm(Developer)' to Obj.") @@ -297,15 +303,24 @@ class TestBuilding(CommandTest): expect = "One Match(#1#7, loc):\n " +\ "Char2(#7) evennia.objects.objects.DefaultCharacter (location: Room(#1))" self.call(building.CmdFind(), "Char2", expect, cmdstring="locate") + self.call(building.CmdFind(), "/ex Char2", # /ex is an ambiguous switch + "locate: Ambiguous switch supplied: Did you mean /exit or /exact?|" + expect, + cmdstring="locate") self.call(building.CmdFind(), "Char2", expect, cmdstring="@locate") - self.call(building.CmdFind(), "/loc Char2", expect, cmdstring="find") + self.call(building.CmdFind(), "/l Char2", expect, cmdstring="find") # /l switch is abbreviated form of /loc self.call(building.CmdFind(), "Char2", "One Match", cmdstring="@find") def test_script(self): self.call(building.CmdScript(), "Obj = scripts.Script", "Script scripts.Script successfully added") def test_teleport(self): - self.call(building.CmdTeleport(), "Room2", "Room2(#2)\n|Teleported to Room2.") + self.call(building.CmdTeleport(), "/quiet Room2", "Room2(#2)\n|Teleported to Room2.") + self.call(building.CmdTeleport(), "/t", # /t switch is abbreviated form of /tonone + "Cannot teleport a puppeted object (Char, puppeted by TestAccount(account 1)) to a Nonelocation.") + self.call(building.CmdTeleport(), "/l Room2", # /l switch is abbreviated form of /loc + "Destination has no location.") + self.call(building.CmdTeleport(), "/q me to Room2", # /q switch is abbreviated form of /quiet + "Char is already at Room2.") def test_spawn(self): def getObject(commandTest, objKeyStr): @@ -321,7 +336,7 @@ class TestBuilding(CommandTest): self.call(building.CmdSpawn(), " ", "Usage: @spawn") # Tests "@spawn " without specifying location. - self.call(building.CmdSpawn(), \ + self.call(building.CmdSpawn(), "{'key':'goblin', 'typeclass':'evennia.DefaultCharacter'}", "Spawned goblin") goblin = getObject(self, "goblin") @@ -340,8 +355,8 @@ class TestBuilding(CommandTest): # char1's default location in the future... spawnLoc = self.room1 - self.call(building.CmdSpawn(), \ - "{'prototype':'GOBLIN', 'key':'goblin', 'location':'%s'}" \ + self.call(building.CmdSpawn(), + "{'prototype':'GOBLIN', 'key':'goblin', 'location':'%s'}" % spawnLoc.dbref, "Spawned goblin") goblin = getObject(self, "goblin") self.assertEqual(goblin.location, spawnLoc) @@ -354,70 +369,81 @@ class TestBuilding(CommandTest): self.assertIsInstance(ball, DefaultObject) ball.delete() - # Tests "@spawn/noloc ..." without specifying a location. + # Tests "@spawn/n ..." without specifying a location. # Location should be "None". - self.call(building.CmdSpawn(), "/noloc 'BALL'", "Spawned Ball") + self.call(building.CmdSpawn(), "/n 'BALL'", "Spawned Ball") # /n switch is abbreviated form of /noloc ball = getObject(self, "Ball") self.assertIsNone(ball.location) ball.delete() # Tests "@spawn/noloc ...", but DO specify a location. # Location should be the specified location. - self.call(building.CmdSpawn(), \ - "/noloc {'prototype':'BALL', 'location':'%s'}" \ + self.call(building.CmdSpawn(), + "/noloc {'prototype':'BALL', 'location':'%s'}" % spawnLoc.dbref, "Spawned Ball") ball = getObject(self, "Ball") self.assertEqual(ball.location, spawnLoc) ball.delete() # test calling spawn with an invalid prototype. - self.call(building.CmdSpawn(), \ - "'NO_EXIST'", "No prototype named 'NO_EXIST'") + self.call(building.CmdSpawn(), "'NO_EXIST'", "No prototype named 'NO_EXIST'") class TestComms(CommandTest): def setUp(self): super(CommandTest, self).setUp() - self.call(comms.CmdChannelCreate(), "testchan;test=Test Channel", "Created channel testchan and connected to it.", receiver=self.account) + self.call(comms.CmdChannelCreate(), "testchan;test=Test Channel", + "Created channel testchan and connected to it.", receiver=self.account) def test_toggle_com(self): - self.call(comms.CmdAddCom(), "tc = testchan", "You are already connected to channel testchan. You can now", receiver=self.account) + self.call(comms.CmdAddCom(), "tc = testchan", + "You are already connected to channel testchan. You can now", receiver=self.account) self.call(comms.CmdDelCom(), "tc", "Your alias 'tc' for channel testchan was cleared.", receiver=self.account) def test_channels(self): - self.call(comms.CmdChannels(), "", "Available channels (use comlist,addcom and delcom to manage", receiver=self.account) + self.call(comms.CmdChannels(), "", + "Available channels (use comlist,addcom and delcom to manage", receiver=self.account) def test_all_com(self): - self.call(comms.CmdAllCom(), "", "Available channels (use comlist,addcom and delcom to manage", receiver=self.account) + self.call(comms.CmdAllCom(), "", + "Available channels (use comlist,addcom and delcom to manage", receiver=self.account) def test_clock(self): - self.call(comms.CmdClock(), "testchan=send:all()", "Lock(s) applied. Current locks on testchan:", receiver=self.account) + self.call(comms.CmdClock(), + "testchan=send:all()", "Lock(s) applied. Current locks on testchan:", receiver=self.account) def test_cdesc(self): - self.call(comms.CmdCdesc(), "testchan = Test Channel", "Description of channel 'testchan' set to 'Test Channel'.", receiver=self.account) + self.call(comms.CmdCdesc(), "testchan = Test Channel", + "Description of channel 'testchan' set to 'Test Channel'.", receiver=self.account) def test_cemit(self): - self.call(comms.CmdCemit(), "testchan = Test Message", "[testchan] Test Message|Sent to channel testchan: Test Message", receiver=self.account) + self.call(comms.CmdCemit(), "testchan = Test Message", + "[testchan] Test Message|Sent to channel testchan: Test Message", receiver=self.account) def test_cwho(self): self.call(comms.CmdCWho(), "testchan", "Channel subscriptions\ntestchan:\n TestAccount", receiver=self.account) def test_page(self): - self.call(comms.CmdPage(), "TestAccount2 = Test", "TestAccount2 is offline. They will see your message if they list their pages later.|You paged TestAccount2 with: 'Test'.", receiver=self.account) + self.call(comms.CmdPage(), "TestAccount2 = Test", + "TestAccount2 is offline. They will see your message if they list their pages later." + "|You paged TestAccount2 with: 'Test'.", receiver=self.account) def test_cboot(self): # No one else connected to boot self.call(comms.CmdCBoot(), "", "Usage: @cboot[/quiet] = [:reason]", receiver=self.account) def test_cdestroy(self): - self.call(comms.CmdCdestroy(), "testchan", "[testchan] TestAccount: testchan is being destroyed. Make sure to change your aliases.|Channel 'testchan' was destroyed.", receiver=self.account) + self.call(comms.CmdCdestroy(), "testchan", + "[testchan] TestAccount: testchan is being destroyed. Make sure to change your aliases." + "|Channel 'testchan' was destroyed.", receiver=self.account) class TestBatchProcess(CommandTest): def test_batch_commands(self): # cannot test batchcode here, it must run inside the server process - self.call(batchprocess.CmdBatchCommands(), "example_batch_cmds", "Running Batchcommand processor Automatic mode for example_batch_cmds") + self.call(batchprocess.CmdBatchCommands(), "example_batch_cmds", + "Running Batchcommand processor Automatic mode for example_batch_cmds") # we make sure to delete the button again here to stop the running reactor confirm = building.CmdDestroy.confirm building.CmdDestroy.confirm = False From df4629651b3f312eaff9b542aef33071a4e6999a Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:24:27 -0500 Subject: [PATCH 04/31] Added options class variable to CmdOption, CmdQuit docstring Switch is plural for multiple Switches --- evennia/commands/default/account.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/evennia/commands/default/account.py b/evennia/commands/default/account.py index 20cc374542..81539bba17 100644 --- a/evennia/commands/default/account.py +++ b/evennia/commands/default/account.py @@ -455,7 +455,7 @@ class CmdOption(COMMAND_DEFAULT_CLASS): Usage: @option[/save] [name = value] - Switch: + Switches: save - Save the current option settings for future logins. clear - Clear the saved options. @@ -467,6 +467,7 @@ class CmdOption(COMMAND_DEFAULT_CLASS): """ key = "@option" aliases = "@options" + options = ("save", "clear") locks = "cmd:all()" # this is used by the parent @@ -650,6 +651,7 @@ class CmdQuit(COMMAND_DEFAULT_CLASS): game. Use the /all switch to disconnect from all sessions. """ key = "@quit" + options = ("all",) locks = "cmd:all()" # this is used by the parent From 2df6684836fde62dbc3dedb90e5e0ae2c09b9aff Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:27:57 -0500 Subject: [PATCH 05/31] Add options class var to some admin commands CmdBoot, CmdDelAccount, CmdEmit + Docstring edit, CmdPerm + Docstring edit. --- evennia/commands/default/admin.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/evennia/commands/default/admin.py b/evennia/commands/default/admin.py index 8b694ffd8f..dba0c49bdd 100644 --- a/evennia/commands/default/admin.py +++ b/evennia/commands/default/admin.py @@ -36,6 +36,7 @@ class CmdBoot(COMMAND_DEFAULT_CLASS): """ key = "@boot" + options = ("quiet", "sid") locks = "cmd:perm(boot) or perm(Admin)" help_category = "Admin" @@ -265,6 +266,7 @@ class CmdDelAccount(COMMAND_DEFAULT_CLASS): """ key = "@delaccount" + options = ("delobj",) locks = "cmd:perm(delaccount) or perm(Developer)" help_category = "Admin" @@ -329,9 +331,9 @@ class CmdEmit(COMMAND_DEFAULT_CLASS): @pemit [, , ... =] Switches: - room : limit emits to rooms only (default) - accounts : limit emits to accounts only - contents : send to the contents of matched objects too + room - limit emits to rooms only (default) + accounts - limit emits to accounts only + contents - send to the contents of matched objects too Emits a message to the selected objects or to your immediate surroundings. If the object is a room, @@ -341,6 +343,7 @@ class CmdEmit(COMMAND_DEFAULT_CLASS): """ key = "@emit" aliases = ["@pemit", "@remit"] + options = ("room", "accounts", "contents") locks = "cmd:perm(emit) or perm(Builder)" help_category = "Admin" @@ -442,14 +445,15 @@ class CmdPerm(COMMAND_DEFAULT_CLASS): @perm[/switch] * [= [,,...]] Switches: - del : delete the given permission from or . - account : set permission on an account (same as adding * to name) + del - delete the given permission from or . + account - set permission on an account (same as adding * to name) This command sets/clears individual permission strings on an object or account. If no permission is given, list all permissions on . """ key = "@perm" aliases = "@setperm" + options = ("del", "account") locks = "cmd:perm(perm) or perm(Developer)" help_category = "Admin" From 77fb0f4aed48b32682c4669b8a47f76a1ab72289 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:31:33 -0500 Subject: [PATCH 06/31] Adds class var options to some system commands CmdPy, CmdScripts, CmdService, CmdServerLoad and plural of Switch is Switches in docstring --- evennia/commands/default/system.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index 7bd092bad5..e4d6242a56 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -245,6 +245,7 @@ class CmdPy(COMMAND_DEFAULT_CLASS): """ key = "@py" aliases = ["!"] + options = ("time", "edit") locks = "cmd:perm(py) or perm(Developer)" help_category = "System" @@ -328,6 +329,7 @@ class CmdScripts(COMMAND_DEFAULT_CLASS): """ key = "@scripts" aliases = ["@globalscript", "@listscripts"] + options = ("start", "stop", "kill", "validate") locks = "cmd:perm(listscripts) or perm(Admin)" help_category = "System" @@ -521,6 +523,7 @@ class CmdService(COMMAND_DEFAULT_CLASS): key = "@service" aliases = ["@services"] + options = ("list", "start", "stop", "delete") locks = "cmd:perm(service) or perm(Developer)" help_category = "System" @@ -672,7 +675,7 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS): Usage: @server[/mem] - Switch: + Switches: mem - return only a string of the current memory usage flushmem - flush the idmapper cache @@ -703,6 +706,7 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS): """ key = "@server" aliases = ["@serverload", "@serverprocess"] + options = ("mem", "flushmem") locks = "cmd:perm(list) or perm(Developer)" help_category = "System" From 345b7f1a7c81ac3a4ad414fb473151bdbe02e4d8 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:37:33 -0500 Subject: [PATCH 07/31] Adds class var options to some comms commands CmdCBoot + docstring Switches plural, CmdCemit, CmdPage, CmdIRC2Chan, CmdIRC2Chan. --- evennia/commands/default/comms.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/evennia/commands/default/comms.py b/evennia/commands/default/comms.py index 37a4934d16..b654bd0297 100644 --- a/evennia/commands/default/comms.py +++ b/evennia/commands/default/comms.py @@ -377,7 +377,7 @@ class CmdCBoot(COMMAND_DEFAULT_CLASS): Usage: @cboot[/quiet] = [:reason] - Switches: + Switch: quiet - don't notify the channel Kicks an account or object from a channel you control. @@ -385,6 +385,7 @@ class CmdCBoot(COMMAND_DEFAULT_CLASS): """ key = "@cboot" + options = ("quiet",) locks = "cmd: not pperm(channel_banned)" help_category = "Comms" @@ -453,6 +454,7 @@ class CmdCemit(COMMAND_DEFAULT_CLASS): key = "@cemit" aliases = ["@cmsg"] + options = ("sendername", "quiet") locks = "cmd: not pperm(channel_banned) and pperm(Player)" help_category = "Comms" @@ -683,6 +685,7 @@ class CmdPage(COMMAND_DEFAULT_CLASS): key = "page" aliases = ['tell'] + options = ("last", "list") locks = "cmd:not pperm(page_banned)" help_category = "Comms" @@ -850,6 +853,7 @@ class CmdIRC2Chan(COMMAND_DEFAULT_CLASS): """ key = "@irc2chan" + options = ("delete", "remove", "disconnect", "list", "ssl") locks = "cmd:serversetting(IRC_ENABLED) and pperm(Developer)" help_category = "Comms" @@ -1016,6 +1020,7 @@ class CmdRSS2Chan(COMMAND_DEFAULT_CLASS): """ key = "@rss2chan" + options = ("disconnect", "remove", "list") locks = "cmd:serversetting(RSS_ENABLED) and pperm(Developer)" help_category = "Comms" From c873acb44eaf84bcb5f2e96cea1b584d65382f80 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:38:37 -0500 Subject: [PATCH 08/31] Add class var options to CmdSetHelp --- evennia/commands/default/help.py | 1 + 1 file changed, 1 insertion(+) diff --git a/evennia/commands/default/help.py b/evennia/commands/default/help.py index ebce460748..a355ac2e3f 100644 --- a/evennia/commands/default/help.py +++ b/evennia/commands/default/help.py @@ -317,6 +317,7 @@ class CmdSetHelp(COMMAND_DEFAULT_CLASS): """ key = "@sethelp" + options = ("edit", "replace", "append", "extend", "delete") locks = "cmd:perm(Helper)" help_category = "Building" From 87316d80465fb4ba553ac47ac6e43799f667b359 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:40:43 -0500 Subject: [PATCH 09/31] Add class var options to CmdExtendedDesc Inherits from default_cmds.CmdDesc, but unused in CmdExtendedDesc, so needs to be set empty. --- evennia/contrib/extended_room.py | 1 + 1 file changed, 1 insertion(+) diff --git a/evennia/contrib/extended_room.py b/evennia/contrib/extended_room.py index 755c2ac22e..b2f61757f9 100644 --- a/evennia/contrib/extended_room.py +++ b/evennia/contrib/extended_room.py @@ -367,6 +367,7 @@ class CmdExtendedDesc(default_cmds.CmdDesc): """ aliases = ["describe", "detail"] + options = () # Inherits from default_cmds.CmdDesc, but unused here def reset_times(self, obj): """By deleteting the caches we force a re-load.""" From c29cb96e4b20f43a94a5491c7b55ae1cc82d4abb Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:44:50 -0500 Subject: [PATCH 10/31] Adds options var to CmdBatchCommands, CmdBatchCode --- evennia/commands/default/batchprocess.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/evennia/commands/default/batchprocess.py b/evennia/commands/default/batchprocess.py index f0b117a816..bf09360c71 100644 --- a/evennia/commands/default/batchprocess.py +++ b/evennia/commands/default/batchprocess.py @@ -237,6 +237,7 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS): """ key = "@batchcommands" aliases = ["@batchcommand", "@batchcmd"] + options = ("interactive",) locks = "cmd:perm(batchcommands) or perm(Developer)" help_category = "Building" @@ -347,6 +348,7 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS): """ key = "@batchcode" aliases = ["@batchcodes"] + options = ("interactive", "debug") locks = "cmd:superuser()" help_category = "Building" From 8af16ad971c2cd9cc73893b8e20f4bd383aa156f Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 07:58:40 -0500 Subject: [PATCH 11/31] Adds class var options and split to CmdTeleport + CmdSetObjAlias, CmdCopy, CmdCpAttr, CmdMvAttr, CmdCreate, CmdDesc, CmdDestroy +docstring edits, CmdDig, CmdTunnel, CmdFind, CmdScript, CmdTag, CmdSpawn --- evennia/commands/default/building.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 4bc107dcfb..f8d0246f39 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -124,6 +124,7 @@ class CmdSetObjAlias(COMMAND_DEFAULT_CLASS): key = "@alias" aliases = "@setobjalias" + options = ("category",) locks = "cmd:perm(setobjalias) or perm(Builder)" help_category = "Building" @@ -218,6 +219,7 @@ class CmdCopy(ObjManipCommand): """ key = "@copy" + options = ("reset",) locks = "cmd:perm(copy) or perm(Builder)" help_category = "Building" @@ -299,6 +301,7 @@ class CmdCpAttr(ObjManipCommand): If you don't supply a source object, yourself is used. """ key = "@cpattr" + options = ("move",) locks = "cmd:perm(cpattr) or perm(Builder)" help_category = "Building" @@ -440,6 +443,7 @@ class CmdMvAttr(ObjManipCommand): object. If you don't supply a source object, yourself is used. """ key = "@mvattr" + options = ("copy",) locks = "cmd:perm(mvattr) or perm(Builder)" help_category = "Building" @@ -488,6 +492,7 @@ class CmdCreate(ObjManipCommand): """ key = "@create" + options = ("drop",) locks = "cmd:perm(create) or perm(Builder)" help_category = "Building" @@ -573,6 +578,7 @@ class CmdDesc(COMMAND_DEFAULT_CLASS): """ key = "@desc" aliases = "@describe" + options = ("edit",) locks = "cmd:perm(desc) or perm(Builder)" help_category = "Building" @@ -631,11 +637,11 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS): Usage: @destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...] - switches: + Switches: override - The @destroy command will usually avoid accidentally destroying account objects. This switch overrides this safety. force - destroy without confirmation. - examples: + Examples: @destroy house, roof, door, 44-78 @destroy 5-10, flower, 45 @destroy/force north @@ -648,6 +654,7 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS): key = "@destroy" aliases = ["@delete", "@del"] + options = ("override", "force") locks = "cmd:perm(destroy) or perm(Builder)" help_category = "Building" @@ -771,6 +778,7 @@ class CmdDig(ObjManipCommand): would be 'north;no;n'. """ key = "@dig" + options = ("teleport",) locks = "cmd:perm(dig) or perm(Builder)" help_category = "Building" @@ -880,7 +888,7 @@ class CmdDig(ObjManipCommand): new_back_exit.dbref, alias_string) caller.msg("%s%s%s" % (room_string, exit_to_string, exit_back_string)) - if new_room and ('teleport' in self.switches or "tel" in self.switches): + if new_room and 'teleport' in self.switches: caller.move_to(new_room) @@ -913,6 +921,7 @@ class CmdTunnel(COMMAND_DEFAULT_CLASS): key = "@tunnel" aliases = ["@tun"] + options = ("oneway", "tel") locks = "cmd: perm(tunnel) or perm(Builder)" help_category = "Building" @@ -2256,6 +2265,7 @@ class CmdFind(COMMAND_DEFAULT_CLASS): key = "@find" aliases = "@search, @locate" + options = ("room", "exit", "char", "exact", "loc") locks = "cmd:perm(find) or perm(Builder)" help_category = "Building" @@ -2292,7 +2302,7 @@ class CmdFind(COMMAND_DEFAULT_CLASS): restrictions = "" if self.switches: - restrictions = ", %s" % (",".join(self.switches)) + restrictions = ", %s" % (", ".join(self.switches)) if is_dbref or is_account: @@ -2372,7 +2382,7 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS): teleport object to another location Usage: - @tel/switch [ =] + @tel/switch [ to||=] Examples: @tel Limbo @@ -2395,6 +2405,8 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS): is teleported to the target location. """ key = "@tel" aliases = "@teleport" + options = ("quiet", "intoexit", "tonone", "loc") + split = " to " locks = "cmd:perm(teleport) or perm(Builder)" help_category = "Building" @@ -2502,6 +2514,7 @@ class CmdScript(COMMAND_DEFAULT_CLASS): key = "@script" aliases = "@addscript" + options = ("start", "stop") locks = "cmd:perm(script) or perm(Builder)" help_category = "Building" @@ -2601,6 +2614,7 @@ class CmdTag(COMMAND_DEFAULT_CLASS): key = "@tag" aliases = ["@tags"] + options = ("search", "del") locks = "cmd:perm(tag) or perm(Builder)" help_category = "Building" arg_regex = r"(/\w+?(\s|$))|\s|$" @@ -2742,6 +2756,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): """ key = "@spawn" + options = ("noloc", ) locks = "cmd:perm(spawn) or perm(Builder)" help_category = "Building" From 062e048c604715e691510cabd2b041342a574285 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Fri, 2 Mar 2018 08:02:31 -0500 Subject: [PATCH 12/31] Adds class var options, split to CmdGive + docstring edit, options and docstring edit to CmdNick. --- evennia/commands/default/general.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/evennia/commands/default/general.py b/evennia/commands/default/general.py index 7bf861c91f..932c4fe898 100644 --- a/evennia/commands/default/general.py +++ b/evennia/commands/default/general.py @@ -88,8 +88,7 @@ class CmdNick(COMMAND_DEFAULT_CLASS): Switches: inputline - replace on the inputline (default) object - replace on object-lookup - account - replace on account-lookup - + account - replace on account-lookup list - show all defined aliases (also "nicks" works) delete - remove nick by index in /list clearall - clear all nicks @@ -118,6 +117,7 @@ class CmdNick(COMMAND_DEFAULT_CLASS): """ key = "nick" + options = ("inputline", "object", "account", "list", "delete", "clearall") aliases = ["nickname", "nicks"] locks = "cmd:all()" @@ -377,12 +377,13 @@ class CmdGive(COMMAND_DEFAULT_CLASS): give away something to someone Usage: - give = + give Gives an items from your inventory to another character, placing it in their inventory. """ key = "give" + split = " to " locks = "cmd:all()" arg_regex = r"\s|$" From 2b9f0750e54fd122be54e82740573a428002fb2c Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 14:08:10 -0500 Subject: [PATCH 13/31] Addresses requested changes + shim code class variable name changes handled by shim --- evennia/commands/default/muxcommand.py | 88 +++++++++++++------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/evennia/commands/default/muxcommand.py b/evennia/commands/default/muxcommand.py index 6d731ea14f..93606cdbe5 100644 --- a/evennia/commands/default/muxcommand.py +++ b/evennia/commands/default/muxcommand.py @@ -80,9 +80,10 @@ class MuxCommand(Command): start with the switch indicator /. Optional variables to aid in parsing, if set: - self.options = (tuple of valid /switches expected - by this command (without the /)) - self.split = Alternate string delimiter to separate left/right hand side. + self.switch_options - (tuple of valid /switches expected by this + command (without the /)) + self.rhs_split - Alternate string delimiter to separate + left/right hand sides. This parser breaks self.args into its constituents and stores them in the following variables: @@ -102,6 +103,18 @@ class MuxCommand(Command): """ raw = self.args args = raw.strip() + # Temporary code to use the old settings before renaming # # + if hasattr(self, "options"): # + self.switch_options = self.options # + if hasattr(self, "split") and not hasattr(self, "rhs_split"): # + self.rhs_split = self.split # # # + # Without explicitly setting these attributes, they assume default values: + if not hasattr(self, "switch_options"): + self.switch_options = None + if not hasattr(self, "rhs_split"): + self.rhs_split = "=" + if not hasattr(self, "account_caller"): + self.account_caller = False # split out switches switches = [] @@ -114,17 +127,19 @@ class MuxCommand(Command): else: args = "" switches = switches[0].split('/') - # Parse mux options, comparing them against user-provided switches, expanding abbreviations. - if hasattr(self, "options") and self.options and switches: - # If specific options are known, test them against given switches. + # If user-provides switches, parse them with parser switch options. + if switches and self.switch_options: valid_switches, unused_switches, extra_switches = [], [], [] for element in switches: - option_check = [each for each in self.options if each.lower().startswith(element.lower())] - if len(option_check) > 1: + option_check = [each for each in self.switch_options + if each.lower() == element.lower() or + each.lower().startswith(element.lower())] + match_count = len(option_check) + if match_count > 1: extra_switches += option_check # Either the option provided is ambiguous, - elif len(option_check) == 1: + elif match_count == 1: valid_switches += option_check # or it is a valid option abbreviation, - elif len(option_check) == 0: + elif match_count == 0: unused_switches += [element] # or an extraneous option to be ignored. if extra_switches: # User provided switches self.msg('|g%s|n: |wAmbiguous switch supplied: Did you mean /|C%s|w?' % @@ -138,21 +153,22 @@ class MuxCommand(Command): # check for arg1, arg2, ... = argA, argB, ... constructs lhs, rhs = args.strip(), None - lhslist, rhslist = [arg.strip() for arg in args.split(',')], [] + best_split = self.rhs_split if lhs: - if '=' in lhs: # Default delimiter has priority - # Parse to separate left into left/right sides using default delimiter - lhs, rhs = lhs.split('=', 1) - elif hasattr(self, "split") and self.split and self.split in lhs: - # Parse to separate left into left/right sides using a custom delimiter, if provided. - lhs, rhs = lhs.split(self.split, 1) # At most, split once, into left and right parts. - # Trim user-injected whitespace - rhs = rhs.strip() if rhs is not None else None - lhs = lhs.strip() - # Further split left/right sides by comma delimiter - lhslist = [arg.strip() for arg in lhs.split(',')] if lhs is not None else "" - rhslist = [arg.strip() for arg in rhs.split(',')] if rhs is not None else "" - + if hasattr(self.rhs_split, '__iter__'): + for this_split in self.rhs_split: + if this_split in lhs: # First delimiter to allow a successful + best_split = this_split # split is the best split. + break + # Parse to separate left into left/right sides using best_split delimiter string + if best_split in lhs: + lhs, rhs = lhs.split(best_split, 1) + # Trim user-injected whitespace + rhs = rhs.strip() if rhs is not None else None + lhs = lhs.strip() + # Further split left/right sides by comma delimiter + lhslist = [arg.strip() for arg in lhs.split(',')] if lhs is not None else "" + rhslist = [arg.strip() for arg in rhs.split(',')] if rhs is not None else "" # save to object properties: self.raw = raw self.switches = switches @@ -167,7 +183,7 @@ class MuxCommand(Command): # sure that self.caller is always the account if possible. We also create # a special property "character" for the puppeted object, if any. This # is convenient for commands defined on the Account only. - if hasattr(self, "account_caller") and self.account_caller: + if self.account_caller: if utils.inherits_from(self.caller, "evennia.objects.objects.DefaultObject"): # caller is an Object/Character self.character = self.caller @@ -203,10 +219,8 @@ class MuxCommand(Command): string += "\nraw argument (self.raw): |w%s|n \n" % self.raw string += "cmd args (self.args): |w%s|n\n" % self.args string += "cmd switches (self.switches): |w%s|n\n" % self.switches - if hasattr(self, "options"): # Optional - string += "cmd options (self.options): |w%s|n\n" % self.options - if hasattr(self, "split"): # Optional - string += "cmd parse left/right using (self.split): |w%s|n\n" % self.split + string += "cmd options (self.switch_options): |w%s|n\n" % self.switch_options + string += "cmd parse left/right using (self.rhs_split): |w%s|n\n" % self.rhs_split string += "space-separated arg list (self.arglist): |w%s|n\n" % self.arglist string += "lhs, left-hand side of '=' (self.lhs): |w%s|n\n" % self.lhs string += "lhs, comma separated (self.lhslist): |w%s|n\n" % self.lhslist @@ -231,18 +245,4 @@ class MuxAccountCommand(MuxCommand): character is actually attached to this Account and Session. """ - def parse(self): - """ - We run the parent parser as usual, then fix the result - """ - super(MuxAccountCommand, self).parse() - - if utils.inherits_from(self.caller, "evennia.objects.objects.DefaultObject"): - # caller is an Object/Character - self.character = self.caller - self.caller = self.caller.account - elif utils.inherits_from(self.caller, "evennia.accounts.accounts.DefaultAccount"): - # caller was already an Account - self.character = self.caller.get_puppet(self.session) - else: - self.character = None + account_caller = True # Using MuxAccountCommand explicitly defaults the caller to an account From f1f94b47887964495928eec871f03a29514fdb4e Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 14:30:37 -0500 Subject: [PATCH 14/31] class var options renamed switch_options --- evennia/commands/default/account.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/commands/default/account.py b/evennia/commands/default/account.py index 81539bba17..40805f7465 100644 --- a/evennia/commands/default/account.py +++ b/evennia/commands/default/account.py @@ -467,7 +467,7 @@ class CmdOption(COMMAND_DEFAULT_CLASS): """ key = "@option" aliases = "@options" - options = ("save", "clear") + switch_options = ("save", "clear") locks = "cmd:all()" # this is used by the parent @@ -651,7 +651,7 @@ class CmdQuit(COMMAND_DEFAULT_CLASS): game. Use the /all switch to disconnect from all sessions. """ key = "@quit" - options = ("all",) + switch_options = ("all",) locks = "cmd:all()" # this is used by the parent From d0cf78fae9cbdb5c9b10b2288f09073b105c292c Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 14:32:52 -0500 Subject: [PATCH 15/31] class var options renamed switch_options --- evennia/commands/default/admin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/evennia/commands/default/admin.py b/evennia/commands/default/admin.py index dba0c49bdd..4e517b77f9 100644 --- a/evennia/commands/default/admin.py +++ b/evennia/commands/default/admin.py @@ -36,7 +36,7 @@ class CmdBoot(COMMAND_DEFAULT_CLASS): """ key = "@boot" - options = ("quiet", "sid") + switch_options = ("quiet", "sid") locks = "cmd:perm(boot) or perm(Admin)" help_category = "Admin" @@ -266,7 +266,7 @@ class CmdDelAccount(COMMAND_DEFAULT_CLASS): """ key = "@delaccount" - options = ("delobj",) + switch_options = ("delobj",) locks = "cmd:perm(delaccount) or perm(Developer)" help_category = "Admin" @@ -343,7 +343,7 @@ class CmdEmit(COMMAND_DEFAULT_CLASS): """ key = "@emit" aliases = ["@pemit", "@remit"] - options = ("room", "accounts", "contents") + switch_options = ("room", "accounts", "contents") locks = "cmd:perm(emit) or perm(Builder)" help_category = "Admin" @@ -453,7 +453,7 @@ class CmdPerm(COMMAND_DEFAULT_CLASS): """ key = "@perm" aliases = "@setperm" - options = ("del", "account") + switch_options = ("del", "account") locks = "cmd:perm(perm) or perm(Developer)" help_category = "Admin" From 567254e028879d73f4e680925b474cf34fd9f63f Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 14:34:10 -0500 Subject: [PATCH 16/31] class var options renamed switch_options --- evennia/commands/default/batchprocess.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/commands/default/batchprocess.py b/evennia/commands/default/batchprocess.py index bf09360c71..5970d35887 100644 --- a/evennia/commands/default/batchprocess.py +++ b/evennia/commands/default/batchprocess.py @@ -237,7 +237,7 @@ class CmdBatchCommands(_COMMAND_DEFAULT_CLASS): """ key = "@batchcommands" aliases = ["@batchcommand", "@batchcmd"] - options = ("interactive",) + switch_options = ("interactive",) locks = "cmd:perm(batchcommands) or perm(Developer)" help_category = "Building" @@ -348,7 +348,7 @@ class CmdBatchCode(_COMMAND_DEFAULT_CLASS): """ key = "@batchcode" aliases = ["@batchcodes"] - options = ("interactive", "debug") + switch_options = ("interactive", "debug") locks = "cmd:superuser()" help_category = "Building" From 51a9c7bfede3e7e58d51874d8d789dc2f78fa1c3 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 14:42:13 -0500 Subject: [PATCH 17/31] class vars options, split renamed switch_options, rhs_split --- evennia/commands/default/building.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index f3e58790f5..e2f61d7054 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -124,7 +124,7 @@ class CmdSetObjAlias(COMMAND_DEFAULT_CLASS): key = "@alias" aliases = "@setobjalias" - options = ("category",) + switch_options = ("category",) locks = "cmd:perm(setobjalias) or perm(Builder)" help_category = "Building" @@ -219,7 +219,7 @@ class CmdCopy(ObjManipCommand): """ key = "@copy" - options = ("reset",) + switch_options = ("reset",) locks = "cmd:perm(copy) or perm(Builder)" help_category = "Building" @@ -301,7 +301,7 @@ class CmdCpAttr(ObjManipCommand): If you don't supply a source object, yourself is used. """ key = "@cpattr" - options = ("move",) + switch_options = ("move",) locks = "cmd:perm(cpattr) or perm(Builder)" help_category = "Building" @@ -443,7 +443,7 @@ class CmdMvAttr(ObjManipCommand): object. If you don't supply a source object, yourself is used. """ key = "@mvattr" - options = ("copy",) + switch_options = ("copy",) locks = "cmd:perm(mvattr) or perm(Builder)" help_category = "Building" @@ -492,7 +492,7 @@ class CmdCreate(ObjManipCommand): """ key = "@create" - options = ("drop",) + switch_options = ("drop",) locks = "cmd:perm(create) or perm(Builder)" help_category = "Building" @@ -578,7 +578,7 @@ class CmdDesc(COMMAND_DEFAULT_CLASS): """ key = "@desc" aliases = "@describe" - options = ("edit",) + switch_options = ("edit",) locks = "cmd:perm(desc) or perm(Builder)" help_category = "Building" @@ -657,7 +657,7 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS): key = "@destroy" aliases = ["@delete", "@del"] - options = ("override", "force") + switch_options = ("override", "force") locks = "cmd:perm(destroy) or perm(Builder)" help_category = "Building" @@ -781,7 +781,7 @@ class CmdDig(ObjManipCommand): would be 'north;no;n'. """ key = "@dig" - options = ("teleport",) + switch_options = ("teleport",) locks = "cmd:perm(dig) or perm(Builder)" help_category = "Building" @@ -924,7 +924,7 @@ class CmdTunnel(COMMAND_DEFAULT_CLASS): key = "@tunnel" aliases = ["@tun"] - options = ("oneway", "tel") + switch_options = ("oneway", "tel") locks = "cmd: perm(tunnel) or perm(Builder)" help_category = "Building" @@ -2285,7 +2285,7 @@ class CmdFind(COMMAND_DEFAULT_CLASS): key = "@find" aliases = "@search, @locate" - options = ("room", "exit", "char", "exact", "loc") + switch_options = ("room", "exit", "char", "exact", "loc") locks = "cmd:perm(find) or perm(Builder)" help_category = "Building" @@ -2426,8 +2426,8 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS): """ key = "@tel" aliases = "@teleport" - options = ("quiet", "intoexit", "tonone", "loc") - split = " to " + switch_options = ("quiet", "intoexit", "tonone", "loc") + rhs_split = " to " locks = "cmd:perm(teleport) or perm(Builder)" help_category = "Building" @@ -2535,7 +2535,7 @@ class CmdScript(COMMAND_DEFAULT_CLASS): key = "@script" aliases = "@addscript" - options = ("start", "stop") + switch_options = ("start", "stop") locks = "cmd:perm(script) or perm(Builder)" help_category = "Building" @@ -2777,7 +2777,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): """ key = "@spawn" - options = ("noloc", ) + switch_options = ("noloc", ) locks = "cmd:perm(spawn) or perm(Builder)" help_category = "Building" From d3c8cd0316e4a09d067e1e951e97680e19b01c3f Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 14:45:07 -0500 Subject: [PATCH 18/31] class var options renamed switch_options --- evennia/commands/default/comms.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/evennia/commands/default/comms.py b/evennia/commands/default/comms.py index b654bd0297..d9fe0b0d20 100644 --- a/evennia/commands/default/comms.py +++ b/evennia/commands/default/comms.py @@ -385,7 +385,7 @@ class CmdCBoot(COMMAND_DEFAULT_CLASS): """ key = "@cboot" - options = ("quiet",) + switch_options = ("quiet",) locks = "cmd: not pperm(channel_banned)" help_category = "Comms" @@ -454,7 +454,7 @@ class CmdCemit(COMMAND_DEFAULT_CLASS): key = "@cemit" aliases = ["@cmsg"] - options = ("sendername", "quiet") + switch_options = ("sendername", "quiet") locks = "cmd: not pperm(channel_banned) and pperm(Player)" help_category = "Comms" @@ -685,7 +685,7 @@ class CmdPage(COMMAND_DEFAULT_CLASS): key = "page" aliases = ['tell'] - options = ("last", "list") + switch_options = ("last", "list") locks = "cmd:not pperm(page_banned)" help_category = "Comms" @@ -853,7 +853,7 @@ class CmdIRC2Chan(COMMAND_DEFAULT_CLASS): """ key = "@irc2chan" - options = ("delete", "remove", "disconnect", "list", "ssl") + switch_options = ("delete", "remove", "disconnect", "list", "ssl") locks = "cmd:serversetting(IRC_ENABLED) and pperm(Developer)" help_category = "Comms" @@ -1020,7 +1020,7 @@ class CmdRSS2Chan(COMMAND_DEFAULT_CLASS): """ key = "@rss2chan" - options = ("disconnect", "remove", "list") + switch_options = ("disconnect", "remove", "list") locks = "cmd:serversetting(RSS_ENABLED) and pperm(Developer)" help_category = "Comms" From 891c7c06f6b12cb10d5076003edd8c37c52d4da2 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 14:52:29 -0500 Subject: [PATCH 19/31] class vars options, split renamed switch_options, rhs_split --- evennia/commands/default/general.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/commands/default/general.py b/evennia/commands/default/general.py index 254081ba18..56e7825d65 100644 --- a/evennia/commands/default/general.py +++ b/evennia/commands/default/general.py @@ -117,7 +117,7 @@ class CmdNick(COMMAND_DEFAULT_CLASS): """ key = "nick" - options = ("inputline", "object", "account", "list", "delete", "clearall") + switch_options = ("inputline", "object", "account", "list", "delete", "clearall") aliases = ["nickname", "nicks"] locks = "cmd:all()" @@ -448,7 +448,7 @@ class CmdGive(COMMAND_DEFAULT_CLASS): placing it in their inventory. """ key = "give" - split = " to " + rhs_split = " to " locks = "cmd:all()" arg_regex = r"\s|$" From af07a119650053486336f4aa6270d3f2e75c89b8 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 15:17:19 -0500 Subject: [PATCH 20/31] class var options renamed switch_options --- evennia/commands/default/help.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/commands/default/help.py b/evennia/commands/default/help.py index a355ac2e3f..d2011629c1 100644 --- a/evennia/commands/default/help.py +++ b/evennia/commands/default/help.py @@ -317,7 +317,7 @@ class CmdSetHelp(COMMAND_DEFAULT_CLASS): """ key = "@sethelp" - options = ("edit", "replace", "append", "extend", "delete") + switch_options = ("edit", "replace", "append", "extend", "delete") locks = "cmd:perm(Helper)" help_category = "Building" From eb3b7815e6e4e4f298b016ff1797a228fdede5cd Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 15:46:17 -0500 Subject: [PATCH 21/31] class var options renamed switch_options --- evennia/commands/default/system.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index e4d6242a56..c454de9aff 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -245,7 +245,7 @@ class CmdPy(COMMAND_DEFAULT_CLASS): """ key = "@py" aliases = ["!"] - options = ("time", "edit") + switch_options = ("time", "edit") locks = "cmd:perm(py) or perm(Developer)" help_category = "System" @@ -329,7 +329,7 @@ class CmdScripts(COMMAND_DEFAULT_CLASS): """ key = "@scripts" aliases = ["@globalscript", "@listscripts"] - options = ("start", "stop", "kill", "validate") + switch_options = ("start", "stop", "kill", "validate") locks = "cmd:perm(listscripts) or perm(Admin)" help_category = "System" @@ -523,7 +523,7 @@ class CmdService(COMMAND_DEFAULT_CLASS): key = "@service" aliases = ["@services"] - options = ("list", "start", "stop", "delete") + switch_options = ("list", "start", "stop", "delete") locks = "cmd:perm(service) or perm(Developer)" help_category = "System" @@ -706,7 +706,7 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS): """ key = "@server" aliases = ["@serverload", "@serverprocess"] - options = ("mem", "flushmem") + switch_options = ("mem", "flushmem") locks = "cmd:perm(list) or perm(Developer)" help_category = "System" From 933388633f8e00773ccd8bf57a32692d005228e0 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 15:52:47 -0500 Subject: [PATCH 22/31] class var options renamed switch_options --- evennia/contrib/extended_room.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/contrib/extended_room.py b/evennia/contrib/extended_room.py index b2f61757f9..6cacca980c 100644 --- a/evennia/contrib/extended_room.py +++ b/evennia/contrib/extended_room.py @@ -367,7 +367,7 @@ class CmdExtendedDesc(default_cmds.CmdDesc): """ aliases = ["describe", "detail"] - options = () # Inherits from default_cmds.CmdDesc, but unused here + switch_options = () # Inherits from default_cmds.CmdDesc, but unused here def reset_times(self, obj): """By deleteting the caches we force a re-load.""" From 8ef981fe6cd87066a632be085dbcbdec4a18e0da Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 17:39:44 -0500 Subject: [PATCH 23/31] Remove code shim after class var renaming Updated rhs_split docstring in MuxCommand --- evennia/commands/default/muxcommand.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/evennia/commands/default/muxcommand.py b/evennia/commands/default/muxcommand.py index 93606cdbe5..7339780fe7 100644 --- a/evennia/commands/default/muxcommand.py +++ b/evennia/commands/default/muxcommand.py @@ -82,8 +82,9 @@ class MuxCommand(Command): Optional variables to aid in parsing, if set: self.switch_options - (tuple of valid /switches expected by this command (without the /)) - self.rhs_split - Alternate string delimiter to separate - left/right hand sides. + self.rhs_split - Alternate string delimiter or tuple of strings + to separate left/right hand sides. tuple form + gives priority split to first string delimeter. This parser breaks self.args into its constituents and stores them in the following variables: @@ -103,11 +104,6 @@ class MuxCommand(Command): """ raw = self.args args = raw.strip() - # Temporary code to use the old settings before renaming # # - if hasattr(self, "options"): # - self.switch_options = self.options # - if hasattr(self, "split") and not hasattr(self, "rhs_split"): # - self.rhs_split = self.split # # # # Without explicitly setting these attributes, they assume default values: if not hasattr(self, "switch_options"): self.switch_options = None From 45d5ee8745f269283abfa63b8bc96368b22cefb6 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 20:11:37 -0500 Subject: [PATCH 24/31] MuxCommand multi-delimiter for splits parse tweaks --- evennia/commands/default/muxcommand.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/evennia/commands/default/muxcommand.py b/evennia/commands/default/muxcommand.py index 7339780fe7..77fd889ed9 100644 --- a/evennia/commands/default/muxcommand.py +++ b/evennia/commands/default/muxcommand.py @@ -84,7 +84,7 @@ class MuxCommand(Command): command (without the /)) self.rhs_split - Alternate string delimiter or tuple of strings to separate left/right hand sides. tuple form - gives priority split to first string delimeter. + gives priority split to first string delimiter. This parser breaks self.args into its constituents and stores them in the following variables: @@ -149,13 +149,15 @@ class MuxCommand(Command): # check for arg1, arg2, ... = argA, argB, ... constructs lhs, rhs = args.strip(), None - best_split = self.rhs_split if lhs: - if hasattr(self.rhs_split, '__iter__'): + if hasattr(self.rhs_split, '__iter__'): # If delimiter is iterable, try each + best_split = self.rhs_split[0] for this_split in self.rhs_split: - if this_split in lhs: # First delimiter to allow a successful - best_split = this_split # split is the best split. + if this_split in lhs: # delimiter to allow first successful + best_split = this_split # split to be the best split. break + else: + best_split = self.rhs_split # Parse to separate left into left/right sides using best_split delimiter string if best_split in lhs: lhs, rhs = lhs.split(best_split, 1) From 87d32abedd59b08da4c4c7e20429ae49029de82d Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 20:14:20 -0500 Subject: [PATCH 25/31] Add multi-delimiter split to CmdTeleport --- evennia/commands/default/building.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index e2f61d7054..702f2e241a 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -2427,7 +2427,7 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS): key = "@tel" aliases = "@teleport" switch_options = ("quiet", "intoexit", "tonone", "loc") - rhs_split = " to " + rhs_split = ("=", " to ") # Prefer = delimiter, but allow " to " usage. locks = "cmd:perm(teleport) or perm(Builder)" help_category = "Building" From 4a0f13804ed5178af1d15f2d58c84e6e6067415d Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 20:16:52 -0500 Subject: [PATCH 26/31] Add multi-delimiter split to CmdGive --- evennia/commands/default/general.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evennia/commands/default/general.py b/evennia/commands/default/general.py index 56e7825d65..eafb7670b0 100644 --- a/evennia/commands/default/general.py +++ b/evennia/commands/default/general.py @@ -448,7 +448,7 @@ class CmdGive(COMMAND_DEFAULT_CLASS): placing it in their inventory. """ key = "give" - rhs_split = " to " + rhs_split = ("=", " to ") # Prefer = delimiter, but allow " to " usage. locks = "cmd:all()" arg_regex = r"\s|$" From 24cec94a6b5d1d6105886167c195ff9a2d87f339 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 20:36:17 -0500 Subject: [PATCH 27/31] Add test_give for CmdGive parse multiple splits --- evennia/commands/default/tests.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index e79277de9b..fd5d4982ed 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -140,6 +140,13 @@ class TestGeneral(CommandTest): self.call(general.CmdGet(), "Obj", "You pick up Obj.") self.call(general.CmdDrop(), "Obj", "You drop Obj.") + def test_give(self): + self.call(general.CmdGive(), "Obj to Char2", "You aren't carrying Obj.") + self.call(general.CmdGive(), "Obj = Char2", "You aren't carrying Obj.") + self.call(general.CmdGet(), "Obj", "You pick up Obj.") + self.call(general.CmdGive(), "Obj to Char2", "You give") + self.call(general.CmdGive(), "Obj = Char", "You give", caller=self.char2) + def test_say(self): self.call(general.CmdSay(), "Testing", "You say, \"Testing\"") From 134cc9bc8a664b5aff17ea1750e25b87889d3518 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Mon, 5 Mar 2018 23:33:34 -0500 Subject: [PATCH 28/31] Bypass no valid index iterable edge case and slight refactor + comments --- evennia/commands/default/muxcommand.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/evennia/commands/default/muxcommand.py b/evennia/commands/default/muxcommand.py index 77fd889ed9..df49d9a215 100644 --- a/evennia/commands/default/muxcommand.py +++ b/evennia/commands/default/muxcommand.py @@ -113,7 +113,7 @@ class MuxCommand(Command): self.account_caller = False # split out switches - switches = [] + switches, delimiters = [], self.rhs_split if args and len(args) > 1 and raw[0] == "/": # we have a switch, or a set of switches. These end with a space. switches = args[1:].split(None, 1) @@ -150,14 +150,14 @@ class MuxCommand(Command): # check for arg1, arg2, ... = argA, argB, ... constructs lhs, rhs = args.strip(), None if lhs: - if hasattr(self.rhs_split, '__iter__'): # If delimiter is iterable, try each - best_split = self.rhs_split[0] - for this_split in self.rhs_split: - if this_split in lhs: # delimiter to allow first successful - best_split = this_split # split to be the best split. + if delimiters and hasattr(delimiters, '__iter__'): # If delimiter is iterable, + best_split = delimiters[0] # (default to first delimiter) + for this_split in delimiters: # try each delimiter + if this_split in lhs: # to find first successful split + best_split = this_split # to be the best split. break else: - best_split = self.rhs_split + best_split = delimiters # Parse to separate left into left/right sides using best_split delimiter string if best_split in lhs: lhs, rhs = lhs.split(best_split, 1) From a7b9112f0439bde3eab0a747d30e8da3bd19bb10 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Sat, 10 Mar 2018 22:41:16 -0500 Subject: [PATCH 29/31] Suggested refactors and change to test exact match --- evennia/commands/default/muxcommand.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/evennia/commands/default/muxcommand.py b/evennia/commands/default/muxcommand.py index df49d9a215..349679f5bd 100644 --- a/evennia/commands/default/muxcommand.py +++ b/evennia/commands/default/muxcommand.py @@ -114,6 +114,8 @@ class MuxCommand(Command): # split out switches switches, delimiters = [], self.rhs_split + if self.switch_options: + self.switch_options = [opt.lower() for opt in self.switch_options] if args and len(args) > 1 and raw[0] == "/": # we have a switch, or a set of switches. These end with a space. switches = args[1:].split(None, 1) @@ -127,16 +129,16 @@ class MuxCommand(Command): if switches and self.switch_options: valid_switches, unused_switches, extra_switches = [], [], [] for element in switches: - option_check = [each for each in self.switch_options - if each.lower() == element.lower() or - each.lower().startswith(element.lower())] + option_check = [opt for opt in self.switch_options if opt == element] + if not option_check: + option_check = [opt for opt in self.switch_options if opt.startswith(element)] match_count = len(option_check) if match_count > 1: - extra_switches += option_check # Either the option provided is ambiguous, + extra_switches.extend(option_check) # Either the option provided is ambiguous, elif match_count == 1: - valid_switches += option_check # or it is a valid option abbreviation, + valid_switches.extend(option_check) # or it is a valid option abbreviation, elif match_count == 0: - unused_switches += [element] # or an extraneous option to be ignored. + unused_switches.append(element) # or an extraneous option to be ignored. if extra_switches: # User provided switches self.msg('|g%s|n: |wAmbiguous switch supplied: Did you mean /|C%s|w?' % (self.cmdstring, ' |nor /|C'.join(extra_switches))) From 1ddab8d6e906e1285b19511ce31693a46bb76ad4 Mon Sep 17 00:00:00 2001 From: BlauFeuer Date: Sat, 10 Mar 2018 22:42:39 -0500 Subject: [PATCH 30/31] Add test_mux_command for exact match --- evennia/commands/default/tests.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index fd5d4982ed..bf01ac3039 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -21,6 +21,7 @@ from mock import Mock, mock from evennia.commands.default.cmdset_character import CharacterCmdSet from evennia.utils.test_resources import EvenniaTest from evennia.commands.default import help, general, system, admin, account, building, batchprocess, comms +from evennia.commands.default.muxcommand import MuxCommand from evennia.commands.command import Command, InterruptCommand from evennia.utils import ansi, utils from evennia.server.sessionhandler import SESSIONS @@ -147,6 +148,26 @@ class TestGeneral(CommandTest): self.call(general.CmdGive(), "Obj to Char2", "You give") self.call(general.CmdGive(), "Obj = Char", "You give", caller=self.char2) + def test_mux_command(self): + + class CmdTest(MuxCommand): + key = 'test' + switch_options = ('test', 'testswitch', 'testswitch2') + + def func(self): + self.msg("Switches matched: {}".format(self.switches)) + + self.call(CmdTest(), "/test/testswitch/testswitch2", "Switches matched: ['test', 'testswitch', 'testswitch2']") + self.call(CmdTest(), "/test", "Switches matched: ['test']") + self.call(CmdTest(), "/test/testswitch", "Switches matched: ['test', 'testswitch']") + self.call(CmdTest(), "/testswitch/testswitch2", "Switches matched: ['testswitch', 'testswitch2']") + self.call(CmdTest(), "/testswitch", "Switches matched: ['testswitch']") + self.call(CmdTest(), "/testswitch2", "Switches matched: ['testswitch2']") + self.call(CmdTest(), "/t", "test: Ambiguous switch supplied: " + "Did you mean /test or /testswitch or /testswitch2?|Switches matched: []") + self.call(CmdTest(), "/tests", "test: Ambiguous switch supplied: " + "Did you mean /testswitch or /testswitch2?|Switches matched: []") + def test_say(self): self.call(general.CmdSay(), "Testing", "You say, \"Testing\"") From b3e97b5c3d8f094bc4853b51423460b33d89da98 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 11 Mar 2018 10:24:27 +0100 Subject: [PATCH 31/31] Remove a boolean check to make numpy arrays work better with .msg() --- evennia/accounts/accounts.py | 2 +- evennia/objects/objects.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index b90b5ac8ab..48d03d8dc8 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -421,7 +421,7 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)): kwargs["options"] = options - if text and not (isinstance(text, basestring) or isinstance(text, tuple)): + if not (isinstance(text, basestring) or isinstance(text, tuple)): # sanitize text before sending across the wire try: text = to_str(text, force_string=True) diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index d4ce390977..f583570707 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -535,8 +535,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)): except Exception: logger.log_trace() - - if text and not (isinstance(text, basestring) or isinstance(text, tuple)): + if not (isinstance(text, basestring) or isinstance(text, tuple)): # sanitize text before sending across the wire try: text = to_str(text, force_string=True)