2010-08-29 18:46:58 +00:00
"""
This is the * abstract * django models for many of the database objects
in Evennia . A django abstract ( obs , not the same as a Python metaclass ! ) is
a model which is not actually created in the database , but which only exists
for other models to inherit from , to avoid code duplication . Any model can
2012-03-30 23:47:22 +02:00
import and inherit from these classes .
2010-08-29 18:46:58 +00:00
Attributes are database objects stored on other objects . The implementing
class needs to supply a ForeignKey field attr_object pointing to the kind
2012-02-14 23:40:16 +01:00
of object being mapped . Attributes storing iterables actually store special
types of iterables named PackedList / PackedDict respectively . These make
2012-03-30 23:47:22 +02:00
sure to save changes to them to database - this is criticial in order to
allow for obj . db . mylist [ 2 ] = data . Also , all dbobjects are saved as
2012-02-14 23:40:16 +01:00
dbrefs but are also aggressively cached .
2010-08-29 18:46:58 +00:00
TypedObjects are objects ' decorated ' with a typeclass - that is , the typeclass
( which is a normal Python class implementing some special tricks with its
get / set attribute methods , allows for the creation of all sorts of different
objects all with the same database object underneath . Usually attributes are
used to permanently store things not hard - coded as field on the database object .
The admin should usually not have to deal directly with the database object
2012-03-30 23:47:22 +02:00
layer .
2010-08-29 18:46:58 +00:00
This module also contains the Managers for the respective models ; inherit from
2012-03-30 23:47:22 +02:00
these to create custom managers .
2010-08-29 18:46:58 +00:00
"""
2011-06-24 20:12:59 +00:00
import sys
2013-04-13 18:13:15 +02:00
#try:
# import cPickle as pickle
#except ImportError:
# import pickle
2010-08-29 18:46:58 +00:00
import traceback
2013-04-13 18:13:15 +02:00
#from collections import defaultdict
2012-04-28 00:37:36 +02:00
2013-07-08 18:13:21 +02:00
from django . db import models
2011-02-28 23:43:14 +00:00
from django . conf import settings
2010-08-29 18:46:58 +00:00
from django . utils . encoding import smart_str
2011-04-05 23:28:40 +00:00
from django . contrib . contenttypes . models import ContentType
2010-08-29 18:46:58 +00:00
from src . utils . idmapper . models import SharedMemoryModel
2012-11-01 11:20:07 +01:00
from src . server . caches import get_field_cache , set_field_cache , del_field_cache
2013-05-29 23:07:44 +02:00
from src . server . caches import get_attr_cache , set_attr_cache
2013-07-12 11:12:21 +02:00
from src . server . caches import get_prop_cache , set_prop_cache , flush_attr_cache
2013-07-08 18:13:21 +02:00
from django . db . models . signals import m2m_changed
2013-07-11 09:51:52 +02:00
from src . server . caches import post_attr_update
2013-07-08 18:13:21 +02:00
2013-05-29 16:16:28 +02:00
#from src.server.caches import call_ndb_hooks
2011-04-30 21:09:19 +00:00
from src . server . models import ServerConfig
2010-08-29 18:46:58 +00:00
from src . typeclasses import managers
2011-03-15 16:08:32 +00:00
from src . locks . lockhandler import LockHandler
2011-04-16 22:26:22 +00:00
from src . utils import logger , utils
2013-07-12 11:12:21 +02:00
from src . utils . utils import make_iter , is_iter , to_str
2013-04-13 15:15:02 +02:00
from src . utils . dbserialize import to_pickle , from_pickle
from src . utils . picklefield import PickledObjectField
2010-08-29 18:46:58 +00:00
2012-03-31 15:09:22 +02:00
__all__ = ( " Attribute " , " TypeNick " , " TypedObject " )
2012-03-31 13:06:29 +02:00
_PERMISSION_HIERARCHY = [ p . lower ( ) for p in settings . PERMISSION_HIERARCHY ]
2011-03-15 16:08:32 +00:00
2012-03-31 13:06:29 +02:00
_CTYPEGET = ContentType . objects . get
_GA = object . __getattribute__
_SA = object . __setattr__
_DA = object . __delattr__
2012-02-05 21:04:10 +01:00
2013-07-08 18:13:21 +02:00
2010-08-29 18:46:58 +00:00
#------------------------------------------------------------
#
2012-03-30 23:47:22 +02:00
# Attributes
2010-08-29 18:46:58 +00:00
#
#------------------------------------------------------------
class Attribute ( SharedMemoryModel ) :
"""
Abstract django model .
Attributes are things that are specific to different types of objects . For
example , a drink container needs to store its fill level , whereas an exit
needs to store its open / closed / locked / unlocked state . These are done via
attributes , rather than making different classes for each object type and
2012-03-30 23:47:22 +02:00
storing them directly . The added benefit is that we can add / remove
2010-08-29 18:46:58 +00:00
attributes on the fly as we like .
The Attribute class defines the following properties :
key - primary identifier
mode - which type of data is stored in attribute
2013-04-10 21:45:56 +02:00
lock_storage - perm strings
2010-08-29 18:46:58 +00:00
obj - which object the attribute is defined on
2013-07-09 00:09:19 +02:00
date_created - when the attribute was created .
value - the data stored in the attribute , in pickled form
using wrappers to be able to store / retrieve models .
2012-02-14 23:40:16 +01:00
2010-08-29 18:46:58 +00:00
"""
#
# Attribute Database Model setup
#
2013-05-29 18:47:51 +02:00
# These database fields are all set using their corresponding properties,
2013-08-04 12:47:00 -05:00
# named same as the field, but withtout the db_* prefix.
2012-02-06 13:18:25 +01:00
db_key = models . CharField ( ' key ' , max_length = 255 , db_index = True )
2012-03-30 23:47:22 +02:00
# access through the value property
2013-07-08 18:13:21 +02:00
db_value = PickledObjectField ( ' value ' , null = True )
2012-03-30 23:47:22 +02:00
# Lock storage
2013-02-03 09:54:36 -06:00
db_lock_storage = models . TextField ( ' locks ' , blank = True )
2010-08-29 18:46:58 +00:00
# time stamp
2012-02-14 23:40:16 +01:00
db_date_created = models . DateTimeField ( ' date_created ' , editable = False , auto_now_add = True )
2012-03-30 23:47:22 +02:00
# Database manager
2010-08-29 18:46:58 +00:00
objects = managers . AttributeManager ( )
2012-03-30 23:47:22 +02:00
2011-03-15 16:08:32 +00:00
# Lock handler self.locks
def __init__ ( self , * args , * * kwargs ) :
" Initializes the parent first -important! "
SharedMemoryModel . __init__ ( self , * args , * * kwargs )
self . locks = LockHandler ( self )
2012-02-14 23:40:16 +01:00
self . no_cache = True
self . cached_value = None
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
class Meta :
" Define Django meta options "
2012-03-30 23:47:22 +02:00
verbose_name = " Evennia Attribute "
2012-02-14 23:40:16 +01:00
2013-07-09 00:09:19 +02:00
2010-08-29 18:46:58 +00:00
# Wrapper properties to easily set database fields. These are
# @property decorators that allows to access these fields using
# normal python operations (without having to remember to save()
# etc). So e.g. a property 'attr' has a get/set/del decorator
2012-03-30 23:47:22 +02:00
# defined that allows the user to do self.attr = value,
# value = self.attr and del self.attr respectively (where self
2010-08-29 18:46:58 +00:00
# is the object in question).
2013-07-13 15:39:16 +02:00
## key property (wraps db_key)
##@property
#def __key_get(self):
# "Getter. Allows for value = self.key"
# return get_field_cache(self, "key")
##@key.setter
#def __key_set(self, value):
# "Setter. Allows for self.key = value"
# set_field_cache(self, "key", value)
##@key.deleter
#def __key_del(self):
# "Deleter. Allows for del self.key"
# raise Exception("Cannot delete attribute key!")
#key = property(__key_get, __key_set, __key_del)
2010-08-29 18:46:58 +00:00
# obj property (wraps db_obj)
#@property
2012-03-31 13:06:29 +02:00
def __obj_get ( self ) :
2010-08-29 18:46:58 +00:00
" Getter. Allows for value = self.obj "
2012-11-01 11:20:07 +01:00
return get_field_cache ( self , " obj " )
2010-08-29 18:46:58 +00:00
#@obj.setter
2012-03-31 13:06:29 +02:00
def __obj_set ( self , value ) :
2010-08-29 18:46:58 +00:00
" Setter. Allows for self.obj = value "
2012-11-01 11:20:07 +01:00
set_field_cache ( self , " obj " , value )
2010-08-29 18:46:58 +00:00
#@obj.deleter
2012-03-31 13:06:29 +02:00
def __obj_del ( self ) :
2010-08-29 18:46:58 +00:00
" Deleter. Allows for del self.obj "
self . db_obj = None
self . save ( )
2012-11-01 11:20:07 +01:00
del_field_cache ( self , " obj " )
2012-03-31 13:06:29 +02:00
obj = property ( __obj_get , __obj_set , __obj_del )
2010-08-29 18:46:58 +00:00
# date_created property (wraps db_date_created)
#@property
2012-03-31 13:06:29 +02:00
def __date_created_get ( self ) :
2010-08-29 18:46:58 +00:00
" Getter. Allows for value = self.date_created "
2012-11-01 11:20:07 +01:00
return get_field_cache ( self , " date_created " )
2010-08-29 18:46:58 +00:00
#@date_created.setter
2012-03-31 13:06:29 +02:00
def __date_created_set ( self , value ) :
2010-08-29 18:46:58 +00:00
" Setter. Allows for self.date_created = value "
2010-09-05 14:42:09 +00:00
raise Exception ( " Cannot edit date_created! " )
2010-08-29 18:46:58 +00:00
#@date_created.deleter
2012-03-31 13:06:29 +02:00
def __date_created_del ( self ) :
2010-08-29 18:46:58 +00:00
" Deleter. Allows for del self.date_created "
raise Exception ( " Cannot delete date_created! " )
2012-03-31 13:06:29 +02:00
date_created = property ( __date_created_get , __date_created_set , __date_created_del )
2010-08-29 18:46:58 +00:00
# value property (wraps db_value)
#@property
2012-03-31 13:06:29 +02:00
def __value_get ( self ) :
2010-08-29 18:46:58 +00:00
"""
2012-02-14 23:40:16 +01:00
Getter . Allows for value = self . value . Reads from cache if possible .
2012-03-30 23:47:22 +02:00
"""
2012-02-14 23:40:16 +01:00
if self . no_cache :
2012-03-30 23:47:22 +02:00
# re-create data from database and cache it
2013-04-14 13:35:25 +02:00
value = from_pickle ( self . db_value , db_obj = self )
2012-02-14 23:40:16 +01:00
self . cached_value = value
2012-03-30 23:47:22 +02:00
self . no_cache = False
2013-04-13 18:13:15 +02:00
return self . cached_value
2010-08-29 18:46:58 +00:00
#@value.setter
2012-03-31 13:06:29 +02:00
def __value_set ( self , new_value ) :
2012-02-14 23:40:16 +01:00
"""
Setter . Allows for self . value = value . We make sure to cache everything .
2012-03-30 23:47:22 +02:00
"""
2013-04-13 15:15:02 +02:00
to_store = to_pickle ( new_value )
self . cached_value = from_pickle ( to_store , db_obj = self )
2012-02-14 23:40:16 +01:00
self . no_cache = False
2013-04-14 13:35:25 +02:00
self . db_value = to_store
2010-08-29 18:46:58 +00:00
self . save ( )
2013-04-13 15:15:02 +02:00
self . at_set ( self . cached_value )
2010-08-29 18:46:58 +00:00
#@value.deleter
2012-03-31 13:06:29 +02:00
def __value_del ( self ) :
2010-08-29 18:46:58 +00:00
" Deleter. Allows for del attr.value. This removes the entire attribute. "
self . delete ( )
2012-03-31 13:06:29 +02:00
value = property ( __value_get , __value_set , __value_del )
2010-08-29 18:46:58 +00:00
2011-03-15 16:08:32 +00:00
# lock_storage property (wraps db_lock_storage)
2012-03-30 23:47:22 +02:00
#@property
2012-03-31 13:06:29 +02:00
def __lock_storage_get ( self ) :
2011-03-15 16:08:32 +00:00
" Getter. Allows for value = self.lock_storage "
2012-11-01 11:20:07 +01:00
return get_field_cache ( self , " lock_storage " )
2011-03-15 16:08:32 +00:00
#@lock_storage.setter
2012-03-31 13:06:29 +02:00
def __lock_storage_set ( self , value ) :
2011-03-15 16:08:32 +00:00
""" Saves the lock_storage. This is usually not called directly, but through self.lock() """
self . db_lock_storage = value
self . save ( )
#@lock_storage.deleter
2012-03-31 13:06:29 +02:00
def __lock_storage_del ( self ) :
2011-03-15 16:08:32 +00:00
" Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead " " "
logger . log_errmsg ( " Lock_Storage (on %s ) cannot be deleted. Use obj.lock.delete() instead. " % self )
2012-03-31 13:06:29 +02:00
lock_storage = property ( __lock_storage_get , __lock_storage_set , __lock_storage_del )
2012-03-30 23:47:22 +02:00
2011-03-15 16:08:32 +00:00
2010-08-29 18:46:58 +00:00
#
#
# Attribute methods
#
#
def __str__ ( self ) :
2013-07-13 15:39:16 +02:00
return smart_str ( " %s ( %s ) " % ( _GA ( self , " db_key " ) , _GA ( self , " id " ) ) )
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
def __unicode__ ( self ) :
2013-07-12 22:08:15 +02:00
return u " %s ( %s ) " % ( _GA ( self , " db_key " , _GA ( self , " id " ) ) )
2010-08-29 18:46:58 +00:00
2011-03-15 16:08:32 +00:00
def access ( self , accessing_obj , access_type = ' read ' , default = False ) :
"""
Determines if another object has permission to access .
accessing_obj - object trying to access this one
access_type - type of access sought
default - what to return if no lock of access_type was found
2012-03-30 23:47:22 +02:00
"""
2011-03-15 16:08:32 +00:00
return self . locks . check ( accessing_obj , access_type = access_type , default = default )
2012-12-08 20:26:44 +01:00
def at_set ( self , new_value ) :
"""
Hook method called when the attribute changes value .
"""
pass
2013-07-12 11:12:21 +02:00
#------------------------------------------------------------
#
# LiteAttributes
#
#------------------------------------------------------------
class LiteAttribute ( models . Model ) :
"""
This specialized model is a middle - road between a Tag
and an Attribute . A LiteAttribute is smaller , less complex
to search than an Attribute ( its key is indexed together with its
category ) but can only hold strings in its db_value property whereas
an Attribute can hold arbitrary data . A LiteAttribute is also not
kept in memory in the same way as Attributes and has no inherent
concept of access restrictions which makes it unsuitable for modification
by untrusted users .
The difference between Liteattrs and Tags are that liteattrs are
not shared / unique but are created separately for each object holding them .
LiteAttributes are accessed through the db_liteattributes many2many field on
Typed Objects .
The main default use of the LiteAttribute is to implement Nick replacement .
In this case the category determines when the replacement is to be checked .
The key value is the input to replace and the data value holds the replacement
text .
The default nick category types used by Evennia are :
nick_inputline ( default ) - match against all input
nick_player - match against player searches
nick_obj - match against object searches
nick_channel - used to store own names for channels
"""
db_key = models . CharField ( ' key ' , max_length = 255 , help_text = ' name if liteattribute ' )
2013-07-12 12:04:57 +02:00
db_category = models . CharField ( ' category ' , max_length = 64 , null = True , blank = True , help_text = " liteattribute category " )
2013-07-12 11:12:21 +02:00
db_data = models . TextField ( ' data ' , help_text = ' holds string data ' )
objects = managers . LiteAttributeManager ( )
class Meta :
" Define Django meta options "
verbose_name = " Lite Attribute "
2013-07-12 12:04:57 +02:00
index_together = ( ( " db_key " , " db_category " ) , )
2013-07-12 11:12:21 +02:00
def __unicode__ ( self ) :
return u " %s " % self . db_key
def __str__ ( self ) :
return str ( self . db_key )
#------------------------------------------------------------
#
# Tags
#
#------------------------------------------------------------
class Tag ( models . Model ) :
"""
Tags are quick markers for objects in - game . An typeobject
can have any number of tags , stored via its db_tags property .
Tagging similar objects will make it easier to quickly locate the
group later ( such as when implementing zones ) . The main advantage
of tagging as opposed to using Attributes is speed ; a tag is very
limited in what data it can hold , and the tag key + category is
indexed for efficient lookup in the database . Tags are shared between
objects - a new tag is only created if the key + category combination
did not previously exist , making them unsuitable for storing
object - related data ( for this a LiteAttribute or a full Attribute
should be used ) .
The ' db_data ' field is intended as a documentation
field for the tag itself , such as to document what this tag + category
stands for and display that in a web interface or similar .
The main default use for Tags is to implement Aliases for objects .
this uses the ' aliases ' tag category , which is also checked by the
default search functions of Evennia to allow quick searches by alias .
"""
2013-07-12 12:04:57 +02:00
db_key = models . CharField ( ' key ' , max_length = 255 , null = True , help_text = " tag identifier " )
db_category = models . CharField ( ' category ' , max_length = 64 , null = True , help_text = " tag category " )
2013-07-12 11:12:21 +02:00
db_data = models . TextField ( ' data ' , null = True , blank = True , help_text = " optional data field with extra information. This is not searched for. " )
objects = managers . TagManager ( )
class Meta :
" Define Django meta options "
verbose_name = " Tag "
2013-07-12 12:04:57 +02:00
unique_together = ( ( ' db_key ' , ' db_category ' ) , )
index_together = ( ( ' db_key ' , ' db_category ' ) , )
2013-07-12 11:12:21 +02:00
def __unicode__ ( self ) :
return u " %s " % self . db_key
def __str__ ( self ) :
return str ( self . db_key )
2010-08-29 18:46:58 +00:00
2013-07-12 14:44:49 +02:00
2011-04-23 11:54:08 +00:00
#
2013-07-12 14:44:49 +02:00
# Helper handlers
2011-04-23 11:54:08 +00:00
#
2013-07-12 14:44:49 +02:00
class TagHandler ( object ) :
2011-04-23 11:54:08 +00:00
"""
2013-07-12 14:44:49 +02:00
Generic tag - handler . Accessed via TypedObject . tags .
2011-04-23 11:54:08 +00:00
"""
2013-07-12 14:44:49 +02:00
def __init__ ( self , obj , category_prefix = " " ) :
"""
Tags are stored internally in the TypedObject . db_tags m2m field
using the category < category_prefix > < tag_category >
"""
self . obj = obj
self . prefix = category_prefix . strip ( ) . lower ( ) if category_prefix else " "
def add ( self , tag , category = None , data = None ) :
" Add a new tag to the handler "
2013-07-12 15:34:54 +02:00
for tag in make_iter ( tag ) :
tag = tag . strip ( ) . lower ( ) if tag != None else None
2013-08-04 09:25:15 -05:00
category = " %s %s " % ( self . prefix , category . strip ( ) . lower ( ) ) if category != None else None
2013-07-12 15:34:54 +02:00
data = str ( data ) if data != None else None
# this will only create tag if no matches existed beforehand (it will overload
# data on an existing tag since that is not considered part of making the tag unique)
tagobj = Tag . objects . create_tag ( key = tag , category = category , data = data )
self . obj . db_tags . add ( tagobj )
2013-07-12 14:44:49 +02:00
def remove ( self , tag , category = None ) :
" Remove a tag from the handler "
2013-07-12 15:34:54 +02:00
for tag in make_iter ( tag ) :
tag = tag . strip ( ) . lower ( ) if tag != None else None
2013-08-04 12:47:00 -05:00
category = " %s %s " % ( self . prefix , category . strip ( ) . lower ( ) ) if category != None else None
2013-07-12 15:34:54 +02:00
#TODO This does not delete the tag object itself. Maybe it should do that when no
# objects reference the tag anymore?
tagobj = self . obj . db_tags . filter ( db_key = tag , db_category = category )
if tagobj :
2013-08-04 12:47:00 -05:00
self . obj . db_tags . remove ( tagobj [ 0 ] )
2013-07-12 15:57:46 +02:00
def clear ( self ) :
" Remove all tags from the handler "
self . obj . db_tags . clear ( )
2013-07-12 14:44:49 +02:00
def all ( self ) :
" Get all tags in this handler "
2013-07-12 20:21:52 +02:00
return [ p [ 0 ] for p in self . obj . db_tags . all ( ) . values_list ( " db_key " ) ]
2013-07-12 14:44:49 +02:00
def __str__ ( self ) :
return " , " . join ( self . all ( ) )
def __unicode ( self ) :
return u " , " . join ( self . all ( ) )
2011-04-23 11:54:08 +00:00
2013-07-12 14:44:49 +02:00
class AliasHandler ( object ) :
2011-04-23 11:54:08 +00:00
"""
2013-07-12 14:44:49 +02:00
Handles alias access and setting . Accessed through TypedObject . aliases .
2012-03-30 23:47:22 +02:00
"""
2013-07-12 14:44:49 +02:00
def __init__ ( self , obj , category_prefix = " object_ " ) :
"""
Aliases are alternate names for an entity .
Implements the alias handler , using Tags for storage and
the < categoryprefix > _alias tag category . It is available
as TypedObjects . aliases .
"""
self . obj = obj
self . category = " %s alias " % category_prefix
2011-04-23 11:54:08 +00:00
2013-07-12 14:44:49 +02:00
def add ( self , alias ) :
" Add a new nick to the handler "
2013-07-12 15:34:54 +02:00
for al in make_iter ( alias ) :
2013-07-12 20:21:52 +02:00
if not al or not al . strip ( ) :
continue
2013-07-12 15:34:54 +02:00
al = al . strip ( )
# create a unique tag only if it didn't already exist
aliasobj = Tag . objects . create_tag ( key = al , category = self . category )
self . obj . db_tags . add ( aliasobj )
2013-07-12 14:44:49 +02:00
def remove ( self , alias ) :
" Remove alias from handler. "
2013-07-12 15:34:54 +02:00
for alias in make_iter ( alias ) :
2013-07-12 15:57:46 +02:00
aliasobj = self . obj . filter ( db_key__iexact = alias . strip ( ) , category = self . category )
2013-07-12 15:34:54 +02:00
#TODO note that this doesn't delete the tag itself. We might want to do this when no object
# uses it anymore ...
self . obj . db_tags . remove ( aliasobj )
2013-07-12 15:57:46 +02:00
def clear ( self ) :
" Clear all aliases from handler "
self . obj . db_tags . remove ( self . obj . filter ( categery = self . category ) )
2013-07-12 14:44:49 +02:00
def all ( self ) :
" Get all aliases in this handler "
2013-07-12 20:21:52 +02:00
return [ p [ 0 ] for p in self . obj . db_tags . filter ( db_category = self . category ) . values_list ( " db_key " ) ]
2013-07-12 14:44:49 +02:00
def __str__ ( self ) :
return " , " . join ( self . all ( ) )
def __unicode ( self ) :
return u " , " . join ( self . all ( ) )
class NickHandler ( object ) :
"""
Handles nick access and setting . Accessed through TypedObject . nicks .
"""
2011-04-23 11:54:08 +00:00
2013-07-12 14:44:49 +02:00
def __init__ ( self , obj , category_prefix = " object_ " ) :
2012-03-29 19:42:08 +02:00
"""
2013-07-12 14:44:49 +02:00
Nicks are alternate names an entity as of ANOTHER entity . The
engine will auto - replace nicks under circumstances dictated
by the nick category . It uses LiteAttributes for storage .
2012-03-29 19:42:08 +02:00
2012-03-30 23:47:22 +02:00
The default nick types used by Evennia are :
2012-03-29 19:42:08 +02:00
inputline ( default ) - match against all input
player - match against player searches
2012-03-30 23:47:22 +02:00
obj - match against object searches
2012-03-29 19:42:08 +02:00
channel - used to store own names for channels
2013-07-12 14:44:49 +02:00
These are all stored interally using categories
< category_prefix > nick_inputline etc .
2012-03-29 19:42:08 +02:00
"""
2011-04-23 11:54:08 +00:00
self . obj = obj
2013-07-12 14:44:49 +02:00
self . prefix = " %s nick_ " % category_prefix . strip ( ) . lower ( ) if category_prefix else " "
2011-04-23 11:54:08 +00:00
2013-07-12 20:21:52 +02:00
def add ( self , nick , realname , category = " inputline " ) :
2012-03-29 19:42:08 +02:00
"""
2012-03-30 23:47:22 +02:00
Assign a new nick for realname .
2013-07-12 20:21:52 +02:00
category used by Evennia are
2012-03-29 19:42:08 +02:00
' inputline ' , ' player ' , ' obj ' and ' channel '
"""
2011-04-23 11:54:08 +00:00
if not nick or not nick . strip ( ) :
2012-03-30 23:47:22 +02:00
return
2013-07-12 15:34:54 +02:00
for nick in make_iter ( nick ) :
nick = nick . strip ( )
real = realname
2013-07-12 20:21:52 +02:00
nick_type = " %s %s " % ( self . prefix , category . strip ( ) . lower ( ) )
2013-07-12 15:34:54 +02:00
query = self . obj . db_liteattributes . filter ( db_key__iexact = nick , db_category__iexact = nick_type )
if query . count ( ) :
old_nick = query [ 0 ]
old_nick . db_data = real
old_nick . save ( )
else :
new_nick = LiteAttribute ( db_key = nick , db_category = nick_type , db_data = real )
new_nick . save ( )
self . obj . db_liteattributes . add ( new_nick )
2013-07-12 14:44:49 +02:00
2013-07-12 20:21:52 +02:00
def remove ( self , key , category = " inputline " ) :
2012-03-29 19:42:08 +02:00
" Removes a previously stored nick "
2013-07-12 20:21:52 +02:00
for nick in make_iter ( key ) :
2013-07-12 15:34:54 +02:00
nick = nick . strip ( )
2013-07-12 20:21:52 +02:00
nick_type = " %s %s " % ( self . prefix , category . strip ( ) . lower ( ) )
query = self . obj . db_liteattributes . filter ( db_key__iexact = nick , db_category__iexact = nick_type )
2013-07-12 15:34:54 +02:00
if query . count ( ) :
# remove the found nick(s)
2013-07-12 20:21:52 +02:00
self . obj . db_liteattributes . remove ( query [ 0 ] )
2013-07-12 14:44:49 +02:00
def delete ( self , * args , * * kwargs ) :
" alias wrapper "
2013-07-12 20:21:52 +02:00
self . remove ( * args , * * kwargs )
2013-07-12 14:44:49 +02:00
2013-07-12 20:21:52 +02:00
def get ( self , key = None , category = " inputline " ) :
"""
Retrieves a given nick object based on the input key and category .
If no key is given , returns a list of all matching nick
objects ( LiteAttributes ) on the object , or the empty list .
"""
returns = [ ]
for nick in make_iter ( key ) :
nick = nick . strip ( ) . lower ( ) if nick != None else None
nick_type = " %s %s " % ( self . prefix , category . strip ( ) . lower ( ) )
if nick :
nicks = _GA ( self . obj , " db_liteattributes " ) . filter ( db_key = nick , db_category = nick_type )
return nicks [ 0 ] if nicks else None
else :
returns . extend ( list ( self . obj . db_liteattributes . all ( ) ) )
return returns
def get_replace ( self , key , category = " inputline " , default = None ) :
2012-03-31 13:06:29 +02:00
"""
2013-07-12 14:44:49 +02:00
Retrieves a given nick replacement based on the input nick . If
given but no matching conversion was found , returns
original input or default if given
If no nick is given , returns a list of all matching nick
objects ( LiteAttributes ) on the object , or the empty list .
2012-03-31 13:06:29 +02:00
"""
2013-07-12 15:34:54 +02:00
returns = [ ]
2013-07-12 20:21:52 +02:00
for nick in make_iter ( key ) :
2013-07-12 15:34:54 +02:00
nick = nick . strip ( ) . lower ( ) if nick != None else None
2013-07-12 20:21:52 +02:00
nick_type = " %s %s " % ( self . prefix , category . strip ( ) . lower ( ) )
nicks = _GA ( self . obj , " db_liteattributes " ) . filter ( db_key = nick , db_category = nick_type )
default = default if default != None else nick
returns . append ( nicks [ 0 ] . db_data ) if nicks else returns . append ( default )
if len ( returns ) == 1 :
return returns [ 0 ]
2013-07-12 15:34:54 +02:00
return returns
def all ( self ) :
" Get all nicks in this handler "
2013-07-12 20:21:52 +02:00
return [ p [ 0 ] for p in self . obj . db_nicks . filter ( db_category = self . category ) . values_list ( " db_key " ) ]
2013-07-12 15:34:54 +02:00
2011-04-23 11:54:08 +00:00
2010-08-29 18:46:58 +00:00
#------------------------------------------------------------
#
2012-03-30 23:47:22 +02:00
# Typed Objects
2010-08-29 18:46:58 +00:00
#
2012-03-30 23:47:22 +02:00
#------------------------------------------------------------
2010-08-29 18:46:58 +00:00
2012-04-28 00:37:36 +02:00
2010-08-29 18:46:58 +00:00
class TypedObject ( SharedMemoryModel ) :
"""
Abstract Django model .
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
This is the basis for a typed object . It also contains all the
2012-03-30 23:47:22 +02:00
mechanics for managing connected attributes .
2010-08-29 18:46:58 +00:00
The TypedObject has the following properties :
key - main name
name - alias for key
typeclass_path - the path to the decorating typeclass
typeclass - auto - linked typeclass
date_created - time stamp of object creation
2012-03-30 23:47:22 +02:00
permissions - perm strings
dbref - #id of object
2010-08-29 18:46:58 +00:00
db - persistent attribute storage
2012-03-30 23:47:22 +02:00
ndb - non - persistent attribute storage
2010-08-29 18:46:58 +00:00
2012-03-30 23:47:22 +02:00
"""
2010-08-29 18:46:58 +00:00
2012-03-30 23:47:22 +02:00
#
2010-08-29 18:46:58 +00:00
# TypedObject Database Model setup
#
#
2013-07-11 15:59:03 +02:00
# These databse fields are all accessed and set using their corresponding properties,
# named same as the field, but without the db_* prefix (no separate save() call is needed)
2012-03-30 23:47:22 +02:00
2013-07-11 15:59:03 +02:00
# Main identifier of the object, for searching. Is accessed with self.key or self.name
2012-02-06 13:18:25 +01:00
db_key = models . CharField ( ' key ' , max_length = 255 , db_index = True )
2013-07-11 15:59:03 +02:00
# This is the python path to the type class this object is tied to the type class is what defines what kind of Object this is)
db_typeclass_path = models . CharField ( ' typeclass ' , max_length = 255 , null = True ,
help_text = " this defines what ' type ' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass. " )
# Creation date. This is not changed once the object is created.
2011-10-02 01:21:03 +02:00
db_date_created = models . DateTimeField ( ' creation date ' , editable = False , auto_now_add = True )
2010-08-29 18:46:58 +00:00
# Permissions (access these through the 'permissions' property)
2013-07-11 15:59:03 +02:00
db_permissions = models . CharField ( ' permissions ' , max_length = 255 , blank = True ,
help_text = " a comma-separated list of text strings checked by in-game locks. They are often used for hierarchies, such as letting a Player have permission ' Wizards ' , ' Builders ' etc. Character objects use ' Players ' by default. Most other objects don ' t have any permissions. " )
2012-03-30 23:47:22 +02:00
# Lock storage
2013-07-11 15:59:03 +02:00
db_lock_storage = models . TextField ( ' locks ' , blank = True ,
help_text = " locks limit access to an entity. A lock is defined as a ' lock string ' on the form ' type:lockfunctions ' , defining what functionality is locked and how to determine access. Not defining a lock means no access is granted. " )
2013-07-12 11:12:21 +02:00
# many2many relationships
2013-07-11 15:59:03 +02:00
db_attributes = models . ManyToManyField ( Attribute , null = True ,
2013-07-21 14:18:36 -05:00
help_text = ' attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases). ' )
2013-07-12 11:12:21 +02:00
db_liteattributes = models . ManyToManyField ( LiteAttribute , null = True ,
help_text = ' liteattributes on this object. A LiteAttribute holds a key, a category and a string field for simple lookups. ' )
db_tags = models . ManyToManyField ( Tag , null = True ,
help_text = ' tags on this object. Tags are simple string markers to identify, group and alias objects. ' )
2010-08-29 18:46:58 +00:00
# Database manager
objects = managers . TypedObjectManager ( )
2013-07-11 15:59:03 +02:00
# quick on-object typeclass cache for speed
2012-03-30 23:47:22 +02:00
_cached_typeclass = None
2011-08-06 18:15:04 +00:00
2011-03-15 16:08:32 +00:00
# lock handler self.locks
def __init__ ( self , * args , * * kwargs ) :
" We must initialize the parent first - important! "
2013-07-12 15:57:46 +02:00
super ( SharedMemoryModel , self ) . __init__ ( * args , * * kwargs )
#SharedMemoryModel.__init__(self, *args, **kwargs)
2013-06-05 18:47:41 +02:00
_SA ( self , " dbobj " , self ) # this allows for self-reference
_SA ( self , " locks " , LockHandler ( self ) )
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
class Meta :
"""
Django setup info .
"""
2012-03-30 23:47:22 +02:00
abstract = True
2010-08-29 18:46:58 +00:00
verbose_name = " Evennia Database Object "
ordering = [ ' -db_date_created ' , ' id ' , ' db_typeclass_path ' , ' db_key ' ]
2012-03-30 23:47:22 +02:00
2013-07-11 15:59:03 +02:00
# wrapper
2010-08-29 18:46:58 +00:00
# Wrapper properties to easily set database fields. These are
# @property decorators that allows to access these fields using
# normal python operations (without having to remember to save()
# etc). So e.g. a property 'attr' has a get/set/del decorator
2012-03-30 23:47:22 +02:00
# defined that allows the user to do self.attr = value,
# value = self.attr and del self.attr respectively (where self
2010-08-29 18:46:58 +00:00
# is the object in question).
# key property (wraps db_key)
#@property
2013-07-12 23:58:22 +02:00
def __key_get ( self ) :
" Getter. Allows for value = self.key "
return _GA ( self , " db_key " )
#return get_field_cache(self, "key")
#@key.setter
def __key_set ( self , value ) :
" Setter. Allows for self.key = value "
set_field_cache ( self , " key " , value )
#@key.deleter
def __key_del ( self ) :
" Deleter. Allows for del self.key "
raise Exception ( " Cannot delete objectdb key! " )
key = property ( __key_get , __key_set , __key_del )
2010-08-29 18:46:58 +00:00
2013-07-11 15:59:03 +02:00
# name property (alias to self.key)
def __name_get ( self ) : return self . key
def __name_set ( self , value ) : self . key = value
def __name_del ( self ) : raise Exception ( " Cannot delete name " )
2012-03-31 13:06:29 +02:00
name = property ( __name_get , __name_set , __name_del )
2010-08-29 18:46:58 +00:00
2013-07-11 15:59:03 +02:00
# typeclass_path property - we manage this separately.
2010-08-29 18:46:58 +00:00
#@property
2013-07-11 15:59:03 +02:00
#def __typeclass_path_get(self):
# "Getter. Allows for value = self.typeclass_path"
# return _GA(self, "db_typeclass_path")
##@typeclass_path.setter
#def __typeclass_path_set(self, value):
# "Setter. Allows for self.typeclass_path = value"
# _SA(self, "db_typeclass_path", value)
# update_fields = ["db_typeclass_path"] if _GA(self, "_get_pk_val")(_GA(self, "_meta")) is not None else None
# _GA(self, "save")(update_fields=update_fields)
##@typeclass_path.deleter
#def __typeclass_path_del(self):
# "Deleter. Allows for del self.typeclass_path"
# self.db_typeclass_path = ""
# _GA(self, "save")(update_fields=["db_typeclass_path"])
#typeclass_path = property(__typeclass_path_get, __typeclass_path_set, __typeclass_path_del)
2010-08-29 18:46:58 +00:00
# date_created property
#@property
2013-07-11 15:59:03 +02:00
#def __date_created_get(self):
# "Getter. Allows for value = self.date_created"
# return get_field_cache(self, "date_created")
##@date_created.setter
#def __date_created_set(self, value):
# "Setter. Allows for self.date_created = value"
# raise Exception("Cannot change date_created!")
##@date_created.deleter
#def __date_created_del(self):
# "Deleter. Allows for del self.date_created"
# raise Exception("Cannot delete date_created!")
#date_created = property(__date_created_get, __date_created_set, __date_created_del)
2010-08-29 18:46:58 +00:00
# permissions property
#@property
2012-03-31 13:06:29 +02:00
def __permissions_get ( self ) :
2010-08-29 18:46:58 +00:00
" Getter. Allows for value = self.name. Returns a list of permissions. "
2012-11-01 11:20:07 +01:00
perms = get_field_cache ( self , " permissions " )
2012-02-25 23:37:50 +01:00
if perms :
return [ perm . strip ( ) for perm in perms . split ( ' , ' ) ]
2010-08-29 18:46:58 +00:00
return [ ]
#@permissions.setter
2012-03-31 13:06:29 +02:00
def __permissions_set ( self , value ) :
2012-03-30 23:47:22 +02:00
" Setter. Allows for self.name = value. Stores as a comma-separated string. "
2012-02-26 01:10:20 +01:00
value = " , " . join ( [ utils . to_unicode ( val ) . strip ( ) for val in make_iter ( value ) ] )
2012-11-01 11:20:07 +01:00
set_field_cache ( self , " permissions " , value )
2010-08-29 18:46:58 +00:00
#@permissions.deleter
2012-03-31 13:06:29 +02:00
def __permissions_del ( self ) :
2010-08-29 18:46:58 +00:00
" Deleter. Allows for del self.name "
self . db_permissions = " "
self . save ( )
2012-11-01 11:20:07 +01:00
del_field_cache ( self , " permissions " )
2012-03-31 13:06:29 +02:00
permissions = property ( __permissions_get , __permissions_set , __permissions_del )
2010-08-29 18:46:58 +00:00
2011-03-15 16:08:32 +00:00
# lock_storage property (wraps db_lock_storage)
2012-03-30 23:47:22 +02:00
#@property
2013-07-11 15:59:03 +02:00
#def __lock_storage_get(self):
# "Getter. Allows for value = self.lock_storage"
# return get_field_cache(self, "lock_storage")
##@lock_storage.setter
#def __lock_storage_set(self, value):
# """Saves the lock_storage. This is usually not called directly, but through self.lock()"""
# set_field_cache(self, "lock_storage", value)
##@lock_storage.deleter
#def __lock_storage_del(self):
# "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead"""
# logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self)
#lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del)
2011-03-15 16:08:32 +00:00
2010-08-29 18:46:58 +00:00
#
#
2012-03-30 23:47:22 +02:00
# TypedObject main class methods and properties
2010-08-29 18:46:58 +00:00
#
#
2012-02-25 23:37:50 +01:00
# these are identifiers for fast Attribute access and caching
2012-03-31 13:06:29 +02:00
_typeclass_paths = settings . OBJECT_TYPECLASS_PATHS
2010-08-29 18:46:58 +00:00
2012-03-30 23:47:22 +02:00
def __eq__ ( self , other ) :
2012-04-26 17:47:25 +02:00
return other and hasattr ( other , ' dbid ' ) and self . dbid == other . dbid
2010-08-29 18:46:58 +00:00
def __str__ ( self ) :
2013-07-12 22:08:15 +02:00
return smart_str ( " %s " % _GA ( self , " db_key " ) )
2010-08-29 18:46:58 +00:00
def __unicode__ ( self ) :
2013-07-12 22:08:15 +02:00
return u " %s " % _GA ( self , " db_key " )
2010-08-29 18:46:58 +00:00
def __getattribute__ ( self , propname ) :
"""
Will predominantly look for an attribute
on this object , but if not found we will
check if it might exist on the typeclass instead . Since
the typeclass refers back to the databaseobject as well , we
have to be very careful to avoid loops .
"""
try :
2012-03-31 13:06:29 +02:00
return _GA ( self , propname )
2010-08-29 18:46:58 +00:00
except AttributeError :
2013-06-06 12:45:39 +02:00
if propname . startswith ( ' _ ' ) :
# don't relay private/special varname lookups to the typeclass
raise AttributeError ( " private property %s not found on db model (typeclass not searched). " % propname )
2010-08-29 18:46:58 +00:00
# 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.)
2012-08-22 22:34:43 +02:00
return _GA ( _GA ( self , ' typeclass ' ) , propname )
2012-08-19 11:45:13 +02:00
2012-09-18 01:03:35 +02:00
def _hasattr ( self , obj , attrname ) :
"""
Loop - safe version of hasattr , to avoid running a lookup that
will be rerouted up the typeclass . Returns True / False .
"""
try :
_GA ( obj , attrname )
return True
except AttributeError :
return False
2012-04-26 17:47:25 +02:00
#@property
def __dbid_get ( self ) :
"""
Caches and returns the unique id of the object .
Use this instead of self . id , which is not cached .
"""
2012-11-01 11:20:07 +01:00
dbid = get_prop_cache ( self , " _dbid " )
2012-04-26 17:47:25 +02:00
if not dbid :
dbid = _GA ( self , " id " )
2012-11-01 11:20:07 +01:00
set_prop_cache ( self , " _dbid " , dbid )
2012-04-26 17:47:25 +02:00
return dbid
def __dbid_set ( self , value ) :
raise Exception ( " dbid cannot be set! " )
def __dbid_del ( self ) :
raise Exception ( " dbid cannot be deleted! " )
dbid = property ( __dbid_get , __dbid_set , __dbid_del )
2010-08-29 18:46:58 +00:00
#@property
2012-03-31 13:06:29 +02:00
def __dbref_get ( self ) :
2010-08-29 18:46:58 +00:00
"""
2012-04-26 17:47:25 +02:00
Returns the object ' s dbref on the form #NN.
2010-08-29 18:46:58 +00:00
"""
2012-04-26 17:47:25 +02:00
return " # %s " % _GA ( self , " _TypedObject__dbid_get " ) ( )
def __dbref_set ( self ) :
raise Exception ( " dbref cannot be set! " )
def __dbref_del ( self ) :
raise Exception ( " dbref cannot be deleted! " )
dbref = property ( __dbref_get , __dbref_set , __dbref_del )
2010-08-29 18:46:58 +00:00
2012-04-28 14:47:11 +02:00
2010-08-29 18:46:58 +00:00
# typeclass property
#@property
2012-03-31 13:06:29 +02:00
def __typeclass_get ( self ) :
2010-08-29 18:46:58 +00:00
"""
Getter . Allows for value = self . typeclass .
The typeclass is a class object found at self . typeclass_path ;
2011-02-28 23:43:14 +00:00
it allows for extending the Typed object for all different
2010-08-29 18:46:58 +00:00
types of objects that the game needs . This property
handles loading and initialization of the typeclass on the fly .
2011-08-06 18:15:04 +00:00
2012-03-31 13:06:29 +02:00
Note : The liberal use of _GA and __setattr__ ( instead
2012-03-30 23:47:22 +02:00
of normal dot notation ) is due to optimization : it avoids calling
the custom self . __getattribute__ more than necessary .
"""
2011-08-06 18:15:04 +00:00
2012-03-31 13:06:29 +02:00
path = _GA ( self , " typeclass_path " )
typeclass = _GA ( self , " _cached_typeclass " )
2011-08-06 18:15:04 +00:00
try :
2012-03-31 13:06:29 +02:00
if typeclass and _GA ( typeclass , " path " ) == path :
2011-10-01 22:00:22 +02:00
# don't call at_init() when returning from cache
2011-08-06 18:15:04 +00:00
return typeclass
except AttributeError :
2011-10-01 22:00:22 +02:00
pass
2010-08-29 18:46:58 +00:00
errstring = " "
if not path :
2011-08-06 18:15:04 +00:00
# this means we should get the default obj without giving errors.
2012-03-31 13:06:29 +02:00
return _GA ( self , " _get_default_typeclass " ) ( cache = True , silent = True , save = True )
2012-03-30 23:47:22 +02:00
else :
2011-05-13 22:26:08 +00:00
# handle loading/importing of typeclasses, searching all paths.
2012-03-31 13:06:29 +02:00
# (self._typeclass_paths is a shortcut to settings.TYPECLASS_*_PATHS
2011-05-13 22:26:08 +00:00
# where '*' is either OBJECT, SCRIPT or PLAYER depending on the typed
2012-03-30 23:47:22 +02:00
# entities).
2012-03-31 13:06:29 +02:00
typeclass_paths = [ path ] + [ " %s . %s " % ( prefix , path ) for prefix in _GA ( self , ' _typeclass_paths ' ) ]
2012-03-30 23:47:22 +02:00
for tpath in typeclass_paths :
2011-08-06 18:15:04 +00:00
# try to import and analyze the result
2012-03-31 13:06:29 +02:00
typeclass = _GA ( self , " _path_import " ) ( tpath )
2013-05-11 16:09:26 +02:00
#print "typeclass:",typeclass,tpath
2011-05-13 22:26:08 +00:00
if callable ( typeclass ) :
2012-03-30 23:47:22 +02:00
# we succeeded to import. Cache and return.
2012-11-05 00:55:25 +01:00
_SA ( self , " typeclass_path " , tpath )
2011-10-01 22:00:22 +02:00
typeclass = typeclass ( self )
2012-03-31 13:06:29 +02:00
_SA ( self , " _cached_typeclass " , typeclass )
2011-10-01 22:00:22 +02:00
try :
typeclass . at_init ( )
2013-05-12 22:13:05 +02:00
except AttributeError :
logger . log_trace ( " \n %s : Error initializing typeclass %s . Using default. " % ( self , tpath ) )
break
2011-10-01 22:00:22 +02:00
except Exception :
logger . log_trace ( )
2011-08-06 18:15:04 +00:00
return typeclass
2011-05-13 22:26:08 +00:00
elif hasattr ( typeclass , ' __file__ ' ) :
errstring + = " \n %s seems to be just the path to a module. You need " % tpath
2010-08-29 18:46:58 +00:00
errstring + = " to specify the actual typeclass name inside the module too. "
2012-03-30 23:47:22 +02:00
else :
errstring + = " \n %s " % typeclass # this will hold a growing error message.
2011-08-06 18:15:04 +00:00
# If we reach this point we couldn't import any typeclasses. Return default. It's up to the calling
2012-03-30 23:47:22 +02:00
# method to use e.g. self.is_typeclass() to detect that the result is not the one asked for.
2012-03-31 13:06:29 +02:00
_GA ( self , " _display_errmsg " ) ( errstring )
2012-04-21 16:15:37 +02:00
_SA ( self , " typeclass_lasterrmsg " , errstring )
2012-03-31 13:06:29 +02:00
return _GA ( self , " _get_default_typeclass " ) ( cache = False , silent = False , save = False )
2011-02-28 23:43:14 +00:00
2010-08-29 18:46:58 +00:00
#@typeclass.deleter
2012-03-31 13:06:29 +02:00
def __typeclass_del ( self ) :
2011-08-06 18:15:04 +00:00
" Deleter. Disallow ' del self.typeclass ' "
raise Exception ( " The typeclass property should never be deleted, only changed in-place! " )
2011-07-03 21:01:06 +00:00
2012-03-30 23:47:22 +02:00
# typeclass property
2012-03-31 13:06:29 +02:00
typeclass = property ( __typeclass_get , fdel = __typeclass_del )
2012-03-30 23:47:22 +02:00
2012-04-21 16:15:37 +02:00
# the last error string will be stored here for accessing methods to access.
# It is set by _display_errmsg, which will print to log if error happens
# during server startup.
typeclass_last_errmsg = " "
2010-08-29 18:46:58 +00:00
def _path_import ( self , path ) :
"""
Import a class from a python path of the
form src . objects . object . Object
"""
errstring = " "
if not path :
2012-03-30 23:47:22 +02:00
# this needs not be bad, it just means
2010-08-29 18:46:58 +00:00
# we should use defaults.
2012-03-30 23:47:22 +02:00
return None
try :
2010-08-29 18:46:58 +00:00
modpath , class_name = path . rsplit ( ' . ' , 1 )
2012-04-21 16:15:37 +02:00
module = __import__ ( modpath , fromlist = [ " none " ] )
2011-06-24 20:12:59 +00:00
return module . __dict__ [ class_name ]
2012-03-30 23:47:22 +02:00
except ImportError :
trc = sys . exc_traceback
2011-06-24 20:12:59 +00:00
if not trc . tb_next :
# we separate between not finding the module, and finding a buggy one.
2012-04-21 16:15:37 +02:00
errstring = " Typeclass not found trying path ' %s ' . " % path
2011-06-24 20:12:59 +00:00
else :
# a bug in the module is reported normally.
2012-03-30 23:47:22 +02:00
trc = traceback . format_exc ( )
2012-04-21 16:15:37 +02:00
errstring = " \n %s Error importing ' %s ' . " % ( trc , path )
except ( ValueError , TypeError ) :
errstring = " Malformed typeclass path ' %s ' . " % path
2010-08-29 18:46:58 +00:00
except KeyError :
2012-03-30 23:47:22 +02:00
errstring = " No class ' %s ' was found in module ' %s ' . "
2010-08-29 18:46:58 +00:00
errstring = errstring % ( class_name , modpath )
except Exception :
2012-03-30 23:47:22 +02:00
trc = traceback . format_exc ( )
errstring = " \n %s Exception importing ' %s ' . " % ( trc , path )
2010-08-29 18:46:58 +00:00
# return the error.
return errstring
2011-08-06 18:15:04 +00:00
2012-03-30 23:47:22 +02:00
def _display_errmsg ( self , message ) :
2011-08-06 18:15:04 +00:00
"""
Helper function to display error .
"""
2012-04-21 16:15:37 +02:00
if ServerConfig . objects . conf ( " server_starting_mode " ) :
print message . strip ( )
else :
_SA ( self , " typeclass_last_errmsg " , message . strip ( ) )
return
2012-03-31 13:06:29 +02:00
def _get_default_typeclass ( self , cache = False , silent = False , save = False ) :
2011-08-06 18:15:04 +00:00
"""
2012-03-30 23:47:22 +02:00
This is called when a typeclass fails to
load for whatever reason .
Overload this in different entities .
2011-08-06 18:15:04 +00:00
Default operation is to load a default typeclass .
"""
2012-03-31 13:06:29 +02:00
defpath = _GA ( self , " _default_typeclass_path " )
typeclass = _GA ( self , " _path_import " ) ( defpath )
2011-08-06 18:15:04 +00:00
# if not silent:
2012-03-30 23:47:22 +02:00
# #errstring = "\n\nUsing Default class '%s'." % defpath
2012-03-31 13:06:29 +02:00
# _GA(self, "_display_errmsg")(errstring)
2011-08-06 18:15:04 +00:00
if not callable ( typeclass ) :
# if typeclass still doesn't exist at this point, we're in trouble.
2012-03-30 23:47:22 +02:00
# fall back to hardcoded core class which is wrong for e.g. scripts/players etc.
2011-08-06 18:15:04 +00:00
failpath = defpath
defpath = " src.objects.objects.Object "
2012-03-31 13:06:29 +02:00
typeclass = _GA ( self , " _path_import " ) ( defpath )
2011-08-06 18:15:04 +00:00
if not silent :
2011-08-11 21:16:35 +00:00
#errstring = " %s\n%s" % (typeclass, errstring)
errstring = " Default class ' %s ' failed to load. " % failpath
2012-03-30 23:47:22 +02:00
errstring + = " \n Using Evennia ' s default class ' %s ' . " % defpath
2012-03-31 13:06:29 +02:00
_GA ( self , " _display_errmsg " ) ( errstring )
2011-08-06 18:15:04 +00:00
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 )
2011-10-01 22:00:22 +02:00
typeclass = typeclass ( self )
2011-08-06 18:15:04 +00:00
if save :
2012-03-31 13:06:29 +02:00
_SA ( self , ' db_typeclass_path ' , defpath )
_GA ( self , ' save ' ) ( )
2011-08-06 18:15:04 +00:00
if cache :
2012-03-31 13:06:29 +02:00
_SA ( self , " _cached_db_typeclass_path " , defpath )
2011-10-01 22:00:22 +02:00
2012-03-31 13:06:29 +02:00
_SA ( self , " _cached_typeclass " , typeclass )
2012-03-30 23:47:22 +02:00
try :
2011-10-01 22:00:22 +02:00
typeclass . at_init ( )
except Exception :
logger . log_trace ( )
2012-03-30 23:47:22 +02:00
return typeclass
2011-08-06 18:15:04 +00:00
def is_typeclass ( self , typeclass , exact = False ) :
2010-08-29 18:46:58 +00:00
"""
Returns true if this object has this type
OR has a typeclass which is an subclass of
2012-04-21 16:15:37 +02:00
the given typeclass . This operates on the actually
loaded typeclass ( this is important since a failing
typeclass may instead have its default currently loaded )
2012-03-30 23:47:22 +02:00
2011-08-06 18:15:04 +00:00
typeclass - can be a class object or the
2012-03-30 23:47:22 +02:00
python path to such an object to match against .
2010-08-29 18:46:58 +00:00
exact - returns true only if the object ' s
type is exactly this typeclass , ignoring
parents .
2012-03-30 23:47:22 +02:00
"""
2011-08-06 18:15:04 +00:00
try :
2012-03-31 13:06:29 +02:00
typeclass = _GA ( typeclass , " path " )
2011-08-06 18:15:04 +00:00
except AttributeError :
2012-03-30 23:47:22 +02:00
pass
2012-03-31 13:06:29 +02:00
typeclasses = [ typeclass ] + [ " %s . %s " % ( path , typeclass ) for path in _GA ( self , " _typeclass_paths " ) ]
2011-08-06 18:15:04 +00:00
if exact :
2012-04-21 16:15:37 +02:00
current_path = _GA ( self . typeclass , " path " ) #"_GA(self, "_cached_db_typeclass_path")
2012-02-25 23:37:50 +01:00
return typeclass and any ( ( current_path == typec for typec in typeclasses ) )
2011-08-06 18:15:04 +00:00
else :
# check parent chain
2012-02-25 23:37:50 +01:00
return any ( ( cls for cls in self . typeclass . __class__ . mro ( )
2012-03-31 13:06:29 +02:00
if any ( ( " %s . %s " % ( _GA ( cls , " __module__ " ) , _GA ( cls , " __name__ " ) ) == typec for typec in typeclasses ) ) ) )
2010-08-29 18:46:58 +00:00
2013-05-11 20:01:19 +02:00
2013-05-14 08:44:40 +02:00
def delete ( self , * args , * * kwargs ) :
"""
Type - level cleanup
"""
flush_attr_cache ( )
super ( TypedObject , self ) . delete ( * args , * * kwargs )
2010-08-29 18:46:58 +00:00
#
# Object manipulation methods
2012-03-30 23:47:22 +02:00
#
2010-08-29 18:46:58 +00:00
#
2011-10-03 23:53:23 +02:00
def swap_typeclass ( self , new_typeclass , clean_attributes = False , no_default = True ) :
2010-08-29 18:46:58 +00:00
"""
This performs an in - situ swap of the typeclass . This means
that in - game , this object will suddenly be something else .
Player will not be affected . To ' move ' a player to a different
2012-03-30 23:47:22 +02:00
object entirely ( while retaining this object ' s type), use
2010-08-29 18:46:58 +00:00
self . player . swap_object ( ) .
2012-03-30 23:47:22 +02:00
Note that this might be an error prone operation if the
2010-08-29 18:46:58 +00:00
old / new typeclass was heavily customized - your code
2012-03-30 23:47:22 +02:00
might expect one and not the other , so be careful to
2010-08-29 18:46:58 +00:00
bug test your code if using this feature ! Often its easiest
to create a new object and just swap the player over to
2012-03-30 23:47:22 +02:00
that one instead .
2010-08-29 18:46:58 +00:00
2012-03-30 23:47:22 +02:00
Arguments :
new_typeclass ( path / classobj ) - type to switch to
2010-08-29 18:46:58 +00:00
clean_attributes ( bool / list ) - will delete all attributes
stored on this object ( but not any
of the database fields such as name or
location ) . You can ' t get attributes back,
but this is often the safest bet to make
sure nothing in the new typeclass clashes
with the old one . If you supply a list ,
only those named attributes will be cleared .
2011-10-03 23:53:23 +02:00
no_default - if this is active , the swapper will not allow for
swapping to a default typeclass in case the given
one fails for some reason . Instead the old one
2012-03-30 23:47:22 +02:00
will be preserved .
Returns :
2012-03-25 16:35:22 +02:00
boolean True / False depending on if the swap worked or not .
2010-08-29 18:46:58 +00:00
"""
if callable ( new_typeclass ) :
# this is an actual class object - build the path
2013-05-11 16:09:26 +02:00
cls = new_typeclass
2010-08-29 18:46:58 +00:00
new_typeclass = " %s . %s " % ( cls . __module__ , cls . __name__ )
2012-11-13 21:34:42 +01:00
else :
new_typeclass = " %s " % to_str ( new_typeclass )
2010-08-29 18:46:58 +00:00
# Try to set the new path
2011-10-03 23:53:23 +02:00
# this will automatically save to database
2012-03-30 23:47:22 +02:00
old_typeclass_path = self . typeclass_path
2012-11-13 21:34:42 +01:00
_SA ( self , " typeclass_path " , new_typeclass . strip ( ) )
2010-08-29 18:46:58 +00:00
# this will automatically use a default class if
# there is an error with the given typeclass.
2011-10-01 22:00:22 +02:00
new_typeclass = self . typeclass
2012-11-13 21:34:42 +01:00
if self . typeclass_path != new_typeclass . path and no_default :
2011-10-03 23:53:23 +02:00
# something went wrong; the default was loaded instead,
# and we don't allow that; instead we return to previous.
2012-03-31 13:06:29 +02:00
_SA ( self , " typeclass_path " , old_typeclass_path )
2011-10-03 23:53:23 +02:00
return False
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
if clean_attributes :
# Clean out old attributes
if is_iter ( clean_attributes ) :
for attr in clean_attributes :
self . attr ( attr , delete = True )
for nattr in clean_attributes :
if hasattr ( self . ndb , nattr ) :
self . nattr ( nattr , delete = True )
else :
#print "deleting attrs ..."
for attr in self . get_all_attributes ( ) :
attr . delete ( )
2012-04-21 16:15:37 +02:00
for nattr in self . ndb . all :
2010-08-29 18:46:58 +00:00
del nattr
2011-03-20 19:45:56 +00:00
# run hooks for this new typeclass
new_typeclass . basetype_setup ( )
2010-08-29 18:46:58 +00:00
new_typeclass . at_object_creation ( )
2012-03-30 23:47:22 +02:00
return True
2010-08-29 18:46:58 +00:00
#
2012-03-30 23:47:22 +02:00
# Attribute handler methods
2010-08-29 18:46:58 +00:00
#
2012-03-30 23:47:22 +02:00
#
2013-07-08 18:13:21 +02:00
# Fully attr_obj attributes. You usually access these
2012-03-30 23:47:22 +02:00
# through the obj.db.attrname method.
2013-07-08 18:13:21 +02:00
# Helper methods for attr_obj attributes
2010-08-29 18:46:58 +00:00
def has_attribute ( self , attribute_name ) :
"""
See if we have an attribute set on the object .
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
attribute_name : ( str ) The attribute ' s name.
2012-03-30 23:47:22 +02:00
"""
2012-11-07 20:22:46 +01:00
if not get_attr_cache ( self , attribute_name ) :
2013-07-08 18:13:21 +02:00
attr_obj = _GA ( self , " db_attributes " ) . filter ( db_key__iexact = attribute_name )
if attr_obj :
set_attr_cache ( self , attribute_name , attr_obj [ 0 ] )
2013-05-29 23:07:44 +02:00
else :
2012-04-26 21:39:16 +02:00
return False
return True
2012-03-30 23:47:22 +02:00
2013-02-03 17:00:46 +01:00
def set_attribute ( self , attribute_name , new_value = None , lockstring = " " ) :
2010-08-29 18:46:58 +00:00
"""
Sets an attribute on an object . Creates the attribute if need
be .
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
attribute_name : ( str ) The attribute ' s name.
new_value : ( python obj ) The value to set the attribute to . If this is not
2012-03-30 23:47:22 +02:00
a str , the object will be stored as a pickle .
2013-02-03 17:00:46 +01:00
lockstring - this sets an access restriction on the attribute object . Note that
this is normally NOT checked - use the secureattr ( ) access method
below to perform access - checked modification of attributes . Lock
types checked by secureattr are ' attrread ' , ' attredit ' , ' attrcreate ' .
2010-08-29 18:46:58 +00:00
"""
2013-07-08 18:13:21 +02:00
attr_obj = get_attr_cache ( self , attribute_name )
if not attr_obj :
# check if attribute already exists
attr_obj = _GA ( self , " db_attributes " ) . filter ( db_key__iexact = attribute_name )
if attr_obj :
# re-use old attribute object
attr_obj = attr_obj [ 0 ]
set_attr_cache ( self , attribute_name , attr_obj ) # renew cache
2012-04-27 00:03:31 +02:00
else :
2013-07-08 18:13:21 +02:00
# no old attr available; create new (caches automatically)
attr_obj = Attribute ( db_key = attribute_name )
attr_obj . save ( ) # important
_GA ( self , " db_attributes " ) . add ( attr_obj )
2013-02-03 17:00:46 +01:00
if lockstring :
2013-07-08 18:13:21 +02:00
attr_obj . locks . add ( lockstring )
# we shouldn't need to fear stale objects, the signalling should catch all cases
attr_obj . value = new_value
2012-03-30 23:47:22 +02:00
2012-06-10 21:46:00 +02:00
def get_attribute_obj ( self , attribute_name , default = None ) :
"""
Get the actual attribute object named attribute_name
"""
2013-07-08 18:13:21 +02:00
attr_obj = get_attr_cache ( self , attribute_name )
if not attr_obj :
attr_obj = _GA ( self , " db_attributes " ) . filter ( db_key__iexact = attribute_name )
if not attr_obj :
2012-06-10 21:46:00 +02:00
return default
2013-07-08 18:13:21 +02:00
attr_obj = attr_obj [ 0 ] # query evaluated here
set_attr_cache ( self , attribute_name , attr_obj )
return attr_obj
2012-06-10 21:46:00 +02:00
2013-07-08 18:13:21 +02:00
def get_attribute ( self , attribute_name , default = None , raise_exception = False ) :
2010-08-29 18:46:58 +00:00
"""
Returns the value of an attribute on an object . You may need to
type cast the returned value from this function since the attribute
2012-03-30 23:47:22 +02:00
can be of any type . Returns default if no match is found .
2010-08-29 18:46:58 +00:00
attribute_name : ( str ) The attribute ' s name.
default : What to return if no attribute is found
2013-07-21 09:44:34 -05:00
raise_exception ( bool ) - raise an exception if no object exists instead of returning default .
2010-08-29 18:46:58 +00:00
"""
2013-07-08 18:13:21 +02:00
attr_obj = get_attr_cache ( self , attribute_name )
if not attr_obj :
2013-07-09 00:09:19 +02:00
attr_obj = _GA ( self , " db_attributes " ) . filter ( db_key__iexact = attribute_name )
2013-07-08 18:13:21 +02:00
if not attr_obj :
if raise_exception :
raise AttributeError
2012-04-26 21:39:16 +02:00
return default
2013-07-08 18:13:21 +02:00
attr_obj = attr_obj [ 0 ] # query is evaluated here
set_attr_cache ( self , attribute_name , attr_obj )
return attr_obj . value
def del_attribute ( self , attribute_name , raise_exception = False ) :
2010-08-29 18:46:58 +00:00
"""
Removes an attribute entirely .
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
attribute_name : ( str ) The attribute ' s name.
2013-07-08 18:13:21 +02:00
raise_exception ( bool ) - raise exception if attribute to delete
could not be found
2010-08-29 18:46:58 +00:00
"""
2012-11-01 11:20:07 +01:00
attr_obj = get_attr_cache ( self , attribute_name )
2013-07-11 09:51:52 +02:00
if not attr_obj :
2013-07-08 18:13:21 +02:00
attr_obj = _GA ( self , " db_attributes " ) . filter ( db_key__iexact = attribute_name )
2013-07-11 09:51:52 +02:00
attr_obj = attr_obj [ 0 ] if attr_obj else None
if not attr_obj :
if raise_exception :
2013-07-08 18:13:21 +02:00
raise AttributeError
2013-07-11 09:51:52 +02:00
return
# the post-remove cache signal will auto-delete the attribute as well,
# don't call attr_obj.delete() after this.
self . db_attributes . remove ( attr_obj )
2010-08-29 18:46:58 +00:00
def get_all_attributes ( self ) :
"""
Returns all attributes defined on the object .
2012-03-30 23:47:22 +02:00
"""
2013-07-08 18:13:21 +02:00
return list ( _GA ( self , " db_attributes " ) . all ( ) )
2010-08-29 18:46:58 +00:00
def attr ( self , attribute_name = None , value = None , delete = False ) :
"""
This is a convenient wrapper for
get_attribute , set_attribute , del_attribute
and get_all_attributes .
If value is None , attr will act like
a getter , otherwise as a setter .
2012-03-30 23:47:22 +02:00
set delete = True to delete the named attribute .
2010-08-29 18:46:58 +00:00
Note that you cannot set the attribute
2012-02-05 21:04:10 +01:00
value to None using this method . Use set_attribute .
2010-08-29 18:46:58 +00:00
"""
2012-03-30 23:47:22 +02:00
if attribute_name == None :
2010-08-29 18:46:58 +00:00
# act as a list method
2012-03-30 23:47:22 +02:00
return self . get_all_attributes ( )
2010-08-29 18:46:58 +00:00
elif delete == True :
self . del_attribute ( attribute_name )
elif value == None :
# act as a getter.
return self . get_attribute ( attribute_name )
else :
# act as a setter
self . set_attribute ( attribute_name , value )
2012-06-10 21:46:00 +02:00
def secure_attr ( self , accessing_object , attribute_name = None , value = None , delete = False ,
default_access_read = True , default_access_edit = True , default_access_create = True ) :
"""
This is a version of attr that requires the accessing object
as input and will use that to check eventual access locks on
the Attribute before allowing any changes or reads .
In the cases when this method wouldn ' t return, it will return
True for a successful operation , None otherwise .
locktypes checked on the Attribute itself :
attrread - control access to reading the attribute value
attredit - control edit / delete access
locktype checked on the object on which the Attribute is / will be stored :
attrcreate - control attribute create access ( this is checked * on the object * not on the Attribute ! )
default_access_ * defines which access is assumed if no
suitable lock is defined on the Atttribute .
"""
if attribute_name == None :
# act as list method, but check access
return [ attr for attr in self . get_all_attributes ( )
if attr . access ( accessing_object , " attread " , default = default_access_read ) ]
elif delete == True :
# act as deleter
attr = self . get_attribute_obj ( attribute_name )
if attr and attr . access ( accessing_object , " attredit " , default = default_access_edit ) :
self . del_attribute ( attribute_name )
return True
elif value == None :
# act as getter
attr = self . get_attribute_obj ( attribute_name )
if attr and attr . access ( accessing_object , " attrread " , default = default_access_read ) :
return attr . value
else :
# act as setter
attr = self . get_attribute_obj ( attribute_name )
if attr :
# attribute already exists
if attr . access ( accessing_object , " attredit " , default = default_access_edit ) :
self . set_attribute ( attribute_name , value )
return True
else :
# creating a new attribute - check access on storing object!
if self . access ( accessing_object , " attrcreate " , default = default_access_create ) :
self . set_attribute ( attribute_name , value )
return True
2010-08-29 18:46:58 +00:00
#@property
2012-03-31 13:06:29 +02:00
def __db_get ( self ) :
2010-08-29 18:46:58 +00:00
"""
A second convenience wrapper for the the attribute methods . It
allows for the syntax
obj . db . attrname = value
and
2012-03-30 23:47:22 +02:00
value = obj . db . attrname
and
del obj . db . attrname
2010-08-29 18:46:58 +00:00
and
2012-06-20 23:22:26 +02:00
all_attr = obj . db . all ( unless there is no attribute named ' all ' , in which
2012-03-30 23:47:22 +02:00
case that will be returned instead ) .
2010-08-29 18:46:58 +00:00
"""
try :
return self . _db_holder
except AttributeError :
class DbHolder ( object ) :
" Holder for allowing property access of attributes "
def __init__ ( self , obj ) :
2012-03-31 13:06:29 +02:00
_SA ( self , ' obj ' , obj )
2012-03-30 23:47:22 +02:00
def __getattribute__ ( self , attrname ) :
2010-08-29 18:46:58 +00:00
if attrname == ' all ' :
2012-06-20 23:22:26 +02:00
# we allow to overload our default .all
2012-03-31 13:06:29 +02:00
attr = _GA ( self , ' obj ' ) . get_attribute ( " all " )
2010-08-29 18:46:58 +00:00
if attr :
return attr
2012-03-31 13:06:29 +02:00
return _GA ( self , ' all ' )
return _GA ( self , ' obj ' ) . get_attribute ( attrname )
2012-03-30 23:47:22 +02:00
def __setattr__ ( self , attrname , value ) :
2012-03-31 13:06:29 +02:00
_GA ( self , ' obj ' ) . set_attribute ( attrname , value )
2012-03-30 23:47:22 +02:00
def __delattr__ ( self , attrname ) :
2012-03-31 13:06:29 +02:00
_GA ( self , ' obj ' ) . del_attribute ( attrname )
2012-04-21 16:15:37 +02:00
def get_all ( self ) :
2012-03-31 13:06:29 +02:00
return _GA ( self , ' obj ' ) . get_all_attributes ( )
2012-04-21 16:15:37 +02:00
all = property ( get_all )
2010-08-29 18:46:58 +00:00
self . _db_holder = DbHolder ( self )
return self . _db_holder
#@db.setter
2012-03-31 13:06:29 +02:00
def __db_set ( self , value ) :
2010-08-29 18:46:58 +00:00
" Stop accidentally replacing the db object "
string = " Cannot assign directly to db object! "
2012-02-05 21:04:10 +01:00
string + = " Use db.attr=value instead. "
2010-08-29 18:46:58 +00:00
raise Exception ( string )
#@db.deleter
2012-03-31 13:06:29 +02:00
def __db_del ( self ) :
2010-08-29 18:46:58 +00:00
" Stop accidental deletion. "
raise Exception ( " Cannot delete the db object! " )
2012-03-31 13:06:29 +02:00
db = property ( __db_get , __db_set , __db_del )
2010-08-29 18:46:58 +00:00
#
2013-07-08 18:13:21 +02:00
# NON-attr_obj storage methods
2010-08-29 18:46:58 +00:00
#
def nattr ( self , attribute_name = None , value = None , delete = False ) :
"""
2013-07-08 18:13:21 +02:00
This is the equivalence of self . attr but for non - attr_obj
2012-09-18 01:03:35 +02:00
stores . Will not raise error but return None .
2010-08-29 18:46:58 +00:00
"""
2012-03-30 23:47:22 +02:00
if attribute_name == None :
2010-08-29 18:46:58 +00:00
# act as a list method
if callable ( self . ndb . all ) :
return self . ndb . all ( )
else :
2012-03-30 23:47:22 +02:00
return [ val for val in self . ndb . __dict__ . keys ( )
if not val . startswith [ ' _ ' ] ]
2010-08-29 18:46:58 +00:00
elif delete == True :
if hasattr ( self . ndb , attribute_name ) :
2012-09-18 01:03:35 +02:00
_DA ( _GA ( self , " db " ) , attribute_name )
2010-08-29 18:46:58 +00:00
elif value == None :
# act as a getter.
if hasattr ( self . ndb , attribute_name ) :
2012-09-18 01:03:35 +02:00
_GA ( _GA ( self , " ndb " ) , attribute_name )
2010-08-29 18:46:58 +00:00
else :
2012-03-30 23:47:22 +02:00
return None
2010-08-29 18:46:58 +00:00
else :
# act as a setter
2013-01-03 09:18:49 +01:00
_SA ( self . ndb , attribute_name , value )
2012-03-30 23:47:22 +02:00
2010-08-29 18:46:58 +00:00
#@property
2012-03-31 13:06:29 +02:00
def __ndb_get ( self ) :
2010-08-29 18:46:58 +00:00
"""
2013-07-08 18:13:21 +02:00
A non - attr_obj store ( ndb : NonDataBase ) . Everything stored
2010-08-29 18:46:58 +00:00
to this is guaranteed to be cleared when a server is shutdown .
2011-10-01 15:10:21 +02:00
Syntax is same as for the _get_db_holder ( ) method and
property , e . g . obj . ndb . attr = value etc .
2010-08-29 18:46:58 +00:00
"""
try :
return self . _ndb_holder
except AttributeError :
class NdbHolder ( object ) :
2013-07-08 18:13:21 +02:00
" Holder for storing non-attr_obj attributes. "
2012-04-21 16:15:37 +02:00
def get_all ( self ) :
2012-03-30 23:47:22 +02:00
return [ val for val in self . __dict__ . keys ( )
2012-04-21 16:15:37 +02:00
if not val . startswith ( ' _ ' ) ]
all = property ( get_all )
2011-08-11 21:16:35 +00:00
def __getattribute__ ( self , key ) :
2012-03-30 23:47:22 +02:00
# return None if no matching attribute was found.
2011-08-11 21:16:35 +00:00
try :
2012-03-31 13:06:29 +02:00
return _GA ( self , key )
2011-08-11 21:16:35 +00:00
except AttributeError :
2012-03-30 23:47:22 +02:00
return None
2013-01-03 09:18:49 +01:00
def __setattr__ ( self , key , value ) :
# hook the oob handler here
2013-05-29 16:16:28 +02:00
#call_ndb_hooks(self, key, value)
2013-01-03 09:18:49 +01:00
_SA ( self , key , value )
2010-08-29 18:46:58 +00:00
self . _ndb_holder = NdbHolder ( )
return self . _ndb_holder
#@ndb.setter
2012-03-31 13:06:29 +02:00
def __ndb_set ( self , value ) :
2010-08-29 18:46:58 +00:00
" Stop accidentally replacing the db object "
string = " Cannot assign directly to ndb object! "
string = " Use ndb.attr=value instead. "
raise Exception ( string )
#@ndb.deleter
2012-03-31 13:06:29 +02:00
def __ndb_del ( self ) :
2010-08-29 18:46:58 +00:00
" Stop accidental deletion. "
raise Exception ( " Cannot delete the ndb object! " )
2012-03-31 13:06:29 +02:00
ndb = property ( __ndb_get , __ndb_set , __ndb_del )
2012-03-30 23:47:22 +02:00
2012-02-05 21:04:10 +01:00
#
2011-03-15 16:08:32 +00:00
# Lock / permission methods
2012-02-05 21:04:10 +01:00
#
2011-03-15 16:08:32 +00:00
def access ( self , accessing_obj , access_type = ' read ' , default = False ) :
"""
Determines if another object has permission to access .
accessing_obj - object trying to access this one
access_type - type of access sought
default - what to return if no lock of access_type was found
2012-03-30 23:47:22 +02:00
"""
2011-03-15 16:08:32 +00:00
return self . locks . check ( accessing_obj , access_type = access_type , default = default )
def check_permstring ( self , permstring ) :
"""
2011-04-23 11:54:08 +00:00
This explicitly checks if we hold particular permission without involving
2011-03-15 16:08:32 +00:00
any locks .
"""
2013-04-12 13:59:15 +02:00
if hasattr ( self , " player " ) :
if self . player and self . player . is_superuser : return True
else :
if self . is_superuser : return True
2011-04-23 11:54:08 +00:00
2011-03-15 16:08:32 +00:00
if not permstring :
2012-03-30 23:47:22 +02:00
return False
2011-03-15 16:08:32 +00:00
perm = permstring . lower ( )
if perm in [ p . lower ( ) for p in self . permissions ] :
# simplest case - we have a direct match
2012-03-30 23:47:22 +02:00
return True
2012-03-31 13:06:29 +02:00
if perm in _PERMISSION_HIERARCHY :
2011-03-15 16:08:32 +00:00
# check if we have a higher hierarchy position
2012-03-31 13:06:29 +02:00
ppos = _PERMISSION_HIERARCHY . index ( perm )
return any ( True for hpos , hperm in enumerate ( _PERMISSION_HIERARCHY )
2011-03-15 16:08:32 +00:00
if hperm in [ p . lower ( ) for p in self . permissions ] and hpos > ppos )
2012-03-30 23:47:22 +02:00
return False
2012-04-15 21:46:43 +02:00
2012-09-29 16:02:43 +02:00
2012-04-15 21:46:43 +02:00
def flush_from_cache ( self ) :
"""
Flush this object instance from cache , forcing an object reload . Note that this
will kill all temporary attributes on this object since it will be recreated
as a new Typeclass instance .
"""
self . __class__ . flush_cached_instance ( self )
2013-07-08 18:13:21 +02:00
2013-07-12 11:12:21 +02:00
# connect to attribute cache signal
2013-07-11 09:51:52 +02:00
m2m_changed . connect ( post_attr_update , sender = TypedObject . db_attributes . through )