Implemented typeclass deleting; you can now do del obj.testval and expect the underlying attribute to be safely deleted from the database. Also fixed some reference errors when assigning to db/ndb properties on objects. Resolves issue 116. Fixed a bug in the command-testing system, so the few command tests that are defined should all work now.

This commit is contained in:
Griatch 2011-02-05 18:06:18 +00:00
parent 19538ff00b
commit 45941e0c69
6 changed files with 76 additions and 33 deletions

View file

@ -63,8 +63,8 @@ class CommandTest(TestCase):
self.char1.player.user.is_superuser = True self.char1.player.user.is_superuser = True
sess = FakeSession() sess = FakeSession()
sess.connectionMade() sess.connectionMade()
sess.login(self.char1.player) sess.session_login(self.char1.player)
# create second player and some objects
self.char2 = create.create_object(settings.BASE_CHARACTER_TYPECLASS, key="char2", location=self.room1) self.char2 = create.create_object(settings.BASE_CHARACTER_TYPECLASS, key="char2", location=self.room1)
self.obj1 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj1", location=self.room1) self.obj1 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj1", location=self.room1)
self.obj2 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj2", location=self.room1) self.obj2 = create.create_object(settings.BASE_OBJECT_TYPECLASS, key="obj2", location=self.room1)
@ -90,12 +90,15 @@ class CommandTest(TestCase):
This also mangles the input in various ways to test if the command This also mangles the input in various ways to test if the command
will be fooled. will be fooled.
""" """
if not VERBOSE:
# only mangle if not VERBOSE, to make fewer return lines
test1 = re.sub(r'\s', '', raw_string) # remove all whitespace inside it test1 = re.sub(r'\s', '', raw_string) # remove all whitespace inside it
test2 = "%s/åäö öäö;-:$£@*~^' 'test" % raw_string # inserting weird characters in call test2 = "%s/åäö öäö;-:$£@*~^' 'test" % raw_string # inserting weird characters in call
test3 = "%s %s" % (raw_string, raw_string) # multiple calls test3 = "%s %s" % (raw_string, raw_string) # multiple calls
self.char1.execute_cmd(test1) self.char1.execute_cmd(test1)
self.char1.execute_cmd(test2) self.char1.execute_cmd(test2)
self.char1.execute_cmd(test3) self.char1.execute_cmd(test3)
# actual call
self.char1.execute_cmd(raw_string) self.char1.execute_cmd(raw_string)
#------------------------------------------------------------ #------------------------------------------------------------
@ -104,6 +107,7 @@ class CommandTest(TestCase):
class TestHome(CommandTest): class TestHome(CommandTest):
def test_call(self): def test_call(self):
self.char1.location = self.room1
self.char1.home = self.room2 self.char1.home = self.room2
self.execute_cmd("home") self.execute_cmd("home")
self.assertEqual(self.char1.location, self.room2) self.assertEqual(self.char1.location, self.room2)

View file

@ -133,7 +133,7 @@ class SessionBase(object):
SESSIONS.add_loggedin_session(self) SESSIONS.add_loggedin_session(self)
#call hook #call hook
self.at_login() self.at_login(player)
def session_disconnect(self): def session_disconnect(self):
""" """

View file

@ -90,12 +90,12 @@ class TelnetProtocol(StatefulTelnetProtocol, session.Session):
string = ansi.parse_ansi(screen.text) string = ansi.parse_ansi(screen.text)
self.at_data_out(string) self.at_data_out(string)
def at_login(self): def at_login(self, player):
""" """
Called after authentication. self.logged_in=True at this point. Called after authentication. self.logged_in=True at this point.
""" """
if self.player.has_attribute('telnet_markup'): if player.has_attribute('telnet_markup'):
self.telnet_markup = self.player.get_attribute("telnet_markup") self.telnet_markup = player.get_attribute("telnet_markup")
else: else:
self.telnet_markup = True self.telnet_markup = True

View file

@ -231,12 +231,12 @@ class WebClientSession(session.Session):
#string = parse_html(screen.text) #string = parse_html(screen.text)
self.at_data_out(screen.text) self.at_data_out(screen.text)
def at_login(self): def at_login(self, player):
""" """
Called after authentication. self.logged_in=True at this point. Called after authentication. self.logged_in=True at this point.
""" """
if self.player.has_attribute('telnet_markup'): if player.has_attribute('telnet_markup'):
self.telnet_markup = self.player.get_attribute("telnet_markup") self.telnet_markup = player.get_attribute("telnet_markup")
else: else:
self.telnet_markup = True self.telnet_markup = True

View file

@ -25,7 +25,6 @@ try:
except AttributeError: except AttributeError:
FULL_PERSISTENCE = True FULL_PERSISTENCE = True
class MetaTypeClass(type): class MetaTypeClass(type):
""" """
This metaclass just makes sure the class object gets This metaclass just makes sure the class object gets
@ -117,12 +116,14 @@ class TypeClass(object):
except AttributeError: except AttributeError:
try: try:
if FULL_PERSISTENCE and propname != 'ndb': if FULL_PERSISTENCE and propname != 'ndb':
db = object.__getattribute__(dbobj, 'db') if not dbobj.has_attribute(propname):
value = object.__getattribute__(db, propname) raise AttributeError
else:
value = dbobj.get_attribute(propname)
else: else:
# Not FULL_PERSISTENCE # Not FULL_PERSISTENCE
ndb = object.__getattribute__(dbobj, 'ndb') ndb = object.__getattribute__(dbobj, 'ndb')
value = object.__getattribute__(ndb, propname) value = getattr(ndb, propname)
return value return value
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."
@ -157,17 +158,14 @@ class TypeClass(object):
if dbobj: # and hasattr(dbobj, propname): if dbobj: # and hasattr(dbobj, propname):
#print " ---> dbobj" #print " ---> dbobj"
if hasattr(dbobj, propname): if hasattr(dbobj, propname):
# if attr already exists on dbobj, assign to it. # only if attr already exists on dbobj, assign to it.
object.__setattr__(dbobj, propname, value) object.__setattr__(dbobj, propname, value)
elif FULL_PERSISTENCE: elif FULL_PERSISTENCE:
#print "full __setattr__1", propname dbobj.set_attribute(propname, value)
db = object.__getattribute__(dbobj, 'db')
#print "full __setattr__2", propname
object.__setattr__(db, propname, value)
else: else:
# not FULL_PERSISTENCE # not FULL_PERSISTENCE
ndb = object.__getattribute__(dbobj, 'ndb') ndb = object.__getattribute__(dbobj, 'ndb')
object.__setattr__(ndb, propname, value) setattr(ndb, propname, value)
else: else:
object.__setattr__(self, propname, value) object.__setattr__(self, propname, value)
@ -180,6 +178,47 @@ class TypeClass(object):
else: else:
return other == self or other == self.dbobj return other == self or other == self.dbobj
def __delattr__(self, propname):
"""
Transparently deletes data from the typeclass or dbobj by first searching on the typeclass,
secondly on the dbobj.db or ndb depending on FULL_PERSISTENCE setting.
Will not allow deletion of properties stored directly on dbobj.
"""
try:
protected = object.__getattribute__(self, '_protected_attrs')
except AttributeError:
protected = PROTECTED
logger.log_trace("Thiis is probably due to an unsafe reload.")
if propname in protected:
string = "%s: '%s' is a protected attribute name."
string += " (protected: [%s])" % (", ".join(protected))
logger.log_errmsg(string % (self.name, propname))
else:
try:
object.__delattr__(self, propname)
except AttributeError:
# not on typeclass, try to delete on db/ndb
try:
dbobj = object.__getattribute__(self, 'dbobj')
except AttributeError:
logger.log_trace("This is probably due to an unsafe reload.")
return # ignore delete
try:
if FULL_PERSISTENCE:
if not dbobj.has_attribute(propname):
raise AttributeError
dbobj.del_attribute(propname)
else:
ndb = object.__getattribute__(dbobj, 'ndb')
ndb.__delattr__(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"
return self.key return self.key