Changed how the Typeclass system returns errors. Instead of echoing typeclass erros to the MUD-info channel (which is not only not only very spammy for everyone but also very hard to make clean so as to avoid recursion at a stage of typeclass failing), the system instead stores a property on itself called 'typeclass_last_errmsg' that holds eventual errors. This means that the task of reporting errors does not fall on the typeclass system itself but on the calling methods, as it should be. So src.utils.create.create_* functions now takes a new optional keyword "report_to" that holds an object to receive errors. If this keyword is given, the function msg():es that object with the error and returns None as before. If report_to is not set however, the create_* methods now return an Exception containing the error text. All default commands have been changed to accomodate for this behaviour, which allows for much more control over errors.

Also, the default ADMIN_MEDIA static files changed location in Django 1.4. The initial_setup function now accounts for this.
This commit is contained in:
Griatch 2012-04-21 16:15:37 +02:00
parent 63329f5420
commit 8c3b49e704
15 changed files with 838 additions and 785 deletions

View file

@ -399,9 +399,8 @@ class CmdCreate(ObjManipCommand):
# object typeclass will automatically be used) # object typeclass will automatically be used)
lockstring = "control:id(%s);examine:perm(Builders);delete:id(%s) or perm(Wizards);get:all()" % (caller.id, caller.id) lockstring = "control:id(%s);examine:perm(Builders);delete:id(%s) or perm(Wizards);get:all()" % (caller.id, caller.id)
obj = create.create_object(typeclass, name, caller, obj = create.create_object(typeclass, name, caller,
home=caller, aliases=aliases, locks=lockstring) home=caller, aliases=aliases, locks=lockstring, report_to=caller)
if not obj: if not obj:
string = "Error when creating object."
continue continue
if aliases: if aliases:
string = "You create a new %s: %s (aliases: %s)." string = "You create a new %s: %s (aliases: %s)."
@ -416,8 +415,8 @@ class CmdCreate(ObjManipCommand):
if caller.location: if caller.location:
obj.home = caller.location obj.home = caller.location
obj.move_to(caller.location, quiet=True) obj.move_to(caller.location, quiet=True)
if string: if string:
caller.msg(string) caller.msg(string)
class CmdDebug(MuxCommand): class CmdDebug(MuxCommand):
@ -630,7 +629,7 @@ class CmdDig(ObjManipCommand):
lockstring = lockstring % (caller.dbref, caller.dbref, caller.dbref) lockstring = lockstring % (caller.dbref, caller.dbref, caller.dbref)
new_room = create.create_object(typeclass, room["name"], new_room = create.create_object(typeclass, room["name"],
aliases=room["aliases"]) aliases=room["aliases"], report_to=caller)
new_room.locks.add(lockstring) new_room.locks.add(lockstring)
alias_string = "" alias_string = ""
if new_room.aliases: if new_room.aliases:
@ -659,7 +658,7 @@ class CmdDig(ObjManipCommand):
new_to_exit = create.create_object(typeclass, to_exit["name"], location, new_to_exit = create.create_object(typeclass, to_exit["name"], location,
aliases=to_exit["aliases"], aliases=to_exit["aliases"],
locks=lockstring, destination=new_room) locks=lockstring, destination=new_room, report_to=caller)
alias_string = "" alias_string = ""
if new_to_exit.aliases: if new_to_exit.aliases:
alias_string = " (%s)" % ", ".join(new_to_exit.aliases) alias_string = " (%s)" % ", ".join(new_to_exit.aliases)
@ -684,7 +683,7 @@ class CmdDig(ObjManipCommand):
typeclass = settings.BASE_EXIT_TYPECLASS typeclass = settings.BASE_EXIT_TYPECLASS
new_back_exit = create.create_object(typeclass, back_exit["name"], new_back_exit = create.create_object(typeclass, back_exit["name"],
new_room, aliases=back_exit["aliases"], new_room, aliases=back_exit["aliases"],
locks=lockstring, destination=location) locks=lockstring, destination=location, report_to=caller)
alias_string = "" alias_string = ""
if new_back_exit.aliases: if new_back_exit.aliases:
alias_string = " (%s)" % ", ".join(new_back_exit.aliases) alias_string = " (%s)" % ", ".join(new_back_exit.aliases)
@ -1083,7 +1082,7 @@ class CmdOpen(ObjManipCommand):
typeclass = settings.BASE_EXIT_TYPECLASS typeclass = settings.BASE_EXIT_TYPECLASS
exit_obj = create.create_object(typeclass, key=exit_name, exit_obj = create.create_object(typeclass, key=exit_name,
location=location, location=location,
aliases=exit_aliases) aliases=exit_aliases, report_to=caller)
if exit_obj: if exit_obj:
# storing a destination is what makes it an exit! # storing a destination is what makes it an exit!
exit_obj.destination = destination exit_obj.destination = destination
@ -1283,9 +1282,9 @@ class CmdTypeclass(MuxCommand):
@typeclass - set object typeclass @typeclass - set object typeclass
Usage: Usage:
@typclass[/switch] <object> [= <typeclass path>] @typclass[/switch] <object> [= <typeclass.path>]
@type '' @type ''
@parent '' @parent ''
Switch: Switch:
reset - clean out *all* the attributes on the object - reset - clean out *all* the attributes on the object -
@ -1295,12 +1294,18 @@ class CmdTypeclass(MuxCommand):
Example: Example:
@type button = examples.red_button.RedButton @type button = examples.red_button.RedButton
Sets an object's typeclass. The typeclass must be identified View or set an object's typeclass. If setting, the creation hooks
by its location using python dot-notation pointing to the correct of the new typeclass will be run on the object. If you have
module and class. If no typeclass is given (or a wrong typeclass clashing properties on the old class, use /reset. By default you
is given), the object will be set to the default typeclass. are protected from changing to a typeclass of the same name as the
The location of the typeclass module is searched from one you already have, use /force to override this protection.
the default typeclass directory, as defined in the server settings.
The given typeclass must be identified by its location using
python dot-notation pointing to the correct module and class. If
no typeclass is given (or a wrong typeclass is given). Errors in
the path or new typeclass will lead to the old typeclass being
kept. The location of the typeclass module is searched from the
default typeclass directory, as defined in the server settings.
""" """
@ -1327,7 +1332,7 @@ class CmdTypeclass(MuxCommand):
# we did not supply a new typeclass, view the # we did not supply a new typeclass, view the
# current one instead. # current one instead.
if hasattr(obj, "typeclass"): if hasattr(obj, "typeclass"):
string = "%s's current typeclass is '%s'." % (obj.name, obj.typeclass.typename) string = "%s's current typeclass is '%s' (%s)." % (obj.name, obj.typeclass.typename, obj.typeclass.path)
else: else:
string = "%s is not a typed object." % obj.name string = "%s is not a typed object." % obj.name
caller.msg(string) caller.msg(string)
@ -1344,25 +1349,31 @@ class CmdTypeclass(MuxCommand):
caller.msg("This object cannot have a type at all!") caller.msg("This object cannot have a type at all!")
return return
if obj.is_typeclass(typeclass) and not 'force' in self.switches: is_same = obj.is_typeclass(typeclass)
string = "%s already has the typeclass '%s'." % (obj.name, typeclass) if is_same and not 'force' in self.switches:
string = "%s already has the typeclass '%s'. Use /force to override." % (obj.name, typeclass)
else: else:
reset = "reset" in self.switches reset = "reset" in self.switches
old_typeclass_name = obj.typeclass.typename old_typeclass_path = obj.typeclass.path
ok = obj.swap_typeclass(typeclass, clean_attributes=reset) ok = obj.swap_typeclass(typeclass, clean_attributes=reset)
if ok: if ok:
string = "%s's type is now %s (instead of %s).\n" % (obj.name, if is_same:
obj.typeclass.typename, string = "%s updated its existing typeclass (%s).\n" % (obj.name, obj.typeclass.path)
old_typeclass_name)
if reset:
string += "All attributes where reset."
else: else:
string += "Note that the new class type could have overwritten " string = "%s's changed typeclass from %s to %s.\n" % (obj.name,
string += "same-named attributes on the existing object." old_typeclass_path,
obj.typeclass.path)
string += "Creation hooks were run."
if reset:
string += " All old attributes where deleted before the swap."
else:
string += " Note that the typeclassed object could have ended up with a mixture of old"
string += "\nand new attributes. Use /reset to remove old attributes if you don't want this."
else: else:
string = "Could not swap '%s' (%s) to typeclass '%s'." % (obj.name, string = obj.typeclass_last_errmsg
old_typeclass_name, string += "\nCould not swap '%s' (%s) to typeclass '%s'." % (obj.name,
typeclass) old_typeclass_path,
typeclass)
caller.msg(string) caller.msg(string)

View file

@ -588,7 +588,7 @@ class CmdAccess(MuxCommand):
caller = self.caller caller = self.caller
hierarchy_full = settings.PERMISSION_HIERARCHY hierarchy_full = settings.PERMISSION_HIERARCHY
string = "\n{wPermission Hierarchy{n (climbing):\n %s" % ", ".join(hierarchy_full) string = "\n{wPermission Hierarchy{n (climbing):\n %s" % ", ".join(hierarchy_full)
hierarchy = [p.lower() for p in hierarchy_full] #hierarchy = [p.lower() for p in hierarchy_full]
if self.caller.player.is_superuser: if self.caller.player.is_superuser:
cperms = "<Superuser>" cperms = "<Superuser>"

View file

@ -8,14 +8,12 @@ import traceback
import os, datetime, time import os, datetime, time
import django, twisted import django, twisted
from django.contrib.auth.models import User
from django.conf import settings from django.conf import settings
from src.server.sessionhandler import SESSIONS from src.server.sessionhandler import SESSIONS
from src.scripts.models import ScriptDB from src.scripts.models import ScriptDB
from src.objects.models import ObjectDB from src.objects.models import ObjectDB
from src.players.models import PlayerDB from src.players.models import PlayerDB
from src.server.models import ServerConfig from src.utils import logger, utils, gametime
from src.utils import create, logger, utils, gametime
from src.commands.default.muxcommand import MuxCommand from src.commands.default.muxcommand import MuxCommand
# limit symbol import for API # limit symbol import for API

View file

@ -182,13 +182,14 @@ its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth m
typeclass = settings.BASE_CHARACTER_TYPECLASS typeclass = settings.BASE_CHARACTER_TYPECLASS
permissions = settings.PERMISSION_PLAYER_DEFAULT permissions = settings.PERMISSION_PLAYER_DEFAULT
new_character = create.create_player(playername, email, password, try:
permissions=permissions, new_character = create.create_player(playername, email, password,
character_typeclass=typeclass, permissions=permissions,
character_location=default_home, character_typeclass=typeclass,
character_home=default_home) character_location=default_home,
if not new_character: character_home=default_home)
session.msg("There was an error creating the default Character/Player. This error was logged. Contact an admin.") except Exception, e:
session.msg("There was an error creating the default Character/Player:\n%s\n If this problem persists, contact an admin.")
return return
new_player = new_character.player new_player = new_character.player

View file

@ -323,7 +323,7 @@ class Object(TypeClass):
""" """
self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default) return self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default)
def access(self, accessing_obj, access_type='read', default=False): def access(self, accessing_obj, access_type='read', default=False):
""" """

View file

@ -58,9 +58,6 @@ def create_objects():
create_character=True, create_character=True,
character_typeclass=character_typeclass) character_typeclass=character_typeclass)
if not god_character:
raise Exception(_("#1 could not be created. Check the Player/Character typeclass for bugs."))
god_character.id = 1 god_character.id = 1
god_character.db.desc = _('This is User #1.') god_character.db.desc = _('This is User #1.')
god_character.locks.add("examine:perm(Immortals);edit:false();delete:false();boot:false();msg:all();puppet:false()") god_character.locks.add("examine:perm(Immortals);edit:false();delete:false();boot:false();msg:all();puppet:false()")
@ -164,7 +161,11 @@ def create_admin_media_links():
on system. on system.
""" """
import django, os import django, os
dpath = os.path.join(django.__path__[0], 'contrib', 'admin', 'media')
if django.get_version() < 1.4:
dpath = os.path.join(django.__path__[0], 'contrib', 'admin', 'media')
else:
dpath = os.path.join(django.__path__[0], 'contrib', 'admin', 'static', 'admin')
apath = os.path.join(settings.ADMIN_MEDIA_ROOT) apath = os.path.join(settings.ADMIN_MEDIA_ROOT)
if os.path.isdir(apath): if os.path.isdir(apath):
print _(" ADMIN_MEDIA_ROOT already exists. Ignored.") print _(" ADMIN_MEDIA_ROOT already exists. Ignored.")
@ -177,8 +178,8 @@ def create_admin_media_links():
try: try:
os.symlink(dpath, apath) os.symlink(dpath, apath)
print _(" Admin-media symlinked to ADMIN_MEDIA_ROOT.") print _(" Admin-media symlinked to ADMIN_MEDIA_ROOT.")
except OSError: except OSError, e:
print _(" There was an error symlinking Admin-media to ADMIN_MEDIA_ROOT. If you see issues, link manually.") print _(" There was an error symlinking Admin-media to ADMIN_MEDIA_ROOT:\n %s\n -> \n %s\n (%s)\n If you see issues, link manually." % (dpath, apath, e))
else: else:
print _(" Admin-media files should be copied manually to ADMIN_MEDIA_ROOT.") print _(" Admin-media files should be copied manually to ADMIN_MEDIA_ROOT.")
@ -194,7 +195,7 @@ def at_initial_setup():
return return
try: try:
mod = __import__(modname, fromlist=[None]) mod = __import__(modname, fromlist=[None])
except ImportError, ValueError: except (ImportError, ValueError):
return return
print _(" Running at_initial_setup() hook.") print _(" Running at_initial_setup() hook.")
if mod.__dict__.get("at_initial_setup", None): if mod.__dict__.get("at_initial_setup", None):

View file

@ -12,6 +12,7 @@ etc.
""" """
import re import re
from src.utils.utils import make_iter from src.utils.utils import make_iter
from src.utils import logger
# variables # variables
MSDP = chr(69) MSDP = chr(69)
@ -24,7 +25,7 @@ MSDP_ARRAY_CLOSE = chr(6)
regex_array = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_ARRAY_OPEN, MSDP_ARRAY_CLOSE)) # return 2-tuple regex_array = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_ARRAY_OPEN, MSDP_ARRAY_CLOSE)) # return 2-tuple
regex_table = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_TABLE_OPEN, MSDP_TABLE_CLOSE)) # return 2-tuple (may be nested) regex_table = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_TABLE_OPEN, MSDP_TABLE_CLOSE)) # return 2-tuple (may be nested)
regex_varval = re.compile(r"%s(.*?)%s(.*?)[%s]" % (MSDP_VAR, MSDP_VAL, ENDING)) # return 2-tuple regex_varval = re.compile(r"%s(.*?)%s(.*?)" % (MSDP_VAR, MSDP_VAL)) # return 2-tuple
class Msdp(object): class Msdp(object):
""" """
@ -111,8 +112,8 @@ class Msdp(object):
tables[table[0]] = dict(regex_varval(table[1])) tables[table[0]] = dict(regex_varval(table[1]))
for array in regex_array.findall(data): for array in regex_array.findall(data):
arrays[array[0]] = dict(regex_varval(array[1])) arrays[array[0]] = dict(regex_varval(array[1]))
variables = dict(regex._varval(regex_array.sub("", regex_table.sub("", data)))) variables = dict(regex_varval(regex_array.sub("", regex_table.sub("", data))))
print variables
# MSDP Commands # MSDP Commands
@ -125,7 +126,7 @@ class Msdp(object):
The List command allows for retrieving various info about the server/client The List command allows for retrieving various info about the server/client
""" """
if arg == 'COMMANDS': if arg == 'COMMANDS':
return self.func_to_msdp(arg, MSDP_COMMANDS.keys()) return self.func_to_msdp(arg, self.MSDP_COMMANDS.keys())
elif arg == 'LISTS': elif arg == 'LISTS':
return self.func_to_msdp(arg, ("COMMANDS", "LISTS", return self.func_to_msdp(arg, ("COMMANDS", "LISTS",
"CONFIGURABLE_VARIABLES", "CONFIGURABLE_VARIABLES",
@ -133,11 +134,11 @@ class Msdp(object):
elif arg == 'CONFIGURABLE_VARIABLES': elif arg == 'CONFIGURABLE_VARIABLES':
return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID")) return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
elif arg == 'REPORTABLE_VARIABLES': elif arg == 'REPORTABLE_VARIABLES':
return self.func_to_msdp(arg, MSDP_REPORTABLE.keys()) return self.func_to_msdp(arg, self.MSDP_REPORTABLE.keys())
elif arg == 'REPORTED_VARIABLES': elif arg == 'REPORTED_VARIABLES':
return self.func_to_msdp(arg, MSDP_REPORTED.keys()) return self.func_to_msdp(arg, self.MSDP_REPORTED.keys())
elif arg == 'SENDABLE_VARIABLES': elif arg == 'SENDABLE_VARIABLES':
return self.func_to_msdp(arg, MSDP_SEND.keys()) return self.func_to_msdp(arg, self.MSDP_SEND.keys())
else: else:
return self.func_to_msdp("LIST", arg) return self.func_to_msdp("LIST", arg)
@ -147,7 +148,7 @@ class Msdp(object):
reportable variable to the client. reportable variable to the client.
""" """
try: try:
MSDP_REPORTABLE[arg](report=True) self.MSDP_REPORTABLE[arg](report=True)
except Exception: except Exception:
logger.log_trace() logger.log_trace()
@ -156,16 +157,16 @@ class Msdp(object):
Unreport a previously reported variable Unreport a previously reported variable
""" """
try: try:
MSDP_REPORTABLE[arg](eport=False) self.MSDP_REPORTABLE[arg](eport=False)
except Exception: except Exception:
logger.log_trace() self.logger.log_trace()
def msdp_cmd_reset(self, arg): def msdp_cmd_reset(self, arg):
""" """
The reset command resets a variable to its initial state. The reset command resets a variable to its initial state.
""" """
try: try:
MSDP_REPORTABLE[arg](reset=True) self.MSDP_REPORTABLE[arg](reset=True)
except Exception: except Exception:
logger.log_trace() logger.log_trace()
@ -177,21 +178,22 @@ class Msdp(object):
arg - this is a list of variables the client wants. arg - this is a list of variables the client wants.
""" """
ret = [] ret = []
for var in makeiter(arg): for var in make_iter(arg):
try: try:
ret.append(MSDP_REPORTABLE[arg](send=True)) ret.append(self.MSDP_REPORTABLE[arg](send=True))
except Exception: except Exception:
logger.log_trace() logger.log_trace()
return ret return ret
MSDP_COMMANDS = { MSDP_COMMANDS = {
"LIST": self.msdp_list, "LIST": "msdp_list",
"REPORT":"mspd_report", "REPORT":"mspd_report",
"RESET":"mspd_reset", "RESET":"mspd_reset",
"SEND":"mspd_send", "SEND":"mspd_send",
"UNREPORT":"mspd_unreport" "UNREPORT":"mspd_unreport"
} }
# MSDP_MAP is a standard suggestions for making it easy to create generic guis. # MSDP_MAP is a standard suggestions for making it easy to create generic guis.
# this maps MSDP command names to Evennia commands found in OOB_FUNC_MODULE. It # this maps MSDP command names to Evennia commands found in OOB_FUNC_MODULE. It
# is up to these commands to return data on proper form. # is up to these commands to return data on proper form.

View file

@ -244,6 +244,7 @@ class ServerSession(Session):
else: else:
logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % funcname) logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % funcname)
if outdata: if outdata:
# we have a result, send it back
self.oob_data_out(outdata) self.oob_data_out(outdata)

View file

@ -951,6 +951,7 @@ class TypedObject(SharedMemoryModel):
# If we reach this point we couldn't import any typeclasses. Return default. It's up to the calling # If we reach this point we couldn't import any typeclasses. Return default. It's up to the calling
# method to use e.g. self.is_typeclass() to detect that the result is not the one asked for. # method to use e.g. self.is_typeclass() to detect that the result is not the one asked for.
_GA(self, "_display_errmsg")(errstring) _GA(self, "_display_errmsg")(errstring)
_SA(self, "typeclass_lasterrmsg", errstring)
return _GA(self, "_get_default_typeclass")(cache=False, silent=False, save=False) return _GA(self, "_get_default_typeclass")(cache=False, silent=False, save=False)
#@typeclass.deleter #@typeclass.deleter
@ -961,6 +962,11 @@ class TypedObject(SharedMemoryModel):
# typeclass property # typeclass property
typeclass = property(__typeclass_get, fdel=__typeclass_del) typeclass = property(__typeclass_get, fdel=__typeclass_del)
# the last error string will be stored here for accessing methods to access.
# It is set by _display_errmsg, which will print to log if error happens
# during server startup.
typeclass_last_errmsg = ""
def _path_import(self, path): def _path_import(self, path):
""" """
Import a class from a python path of the Import a class from a python path of the
@ -973,17 +979,19 @@ class TypedObject(SharedMemoryModel):
return None return None
try: try:
modpath, class_name = path.rsplit('.', 1) modpath, class_name = path.rsplit('.', 1)
module = __import__(modpath, fromlist=[class_name]) module = __import__(modpath, fromlist=["none"])
return module.__dict__[class_name] return module.__dict__[class_name]
except ImportError: except ImportError:
trc = sys.exc_traceback trc = sys.exc_traceback
if not trc.tb_next: if not trc.tb_next:
# we separate between not finding the module, and finding a buggy one. # we separate between not finding the module, and finding a buggy one.
errstring += "(Tried path '%s')." % path errstring = "Typeclass not found trying path '%s'." % path
else: else:
# a bug in the module is reported normally. # a bug in the module is reported normally.
trc = traceback.format_exc() trc = traceback.format_exc()
errstring += "\n%sError importing '%s'." % (trc, path) errstring = "\n%sError importing '%s'." % (trc, path)
except (ValueError, TypeError):
errstring = "Malformed typeclass path '%s'." % path
except KeyError: except KeyError:
errstring = "No class '%s' was found in module '%s'." errstring = "No class '%s' was found in module '%s'."
errstring = errstring % (class_name, modpath) errstring = errstring % (class_name, modpath)
@ -997,26 +1005,32 @@ class TypedObject(SharedMemoryModel):
""" """
Helper function to display error. Helper function to display error.
""" """
infochan = None if ServerConfig.objects.conf("server_starting_mode"):
cmessage = message print message.strip()
try: else:
from src.comms.models import Channel _SA(self, "typeclass_last_errmsg", message.strip())
infochan = settings.CHANNEL_MUDINFO return
infochan = Channel.objects.get_channel(infochan[0])
if infochan: #infochan = None
cname = infochan.key #cmessage = message
cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n') if line]) #try:
cmessage = cmessage.strip() # from src.comms.models import Channel
infochan.msg(cmessage) # infochan = settings.CHANNEL_MUDINFO
else: # infochan = Channel.objects.get_channel(infochan[0])
# no mudinfo channel is found. Log instead. # if infochan:
cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')]) # cname = infochan.key
logger.log_errmsg(cmessage) # cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n') if line])
except Exception: # cmessage = cmessage.strip()
if ServerConfig.objects.conf("server_starting_mode"): # infochan.msg(cmessage)
print cmessage # else:
else: # # no mudinfo channel is found. Log instead.
logger.log_trace(cmessage) # cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')])
# logger.log_errmsg(cmessage)
#except Exception:
# if ServerConfig.objects.conf("server_starting_mode"):
# print cmessage
# else:
# logger.log_trace(cmessage)
def _get_default_typeclass(self, cache=False, silent=False, save=False): def _get_default_typeclass(self, cache=False, silent=False, save=False):
""" """
@ -1064,7 +1078,9 @@ class TypedObject(SharedMemoryModel):
""" """
Returns true if this object has this type Returns true if this object has this type
OR has a typeclass which is an subclass of OR has a typeclass which is an subclass of
the given typeclass. the given typeclass. This operates on the actually
loaded typeclass (this is important since a failing
typeclass may instead have its default currently loaded)
typeclass - can be a class object or the typeclass - can be a class object or the
python path to such an object to match against. python path to such an object to match against.
@ -1079,7 +1095,7 @@ class TypedObject(SharedMemoryModel):
pass pass
typeclasses = [typeclass] + ["%s.%s" % (path, typeclass) for path in _GA(self, "_typeclass_paths")] typeclasses = [typeclass] + ["%s.%s" % (path, typeclass) for path in _GA(self, "_typeclass_paths")]
if exact: if exact:
current_path = _GA(self, "_cached_db_typeclass_path") current_path = _GA(self.typeclass, "path") #"_GA(self, "_cached_db_typeclass_path")
return typeclass and any((current_path == typec for typec in typeclasses)) return typeclass and any((current_path == typec for typec in typeclasses))
else: else:
# check parent chain # check parent chain
@ -1158,10 +1174,9 @@ class TypedObject(SharedMemoryModel):
self.nattr(nattr, delete=True) self.nattr(nattr, delete=True)
else: else:
#print "deleting attrs ..." #print "deleting attrs ..."
self.get_all_attributes()
for attr in self.get_all_attributes(): for attr in self.get_all_attributes():
attr.delete() attr.delete()
for nattr in self.ndb.all(): for nattr in self.ndb.all:
del nattr del nattr
# run hooks for this new typeclass # run hooks for this new typeclass
@ -1328,8 +1343,9 @@ class TypedObject(SharedMemoryModel):
_GA(self, 'obj').set_attribute(attrname, value) _GA(self, 'obj').set_attribute(attrname, value)
def __delattr__(self, attrname): def __delattr__(self, attrname):
_GA(self, 'obj').del_attribute(attrname) _GA(self, 'obj').del_attribute(attrname)
def all(self): def get_all(self):
return _GA(self, 'obj').get_all_attributes() return _GA(self, 'obj').get_all_attributes()
all = property(get_all)
self._db_holder = DbHolder(self) self._db_holder = DbHolder(self)
return self._db_holder return self._db_holder
#@db.setter #@db.setter
@ -1386,9 +1402,10 @@ class TypedObject(SharedMemoryModel):
except AttributeError: except AttributeError:
class NdbHolder(object): class NdbHolder(object):
"Holder for storing non-persistent attributes." "Holder for storing non-persistent attributes."
def all(self): def get_all(self):
return [val for val in self.__dict__.keys() return [val for val in self.__dict__.keys()
if not val.startswith['_']] if not val.startswith('_')]
all = property(get_all)
def __getattribute__(self, key): def __getattribute__(self, key):
# return None if no matching attribute was found. # return None if no matching attribute was found.
try: try:

View file

@ -26,8 +26,7 @@ from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import IntegrityError from django.db import IntegrityError
from src.utils.idmapper.models import SharedMemoryModel from src.utils.idmapper.models import SharedMemoryModel
from src.utils import logger, utils, idmapper from src.utils import utils, logger
from src.utils.utils import is_iter, has_parent, inherits_from
# limit symbol import from API # limit symbol import from API
__all__ = ("create_object", "create_script", "create_help_entry", "create_message", "create_channel", "create_player") __all__ = ("create_object", "create_script", "create_help_entry", "create_message", "create_channel", "create_player")
@ -41,7 +40,7 @@ GA = object.__getattribute__
def create_object(typeclass, key=None, location=None, def create_object(typeclass, key=None, location=None,
home=None, player=None, permissions=None, locks=None, home=None, player=None, permissions=None, locks=None,
aliases=None, destination=None): aliases=None, destination=None, report_to=None):
""" """
Create a new in-game object. Any game object is a combination Create a new in-game object. Any game object is a combination
of a database object that stores data persistently to of a database object that stores data persistently to
@ -52,6 +51,11 @@ def create_object(typeclass, key=None, location=None,
See src.objects.managers for methods to manipulate existing objects See src.objects.managers for methods to manipulate existing objects
in the database. src.objects.objects holds the base typeclasses in the database. src.objects.objects holds the base typeclasses
and src.objects.models hold the database model. and src.objects.models hold the database model.
report_to is an optional object for reporting errors to in string form.
If report_to is not set, errors will be raised as en Exception
containing the error message. If set, this method will return
None upon errors.
""" """
# deferred import to avoid loops # deferred import to avoid loops
from src.objects.objects import Object from src.objects.objects import Object
@ -83,11 +87,15 @@ def create_object(typeclass, key=None, location=None,
# this will either load the typeclass or the default one # this will either load the typeclass or the default one
new_object = new_db_object.typeclass new_object = new_db_object.typeclass
if not GA(new_object, "is_typeclass")(typeclass, exact=True):
if not GA(new_db_object, "is_typeclass")(typeclass, exact=True):
# this will fail if we gave a typeclass as input and it still gave us a default # this will fail if we gave a typeclass as input and it still gave us a default
SharedMemoryModel.delete(new_db_object) SharedMemoryModel.delete(new_db_object)
return None if report_to:
GA(report_to, "msg")("Error creating %s (%s):\n%s" % (new_db_object.key, typeclass,
GA(new_db_object, "typeclass_last_errmsg")))
return None
else:
raise Exception(GA(new_db_object, "typeclass_last_errmsg"))
# from now on we can use the typeclass object # from now on we can use the typeclass object
# as if it was the database object. # as if it was the database object.
@ -141,7 +149,7 @@ object = create_object
def create_script(typeclass, key=None, obj=None, locks=None, def create_script(typeclass, key=None, obj=None, locks=None,
interval=None, start_delay=None, repeats=None, interval=None, start_delay=None, repeats=None,
persistent=None, autostart=True): persistent=None, autostart=True, report_to=None):
""" """
Create a new script. All scripts are a combination Create a new script. All scripts are a combination
of a database object that communicates with the of a database object that communicates with the
@ -160,6 +168,11 @@ def create_script(typeclass, key=None, obj=None, locks=None,
See src.scripts.manager for methods to manipulate existing See src.scripts.manager for methods to manipulate existing
scripts in the database. scripts in the database.
report_to is an obtional object to receive error messages.
If report_to is not set, an Exception with the
error will be raised. If set, this method will
return None upon errors.
""" """
# deferred import to avoid loops. # deferred import to avoid loops.
@ -171,7 +184,7 @@ def create_script(typeclass, key=None, obj=None, locks=None,
typeclass = settings.BASE_SCRIPT_TYPECLASS typeclass = settings.BASE_SCRIPT_TYPECLASS
elif isinstance(typeclass, ScriptDB): elif isinstance(typeclass, ScriptDB):
# this is already an scriptdb instance, extract its typeclass # this is already an scriptdb instance, extract its typeclass
typeclass = new_db_object.typeclass.path typeclass = typeclass.typeclass.path
elif isinstance(typeclass, Script) or utils.inherits_from(typeclass, Script): elif isinstance(typeclass, Script) or utils.inherits_from(typeclass, Script):
# this is already an object typeclass, extract its path # this is already an object typeclass, extract its path
typeclass = typeclass.path typeclass = typeclass.path
@ -195,9 +208,13 @@ def create_script(typeclass, key=None, obj=None, locks=None,
if not GA(new_db_script, "is_typeclass")(typeclass, exact=True): if not GA(new_db_script, "is_typeclass")(typeclass, exact=True):
# this will fail if we gave a typeclass as input and it still gave us a default # this will fail if we gave a typeclass as input and it still gave us a default
print "failure:", new_db_script, typeclass
SharedMemoryModel.delete(new_db_script) SharedMemoryModel.delete(new_db_script)
return None if report_to:
GA(report_to, "msg")("Error creating %s (%s): %s" % (new_db_script.key, typeclass,
GA(new_db_script, "typeclass_last_errmsg")))
return None
else:
raise Exception(GA(new_db_script, "typeclass_last_errmsg"))
if obj: if obj:
try: try:
@ -315,14 +332,14 @@ def create_message(senderobj, message, channels=None,
new_message.message = message new_message.message = message
new_message.save() new_message.save()
if channels: if channels:
if not is_iter(channels): if not utils.is_iter(channels):
channels = [channels] channels = [channels]
new_message.channels = [channel for channel in new_message.channels = [channel for channel in
[to_object(channel, objtype='channel') [to_object(channel, objtype='channel')
for channel in channels] if channel] for channel in channels] if channel]
if receivers: if receivers:
#print "Found receiver:", receivers #print "Found receiver:", receivers
if not is_iter(receivers): if not utils.is_iter(receivers):
receivers = [receivers] receivers = [receivers]
#print "to_player: %s" % to_player(receivers[0]) #print "to_player: %s" % to_player(receivers[0])
new_message.receivers = [to_player(receiver) for receiver in new_message.receivers = [to_player(receiver) for receiver in
@ -356,7 +373,7 @@ def create_channel(key, aliases=None, desc=None,
new_channel = Channel() new_channel = Channel()
new_channel.key = key new_channel.key = key
if aliases: if aliases:
if not is_iter(aliases): if not utils.is_iter(aliases):
aliases = [aliases] aliases = [aliases]
new_channel.aliases = ",".join([alias for alias in aliases]) new_channel.aliases = ",".join([alias for alias in aliases])
new_channel.desc = desc new_channel.desc = desc
@ -384,7 +401,7 @@ def create_player(name, email, password,
locks=None, permissions=None, locks=None, permissions=None,
create_character=True, character_typeclass=None, create_character=True, character_typeclass=None,
character_location=None, character_home=None, character_location=None, character_home=None,
player_dbobj=None): player_dbobj=None, report_to=None):
""" """
@ -468,7 +485,12 @@ def create_player(name, email, password,
if not GA(new_db_player, "is_typeclass")(typeclass, exact=True): if not GA(new_db_player, "is_typeclass")(typeclass, exact=True):
# this will fail if we gave a typeclass as input and it still gave us a default # this will fail if we gave a typeclass as input and it still gave us a default
SharedMemoryModel.delete(new_db_player) SharedMemoryModel.delete(new_db_player)
return None if report_to:
GA(report_to, "msg")("Error creating %s (%s):\n%s" % (new_db_player.key, typeclass,
GA(new_db_player, "typeclass_last_errmsg")))
return None
else:
raise Exception(GA(new_db_player, "typeclass_last_errmsg"))
new_player.basetype_setup() # setup the basic locks and cmdset new_player.basetype_setup() # setup the basic locks and cmdset
# call hook method (may override default permissions) # call hook method (may override default permissions)
@ -492,10 +514,10 @@ def create_player(name, email, password,
new_character = create_object(character_typeclass, key=name, new_character = create_object(character_typeclass, key=name,
location=None, home=character_location, location=None, home=character_location,
permissions=permissions, permissions=permissions,
player=new_player) player=new_player, report_to=report_to)
return new_character return new_character
return new_player return new_player
except Exception,e: except Exception:
# a failure in creating the character # a failure in creating the character
if not user: if not user:
# in there was a failure we clean up everything we can # in there was a failure we clean up everything we can