From 7b2a4e4467bcd6ec367fe966d283f6eb197a7cdc Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 5 Feb 2012 21:04:10 +0100 Subject: [PATCH] Cleaned up typeclasses, removing a lot of extranenous and ineffective code from the setattr/getattr methods that are called most often. --- src/objects/models.py | 6 +- src/players/models.py | 5 +- src/scripts/models.py | 6 +- src/typeclasses/models.py | 220 +++++++++++++++++------------------ src/typeclasses/typeclass.py | 141 +++++++++++----------- 5 files changed, 186 insertions(+), 192 deletions(-) diff --git a/src/objects/models.py b/src/objects/models.py index d13555a292..398297ec3c 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -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: diff --git a/src/players/models.py b/src/players/models.py index cd8dcbc120..5201282e47 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -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 diff --git a/src/scripts/models.py b/src/scripts/models.py index 06e1b97d31..65d93e97ba 100644 --- a/src/scripts/models.py +++ b/src/scripts/models.py @@ -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: diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index 840d2dd86a..de9c63d07c 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -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): """ diff --git a/src/typeclasses/typeclass.py b/src/typeclasses/typeclass.py index 2c06b13b1f..135b903e0a 100644 --- a/src/typeclasses/typeclass.py +++ b/src/typeclasses/typeclass.py @@ -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"