diff --git a/src/commands/cmdset.py b/src/commands/cmdset.py index c54383ef1b..ebab1aad09 100644 --- a/src/commands/cmdset.py +++ b/src/commands/cmdset.py @@ -152,21 +152,21 @@ class CmdSet(object): # Priority-sensitive merge operations for cmdsets - def _union(self, cmdset_a, cmdset_b, duplicates=False): + def _union(self, cmdset_a, cmdset_b): "C = A U B. CmdSet A is assumed to have higher priority" cmdset_c = cmdset_a._duplicate() # we make copies, not refs by use of [:] cmdset_c.commands = cmdset_a.commands[:] - if duplicates and cmdset_a.priority == cmdset_b.priority: + if cmdset_a.duplicates and cmdset_a.priority == cmdset_b.priority: cmdset_c.commands.extend(cmdset_b.commands) else: cmdset_c.commands.extend([cmd for cmd in cmdset_b if not cmd in cmdset_a]) return cmdset_c - def _intersect(self, cmdset_a, cmdset_b, duplicates=False): + def _intersect(self, cmdset_a, cmdset_b): "C = A (intersect) B. A is assumed higher priority" cmdset_c = cmdset_a._duplicate() - if duplicates and cmdset_a.priority == cmdset_b.priority: + if cmdset_a.duplicates and cmdset_a.priority == cmdset_b.priority: for cmd in [cmd for cmd in cmdset_a if cmd in cmdset_b]: cmdset_c.add(cmd) cmdset_c.add(cmdset_b.get(cmd)) @@ -174,13 +174,13 @@ class CmdSet(object): cmdset_c.commands = [cmd for cmd in cmdset_a if cmd in cmdset_b] return cmdset_c - def _replace(self, cmdset_a, cmdset_b, cmdset_c): + def _replace(self, cmdset_a, cmdset_b): "C = A + B where the result is A." cmdset_c = cmdset_a._duplicate() cmdset_c.commands = cmdset_a.commands[:] return cmdset_c - def _remove(self, cmdset_a, cmdset_b, cmdset_c): + def _remove(self, cmdset_a, cmdset_b): "C = A + B, where B is filtered by A" cmdset_c = cmdset_a._duplicate() cmdset_c.commands = [cmd for cmd in cmdset_b if not cmd in cmdset_a] @@ -267,13 +267,13 @@ class CmdSet(object): mergetype = self.key_mergetypes.get(cmdset_b.key, self.mergetype) if mergetype == "Intersect": - cmdset_c = self._intersect(self, cmdset_b, cmdset_b.duplicates) + cmdset_c = self._intersect(self, cmdset_b) elif mergetype == "Replace": - cmdset_c = self._replace(self, cmdset_b, cmdset_b.duplicates) + cmdset_c = self._replace(self, cmdset_b) elif mergetype == "Remove": - cmdset_c = self._remove(self, cmdset_b, cmdset_b.duplicates) + cmdset_c = self._remove(self, cmdset_b) else: # Union - cmdset_c = self._union(self, cmdset_b, cmdset_b.duplicates) + cmdset_c = self._union(self, cmdset_b) cmdset_c.no_channels = self.no_channels cmdset_c.no_exits = self.no_exits cmdset_c.no_objs = self.no_objs @@ -286,13 +286,13 @@ class CmdSet(object): mergetype = cmdset_b.key_mergetypes.get(self.key, cmdset_b.mergetype) if mergetype == "Intersect": - cmdset_c = self._intersect(cmdset_b, self, self.duplicates) + cmdset_c = self._intersect(cmdset_b, self) elif mergetype == "Replace": - cmdset_c = self._replace(cmdset_b, self, self.duplicates) + cmdset_c = self._replace(cmdset_b, self) elif mergetype == "Remove": - cmdset_c = self._remove(self, cmdset_b, self.duplicates) + cmdset_c = self._remove(self, cmdset_b) else: # Union - cmdset_c = self._union(cmdset_b, self, self.duplicates) + cmdset_c = self._union(cmdset_b, self) cmdset_c.no_channels = cmdset_b.no_channels cmdset_c.no_exits = cmdset_b.no_exits cmdset_c.no_objs = cmdset_b.no_objs diff --git a/src/commands/command.py b/src/commands/command.py index 13988352dd..e6badfe15c 100644 --- a/src/commands/command.py +++ b/src/commands/command.py @@ -9,58 +9,70 @@ import re from src.locks.lockhandler import LockHandler from src.utils.utils import is_iter, fill +def _init_command(mcs, **kwargs): + """ + Helper command. + Makes sure all data are stored as lowercase and + do checking on all properties that should be in list form. + Sets up locks to be more forgiving. This is used both by the metaclass + and (optionally) at instantiation time. + + If kwargs are given, these are set as instance-specific properties on the command. + """ + for i in range(len(kwargs)): + # used for dynamic creation of commands + key, value = kwargs.popitem() + setattr(mcs, key, value) + + mcs.key = mcs.key.lower() + if mcs.aliases and not is_iter(mcs.aliases): + try: + mcs.aliases = [str(alias).strip().lower() for alias in mcs.aliases.split(',')] + except Exception: + mcs.aliases = [] + mcs.aliases = list(set(alias for alias in mcs.aliases if alias != mcs.key)) + + # optimization - a set is much faster to match against than a list + mcs._matchset = set([mcs.key] + mcs.aliases) + # optimization for looping over keys+aliases + mcs._keyaliases = tuple(mcs._matchset) + + # by default we don't save the command between runs + if not hasattr(mcs, "save_for_next"): + mcs.save_for_next = False + + # pre-process locks as defined in class definition + temp = [] + if hasattr(mcs, 'permissions'): + mcs.locks = mcs.permissions + if not hasattr(mcs, 'locks'): + # default if one forgets to define completely + mcs.locks = "cmd:all()" + for lockstring in mcs.locks.split(';'): + if lockstring and not ':' in lockstring: + lockstring = "cmd:%s" % lockstring + temp.append(lockstring) + mcs.lock_storage = ";".join(temp) + + if hasattr(mcs, 'arg_regex') and isinstance(mcs.arg_regex, basestring): + mcs.arg_regex = re.compile(r"%s" % mcs.arg_regex, re.I) + else: + mcs.arg_regex = None + if not hasattr(mcs, "auto_help"): + mcs.auto_help = True + if not hasattr(mcs, 'is_exit'): + mcs.is_exit = False + if not hasattr(mcs, "help_category"): + mcs.help_category = "general" + mcs.help_category = mcs.help_category.lower() + + class CommandMeta(type): """ - This metaclass makes some minor on-the-fly convenience fixes to the command - class in case the admin forgets to put things in lowercase etc. + The metaclass cleans up all properties on the class """ def __init__(mcs, *args, **kwargs): - """ - Simply make sure all data are stored as lowercase and - do checking on all properties that should be in list form. - Sets up locks to be more forgiving. - """ - mcs.key = mcs.key.lower() - if mcs.aliases and not is_iter(mcs.aliases): - try: - mcs.aliases = [str(alias).strip().lower() for alias in mcs.aliases.split(',')] - except Exception: - mcs.aliases = [] - mcs.aliases = list(set(alias for alias in mcs.aliases if alias != mcs.key)) - - # optimization - a set is much faster to match against than a list - mcs._matchset = set([mcs.key] + mcs.aliases) - # optimization for looping over keys+aliases - mcs._keyaliases = tuple(mcs._matchset) - - # by default we don't save the command between runs - if not hasattr(mcs, "save_for_next"): - mcs.save_for_next = False - - # pre-process locks as defined in class definition - temp = [] - if hasattr(mcs, 'permissions'): - mcs.locks = mcs.permissions - if not hasattr(mcs, 'locks'): - # default if one forgets to define completely - mcs.locks = "cmd:all()" - for lockstring in mcs.locks.split(';'): - if lockstring and not ':' in lockstring: - lockstring = "cmd:%s" % lockstring - temp.append(lockstring) - mcs.lock_storage = ";".join(temp) - - if hasattr(mcs, 'arg_regex') and isinstance(mcs.arg_regex, basestring): - mcs.arg_regex = re.compile(r"%s" % mcs.arg_regex, re.I) - else: - mcs.arg_regex = None - if not hasattr(mcs, "auto_help"): - mcs.auto_help = True - if not hasattr(mcs, 'is_exit'): - mcs.is_exit = False - if not hasattr(mcs, "help_category"): - mcs.help_category = "general" - mcs.help_category = mcs.help_category.lower() + _init_command(mcs, **kwargs) super(CommandMeta, mcs).__init__(*args, **kwargs) # The Command class is the basic unit of an Evennia command; when @@ -125,8 +137,12 @@ class Command(object): # sessid - which session-id (if any) is responsible for triggering this command # - def __init__(self): - "the lockhandler works the same as for objects." + def __init__(self, **kwargs): + """the lockhandler works the same as for objects. + optional kwargs will be set as properties on the Command at runtime, + overloading evential same-named class properties.""" + if kwargs: + _init_command(self, **kwargs) self.lockhandler = LockHandler(self) def __str__(self): diff --git a/src/comms/channelhandler.py b/src/comms/channelhandler.py index 876f1494df..e020e88e95 100644 --- a/src/comms/channelhandler.py +++ b/src/comms/channelhandler.py @@ -136,14 +136,11 @@ class ChannelHandler(object): and run self.update on the handler. """ # map the channel to a searchable command - cmd = ChannelCommand() - cmd.key = channel.key.strip().lower() - cmd.obj = channel + cmd = ChannelCommand(key=channel.key.strip().lower(), + aliases=channel.aliases if channel.aliases else [], + locks="cmd:all();%s" % channel.locks, + obj=channel) cmd.__doc__= self._format_help(channel) - if channel.aliases: - cmd.aliases = channel.aliases - cmd.lock_storage = "cmd:all();%s" % channel.locks - cmd.lockhandler.reset() self.cached_channel_cmds.append(cmd) self.cached_cmdsets = {} diff --git a/src/objects/objects.py b/src/objects/objects.py index ed67fc026c..1df919d032 100644 --- a/src/objects/objects.py +++ b/src/objects/objects.py @@ -907,13 +907,12 @@ class Exit(Object): self.obj.at_failed_traverse(self.caller) # create an exit command. - cmd = ExitCommand() - cmd.key = exidbobj.db_key.strip().lower() - cmd.obj = exidbobj - cmd.aliases = exidbobj.aliases - cmd.locks = str(exidbobj.locks) - cmd.destination = exidbobj.db_destination - cmd.auto_help = False + cmd = ExitCommand(key=exidbobj.db_key.strip().lower(), + aliases=exidbobj.aliases, + locks=str(exidbobj.locks), + auto_help=False, + destination=exidbobj.db_destination, + obj=exidbobj) # create a cmdset exit_cmdset = cmdset.CmdSet(None) exit_cmdset.key = '_exitset'