Extended cmdhandler error reporting with a lot - every inlineCallback needs its own internal error reporting in order to catch everything.
This commit is contained in:
parent
c1243a9d6d
commit
d1cd9da6bf
2 changed files with 188 additions and 142 deletions
|
|
@ -71,8 +71,39 @@ CMD_CHANNEL = "__send_to_channel_command"
|
||||||
# (is expected to display the login screen)
|
# (is expected to display the login screen)
|
||||||
CMD_LOGINSTART = "__unloggedin_look_command"
|
CMD_LOGINSTART = "__unloggedin_look_command"
|
||||||
|
|
||||||
# custom Exceptions
|
# Output strings
|
||||||
|
|
||||||
|
_ERROR_UNTRAPPED = "{traceback}\n" \
|
||||||
|
"Above traceback is from an untrapped error. " \
|
||||||
|
"Please file a bug report."
|
||||||
|
|
||||||
|
_ERROR_CMDSETS = "{traceback}\n" \
|
||||||
|
"Above traceback is from a cmdset merger error. " \
|
||||||
|
"Please file a bug report."
|
||||||
|
|
||||||
|
_ERROR_NOCMDSETS = "No command sets found! This is a sign of a critical bug." \
|
||||||
|
"\nThe error was logged. If disconnecting/reconnecting doesn't" \
|
||||||
|
"\nsolve the problem, try to contact the server admin through" \
|
||||||
|
"\nsome other means for assistance."
|
||||||
|
|
||||||
|
_ERROR_CMDHANDLER = "{traceback}\n"\
|
||||||
|
"Above traceback is from a Command handler bug." \
|
||||||
|
"Please file a bug report with the Evennia project."
|
||||||
|
|
||||||
|
|
||||||
|
def _msg_err(receiver, string):
|
||||||
|
"""
|
||||||
|
Helper function for returning an error to the caller.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
receiver (Object): object to get the error message
|
||||||
|
string (str): string with a {traceback} format marker inside it.
|
||||||
|
|
||||||
|
"""
|
||||||
|
receiver.msg(string.format(traceback=format_exc()))
|
||||||
|
|
||||||
|
|
||||||
|
# custom Exceptions
|
||||||
|
|
||||||
class NoCmdSets(Exception):
|
class NoCmdSets(Exception):
|
||||||
"No cmdsets found. Critical error."
|
"No cmdsets found. Critical error."
|
||||||
|
|
@ -86,8 +117,10 @@ class ExecSystemCommand(Exception):
|
||||||
self.syscmd = syscmd
|
self.syscmd = syscmd
|
||||||
self.sysarg = sysarg
|
self.sysarg = sysarg
|
||||||
|
|
||||||
# Helper function
|
class ErrorReported(Exception):
|
||||||
|
"Re-raised when a subsructure already reported the error"
|
||||||
|
|
||||||
|
# Helper function
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def get_and_merge_cmdsets(caller, session, player, obj,
|
def get_and_merge_cmdsets(caller, session, player, obj,
|
||||||
|
|
@ -107,146 +140,163 @@ def get_and_merge_cmdsets(caller, session, player, obj,
|
||||||
|
|
||||||
Note that this function returns a deferred!
|
Note that this function returns a deferred!
|
||||||
"""
|
"""
|
||||||
local_obj_cmdsets = [None]
|
try:
|
||||||
|
|
||||||
@inlineCallbacks
|
|
||||||
def _get_channel_cmdsets(player, player_cmdset):
|
|
||||||
"Channel-cmdsets"
|
|
||||||
# Create cmdset for all player's available channels
|
|
||||||
channel_cmdset = None
|
|
||||||
if not player_cmdset.no_channels:
|
|
||||||
channel_cmdset = yield CHANNELHANDLER.get_cmdset(player)
|
|
||||||
returnValue(channel_cmdset)
|
|
||||||
|
|
||||||
@inlineCallbacks
|
|
||||||
def _get_local_obj_cmdsets(obj, obj_cmdset):
|
|
||||||
"Object-level cmdsets"
|
|
||||||
# Gather cmdsets from location, objects in location or carried
|
|
||||||
local_obj_cmdsets = [None]
|
local_obj_cmdsets = [None]
|
||||||
try:
|
|
||||||
location = obj.location
|
@inlineCallbacks
|
||||||
except Exception:
|
def _get_channel_cmdsets(player, player_cmdset):
|
||||||
location = None
|
"Channel-cmdsets"
|
||||||
if location and not obj_cmdset.no_objs:
|
# Create cmdset for all player's available channels
|
||||||
# Gather all cmdsets stored on objects in the room and
|
try:
|
||||||
# also in the caller's inventory and the location itself
|
channel_cmdset = None
|
||||||
local_objlist = yield (location.contents_get(exclude=obj) +
|
if not player_cmdset.no_channels:
|
||||||
obj.contents +
|
channel_cmdset = yield CHANNELHANDLER.get_cmdset(player)
|
||||||
[location])
|
returnValue(channel_cmdset)
|
||||||
for lobj in local_objlist:
|
except Exception:
|
||||||
|
logger.log_trace()
|
||||||
|
_msg_err(caller, _ERROR_CMDSETS)
|
||||||
|
raise ErrorReported
|
||||||
|
|
||||||
|
@inlineCallbacks
|
||||||
|
def _get_local_obj_cmdsets(obj, obj_cmdset):
|
||||||
|
"Object-level cmdsets"
|
||||||
|
# Gather cmdsets from location, objects in location or carried
|
||||||
|
try:
|
||||||
|
local_obj_cmdsets = [None]
|
||||||
try:
|
try:
|
||||||
# call hook in case we need to do dynamic changing to cmdset
|
location = obj.location
|
||||||
_GA(lobj, "at_cmdset_get")()
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
location = None
|
||||||
# the call-type lock is checked here, it makes sure a player
|
if location and not obj_cmdset.no_objs:
|
||||||
# is not seeing e.g. the commands on a fellow player (which is why
|
# Gather all cmdsets stored on objects in the room and
|
||||||
# the no_superuser_bypass must be True)
|
# also in the caller's inventory and the location itself
|
||||||
local_obj_cmdsets = \
|
local_objlist = yield (location.contents_get(exclude=obj) +
|
||||||
yield [lobj.cmdset.current for lobj in local_objlist
|
obj.contents +
|
||||||
if (lobj.cmdset.current and
|
[location])
|
||||||
lobj.locks.check(caller, 'call', no_superuser_bypass=True))]
|
for lobj in local_objlist:
|
||||||
for cset in local_obj_cmdsets:
|
try:
|
||||||
#This is necessary for object sets, or we won't be able to
|
# call hook in case we need to do dynamic changing to cmdset
|
||||||
# separate the command sets from each other in a busy room.
|
_GA(lobj, "at_cmdset_get")()
|
||||||
cset.old_duplicates = cset.duplicates
|
except Exception:
|
||||||
cset.duplicates = True
|
logger.log_trace()
|
||||||
returnValue(local_obj_cmdsets)
|
# the call-type lock is checked here, it makes sure a player
|
||||||
|
# is not seeing e.g. the commands on a fellow player (which is why
|
||||||
|
# the no_superuser_bypass must be True)
|
||||||
|
local_obj_cmdsets = \
|
||||||
|
yield [lobj.cmdset.current for lobj in local_objlist
|
||||||
|
if (lobj.cmdset.current and
|
||||||
|
lobj.locks.check(caller, 'call', no_superuser_bypass=True))]
|
||||||
|
for cset in local_obj_cmdsets:
|
||||||
|
#This is necessary for object sets, or we won't be able to
|
||||||
|
# separate the command sets from each other in a busy room.
|
||||||
|
cset.old_duplicates = cset.duplicates
|
||||||
|
cset.duplicates = True
|
||||||
|
returnValue(local_obj_cmdsets)
|
||||||
|
except Exception:
|
||||||
|
logger.log_trace()
|
||||||
|
_msg_err(caller, _ERROR_CMDSETS)
|
||||||
|
raise ErrorReported
|
||||||
|
|
||||||
@inlineCallbacks
|
|
||||||
def _get_cmdset(obj):
|
|
||||||
"Get cmdset, triggering all hooks"
|
|
||||||
try:
|
|
||||||
yield obj.at_cmdset_get()
|
|
||||||
except Exception:
|
|
||||||
logger.log_trace()
|
|
||||||
try:
|
|
||||||
returnValue(obj.cmdset.current)
|
|
||||||
except AttributeError:
|
|
||||||
returnValue(None)
|
|
||||||
|
|
||||||
if callertype == "session":
|
@inlineCallbacks
|
||||||
# we are calling the command from the session level
|
def _get_cmdset(obj):
|
||||||
report_to = session
|
"Get cmdset, triggering all hooks"
|
||||||
session_cmdset = yield _get_cmdset(session)
|
try:
|
||||||
cmdsets = [session_cmdset]
|
yield obj.at_cmdset_get()
|
||||||
if player: # this automatically implies logged-in
|
except Exception:
|
||||||
|
logger.log_trace()
|
||||||
|
try:
|
||||||
|
returnValue(obj.cmdset.current)
|
||||||
|
except AttributeError:
|
||||||
|
returnValue(None)
|
||||||
|
|
||||||
|
if callertype == "session":
|
||||||
|
# we are calling the command from the session level
|
||||||
|
report_to = session
|
||||||
|
session_cmdset = yield _get_cmdset(session)
|
||||||
|
cmdsets = [session_cmdset]
|
||||||
|
if player: # this automatically implies logged-in
|
||||||
|
player_cmdset = yield _get_cmdset(player)
|
||||||
|
channel_cmdset = yield _get_channel_cmdsets(player, player_cmdset)
|
||||||
|
cmdsets.extend([player_cmdset, channel_cmdset])
|
||||||
|
if obj:
|
||||||
|
obj_cmdset = yield _get_cmdset(obj)
|
||||||
|
local_obj_cmdsets = yield _get_local_obj_cmdsets(obj, obj_cmdset)
|
||||||
|
cmdsets.extend([obj_cmdset] + local_obj_cmdsets)
|
||||||
|
elif callertype == "player":
|
||||||
|
# we are calling the command from the player level
|
||||||
|
report_to = player
|
||||||
player_cmdset = yield _get_cmdset(player)
|
player_cmdset = yield _get_cmdset(player)
|
||||||
channel_cmdset = yield _get_channel_cmdsets(player, player_cmdset)
|
channel_cmdset = yield _get_channel_cmdsets(player, player_cmdset)
|
||||||
cmdsets.extend([player_cmdset, channel_cmdset])
|
cmdsets = [player_cmdset, channel_cmdset]
|
||||||
if obj:
|
if obj:
|
||||||
obj_cmdset = yield _get_cmdset(obj)
|
obj_cmdset = yield _get_cmdset(obj)
|
||||||
local_obj_cmdsets = yield _get_local_obj_cmdsets(obj, obj_cmdset)
|
local_obj_cmdsets = yield _get_local_obj_cmdsets(obj, obj_cmdset)
|
||||||
cmdsets.extend([obj_cmdset] + local_obj_cmdsets)
|
cmdsets.extend([obj_cmdset] + local_obj_cmdsets)
|
||||||
elif callertype == "player":
|
elif callertype == "object":
|
||||||
# we are calling the command from the player level
|
# we are calling the command from the object level
|
||||||
report_to = player
|
report_to = obj
|
||||||
player_cmdset = yield _get_cmdset(player)
|
|
||||||
channel_cmdset = yield _get_channel_cmdsets(player, player_cmdset)
|
|
||||||
cmdsets = [player_cmdset, channel_cmdset]
|
|
||||||
if obj:
|
|
||||||
obj_cmdset = yield _get_cmdset(obj)
|
obj_cmdset = yield _get_cmdset(obj)
|
||||||
local_obj_cmdsets = yield _get_local_obj_cmdsets(obj, obj_cmdset)
|
local_obj_cmdsets = yield _get_local_obj_cmdsets(obj, obj_cmdset)
|
||||||
cmdsets.extend([obj_cmdset] + local_obj_cmdsets)
|
cmdsets = [obj_cmdset] + local_obj_cmdsets
|
||||||
elif callertype == "object":
|
|
||||||
# we are calling the command from the object level
|
|
||||||
report_to = obj
|
|
||||||
obj_cmdset = yield _get_cmdset(obj)
|
|
||||||
local_obj_cmdsets = yield _get_local_obj_cmdsets(obj, obj_cmdset)
|
|
||||||
cmdsets = [obj_cmdset] + local_obj_cmdsets
|
|
||||||
else:
|
|
||||||
raise Exception("get_and_merge_cmdsets: callertype %s is not valid." % callertype)
|
|
||||||
#cmdsets = yield [caller_cmdset] + [player_cmdset] +
|
|
||||||
# [channel_cmdset] + local_obj_cmdsets
|
|
||||||
|
|
||||||
# weed out all non-found sets
|
|
||||||
cmdsets = yield [cmdset for cmdset in cmdsets
|
|
||||||
if cmdset and cmdset.key != "_EMPTY_CMDSET"]
|
|
||||||
# report cmdset errors to user (these should already have been logged)
|
|
||||||
yield [report_to.msg(cmdset.errmessage) for cmdset in cmdsets
|
|
||||||
if cmdset.key == "_CMDSET_ERROR"]
|
|
||||||
|
|
||||||
if cmdsets:
|
|
||||||
# faster to do tuple on list than to build tuple directly
|
|
||||||
mergehash = tuple([id(cmdset) for cmdset in cmdsets])
|
|
||||||
if mergehash in _CMDSET_MERGE_CACHE:
|
|
||||||
# cached merge exist; use that
|
|
||||||
cmdset = _CMDSET_MERGE_CACHE[mergehash]
|
|
||||||
else:
|
else:
|
||||||
# we group and merge all same-prio cmdsets separately (this avoids
|
raise Exception("get_and_merge_cmdsets: callertype %s is not valid." % callertype)
|
||||||
# order-dependent clashes in certain cases, such as
|
#cmdsets = yield [caller_cmdset] + [player_cmdset] +
|
||||||
# when duplicates=True)
|
# [channel_cmdset] + local_obj_cmdsets
|
||||||
tempmergers = {}
|
|
||||||
for cmdset in cmdsets:
|
|
||||||
prio = cmdset.priority
|
|
||||||
#print cmdset.key, prio
|
|
||||||
if prio in tempmergers:
|
|
||||||
# merge same-prio cmdset together separately
|
|
||||||
tempmergers[prio] = yield cmdset + tempmergers[prio]
|
|
||||||
else:
|
|
||||||
tempmergers[prio] = cmdset
|
|
||||||
|
|
||||||
# sort cmdsets after reverse priority (highest prio are merged in last)
|
# weed out all non-found sets
|
||||||
cmdsets = yield sorted(tempmergers.values(), key=lambda x: x.priority)
|
cmdsets = yield [cmdset for cmdset in cmdsets
|
||||||
|
if cmdset and cmdset.key != "_EMPTY_CMDSET"]
|
||||||
|
# report cmdset errors to user (these should already have been logged)
|
||||||
|
yield [report_to.msg(cmdset.errmessage) for cmdset in cmdsets
|
||||||
|
if cmdset.key == "_CMDSET_ERROR"]
|
||||||
|
|
||||||
# Merge all command sets into one, beginning with the lowest-prio one
|
if cmdsets:
|
||||||
cmdset = cmdsets[0]
|
# faster to do tuple on list than to build tuple directly
|
||||||
for merging_cmdset in cmdsets[1:]:
|
mergehash = tuple([id(cmdset) for cmdset in cmdsets])
|
||||||
#print "<%s(%s,%s)> onto <%s(%s,%s)>" % (merging_cmdset.key, merging_cmdset.priority, merging_cmdset.mergetype,
|
if mergehash in _CMDSET_MERGE_CACHE:
|
||||||
# cmdset.key, cmdset.priority, cmdset.mergetype)
|
# cached merge exist; use that
|
||||||
cmdset = yield merging_cmdset + cmdset
|
cmdset = _CMDSET_MERGE_CACHE[mergehash]
|
||||||
# store the full sets for diagnosis
|
else:
|
||||||
cmdset.merged_from = cmdsets
|
# we group and merge all same-prio cmdsets separately (this avoids
|
||||||
# cache
|
# order-dependent clashes in certain cases, such as
|
||||||
_CMDSET_MERGE_CACHE[mergehash] = cmdset
|
# when duplicates=True)
|
||||||
else:
|
tempmergers = {}
|
||||||
cmdset = None
|
for cmdset in cmdsets:
|
||||||
|
prio = cmdset.priority
|
||||||
|
#print cmdset.key, prio
|
||||||
|
if prio in tempmergers:
|
||||||
|
# merge same-prio cmdset together separately
|
||||||
|
tempmergers[prio] = yield cmdset + tempmergers[prio]
|
||||||
|
else:
|
||||||
|
tempmergers[prio] = cmdset
|
||||||
|
|
||||||
for cset in (cset for cset in local_obj_cmdsets if cset):
|
# sort cmdsets after reverse priority (highest prio are merged in last)
|
||||||
cset.duplicates = cset.old_duplicates
|
cmdsets = yield sorted(tempmergers.values(), key=lambda x: x.priority)
|
||||||
#print "merged set:", cmdset.key
|
|
||||||
returnValue(cmdset)
|
|
||||||
|
|
||||||
|
# Merge all command sets into one, beginning with the lowest-prio one
|
||||||
|
cmdset = cmdsets[0]
|
||||||
|
for merging_cmdset in cmdsets[1:]:
|
||||||
|
#print "<%s(%s,%s)> onto <%s(%s,%s)>" % (merging_cmdset.key, merging_cmdset.priority, merging_cmdset.mergetype,
|
||||||
|
# cmdset.key, cmdset.priority, cmdset.mergetype)
|
||||||
|
cmdset = yield merging_cmdset + cmdset
|
||||||
|
# store the full sets for diagnosis
|
||||||
|
cmdset.merged_from = cmdsets
|
||||||
|
# cache
|
||||||
|
_CMDSET_MERGE_CACHE[mergehash] = cmdset
|
||||||
|
else:
|
||||||
|
cmdset = None
|
||||||
|
|
||||||
|
for cset in (cset for cset in local_obj_cmdsets if cset):
|
||||||
|
cset.duplicates = cset.old_duplicates
|
||||||
|
#print "merged set:", cmdset.key
|
||||||
|
returnValue(cmdset)
|
||||||
|
except ErrorReported:
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
logger.log_trace()
|
||||||
|
_msg_err(caller, _ERROR_CMDSETS)
|
||||||
|
raise ErrorReported
|
||||||
|
|
||||||
# Main command-handler function
|
# Main command-handler function
|
||||||
|
|
||||||
|
|
@ -344,10 +394,9 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
|
||||||
returnValue(ret)
|
returnValue(ret)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
string = "%s\nAbove traceback is from an untrapped error."
|
logger.log_trace()
|
||||||
string += " Please file a bug report."
|
_msg_err(caller, _ERROR_UNTRAPPED)
|
||||||
logger.log_trace(_(string))
|
raise ErrorReported
|
||||||
caller.msg(string % format_exc())
|
|
||||||
|
|
||||||
raw_string = to_unicode(raw_string, force_string=True)
|
raw_string = to_unicode(raw_string, force_string=True)
|
||||||
|
|
||||||
|
|
@ -449,6 +498,11 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
|
||||||
ret = yield _run_command(cmd, cmdname, args)
|
ret = yield _run_command(cmd, cmdname, args)
|
||||||
returnValue(ret)
|
returnValue(ret)
|
||||||
|
|
||||||
|
except ErrorReported:
|
||||||
|
# this error was already reported, so we
|
||||||
|
# catch it here and don't pass it on.
|
||||||
|
pass
|
||||||
|
|
||||||
except ExecSystemCommand, exc:
|
except ExecSystemCommand, exc:
|
||||||
# Not a normal command: run a system command, if available,
|
# Not a normal command: run a system command, if available,
|
||||||
# or fall back to a return string.
|
# or fall back to a return string.
|
||||||
|
|
@ -464,24 +518,15 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
|
||||||
|
|
||||||
except NoCmdSets:
|
except NoCmdSets:
|
||||||
# Critical error.
|
# Critical error.
|
||||||
string = "No command sets found! This is a sign of a critical bug.\n"
|
|
||||||
string += "The error was logged.\n"
|
|
||||||
string += "If logging out/in doesn't solve the problem, try to "
|
|
||||||
string += "contact the server admin through some other means "
|
|
||||||
string += "for assistance."
|
|
||||||
caller.msg(_(string))
|
|
||||||
logger.log_errmsg("No cmdsets found: %s" % caller)
|
logger.log_errmsg("No cmdsets found: %s" % caller)
|
||||||
|
caller.msg(_ERROR_NOCMDSETS)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# We should not end up here. If we do, it's a programming bug.
|
# We should not end up here. If we do, it's a programming bug.
|
||||||
string = "%s\nAbove traceback is from an untrapped error."
|
logger.log_trace()
|
||||||
string += " Please file a bug report."
|
_msg_err(caller, _ERROR_UNTRAPPED)
|
||||||
logger.log_trace(_(string))
|
|
||||||
caller.msg(string % format_exc())
|
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# This catches exceptions in cmdhandler exceptions themselves
|
# This catches exceptions in cmdhandler exceptions themselves
|
||||||
string = "%s\nAbove traceback is from a Command handler bug."
|
logger.log_trace()
|
||||||
string += " Please contact an admin and/or file a bug report."
|
_msg_err(caller, _ERROR_CMDHANDLER)
|
||||||
logger.log_trace(_(string))
|
|
||||||
caller.msg(string % format_exc())
|
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,7 @@ class Mob(tut_objects.TutorialObject):
|
||||||
if last_interval:
|
if last_interval:
|
||||||
# we have a previous subscription, kill this first.
|
# we have a previous subscription, kill this first.
|
||||||
TICKER_HANDLER.remove(self, last_interval, idstring)
|
TICKER_HANDLER.remove(self, last_interval, idstring)
|
||||||
|
self.db.last_ticker_interval = interval
|
||||||
if not stop:
|
if not stop:
|
||||||
# set the new ticker
|
# set the new ticker
|
||||||
TICKER_HANDLER.add(self, interval, idstring, hook_key)
|
TICKER_HANDLER.add(self, interval, idstring, hook_key)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue