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.cmdset.update(init_mode=True)
|
||||||
self.scripts = ScriptHandler(self)
|
self.scripts = ScriptHandler(self)
|
||||||
self.nicks = ObjectNickHandler(self)
|
self.nicks = ObjectNickHandler(self)
|
||||||
|
# store the attribute class
|
||||||
|
|
||||||
# Wrapper properties to easily set database fields. These are
|
# Wrapper properties to easily set database fields. These are
|
||||||
# @property decorators that allows to access these fields using
|
# @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.
|
# this is required to properly handle attributes and typeclass loading.
|
||||||
attribute_model_path = "src.objects.models"
|
#attribute_model_path = "src.objects.models"
|
||||||
attribute_model_name = "ObjAttribute"
|
#attribute_model_name = "ObjAttribute"
|
||||||
typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
||||||
|
attribute_class = ObjAttribute
|
||||||
|
|
||||||
# this is used by all typedobjects as a fallback
|
# this is used by all typedobjects as a fallback
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -264,9 +264,10 @@ class PlayerDB(TypedObject):
|
||||||
default_typeclass_path = "src.players.player.Player"
|
default_typeclass_path = "src.players.player.Player"
|
||||||
|
|
||||||
# this is required to properly handle attributes and typeclass loading
|
# this is required to properly handle attributes and typeclass loading
|
||||||
attribute_model_path = "src.players.models"
|
#attribute_model_path = "src.players.models"
|
||||||
attribute_model_name = "PlayerAttribute"
|
#attribute_model_name = "PlayerAttribute"
|
||||||
typeclass_paths = settings.PLAYER_TYPECLASS_PATHS
|
typeclass_paths = settings.PLAYER_TYPECLASS_PATHS
|
||||||
|
attribute_class = PlayerAttribute
|
||||||
|
|
||||||
# name property (wraps self.user.username)
|
# name property (wraps self.user.username)
|
||||||
#@property
|
#@property
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ Common examples of uses of Scripts:
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from src.typeclasses.models import Attribute, TypedObject
|
from src.typeclasses.models import Attribute, TypedObject
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from src.scripts.manager import ScriptManager
|
from src.scripts.manager import ScriptManager
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -244,9 +245,10 @@ class ScriptDB(TypedObject):
|
||||||
#
|
#
|
||||||
|
|
||||||
# this is required to properly handle attributes and typeclass loading
|
# this is required to properly handle attributes and typeclass loading
|
||||||
attribute_model_path = "src.scripts.models"
|
#attribute_model_path = "src.scripts.models"
|
||||||
attribute_model_name = "ScriptAttribute"
|
#attribute_model_name = "ScriptAttribute"
|
||||||
typeclass_paths = settings.SCRIPT_TYPECLASS_PATHS
|
typeclass_paths = settings.SCRIPT_TYPECLASS_PATHS
|
||||||
|
attribute_class = ScriptAttribute
|
||||||
|
|
||||||
# this is used by all typedobjects as a fallback
|
# this is used by all typedobjects as a fallback
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,11 @@ from src.utils.utils import is_iter, has_parent
|
||||||
|
|
||||||
PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
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.
|
# used by Attribute to efficiently identify stored object types.
|
||||||
# Note that these have to be updated if directory structure changes.
|
# Note that these have to be updated if directory structure changes.
|
||||||
PARENTS = {
|
PARENTS = {
|
||||||
|
|
@ -359,7 +364,7 @@ class Attribute(SharedMemoryModel):
|
||||||
# unpack a previously packed db_object
|
# unpack a previously packed db_object
|
||||||
try:
|
try:
|
||||||
#print "unpack:", item.id, item.db_model
|
#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:
|
try:
|
||||||
ret = mclass.objects.dbref_search(item.id)
|
ret = mclass.objects.dbref_search(item.id)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
@ -618,7 +623,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
#@property
|
#@property
|
||||||
def typeclass_path_get(self):
|
def typeclass_path_get(self):
|
||||||
"Getter. Allows for value = self.typeclass_path"
|
"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:
|
if typeclass_path:
|
||||||
return typeclass_path
|
return typeclass_path
|
||||||
return self.db_typeclass_path
|
return self.db_typeclass_path
|
||||||
|
|
@ -627,7 +632,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
"Setter. Allows for self.typeclass_path = value"
|
"Setter. Allows for self.typeclass_path = value"
|
||||||
self.db_typeclass_path = value
|
self.db_typeclass_path = value
|
||||||
self.save()
|
self.save()
|
||||||
object.__setattr__(self, 'cached_typeclass_path', value)
|
SA(self, 'cached_typeclass_path', value)
|
||||||
#@typeclass_path.deleter
|
#@typeclass_path.deleter
|
||||||
def typeclass_path_del(self):
|
def typeclass_path_del(self):
|
||||||
"Deleter. Allows for del self.typeclass_path"
|
"Deleter. Allows for del self.typeclass_path"
|
||||||
|
|
@ -698,9 +703,10 @@ class TypedObject(SharedMemoryModel):
|
||||||
|
|
||||||
# Each subclass should set this property to their respective
|
# Each subclass should set this property to their respective
|
||||||
# attribute model (ObjAttribute, PlayerAttribute etc).
|
# attribute model (ObjAttribute, PlayerAttribute etc).
|
||||||
attribute_model_path = "src.typeclasses.models"
|
#attribute_model_path = "src.typeclasses.models"
|
||||||
attribute_model_name = "Attribute"
|
#attribute_model_name = "Attribute"
|
||||||
typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
||||||
|
attribute_class = Attribute # replaced by relevant attribute class for child
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return other and hasattr(other, 'id') and self.id == other.id
|
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.
|
have to be very careful to avoid loops.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return object.__getattribute__(self, propname)
|
return GA(self, propname)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# check if the attribute exists on the typeclass instead
|
# check if the attribute exists on the typeclass instead
|
||||||
# (we make sure to not incur a loop by not triggering the
|
# (we make sure to not incur a loop by not triggering the
|
||||||
# typeclass' __getattribute__, since that one would
|
# typeclass' __getattribute__, since that one would
|
||||||
# try to look back to this very database object.)
|
# try to look back to this very database object.)
|
||||||
typeclass = object.__getattribute__(self, 'typeclass')
|
typeclass = GA(self, 'typeclass')
|
||||||
if typeclass:
|
if typeclass:
|
||||||
return object.__getattribute__(typeclass, propname)
|
return GA(typeclass, propname)
|
||||||
else:
|
else:
|
||||||
raise AttributeError
|
raise AttributeError
|
||||||
|
|
||||||
|
|
@ -752,17 +758,17 @@ class TypedObject(SharedMemoryModel):
|
||||||
types of objects that the game needs. This property
|
types of objects that the game needs. This property
|
||||||
handles loading and initialization of the typeclass on the fly.
|
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
|
of normal dot notation) is due to optimization: it avoids calling
|
||||||
the custom self.__getattribute__ more than necessary.
|
the custom self.__getattribute__ more than necessary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
path = object.__getattribute__(self, "cached_typeclass_path")
|
path = GA(self, "cached_typeclass_path")
|
||||||
if not path:
|
if not path:
|
||||||
path = object.__getattribute__(self, 'db_typeclass_path')
|
path = GA(self, 'db_typeclass_path')
|
||||||
typeclass = object.__getattribute__(self, "cached_typeclass")
|
typeclass = GA(self, "cached_typeclass")
|
||||||
try:
|
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
|
# don't call at_init() when returning from cache
|
||||||
return typeclass
|
return typeclass
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
@ -771,25 +777,25 @@ class TypedObject(SharedMemoryModel):
|
||||||
errstring = ""
|
errstring = ""
|
||||||
if not path:
|
if not path:
|
||||||
# this means we should get the default obj without giving errors.
|
# 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:
|
else:
|
||||||
# handle loading/importing of typeclasses, searching all paths.
|
# handle loading/importing of typeclasses, searching all paths.
|
||||||
# (self.typeclass_paths is a shortcut to settings.TYPECLASS_*_PATHS
|
# (self.typeclass_paths is a shortcut to settings.TYPECLASS_*_PATHS
|
||||||
# where '*' is either OBJECT, SCRIPT or PLAYER depending on the typed
|
# where '*' is either OBJECT, SCRIPT or PLAYER depending on the typed
|
||||||
# entities).
|
# 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:
|
for tpath in typeclass_paths:
|
||||||
|
|
||||||
# try to import and analyze the result
|
# try to import and analyze the result
|
||||||
typeclass = object.__getattribute__(self, "_path_import")(tpath)
|
typeclass = GA(self, "_path_import")(tpath)
|
||||||
if callable(typeclass):
|
if callable(typeclass):
|
||||||
# we succeeded to import. Cache and return.
|
# we succeeded to import. Cache and return.
|
||||||
object.__setattr__(self, 'db_typeclass_path', tpath)
|
SA(self, 'db_typeclass_path', tpath)
|
||||||
object.__getattribute__(self, 'save')()
|
GA(self, 'save')()
|
||||||
object.__setattr__(self, "cached_typeclass_path", tpath)
|
SA(self, "cached_typeclass_path", tpath)
|
||||||
typeclass = typeclass(self)
|
typeclass = typeclass(self)
|
||||||
object.__setattr__(self, "cached_typeclass", typeclass)
|
SA(self, "cached_typeclass", typeclass)
|
||||||
try:
|
try:
|
||||||
typeclass.at_init()
|
typeclass.at_init()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -802,8 +808,8 @@ class TypedObject(SharedMemoryModel):
|
||||||
errstring += "\n%s" % typeclass # this will hold a growing error message.
|
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
|
# 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.
|
||||||
object.__getattribute__(self, "_display_errmsg")(errstring)
|
GA(self, "_display_errmsg")(errstring)
|
||||||
return object.__getattribute__(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
|
||||||
def typeclass_del(self):
|
def typeclass_del(self):
|
||||||
|
|
@ -878,34 +884,34 @@ class TypedObject(SharedMemoryModel):
|
||||||
|
|
||||||
Default operation is to load a default typeclass.
|
Default operation is to load a default typeclass.
|
||||||
"""
|
"""
|
||||||
defpath = object.__getattribute__(self, "default_typeclass_path")
|
defpath = GA(self, "default_typeclass_path")
|
||||||
typeclass = object.__getattribute__(self, "_path_import")(defpath)
|
typeclass = GA(self, "_path_import")(defpath)
|
||||||
# if not silent:
|
# if not silent:
|
||||||
# #errstring = "\n\nUsing Default class '%s'." % defpath
|
# #errstring = "\n\nUsing Default class '%s'." % defpath
|
||||||
# object.__getattribute__(self, "_display_errmsg")(errstring)
|
# GA(self, "_display_errmsg")(errstring)
|
||||||
|
|
||||||
if not callable(typeclass):
|
if not callable(typeclass):
|
||||||
# if typeclass still doesn't exist at this point, we're in trouble.
|
# 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.
|
# fall back to hardcoded core class which is wrong for e.g. scripts/players etc.
|
||||||
failpath = defpath
|
failpath = defpath
|
||||||
defpath = "src.objects.objects.Object"
|
defpath = "src.objects.objects.Object"
|
||||||
typeclass = object.__getattribute__(self, "_path_import")(defpath)
|
typeclass = GA(self, "_path_import")(defpath)
|
||||||
if not silent:
|
if not silent:
|
||||||
#errstring = " %s\n%s" % (typeclass, errstring)
|
#errstring = " %s\n%s" % (typeclass, errstring)
|
||||||
errstring = " Default class '%s' failed to load." % failpath
|
errstring = " Default class '%s' failed to load." % failpath
|
||||||
errstring += "\n Using Evennia's default class '%s'." % defpath
|
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 not callable(typeclass):
|
||||||
# if this is still giving an error, Evennia is wrongly configured or buggy
|
# 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)
|
raise Exception("CRITICAL ERROR: The final fallback typeclass %s cannot load!!" % defpath)
|
||||||
typeclass = typeclass(self)
|
typeclass = typeclass(self)
|
||||||
if save:
|
if save:
|
||||||
object.__setattr__(self, 'db_typeclass_path', defpath)
|
SA(self, 'db_typeclass_path', defpath)
|
||||||
object.__getattribute__(self, 'save')()
|
GA(self, 'save')()
|
||||||
if cache:
|
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:
|
try:
|
||||||
typeclass.at_init()
|
typeclass.at_init()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -931,7 +937,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
pass
|
pass
|
||||||
typeclasses = [typeclass] + ["%s.%s" % (path, typeclass) for path in self.typeclass_paths]
|
typeclasses = [typeclass] + ["%s.%s" % (path, typeclass) for path in self.typeclass_paths]
|
||||||
if exact:
|
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])
|
return typeclass and any([current_path == typec for typec in typeclasses])
|
||||||
else:
|
else:
|
||||||
# check parent chain
|
# check parent chain
|
||||||
|
|
@ -1033,12 +1039,9 @@ class TypedObject(SharedMemoryModel):
|
||||||
|
|
||||||
attribute_name: (str) The attribute's name.
|
attribute_name: (str) The attribute's name.
|
||||||
"""
|
"""
|
||||||
exec("from %s import %s" % (self.attribute_model_path,
|
return self.attribute_class.objects.filter(db_obj=self).filter(
|
||||||
self.attribute_model_name))
|
db_key__iexact=attribute_name).count()
|
||||||
model = eval("%s" % self.attribute_model_name)
|
|
||||||
attr = model.objects.attr_namesearch(attribute_name, self)
|
|
||||||
return attr.count() > 0
|
|
||||||
|
|
||||||
def set_attribute(self, attribute_name, new_value=None):
|
def set_attribute(self, attribute_name, new_value=None):
|
||||||
"""
|
"""
|
||||||
Sets an attribute on an object. Creates the attribute if need
|
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.
|
a str, the object will be stored as a pickle.
|
||||||
"""
|
"""
|
||||||
attrib_obj = None
|
attrib_obj = None
|
||||||
if self.has_attribute(attribute_name):
|
attrclass = self.attribute_class
|
||||||
exec("from %s import %s" % (self.attribute_model_path,
|
try:
|
||||||
self.attribute_model_name))
|
attrib_obj = attrclass.objects.filter(
|
||||||
model = eval("%s" % self.attribute_model_name)
|
db_obj=self).filter(db_key__iexact=attribute_name)[0]
|
||||||
#print "attr: model:", self.attribute_model_name
|
except IndexError:
|
||||||
attrib_obj = \
|
# no match; create new attribute
|
||||||
model.objects.filter(
|
new_attrib = attrclass(db_key=attribute_name, db_obj=self)
|
||||||
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
|
|
||||||
new_attrib.value = new_value
|
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):
|
def get_attribute(self, attribute_name, default=None):
|
||||||
"""
|
"""
|
||||||
Returns the value of an attribute on an object. You may need to
|
Returns the value of an attribute on an object. You may need to
|
||||||
type cast the returned value from this function since the attribute
|
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.
|
attribute_name: (str) The attribute's name.
|
||||||
default: What to return if no attribute is found
|
default: What to return if no attribute is found
|
||||||
"""
|
"""
|
||||||
if self.has_attribute(attribute_name):
|
attrib_obj = default
|
||||||
try:
|
try:
|
||||||
exec("from %s import %s" % (self.attribute_model_path,
|
attrib_obj = self.attribute_class.objects.filter(
|
||||||
self.attribute_model_name))
|
db_obj=self).filter(db_key__iexact=attribute_name)[0]
|
||||||
model = eval("%s" % self.attribute_model_name)
|
except IndexError:
|
||||||
attrib = model.objects.filter(
|
return default
|
||||||
db_obj=self).filter(
|
return attrib_obj.value
|
||||||
db_key=attribute_name)[0]
|
|
||||||
except Exception:
|
def get_attribute_raise(self, attribute_name):
|
||||||
# safety, if something goes wrong (like unsynced db), catch it.
|
"""
|
||||||
logger.log_trace()
|
Returns value of an attribute. Raises AttributeError
|
||||||
return default
|
if no match is found.
|
||||||
return attrib.value
|
|
||||||
else:
|
attribute_name: (str) The attribute's name.
|
||||||
return default
|
"""
|
||||||
|
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):
|
def del_attribute(self, attribute_name):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1103,23 +1100,30 @@ class TypedObject(SharedMemoryModel):
|
||||||
|
|
||||||
attribute_name: (str) The attribute's name.
|
attribute_name: (str) The attribute's name.
|
||||||
"""
|
"""
|
||||||
exec("from %s import %s" % (self.attribute_model_path,
|
try:
|
||||||
self.attribute_model_name))
|
self.attribute_class.objects.filter(
|
||||||
model = eval("%s" % self.attribute_model_name)
|
db_obj=self).filter(db_key__iexact=attribute_name)[0].delete()
|
||||||
#print "delete attr", model, attribute_name
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
attrs = \
|
def del_attribute_raise(self, attribute_name):
|
||||||
model.objects.attr_namesearch(attribute_name, self)
|
"""
|
||||||
#print "found attrs:", attrs
|
Removes and attribute. Raises AttributeError if
|
||||||
if attrs:
|
attribute is not found.
|
||||||
attrs[0].delete()
|
|
||||||
|
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):
|
def get_all_attributes(self):
|
||||||
"""
|
"""
|
||||||
Returns all attributes defined on the object.
|
Returns all attributes defined on the object.
|
||||||
"""
|
"""
|
||||||
attr_set_all = eval("self.%s_set.all()" % (self.attribute_model_name.lower()))
|
return list(self.attribute_class.objects.filter(db_obj=self))
|
||||||
return [attr for attr in attr_set_all]
|
|
||||||
|
|
||||||
def attr(self, attribute_name=None, value=None, delete=False):
|
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.
|
set delete=True to delete the named attribute.
|
||||||
|
|
||||||
Note that you cannot set the attribute
|
Note that you cannot set the attribute
|
||||||
value to None using this method should you
|
value to None using this method. Use set_attribute.
|
||||||
want that, use set_attribute for that.
|
|
||||||
"""
|
"""
|
||||||
if attribute_name == None:
|
if attribute_name == None:
|
||||||
# act as a list method
|
# act as a list method
|
||||||
|
|
@ -1166,34 +1169,29 @@ class TypedObject(SharedMemoryModel):
|
||||||
class DbHolder(object):
|
class DbHolder(object):
|
||||||
"Holder for allowing property access of attributes"
|
"Holder for allowing property access of attributes"
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
object.__setattr__(self, 'obj', obj)
|
SA(self, 'obj', obj)
|
||||||
def __getattribute__(self, attrname):
|
def __getattribute__(self, attrname):
|
||||||
obj = object.__getattribute__(self, 'obj')
|
|
||||||
if attrname == 'all':
|
if attrname == 'all':
|
||||||
# we allow for overwriting the all() method
|
# we allow for overwriting the all() method
|
||||||
# with an attribute named 'all'.
|
# with an attribute named 'all'.
|
||||||
attr = obj.get_attribute("all")
|
attr = GA(self, 'obj').get_attribute("all")
|
||||||
if attr:
|
if attr:
|
||||||
return attr
|
return attr
|
||||||
return object.__getattribute__(self, 'all')
|
return GA(self, 'all')
|
||||||
return obj.get_attribute(attrname)
|
return GA(self, 'obj').get_attribute(attrname)
|
||||||
|
|
||||||
def __setattr__(self, attrname, value):
|
def __setattr__(self, attrname, value):
|
||||||
obj = object.__getattribute__(self, 'obj')
|
GA(self, 'obj').set_attribute(attrname, value)
|
||||||
obj.set_attribute(attrname, value)
|
|
||||||
def __delattr__(self, attrname):
|
def __delattr__(self, attrname):
|
||||||
obj = object.__getattribute__(self, 'obj')
|
GA(self, 'obj').del_attribute(attrname)
|
||||||
obj.del_attribute(attrname)
|
|
||||||
def all(self):
|
def all(self):
|
||||||
obj = object.__getattribute__(self, 'obj')
|
return GA(self, 'obj').get_all_attributes()
|
||||||
return obj.get_all_attributes()
|
|
||||||
self._db_holder = DbHolder(self)
|
self._db_holder = DbHolder(self)
|
||||||
return self._db_holder
|
return self._db_holder
|
||||||
#@db.setter
|
#@db.setter
|
||||||
def db_set(self, value):
|
def db_set(self, value):
|
||||||
"Stop accidentally replacing the db object"
|
"Stop accidentally replacing the db object"
|
||||||
string = "Cannot assign directly to db object! "
|
string = "Cannot assign directly to db object! "
|
||||||
string = "Use db.attr=value instead."
|
string += "Use db.attr=value instead."
|
||||||
raise Exception(string)
|
raise Exception(string)
|
||||||
#@db.deleter
|
#@db.deleter
|
||||||
def db_del(self):
|
def db_del(self):
|
||||||
|
|
@ -1202,9 +1200,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
db = property(db_get, db_set, db_del)
|
db = property(db_get, db_set, db_del)
|
||||||
|
|
||||||
#
|
#
|
||||||
# NON-PERSISTENT store. If you want to loose data on server reboot
|
# NON-PERSISTENT storage methods
|
||||||
# you should use this explicitly. Otherwise there is
|
|
||||||
# little point in using the non-persistent methods.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
def nattr(self, attribute_name=None, value=None, delete=False):
|
def nattr(self, attribute_name=None, value=None, delete=False):
|
||||||
|
|
@ -1221,16 +1217,16 @@ class TypedObject(SharedMemoryModel):
|
||||||
if not val.startswith['_']]
|
if not val.startswith['_']]
|
||||||
elif delete == True:
|
elif delete == True:
|
||||||
if hasattr(self.ndb, attribute_name):
|
if hasattr(self.ndb, attribute_name):
|
||||||
object.__delattr__(self.db, attribute_name)
|
DA(self.db, attribute_name)
|
||||||
elif value == None:
|
elif value == None:
|
||||||
# act as a getter.
|
# act as a getter.
|
||||||
if hasattr(self.ndb, attribute_name):
|
if hasattr(self.ndb, attribute_name):
|
||||||
object.__getattribute__(self.ndb, attribute_name)
|
GA(self.ndb, attribute_name)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
# act as a setter
|
# act as a setter
|
||||||
object.__setattr__(self.db, attribute_name, value)
|
SA(self.db, attribute_name, value)
|
||||||
|
|
||||||
#@property
|
#@property
|
||||||
def ndb_get(self):
|
def ndb_get(self):
|
||||||
|
|
@ -1247,11 +1243,11 @@ class TypedObject(SharedMemoryModel):
|
||||||
"Holder for storing non-persistent attributes."
|
"Holder for storing non-persistent attributes."
|
||||||
def all(self):
|
def 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['_']]
|
||||||
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:
|
||||||
return object.__getattribute__(self, key)
|
return GA(self, key)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
self._ndb_holder = NdbHolder()
|
self._ndb_holder = NdbHolder()
|
||||||
|
|
@ -1268,7 +1264,9 @@ class TypedObject(SharedMemoryModel):
|
||||||
raise Exception("Cannot delete the ndb object!")
|
raise Exception("Cannot delete the ndb object!")
|
||||||
ndb = property(ndb_get, ndb_set, ndb_del)
|
ndb = property(ndb_get, ndb_set, ndb_del)
|
||||||
|
|
||||||
|
#
|
||||||
# Lock / permission methods
|
# Lock / permission methods
|
||||||
|
#
|
||||||
|
|
||||||
def access(self, accessing_obj, access_type='read', default=False):
|
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 src.utils import logger
|
||||||
from django.conf import settings
|
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
|
# To ensure the sanity of the model, there are a
|
||||||
# few property names we won't allow the admin to
|
# few property names we won't allow the admin to
|
||||||
# set on the typeclass just like that. Note that these are *not* related
|
# set on the typeclass just like that. Note that these are *not* related
|
||||||
# to *in-game* safety (if you can edit typeclasses you have
|
# to *in-game* safety (if you can edit typeclasses you have
|
||||||
# full access anyway), so no protection against changing
|
# full access anyway), so no protection against changing
|
||||||
# e.g. 'locks' or 'permissions' should go here.
|
# e.g. 'locks' or 'permissions' should go here.
|
||||||
PROTECTED = ['id', 'dbobj', 'db', 'ndb', 'objects', 'typeclass',
|
PROTECTED = ('id', 'dbobj', 'db', 'ndb', 'objects', 'typeclass',
|
||||||
'attr', 'save', 'delete']
|
'attr', 'save', 'delete')
|
||||||
|
|
||||||
# If this is true, all non-protected property assignments
|
# If this is true, all non-protected property assignments
|
||||||
# are directly stored to a database attribute
|
# 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
|
# typecheck of dbobj - we can't allow it to be added here
|
||||||
# unless it's really a TypedObject.
|
# unless it's really a TypedObject.
|
||||||
dbobj_cls = object.__getattribute__(dbobj, '__class__')
|
dbobj_cls = GA(dbobj, '__class__')
|
||||||
dbobj_mro = object.__getattribute__(dbobj_cls, '__mro__')
|
dbobj_mro = GA(dbobj_cls, '__mro__')
|
||||||
if not any('src.typeclasses.models.TypedObject'
|
if not any('src.typeclasses.models.TypedObject'
|
||||||
in str(mro) for mro in dbobj_mro):
|
in str(mro) for mro in dbobj_mro):
|
||||||
raise Exception("dbobj is not a TypedObject: %s: %s" % \
|
raise Exception("dbobj is not a TypedObject: %s: %s" % \
|
||||||
(dbobj_cls, dbobj_mro))
|
(dbobj_cls, dbobj_mro))
|
||||||
object.__setattr__(self, 'dbobj', dbobj)
|
|
||||||
|
|
||||||
# store the needed things on the typeclass
|
# store the needed things on the typeclass
|
||||||
object.__setattr__(self, '_protected_attrs', PROTECTED)
|
SA(self, 'dbobj', dbobj)
|
||||||
|
|
||||||
# # sync the database object to this typeclass.
|
# # sync the database object to this typeclass.
|
||||||
# cls = object.__getattribute__(self, '__class__')
|
# cls = GA(self, '__class__')
|
||||||
# db_typeclass_path = "%s.%s" % (object.__getattribute__(cls, '__module__'),
|
# db_typeclass_path = "%s.%s" % (GA(cls, '__module__'),
|
||||||
# object.__getattribute__(cls, '__name__'))
|
# GA(cls, '__name__'))
|
||||||
# if not object.__getattribute__(dbobj, "db_typeclass_path") == db_typeclass_path:
|
# if not GA(dbobj, "db_typeclass_path") == db_typeclass_path:
|
||||||
# object.__setattr__(dbobj, "db_typeclass_path", db_typeclass_path)
|
# SA(dbobj, "db_typeclass_path", db_typeclass_path)
|
||||||
# object.__getattribute__(dbobj, "save")()
|
# GA(dbobj, "save")()
|
||||||
|
|
||||||
def __getattribute__(self, propname):
|
def __getattribute__(self, propname):
|
||||||
"""
|
"""
|
||||||
|
|
@ -98,7 +102,7 @@ class TypeClass(object):
|
||||||
accessible through getattr.
|
accessible through getattr.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
dbobj = object.__getattribute__(self, 'dbobj')
|
dbobj = GA(self, 'dbobj')
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
dbobj = None
|
dbobj = None
|
||||||
logger.log_trace("This is probably due to an unsafe reload.")
|
logger.log_trace("This is probably due to an unsafe reload.")
|
||||||
|
|
@ -108,25 +112,20 @@ class TypeClass(object):
|
||||||
if propname.startswith('__') and propname.endswith('__'):
|
if propname.startswith('__') and propname.endswith('__'):
|
||||||
# python specials are parsed as-is (otherwise things like
|
# python specials are parsed as-is (otherwise things like
|
||||||
# isinstance() fail to identify the typeclass)
|
# isinstance() fail to identify the typeclass)
|
||||||
return object.__getattribute__(self, propname)
|
return GA(self, propname)
|
||||||
#print "get %s (dbobj:%s)" % (propname, type(dbobj))
|
#print "get %s (dbobj:%s)" % (propname, type(dbobj))
|
||||||
try:
|
try:
|
||||||
return object.__getattribute__(self, propname)
|
return GA(self, propname)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
try:
|
try:
|
||||||
return object.__getattribute__(dbobj, propname)
|
return GA(dbobj, propname)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
try:
|
try:
|
||||||
if propname != 'ndb':
|
if propname == 'ndb':
|
||||||
if not dbobj.has_attribute(propname):
|
|
||||||
raise AttributeError
|
|
||||||
else:
|
|
||||||
value = dbobj.get_attribute(propname)
|
|
||||||
else:
|
|
||||||
# get non-persistent data
|
# get non-persistent data
|
||||||
ndb = object.__getattribute__(dbobj, 'ndb')
|
return getattr(GA(dbobj, 'ndb'), propname)
|
||||||
value = getattr(ndb, propname)
|
else:
|
||||||
return value
|
return dbobj.get_attribute_raise(propname)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
string = "Object: '%s' not found on %s(%s), nor on its typeclass %s."
|
string = "Object: '%s' not found on %s(%s), nor on its typeclass %s."
|
||||||
raise AttributeError(string % (propname, dbobj,
|
raise AttributeError(string % (propname, dbobj,
|
||||||
|
|
@ -142,39 +141,38 @@ class TypeClass(object):
|
||||||
corresponding to a field on ObjectDB model.
|
corresponding to a field on ObjectDB model.
|
||||||
"""
|
"""
|
||||||
#print "set %s -> %s" % (propname, value)
|
#print "set %s -> %s" % (propname, value)
|
||||||
try:
|
if propname in PROTECTED:
|
||||||
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:
|
|
||||||
string = "%s: '%s' is a protected attribute name."
|
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))
|
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:
|
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:
|
except AttributeError:
|
||||||
dbobj = None
|
dbobj.set_attribute(propname, value)
|
||||||
logger.log_trace("This is probably due to an unsafe reload.")
|
else:
|
||||||
if dbobj: # and hasattr(dbobj, propname):
|
SA(self, propname, value)
|
||||||
#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)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""
|
"""
|
||||||
dbobj-recognized comparison
|
dbobj-recognized comparison
|
||||||
"""
|
"""
|
||||||
if hasattr(other, 'user'):
|
try:
|
||||||
return other == self or other == self.dbobj or other == self.dbobj.user
|
return other == self or other == GA(self, dbobj) or other == GA(self, dbobj).user
|
||||||
else:
|
except AttributeError:
|
||||||
return other == self or other == self.dbobj
|
# if self.dbobj.user fails it means the two previous comparisons failed already
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def __delattr__(self, propname):
|
def __delattr__(self, propname):
|
||||||
|
|
@ -183,35 +181,28 @@ class TypeClass(object):
|
||||||
secondly on the dbobj.db.
|
secondly on the dbobj.db.
|
||||||
Will not allow deletion of properties stored directly on dbobj.
|
Will not allow deletion of properties stored directly on dbobj.
|
||||||
"""
|
"""
|
||||||
try:
|
if propname in PROTECTED:
|
||||||
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:
|
|
||||||
string = "%s: '%s' is a protected attribute name."
|
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))
|
logger.log_errmsg(string % (self.name, propname))
|
||||||
else:
|
return
|
||||||
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,))
|
|
||||||
|
|
||||||
|
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):
|
def __str__(self):
|
||||||
"represent the object"
|
"represent the object"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue