mirror of
https://github.com/evennia/evennia.git
synced 2026-03-18 05:46:31 +01:00
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:
parent
f306c5a6a2
commit
7b2a4e4467
5 changed files with 186 additions and 192 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue