Continuing to make more methods _private to simplify API.

This commit is contained in:
Griatch 2012-03-31 16:09:48 +02:00
parent c8df141e89
commit d44dd92b5f
6 changed files with 277 additions and 268 deletions

View file

@ -44,25 +44,29 @@ from src.commands.cmdsethandler import import_cmdset
from src.utils import logger, utils
from src.commands.cmdparser import at_multimatch_cmd
#This switches the command parser to a user-defined one.
# You have to restart the server for this to take effect.
COMMAND_PARSER = utils.mod_import(*settings.COMMAND_PARSER.rsplit('.', 1))
__all__ = ("cmdhandler",)
# There are a few system-hardcoded command names. These
# allow for custom behaviour when the command handler hits
# special situations -- it then calls a normal Command
# that you can customize!
# Import these variables and use them rather than trying
# to remember the actual string constants.
# This decides which command parser is to be used.
# You have to restart the server for changes to take effect.
_COMMAND_PARSER = utils.mod_import(*settings.COMMAND_PARSER.rsplit('.', 1))
# System command names - import these variables rather than trying to
# remember the actual string constants. If not defined, Evennia
# hard-coded defaults are used instead.
# command to call if user just presses <return> with no input
CMD_NOINPUT = "__noinput_command"
# command to call if no command match was found
CMD_NOMATCH = "__nomatch_command"
# command to call if multiple command matches were found
CMD_MULTIMATCH = "__multimatch_command"
# command to call if found command is the name of a channel
CMD_CHANNEL = "__send_to_channel_command"
# this is the name of the command the engine calls when the player
# connects. It is expected to show the login screen.
# command to call as the very first one when the user connects.
# (is expected to display the login screen)
CMD_LOGINSTART = "__unloggedin_look_command"
# custom Exceptions
class NoCmdSets(Exception):
"No cmdsets found. Critical error."
@ -74,11 +78,15 @@ class ExecSystemCommand(Exception):
self.syscmd = syscmd
self.sysarg = sysarg
# Helper function
@inlineCallbacks
def get_and_merge_cmdsets(caller):
def _get_and_merge_cmdsets(caller):
"""
Gather all relevant cmdsets and merge them. Note
that this is only relevant for logged-in callers.
Note that this function returns a deferred!
"""
# The calling object's cmdset
try:
@ -163,7 +171,7 @@ def cmdhandler(caller, raw_string, testing=False):
try: # catch bugs in cmdhandler itself
try: # catch special-type commands
cmdset = yield get_and_merge_cmdsets(caller)
cmdset = yield _get_and_merge_cmdsets(caller)
if not cmdset:
# this is bad and shouldn't happen.
raise NoCmdSets
@ -177,7 +185,7 @@ def cmdhandler(caller, raw_string, testing=False):
# Parse the input string and match to available cmdset.
# This also checks for permissions, so all commands in match
# are commands the caller is allowed to call.
matches = yield COMMAND_PARSER(raw_string, cmdset, caller)
matches = yield _COMMAND_PARSER(raw_string, cmdset, caller)
# Deal with matches
if not matches:
# No commands match our entered command
@ -302,5 +310,3 @@ def cmdhandler(caller, raw_string, testing=False):
string += " Please contact an admin and/or file a bug report."
logger.log_trace(string)
caller.msg(string % format_exc())
#----------------------------------------------------- end cmdhandler

View file

@ -16,10 +16,9 @@ together to create interesting in-game effects.
import copy
from src.utils.utils import inherits_from, is_iter
__all__ = ("CmdSet",)
RECURSIVE_PROTECTION = False
class CmdSetMeta(type):
class _CmdSetMeta(type):
"""
This metaclass makes some minor on-the-fly convenience fixes to
the cmdset class.
@ -37,55 +36,7 @@ class CmdSetMeta(type):
if not type(mcs.key_mergetypes) == dict:
mcs.key_mergetypes = {}
super(CmdSetMeta, mcs).__init__(*args, **kwargs)
# Some priority-sensitive merge operations for cmdsets
def union(cmdset_a, cmdset_b, duplicates=False):
"C = A U B. CmdSet A is assumed to have higher priority"
cmdset_c = cmdset_a.copy_this()
# we make copies, not refs by use of [:]
cmdset_c.commands = cmdset_a.commands[:]
if 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(cmdset_a, cmdset_b, duplicates=False):
"C = A (intersect) B. A is assumed higher priority"
cmdset_c = cmdset_a.copy_this()
if 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))
else:
cmdset_c.commands = [cmd for cmd in cmdset_a if cmd in cmdset_b]
return cmdset_c
def replace(cmdset_a, cmdset_b, cmdset_c):
"C = A + B where the result is A."
cmdset_c = cmdset_a.copy_this()
cmdset_c.commands = cmdset_a.commands[:]
return cmdset_c
def remove(cmdset_a, cmdset_b, cmdset_c):
"C = A + B, where B is filtered by A"
cmdset_c = cmdset_a.copy_this()
cmdset_c.commands = [cmd for cmd in cmdset_b if not cmd in cmdset_a]
return cmdset_c
def instantiate(cmd):
"""
checks so that object is an instantiated command
and not, say a cmdclass. If it is, instantiate it.
Other types, like strings, are passed through.
"""
try:
return cmd()
except TypeError:
return cmd
super(_CmdSetMeta, mcs).__init__(*args, **kwargs)
class CmdSet(object):
"""
@ -166,7 +117,7 @@ class CmdSet(object):
"""
__metaclass__ = CmdSetMeta
__metaclass__ = _CmdSetMeta
key = "Unnamed CmdSet"
mergetype = "Union"
@ -177,6 +128,9 @@ class CmdSet(object):
no_objs = False
no_channels = False
permanent = False
# pre-store properties to duplicate straight off
to_duplicate = ("key", "cmdsetobj", "no_exits", "no_objs", "no_channels", "permanent",
"mergetype", "priority", "duplicates")
def __init__(self, cmdsetobj=None, key=None):
"""
@ -194,127 +148,63 @@ class CmdSet(object):
# initialize system
self.at_cmdset_creation()
def at_cmdset_creation(self):
"""
Hook method - this should be overloaded in the inheriting
class, and should take care of populating the cmdset
by use of self.add().
"""
pass
# Priority-sensitive merge operations for cmdsets
def add(self, cmd):
"""
Add a command, a list of commands or a cmdset to this cmdset.
Note that if cmd already exists in set,
it will replace the old one (no priority checking etc
at this point; this is often used to overload
default commands).
If cmd is another cmdset class or -instance, the commands
of that command set is added to this one, as if they were part
of the original cmdset definition. No merging or priority checks
are made, rather later added commands will simply replace
existing ones to make a unique set.
"""
if inherits_from(cmd, "src.commands.cmdset.CmdSet"):
# cmd is a command set so merge all commands in that set
# to this one. We raise a visible error if we created
# an infinite loop (adding cmdset to itself somehow)
try:
cmd = instantiate(cmd)
except RuntimeError, e:
string = "Adding cmdset %s to %s lead to an infinite loop. When adding a cmdset to another, "
string += "make sure they are not themself cyclically added to the new cmdset somewhere in the chain."
raise RuntimeError(string % (cmd, self.__class__))
cmds = cmd.commands
elif is_iter(cmd):
cmds = [instantiate(c) for c in cmd]
def _union(self, cmdset_a, cmdset_b, duplicates=False):
"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:
cmdset_c.commands.extend(cmdset_b.commands)
else:
cmds = [instantiate(cmd)]
for cmd in cmds:
# add all commands
if not hasattr(cmd, 'obj'):
cmd.obj = self.cmdsetobj
try:
ic = self.commands.index(cmd)
self.commands[ic] = cmd # replace
except ValueError:
self.commands.append(cmd)
# extra run to make sure to avoid doublets
self.commands = list(set(self.commands))
#print "In cmdset.add(cmd):", self.key, cmd
cmdset_c.commands.extend([cmd for cmd in cmdset_b if not cmd in cmdset_a])
return cmdset_c
def remove(self, cmd):
"""
Remove a command instance from the cmdset.
cmd can be either a cmd instance or a key string.
"""
cmd = instantiate(cmd)
self.commands = [oldcmd for oldcmd in self.commands if oldcmd != cmd]
def _intersect(self, cmdset_a, cmdset_b, duplicates=False):
"C = A (intersect) B. A is assumed higher priority"
cmdset_c = cmdset_a._duplicate()
if 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))
else:
cmdset_c.commands = [cmd for cmd in cmdset_a if cmd in cmdset_b]
return cmdset_c
def get(self, cmd):
"""
Return the command in this cmdset that matches the
given command. cmd may be either a command instance or
a key string.
"""
cmd = instantiate(cmd)
for thiscmd in self.commands:
if thiscmd == cmd:
return thiscmd
def _replace(self, cmdset_a, cmdset_b, cmdset_c):
"C = A + B where the result is A."
cmdset_c = cmdset_a._duplicate()
cmdset_c.commands = cmdset_a.commands[:]
return cmdset_c
def count(self):
"Return number of commands in set"
return len(self.commands)
def _remove(self, cmdset_a, cmdset_b, cmdset_c):
"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]
return cmdset_c
def get_system_cmds(self):
def _instantiate(self, cmd):
"""
Return system commands in the cmdset, defined as
commands starting with double underscore __.
These are excempt from merge operations.
checks so that object is an instantiated command
and not, say a cmdclass. If it is, instantiate it.
Other types, like strings, are passed through.
"""
return [cmd for cmd in self.commands if cmd.key.startswith('__')]
try:
return cmd()
except TypeError:
return cmd
def copy_this(self):
def _duplicate(self):
"""
Returns a new cmdset with the same settings as this one
(no commands are copied over)
(no actual commands are copied over)
"""
cmdset = CmdSet()
cmdset.key = self.key
cmdset.cmdsetobj = self.cmdsetobj
cmdset.no_exits = self.no_exits
cmdset.no_objs = self.no_objs
cmdset.no_channels = self.no_channels
cmdset.mergetype = self.mergetype
cmdset.priority = self.priority
cmdset.duplicates = self.duplicates
cmdset.__dict__.update(dict((key, val) for key, val in self.__dict__.items() if key in self.to_duplicate))
cmdset.key_mergetypes = self.key_mergetypes.copy() #copy.deepcopy(self.key_mergetypes)
return cmdset
def make_unique(self, caller):
"""
This is an unsafe command meant to clean out a cmdset of
doublet commands after it has been created. It is useful
for commands inheriting cmdsets from the cmdhandler where
obj-based cmdsets always are added double. Doublets will
be weeded out with preference to commands defined on caller,
otherwise just by first-come-first-served.
"""
unique = {}
for cmd in self.commands:
if cmd.key in unique:
ocmd = unique[cmd.key]
if (hasattr(cmd, 'obj') and cmd.obj == caller) and not \
(hasattr(ocmd, 'obj') and ocmd.obj == caller):
unique[cmd.key] = cmd
else:
unique[cmd.key] = cmd
self.commands = unique.values()
def __str__(self):
"""
Show all commands in cmdset when printing it.
@ -360,24 +250,24 @@ class CmdSet(object):
# A higher or equal priority than B
mergetype = self.key_mergetypes.get(cmdset_b.key, self.mergetype)
if mergetype == "Intersect":
cmdset_c = intersect(self, cmdset_b, cmdset_b.duplicates)
cmdset_c = self._intersect(self, cmdset_b, cmdset_b.duplicates)
elif mergetype == "Replace":
cmdset_c = replace(self, cmdset_b, cmdset_b.duplicates)
cmdset_c = self._replace(self, cmdset_b, cmdset_b.duplicates)
elif mergetype == "Remove":
cmdset_c = remove(self, cmdset_b, cmdset_b.duplicates)
cmdset_c = self._remove(self, cmdset_b, cmdset_b.duplicates)
else: # Union
cmdset_c = union(self, cmdset_b, cmdset_b.duplicates)
cmdset_c = self._union(self, cmdset_b, cmdset_b.duplicates)
else:
# B higher priority than A
mergetype = cmdset_b.key_mergetypes.get(self.key, cmdset_b.mergetype)
if mergetype == "Intersect":
cmdset_c = intersect(cmdset_b, self, self.duplicates)
cmdset_c = self._intersect(cmdset_b, self, self.duplicates)
elif mergetype == "Replace":
cmdset_c = replace(cmdset_b, self, self.duplicates)
cmdset_c = self._replace(cmdset_b, self, self.duplicates)
elif mergetype == "Remove":
cmdset_c = remove(self, cmdset_b, self.duplicates)
cmdset_c = self._remove(self, cmdset_b, self.duplicates)
else: # Union
cmdset_c = union(cmdset_b, self, self.duplicates)
cmdset_c = self._union(cmdset_b, self, self.duplicates)
# we store actual_mergetype since key_mergetypes
# might be different from the main mergetype.
@ -388,3 +278,107 @@ class CmdSet(object):
cmdset_c.add(sys_commands)
return cmdset_c
def add(self, cmd):
"""
Add a command, a list of commands or a cmdset to this cmdset.
Note that if cmd already exists in set,
it will replace the old one (no priority checking etc
at this point; this is often used to overload
default commands).
If cmd is another cmdset class or -instance, the commands
of that command set is added to this one, as if they were part
of the original cmdset definition. No merging or priority checks
are made, rather later added commands will simply replace
existing ones to make a unique set.
"""
if inherits_from(cmd, "src.commands.cmdset.CmdSet"):
# cmd is a command set so merge all commands in that set
# to this one. We raise a visible error if we created
# an infinite loop (adding cmdset to itself somehow)
try:
cmd = self._instantiate(cmd)
except RuntimeError, e:
string = "Adding cmdset %s to %s lead to an infinite loop. When adding a cmdset to another, "
string += "make sure they are not themself cyclically added to the new cmdset somewhere in the chain."
raise RuntimeError(string % (cmd, self.__class__))
cmds = cmd.commands
elif is_iter(cmd):
cmds = [self._instantiate(c) for c in cmd]
else:
cmds = [self._instantiate(cmd)]
for cmd in cmds:
# add all commands
if not hasattr(cmd, 'obj'):
cmd.obj = self.cmdsetobj
try:
ic = self.commands.index(cmd)
self.commands[ic] = cmd # replace
except ValueError:
self.commands.append(cmd)
# extra run to make sure to avoid doublets
self.commands = list(set(self.commands))
#print "In cmdset.add(cmd):", self.key, cmd
def remove(self, cmd):
"""
Remove a command instance from the cmdset.
cmd can be either a cmd instance or a key string.
"""
cmd = self._instantiate(cmd)
self.commands = [oldcmd for oldcmd in self.commands if oldcmd != cmd]
def get(self, cmd):
"""
Return the command in this cmdset that matches the
given command. cmd may be either a command instance or
a key string.
"""
cmd = self._instantiate(cmd)
for thiscmd in self.commands:
if thiscmd == cmd:
return thiscmd
def count(self):
"Return number of commands in set"
return len(self.commands)
def get_system_cmds(self):
"""
Return system commands in the cmdset, defined as
commands starting with double underscore __.
These are excempt from merge operations.
"""
return [cmd for cmd in self.commands if cmd.key.startswith('__')]
def make_unique(self, caller):
"""
This is an unsafe command meant to clean out a cmdset of
doublet commands after it has been created. It is useful
for commands inheriting cmdsets from the cmdhandler where
obj-based cmdsets always are added double. Doublets will
be weeded out with preference to commands defined on caller,
otherwise just by first-come-first-served.
"""
unique = {}
for cmd in self.commands:
if cmd.key in unique:
ocmd = unique[cmd.key]
if (hasattr(cmd, 'obj') and cmd.obj == caller) and not \
(hasattr(ocmd, 'obj') and ocmd.obj == caller):
unique[cmd.key] = cmd
else:
unique[cmd.key] = cmd
self.commands = unique.values()
def at_cmdset_creation(self):
"""
Hook method - this should be overloaded in the inheriting
class, and should take care of populating the cmdset
by use of self.add().
"""
pass

View file

@ -67,8 +67,9 @@ import traceback
from src.utils import logger, utils
from src.commands.cmdset import CmdSet
from src.server.models import ServerConfig
__all__ = ("import_cmdset", "CmdSetHandler")
CACHED_CMDSETS = {}
_CACHED_CMDSETS = {}
def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
"""
@ -87,9 +88,9 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
try:
try:
#print "importing %s: CACHED_CMDSETS=%s" % (python_path, CACHED_CMDSETS)
#print "importing %s: _CACHED_CMDSETS=%s" % (python_path, _CACHED_CMDSETS)
wanted_cache_key = python_path
cmdsetclass = CACHED_CMDSETS.get(wanted_cache_key, None)
cmdsetclass = _CACHED_CMDSETS.get(wanted_cache_key, None)
errstring = ""
if not cmdsetclass:
#print "cmdset '%s' not in cache. Reloading %s on %s." % (wanted_cache_key, python_path, cmdsetobj)
@ -97,7 +98,7 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
modulepath, classname = python_path.rsplit('.', 1)
module = __import__(modulepath, fromlist=[True])
cmdsetclass = module.__dict__[classname]
CACHED_CMDSETS[wanted_cache_key] = cmdsetclass
_CACHED_CMDSETS[wanted_cache_key] = cmdsetclass
#instantiate the cmdset (and catch its errors)
if callable(cmdsetclass):
cmdsetclass = cmdsetclass(cmdsetobj)
@ -115,13 +116,15 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
errstring = "\n%s\nCompile/Run error when loading cmdset '%s'."
errstring = errstring % (traceback.format_exc(), python_path)
raise
except Exception:
except Exception, e:
if errstring and not no_logging:
print errstring
logger.log_trace()
if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"):
object.__getattribute__(emit_to_obj, "msg")(errstring)
logger.log_errmsg("Error: %s" % errstring)
if not errstring:
errstring = "Error in import cmdset: %s" % e
logger.log_errmsg(errstring)
#cannot raise - it kills the server if no base cmdset exists!
# classes
@ -198,6 +201,17 @@ class CmdSetHandler(object):
", ".join(cmd.key for cmd in sorted(self.current, key=lambda o:o.key)))
return string.strip()
def _import_cmdset(self, cmdset_path, emit_to_obj=None):
"""
Method wrapper for import_cmdset.
load a cmdset from a module.
cmdset_path - the python path to an cmdset object.
emit_to_obj - object to send error messages to
"""
if not emit_to_obj:
emit_to_obj = self.obj
return import_cmdset(cmdset_path, self.obj, emit_to_obj)
def update(self, init_mode=False):
"""
Re-adds all sets in the handler to have an updated
@ -216,7 +230,7 @@ class CmdSetHandler(object):
if pos == 0 and not path:
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
elif path:
cmdset = self.import_cmdset(path)
cmdset = self._import_cmdset(path)
if cmdset:
cmdset.permanent = True
self.cmdset_stack.append(cmdset)
@ -233,17 +247,6 @@ class CmdSetHandler(object):
self.mergetype_stack.append(new_current.actual_mergetype)
self.current = new_current
def import_cmdset(self, cmdset_path, emit_to_obj=None):
"""
Method wrapper for import_cmdset.
load a cmdset from a module.
cmdset_path - the python path to an cmdset object.
emit_to_obj - object to send error messages to
"""
if not emit_to_obj:
emit_to_obj = self.obj
return import_cmdset(cmdset_path, self.obj, emit_to_obj)
def add(self, cmdset, emit_to_obj=None, permanent=False):
"""
Add a cmdset to the handler, on top of the old ones.
@ -271,7 +274,7 @@ class CmdSetHandler(object):
cmdset = cmdset(self.obj)
elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache.
cmdset = self.import_cmdset(cmdset)
cmdset = self._import_cmdset(cmdset)
if cmdset:
if permanent:
# store the path permanently
@ -303,7 +306,7 @@ class CmdSetHandler(object):
cmdset = cmdset(self.obj)
elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache.
cmdset = self.import_cmdset(cmdset)
cmdset = self._import_cmdset(cmdset)
if cmdset:
if self.cmdset_stack:
self.cmdset_stack[0] = cmdset
@ -430,7 +433,7 @@ class CmdSetHandler(object):
def reset(self):
"""
Force reload of all cmdsets in handler. This should be called
after CACHED_CMDSETS have been cleared (normally by @reload).
after _CACHED_CMDSETS have been cleared (normally by @reload).
"""
new_cmdset_stack = []
new_mergetype_stack = []
@ -439,7 +442,7 @@ class CmdSetHandler(object):
new_cmdset_stack.append(cmdset)
new_mergetype_stack.append("Union")
else:
new_cmdset_stack.append(self.import_cmdset(cmdset.path))
new_cmdset_stack.append(self._import_cmdset(cmdset.path))
new_mergetype_stack.append(cmdset.mergetype)
self.cmdset_stack = new_cmdset_stack
self.mergetype_stack = new_mergetype_stack