Cleaned up typeclasses, removing a lot of extranenous and ineffective code from the setattr/getattr methods that are called most often.

This commit is contained in:
Griatch 2012-02-05 21:04:10 +01:00
parent f306c5a6a2
commit 7b2a4e4467
5 changed files with 186 additions and 192 deletions

View file

@ -194,6 +194,7 @@ class ObjectDB(TypedObject):
self.cmdset.update(init_mode=True)
self.scripts = ScriptHandler(self)
self.nicks = ObjectNickHandler(self)
# store the attribute class
# Wrapper properties to easily set database fields. These are
# @property decorators that allows to access these fields using
@ -420,9 +421,10 @@ class ObjectDB(TypedObject):
#
# this is required to properly handle attributes and typeclass loading.
attribute_model_path = "src.objects.models"
attribute_model_name = "ObjAttribute"
#attribute_model_path = "src.objects.models"
#attribute_model_name = "ObjAttribute"
typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
attribute_class = ObjAttribute
# this is used by all typedobjects as a fallback
try:

View file

@ -264,9 +264,10 @@ class PlayerDB(TypedObject):
default_typeclass_path = "src.players.player.Player"
# this is required to properly handle attributes and typeclass loading
attribute_model_path = "src.players.models"
attribute_model_name = "PlayerAttribute"
#attribute_model_path = "src.players.models"
#attribute_model_name = "PlayerAttribute"
typeclass_paths = settings.PLAYER_TYPECLASS_PATHS
attribute_class = PlayerAttribute
# name property (wraps self.user.username)
#@property

View file

@ -27,6 +27,7 @@ Common examples of uses of Scripts:
from django.conf import settings
from django.db import models
from src.typeclasses.models import Attribute, TypedObject
from django.contrib.contenttypes.models import ContentType
from src.scripts.manager import ScriptManager
#------------------------------------------------------------
@ -244,9 +245,10 @@ class ScriptDB(TypedObject):
#
# this is required to properly handle attributes and typeclass loading
attribute_model_path = "src.scripts.models"
attribute_model_name = "ScriptAttribute"
#attribute_model_path = "src.scripts.models"
#attribute_model_name = "ScriptAttribute"
typeclass_paths = settings.SCRIPT_TYPECLASS_PATHS
attribute_class = ScriptAttribute
# this is used by all typedobjects as a fallback
try:

View file

@ -41,6 +41,11 @@ from src.utils.utils import is_iter, has_parent
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
CTYPEGET = ContentType.objects.get
GA = object.__getattribute__
SA = object.__setattr__
DA = object.__delattr__
# used by Attribute to efficiently identify stored object types.
# Note that these have to be updated if directory structure changes.
PARENTS = {
@ -359,7 +364,7 @@ class Attribute(SharedMemoryModel):
# unpack a previously packed db_object
try:
#print "unpack:", item.id, item.db_model
mclass = ContentType.objects.get(model=item.db_model).model_class()
mclass = CTYPEGET(model=item.db_model).model_class()
try:
ret = mclass.objects.dbref_search(item.id)
except AttributeError:
@ -618,7 +623,7 @@ class TypedObject(SharedMemoryModel):
#@property
def typeclass_path_get(self):
"Getter. Allows for value = self.typeclass_path"
typeclass_path = object.__getattribute__(self, 'cached_typeclass_path')
typeclass_path = GA(self, 'cached_typeclass_path')
if typeclass_path:
return typeclass_path
return self.db_typeclass_path
@ -627,7 +632,7 @@ class TypedObject(SharedMemoryModel):
"Setter. Allows for self.typeclass_path = value"
self.db_typeclass_path = value
self.save()
object.__setattr__(self, 'cached_typeclass_path', value)
SA(self, 'cached_typeclass_path', value)
#@typeclass_path.deleter
def typeclass_path_del(self):
"Deleter. Allows for del self.typeclass_path"
@ -698,9 +703,10 @@ class TypedObject(SharedMemoryModel):
# Each subclass should set this property to their respective
# attribute model (ObjAttribute, PlayerAttribute etc).
attribute_model_path = "src.typeclasses.models"
attribute_model_name = "Attribute"
#attribute_model_path = "src.typeclasses.models"
#attribute_model_name = "Attribute"
typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
attribute_class = Attribute # replaced by relevant attribute class for child
def __eq__(self, other):
return other and hasattr(other, 'id') and self.id == other.id
@ -720,15 +726,15 @@ class TypedObject(SharedMemoryModel):
have to be very careful to avoid loops.
"""
try:
return object.__getattribute__(self, propname)
return GA(self, propname)
except AttributeError:
# check if the attribute exists on the typeclass instead
# (we make sure to not incur a loop by not triggering the
# typeclass' __getattribute__, since that one would
# try to look back to this very database object.)
typeclass = object.__getattribute__(self, 'typeclass')
typeclass = GA(self, 'typeclass')
if typeclass:
return object.__getattribute__(typeclass, propname)
return GA(typeclass, propname)
else:
raise AttributeError
@ -752,17 +758,17 @@ class TypedObject(SharedMemoryModel):
types of objects that the game needs. This property
handles loading and initialization of the typeclass on the fly.
Note: The liberal use of object.__getattribute__ and __setattr__ (instead
Note: The liberal use of GA and __setattr__ (instead
of normal dot notation) is due to optimization: it avoids calling
the custom self.__getattribute__ more than necessary.
"""
path = object.__getattribute__(self, "cached_typeclass_path")
path = GA(self, "cached_typeclass_path")
if not path:
path = object.__getattribute__(self, 'db_typeclass_path')
typeclass = object.__getattribute__(self, "cached_typeclass")
path = GA(self, 'db_typeclass_path')
typeclass = GA(self, "cached_typeclass")
try:
if typeclass and object.__getattribute__(typeclass, "path") == path:
if typeclass and GA(typeclass, "path") == path:
# don't call at_init() when returning from cache
return typeclass
except AttributeError:
@ -771,25 +777,25 @@ class TypedObject(SharedMemoryModel):
errstring = ""
if not path:
# this means we should get the default obj without giving errors.
return object.__getattribute__(self, "get_default_typeclass")(cache=True, silent=True, save=True)
return GA(self, "get_default_typeclass")(cache=True, silent=True, save=True)
else:
# handle loading/importing of typeclasses, searching all paths.
# (self.typeclass_paths is a shortcut to settings.TYPECLASS_*_PATHS
# where '*' is either OBJECT, SCRIPT or PLAYER depending on the typed
# entities).
typeclass_paths = [path] + ["%s.%s" % (prefix, path) for prefix in object.__getattribute__(self, 'typeclass_paths')]
typeclass_paths = [path] + ["%s.%s" % (prefix, path) for prefix in GA(self, 'typeclass_paths')]
for tpath in typeclass_paths:
# try to import and analyze the result
typeclass = object.__getattribute__(self, "_path_import")(tpath)
typeclass = GA(self, "_path_import")(tpath)
if callable(typeclass):
# we succeeded to import. Cache and return.
object.__setattr__(self, 'db_typeclass_path', tpath)
object.__getattribute__(self, 'save')()
object.__setattr__(self, "cached_typeclass_path", tpath)
SA(self, 'db_typeclass_path', tpath)
GA(self, 'save')()
SA(self, "cached_typeclass_path", tpath)
typeclass = typeclass(self)
object.__setattr__(self, "cached_typeclass", typeclass)
SA(self, "cached_typeclass", typeclass)
try:
typeclass.at_init()
except Exception:
@ -802,8 +808,8 @@ class TypedObject(SharedMemoryModel):
errstring += "\n%s" % typeclass # this will hold a growing error message.
# 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.
object.__getattribute__(self, "_display_errmsg")(errstring)
return object.__getattribute__(self, "get_default_typeclass")(cache=False, silent=False, save=False)
GA(self, "_display_errmsg")(errstring)
return GA(self, "get_default_typeclass")(cache=False, silent=False, save=False)
#@typeclass.deleter
def typeclass_del(self):
@ -878,34 +884,34 @@ class TypedObject(SharedMemoryModel):
Default operation is to load a default typeclass.
"""
defpath = object.__getattribute__(self, "default_typeclass_path")
typeclass = object.__getattribute__(self, "_path_import")(defpath)
defpath = GA(self, "default_typeclass_path")
typeclass = GA(self, "_path_import")(defpath)
# if not silent:
# #errstring = "\n\nUsing Default class '%s'." % defpath
# object.__getattribute__(self, "_display_errmsg")(errstring)
# GA(self, "_display_errmsg")(errstring)
if not callable(typeclass):
# if typeclass still doesn't exist at this point, we're in trouble.
# fall back to hardcoded core class which is wrong for e.g. scripts/players etc.
failpath = defpath
defpath = "src.objects.objects.Object"
typeclass = object.__getattribute__(self, "_path_import")(defpath)
typeclass = GA(self, "_path_import")(defpath)
if not silent:
#errstring = " %s\n%s" % (typeclass, errstring)
errstring = " Default class '%s' failed to load." % failpath
errstring += "\n Using Evennia's default class '%s'." % defpath
object.__getattribute__(self, "_display_errmsg")(errstring)
GA(self, "_display_errmsg")(errstring)
if not callable(typeclass):
# if this is still giving an error, Evennia is wrongly configured or buggy
raise Exception("CRITICAL ERROR: The final fallback typeclass %s cannot load!!" % defpath)
typeclass = typeclass(self)
if save:
object.__setattr__(self, 'db_typeclass_path', defpath)
object.__getattribute__(self, 'save')()
SA(self, 'db_typeclass_path', defpath)
GA(self, 'save')()
if cache:
object.__setattr__(self, "cached_typeclass_path", defpath)
SA(self, "cached_typeclass_path", defpath)
object.__setattr__(self, "cached_typeclass", typeclass)
SA(self, "cached_typeclass", typeclass)
try:
typeclass.at_init()
except Exception:
@ -931,7 +937,7 @@ class TypedObject(SharedMemoryModel):
pass
typeclasses = [typeclass] + ["%s.%s" % (path, typeclass) for path in self.typeclass_paths]
if exact:
current_path = object.__getattribute__(self, "cached_typeclass_path")
current_path = GA(self, "cached_typeclass_path")
return typeclass and any([current_path == typec for typec in typeclasses])
else:
# check parent chain
@ -1033,12 +1039,9 @@ class TypedObject(SharedMemoryModel):
attribute_name: (str) The attribute's name.
"""
exec("from %s import %s" % (self.attribute_model_path,
self.attribute_model_name))
model = eval("%s" % self.attribute_model_name)
attr = model.objects.attr_namesearch(attribute_name, self)
return attr.count() > 0
return self.attribute_class.objects.filter(db_obj=self).filter(
db_key__iexact=attribute_name).count()
def set_attribute(self, attribute_name, new_value=None):
"""
Sets an attribute on an object. Creates the attribute if need
@ -1049,53 +1052,47 @@ class TypedObject(SharedMemoryModel):
a str, the object will be stored as a pickle.
"""
attrib_obj = None
if self.has_attribute(attribute_name):
exec("from %s import %s" % (self.attribute_model_path,
self.attribute_model_name))
model = eval("%s" % self.attribute_model_name)
#print "attr: model:", self.attribute_model_name
attrib_obj = \
model.objects.filter(
db_obj=self).filter(
db_key__iexact=attribute_name)[0]
if attrib_obj:
# Save over the existing attribute's value.
#print "attr:overwrite: %s.%s = %s" % (attrib_obj.db_obj.key, attribute_name, new_value)
attrib_obj.value = new_value
else:
# Create a new attribute
exec("from %s import %s" % (self.attribute_model_path,
self.attribute_model_name))
new_attrib = eval("%s()" % self.attribute_model_name)
new_attrib.db_key = attribute_name
new_attrib.db_obj = self
attrclass = self.attribute_class
try:
attrib_obj = attrclass.objects.filter(
db_obj=self).filter(db_key__iexact=attribute_name)[0]
except IndexError:
# no match; create new attribute
new_attrib = attrclass(db_key=attribute_name, db_obj=self)
new_attrib.value = new_value
#print "attr:new: %s.%s = %s" % (new_attrib.db_obj.key, attribute_name, new_value)
return
# re-set an old attribute value
attrib_obj.value = new_value
def get_attribute(self, attribute_name, default=None):
"""
Returns the value of an attribute on an object. You may need to
type cast the returned value from this function since the attribute
can be of any type.
can be of any type. Returns default if no match is found.
attribute_name: (str) The attribute's name.
default: What to return if no attribute is found
"""
if self.has_attribute(attribute_name):
try:
exec("from %s import %s" % (self.attribute_model_path,
self.attribute_model_name))
model = eval("%s" % self.attribute_model_name)
attrib = model.objects.filter(
db_obj=self).filter(
db_key=attribute_name)[0]
except Exception:
# safety, if something goes wrong (like unsynced db), catch it.
logger.log_trace()
return default
return attrib.value
else:
return default
attrib_obj = default
try:
attrib_obj = self.attribute_class.objects.filter(
db_obj=self).filter(db_key__iexact=attribute_name)[0]
except IndexError:
return default
return attrib_obj.value
def get_attribute_raise(self, attribute_name):
"""
Returns value of an attribute. Raises AttributeError
if no match is found.
attribute_name: (str) The attribute's name.
"""
try:
return self.attribute_class.objects.filter(
db_obj=self).filter(db_key__iexact=attribute_name)[0].value
except IndexError:
raise AttributeError
def del_attribute(self, attribute_name):
"""
@ -1103,23 +1100,30 @@ class TypedObject(SharedMemoryModel):
attribute_name: (str) The attribute's name.
"""
exec("from %s import %s" % (self.attribute_model_path,
self.attribute_model_name))
model = eval("%s" % self.attribute_model_name)
#print "delete attr", model, attribute_name
try:
self.attribute_class.objects.filter(
db_obj=self).filter(db_key__iexact=attribute_name)[0].delete()
except IndexError:
pass
attrs = \
model.objects.attr_namesearch(attribute_name, self)
#print "found attrs:", attrs
if attrs:
attrs[0].delete()
def del_attribute_raise(self, attribute_name):
"""
Removes and attribute. Raises AttributeError if
attribute is not found.
attribute_name: (str) The attribute's name.
"""
try:
self.attribute_class.objects.filter(
db_obj=self).filter(db_key__iexact=attribute_name)[0].delete()
except IndexError:
raise AttributeError
def get_all_attributes(self):
"""
Returns all attributes defined on the object.
"""
attr_set_all = eval("self.%s_set.all()" % (self.attribute_model_name.lower()))
return [attr for attr in attr_set_all]
"""
return list(self.attribute_class.objects.filter(db_obj=self))
def attr(self, attribute_name=None, value=None, delete=False):
"""
@ -1131,8 +1135,7 @@ class TypedObject(SharedMemoryModel):
set delete=True to delete the named attribute.
Note that you cannot set the attribute
value to None using this method should you
want that, use set_attribute for that.
value to None using this method. Use set_attribute.
"""
if attribute_name == None:
# act as a list method
@ -1166,34 +1169,29 @@ class TypedObject(SharedMemoryModel):
class DbHolder(object):
"Holder for allowing property access of attributes"
def __init__(self, obj):
object.__setattr__(self, 'obj', obj)
SA(self, 'obj', obj)
def __getattribute__(self, attrname):
obj = object.__getattribute__(self, 'obj')
if attrname == 'all':
# we allow for overwriting the all() method
# with an attribute named 'all'.
attr = obj.get_attribute("all")
attr = GA(self, 'obj').get_attribute("all")
if attr:
return attr
return object.__getattribute__(self, 'all')
return obj.get_attribute(attrname)
return GA(self, 'all')
return GA(self, 'obj').get_attribute(attrname)
def __setattr__(self, attrname, value):
obj = object.__getattribute__(self, 'obj')
obj.set_attribute(attrname, value)
GA(self, 'obj').set_attribute(attrname, value)
def __delattr__(self, attrname):
obj = object.__getattribute__(self, 'obj')
obj.del_attribute(attrname)
GA(self, 'obj').del_attribute(attrname)
def all(self):
obj = object.__getattribute__(self, 'obj')
return obj.get_all_attributes()
return GA(self, 'obj').get_all_attributes()
self._db_holder = DbHolder(self)
return self._db_holder
#@db.setter
def db_set(self, value):
"Stop accidentally replacing the db object"
string = "Cannot assign directly to db object! "
string = "Use db.attr=value instead."
string += "Use db.attr=value instead."
raise Exception(string)
#@db.deleter
def db_del(self):
@ -1202,9 +1200,7 @@ class TypedObject(SharedMemoryModel):
db = property(db_get, db_set, db_del)
#
# NON-PERSISTENT store. If you want to loose data on server reboot
# you should use this explicitly. Otherwise there is
# little point in using the non-persistent methods.
# NON-PERSISTENT storage methods
#
def nattr(self, attribute_name=None, value=None, delete=False):
@ -1221,16 +1217,16 @@ class TypedObject(SharedMemoryModel):
if not val.startswith['_']]
elif delete == True:
if hasattr(self.ndb, attribute_name):
object.__delattr__(self.db, attribute_name)
DA(self.db, attribute_name)
elif value == None:
# act as a getter.
if hasattr(self.ndb, attribute_name):
object.__getattribute__(self.ndb, attribute_name)
GA(self.ndb, attribute_name)
else:
return None
else:
# act as a setter
object.__setattr__(self.db, attribute_name, value)
SA(self.db, attribute_name, value)
#@property
def ndb_get(self):
@ -1247,11 +1243,11 @@ class TypedObject(SharedMemoryModel):
"Holder for storing non-persistent attributes."
def all(self):
return [val for val in self.__dict__.keys()
if not val.startswith['_']]
if not val.startswith['_']]
def __getattribute__(self, key):
# return None if no matching attribute was found.
try:
return object.__getattribute__(self, key)
return GA(self, key)
except AttributeError:
return None
self._ndb_holder = NdbHolder()
@ -1268,7 +1264,9 @@ class TypedObject(SharedMemoryModel):
raise Exception("Cannot delete the ndb object!")
ndb = property(ndb_get, ndb_set, ndb_del)
#
# Lock / permission methods
#
def access(self, accessing_obj, access_type='read', default=False):
"""

View file

@ -13,14 +13,19 @@ used by the typesystem or django itself.
from src.utils import logger
from django.conf import settings
# these are called so many times it's worth to avoid lookup calls
GA = object.__getattribute__
SA = object.__setattr__
DA = object.__delattr__
# To ensure the sanity of the model, there are a
# few property names we won't allow the admin to
# set on the typeclass just like that. Note that these are *not* related
# to *in-game* safety (if you can edit typeclasses you have
# full access anyway), so no protection against changing
# e.g. 'locks' or 'permissions' should go here.
PROTECTED = ['id', 'dbobj', 'db', 'ndb', 'objects', 'typeclass',
'attr', 'save', 'delete']
PROTECTED = ('id', 'dbobj', 'db', 'ndb', 'objects', 'typeclass',
'attr', 'save', 'delete')
# If this is true, all non-protected property assignments
# are directly stored to a database attribute
@ -69,24 +74,23 @@ class TypeClass(object):
"""
# typecheck of dbobj - we can't allow it to be added here
# unless it's really a TypedObject.
dbobj_cls = object.__getattribute__(dbobj, '__class__')
dbobj_mro = object.__getattribute__(dbobj_cls, '__mro__')
dbobj_cls = GA(dbobj, '__class__')
dbobj_mro = GA(dbobj_cls, '__mro__')
if not any('src.typeclasses.models.TypedObject'
in str(mro) for mro in dbobj_mro):
raise Exception("dbobj is not a TypedObject: %s: %s" % \
(dbobj_cls, dbobj_mro))
object.__setattr__(self, 'dbobj', dbobj)
# store the needed things on the typeclass
object.__setattr__(self, '_protected_attrs', PROTECTED)
SA(self, 'dbobj', dbobj)
# # sync the database object to this typeclass.
# cls = object.__getattribute__(self, '__class__')
# db_typeclass_path = "%s.%s" % (object.__getattribute__(cls, '__module__'),
# object.__getattribute__(cls, '__name__'))
# if not object.__getattribute__(dbobj, "db_typeclass_path") == db_typeclass_path:
# object.__setattr__(dbobj, "db_typeclass_path", db_typeclass_path)
# object.__getattribute__(dbobj, "save")()
# cls = GA(self, '__class__')
# db_typeclass_path = "%s.%s" % (GA(cls, '__module__'),
# GA(cls, '__name__'))
# if not GA(dbobj, "db_typeclass_path") == db_typeclass_path:
# SA(dbobj, "db_typeclass_path", db_typeclass_path)
# GA(dbobj, "save")()
def __getattribute__(self, propname):
"""
@ -98,7 +102,7 @@ class TypeClass(object):
accessible through getattr.
"""
try:
dbobj = object.__getattribute__(self, 'dbobj')
dbobj = GA(self, 'dbobj')
except AttributeError:
dbobj = None
logger.log_trace("This is probably due to an unsafe reload.")
@ -108,25 +112,20 @@ class TypeClass(object):
if propname.startswith('__') and propname.endswith('__'):
# python specials are parsed as-is (otherwise things like
# isinstance() fail to identify the typeclass)
return object.__getattribute__(self, propname)
return GA(self, propname)
#print "get %s (dbobj:%s)" % (propname, type(dbobj))
try:
return object.__getattribute__(self, propname)
return GA(self, propname)
except AttributeError:
try:
return object.__getattribute__(dbobj, propname)
return GA(dbobj, propname)
except AttributeError:
try:
if propname != 'ndb':
if not dbobj.has_attribute(propname):
raise AttributeError
else:
value = dbobj.get_attribute(propname)
else:
if propname == 'ndb':
# get non-persistent data
ndb = object.__getattribute__(dbobj, 'ndb')
value = getattr(ndb, propname)
return value
return getattr(GA(dbobj, 'ndb'), propname)
else:
return dbobj.get_attribute_raise(propname)
except AttributeError:
string = "Object: '%s' not found on %s(%s), nor on its typeclass %s."
raise AttributeError(string % (propname, dbobj,
@ -142,39 +141,38 @@ class TypeClass(object):
corresponding to a field on ObjectDB model.
"""
#print "set %s -> %s" % (propname, value)
try:
protected = object.__getattribute__(self, '_protected_attrs')
except AttributeError:
protected = PROTECTED
logger.log_trace("This is probably due to an unsafe reload.")
if propname in protected:
if propname in PROTECTED:
string = "%s: '%s' is a protected attribute name."
string += " (protected: [%s])" % (", ".join(protected))
string += " (protected: [%s])" % (", ".join(PROTECTED))
logger.log_errmsg(string % (self.name, propname))
else:
return
try:
dbobj = GA(self, 'dbobj')
except AttributeError:
dbobj = None
logger.log_trace("This is probably due to an unsafe reload.")
if dbobj:
try:
dbobj = object.__getattribute__(self, 'dbobj')
# only set value on propname if propname already exists
# on dbobj. __getattribute__ will raise attribute error otherwise.
GA(dbobj, propname)
SA(dbobj, propname, value)
except AttributeError:
dbobj = None
logger.log_trace("This is probably due to an unsafe reload.")
if dbobj: # and hasattr(dbobj, propname):
#print " ---> dbobj"
if hasattr(dbobj, propname):
# only if attr already exists on dbobj, assign to it.
object.__setattr__(dbobj, propname, value)
else:
dbobj.set_attribute(propname, value)
else:
object.__setattr__(self, propname, value)
dbobj.set_attribute(propname, value)
else:
SA(self, propname, value)
def __eq__(self, other):
"""
dbobj-recognized comparison
"""
if hasattr(other, 'user'):
return other == self or other == self.dbobj or other == self.dbobj.user
else:
return other == self or other == self.dbobj
try:
return other == self or other == GA(self, dbobj) or other == GA(self, dbobj).user
except AttributeError:
# if self.dbobj.user fails it means the two previous comparisons failed already
return False
def __delattr__(self, propname):
@ -183,35 +181,28 @@ class TypeClass(object):
secondly on the dbobj.db.
Will not allow deletion of properties stored directly on dbobj.
"""
try:
protected = object.__getattribute__(self, '_protected_attrs')
except AttributeError:
protected = PROTECTED
logger.log_trace("Thiis is probably due to an unsafe reload.")
if propname in protected:
if propname in PROTECTED:
string = "%s: '%s' is a protected attribute name."
string += " (protected: [%s])" % (", ".join(protected))
string += " (protected: [%s])" % (", ".join(PROTECTED))
logger.log_errmsg(string % (self.name, propname))
else:
try:
object.__delattr__(self, propname)
except AttributeError:
# not on typeclass, try to delete on db/ndb
try:
dbobj = object.__getattribute__(self, 'dbobj')
except AttributeError:
logger.log_trace("This is probably due to an unsafe reload.")
return # ignore delete
try:
if not dbobj.has_attribute(propname):
raise AttributeError
dbobj.del_attribute(propname)
except AttributeError:
string = "Object: '%s' not found on %s(%s), nor on its typeclass %s."
raise AttributeError(string % (propname, dbobj,
dbobj.dbref,
dbobj.typeclass_path,))
return
try:
DA(self, propname)
except AttributeError:
# not on typeclass, try to delete on db/ndb
try:
dbobj = GA(self, 'dbobj')
except AttributeError:
logger.log_trace("This is probably due to an unsafe reload.")
return # ignore delete
try:
dbobj.del_attribute_raise(propname)
except AttributeError:
string = "Object: '%s' not found on %s(%s), nor on its typeclass %s."
raise AttributeError(string % (propname, dbobj,
dbobj.dbref,
dbobj.typeclass_path,))
def __str__(self):
"represent the object"