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:
parent
19538ff00b
commit
45941e0c69
6 changed files with 76 additions and 33 deletions
|
|
@ -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.
|
||||||
"""
|
"""
|
||||||
test1 = re.sub(r'\s', '', raw_string) # remove all whitespace inside it
|
if not VERBOSE:
|
||||||
test2 = "%s/åäö öäö;-:$£@*~^' 'test" % raw_string # inserting weird characters in call
|
# only mangle if not VERBOSE, to make fewer return lines
|
||||||
test3 = "%s %s" % (raw_string, raw_string) # multiple calls
|
test1 = re.sub(r'\s', '', raw_string) # remove all whitespace inside it
|
||||||
self.char1.execute_cmd(test1)
|
test2 = "%s/åäö öäö;-:$£@*~^' 'test" % raw_string # inserting weird characters in call
|
||||||
self.char1.execute_cmd(test2)
|
test3 = "%s %s" % (raw_string, raw_string) # multiple calls
|
||||||
self.char1.execute_cmd(test3)
|
self.char1.execute_cmd(test1)
|
||||||
|
self.char1.execute_cmd(test2)
|
||||||
|
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)
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue