Made objects clear more handlers on deletion, also scramble some methods and all database access wrappers to avoid an object memory instance being accessed after it has been deleted. See #509.
This commit is contained in:
parent
81aa43933c
commit
3a6a8d5c48
3 changed files with 34 additions and 4 deletions
|
|
@ -542,7 +542,7 @@ class CmdDestroy(MuxCommand):
|
||||||
# do the deletion
|
# do the deletion
|
||||||
okay = obj.delete()
|
okay = obj.delete()
|
||||||
if not okay:
|
if not okay:
|
||||||
string += "\nERROR: %s not deleted, probably because at_obj_delete() returned False." % objname
|
string += "\nERROR: %s not deleted, probably because delete() returned False." % objname
|
||||||
else:
|
else:
|
||||||
string += "\n%s was destroyed." % objname
|
string += "\n%s was destroyed." % objname
|
||||||
if had_exits:
|
if had_exits:
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import traceback
|
||||||
import weakref
|
import weakref
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
@ -1183,15 +1184,36 @@ class TypedObject(SharedMemoryModel):
|
||||||
if hperm in perms and hpos > ppos)
|
if hperm in perms and hpos > ppos)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
#
|
||||||
|
# Deletion methods
|
||||||
|
#
|
||||||
|
|
||||||
|
def _deleted(self, *args, **kwargs):
|
||||||
|
"Scrambling method for already deleted objects"
|
||||||
|
raise ObjectDoesNotExist("This object was already deleted!")
|
||||||
|
|
||||||
|
_is_deleted = False # this is checked by db_* wrappers
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"Cleaning up handlers on the typeclass level"
|
"Cleaning up handlers on the typeclass level"
|
||||||
global TICKER_HANDLER
|
global TICKER_HANDLER
|
||||||
if not TICKER_HANDLER:
|
if not TICKER_HANDLER:
|
||||||
from src.scripts.tickerhandler import TICKER_HANDLER
|
from src.scripts.tickerhandler import TICKER_HANDLER
|
||||||
TICKER_HANDLER.remove(self) # removes all ticker subscriptions
|
TICKER_HANDLER.remove(self) # removes objects' all ticker subscriptions
|
||||||
_GA(self, "permissions").clear()
|
if not isinstance(_GA(self, "permissions"), LazyLoadHandler):
|
||||||
|
_GA(self, "permissions").clear()
|
||||||
|
if not isinstance(_GA(self, "attributes"), LazyLoadHandler):
|
||||||
|
_GA(self, "attributes").clear()
|
||||||
|
if not isinstance(_GA(self, "aliases"), LazyLoadHandler):
|
||||||
|
_GA(self, "aliases").clear()
|
||||||
|
if not isinstance(_GA(self, "nicks"), LazyLoadHandler):
|
||||||
|
_GA(self, "nicks").clear()
|
||||||
_SA(self, "_cached_typeclass", None)
|
_SA(self, "_cached_typeclass", None)
|
||||||
_GA(self, "flush_from_cache")()
|
_GA(self, "flush_from_cache")()
|
||||||
|
|
||||||
|
# scrambling properties
|
||||||
|
self.delete = self._deleted
|
||||||
|
self._is_deleted = True
|
||||||
super(TypedObject, self).delete()
|
super(TypedObject, self).delete()
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -86,9 +86,13 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
def _get(cls, fname):
|
def _get(cls, fname):
|
||||||
"Wrapper for getting database field"
|
"Wrapper for getting database field"
|
||||||
#print "_get:", fieldname, wrappername,_GA(cls,fieldname)
|
#print "_get:", fieldname, wrappername,_GA(cls,fieldname)
|
||||||
|
if _GA(cls, "_is_deleted"):
|
||||||
|
raise ObjectDoesNotExist("Cannot access %s: Hosting object was already deleted." % fname)
|
||||||
return _GA(cls, fieldname)
|
return _GA(cls, fieldname)
|
||||||
def _get_foreign(cls, fname):
|
def _get_foreign(cls, fname):
|
||||||
"Wrapper for returing foreignkey fields"
|
"Wrapper for returing foreignkey fields"
|
||||||
|
if _GA(cls, "_is_deleted"):
|
||||||
|
raise ObjectDoesNotExist("Cannot access %s: Hosting object was already deleted." % fname)
|
||||||
value = _GA(cls, fieldname)
|
value = _GA(cls, fieldname)
|
||||||
#print "_get_foreign:value:", value
|
#print "_get_foreign:value:", value
|
||||||
try:
|
try:
|
||||||
|
|
@ -100,6 +104,8 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
raise FieldError("Field %s cannot be edited." % fname)
|
raise FieldError("Field %s cannot be edited." % fname)
|
||||||
def _set(cls, fname, value):
|
def _set(cls, fname, value):
|
||||||
"Wrapper for setting database field"
|
"Wrapper for setting database field"
|
||||||
|
if _GA(cls, "_is_deleted"):
|
||||||
|
raise ObjectDoesNotExist("Cannot set %s to %s: Hosting object was already deleted!" % (fname, value))
|
||||||
_SA(cls, fname, value)
|
_SA(cls, fname, value)
|
||||||
# only use explicit update_fields in save if we actually have a
|
# only use explicit update_fields in save if we actually have a
|
||||||
# primary key assigned already (won't be set when first creating object)
|
# primary key assigned already (won't be set when first creating object)
|
||||||
|
|
@ -107,6 +113,8 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
_GA(cls, "save")(update_fields=update_fields)
|
_GA(cls, "save")(update_fields=update_fields)
|
||||||
def _set_foreign(cls, fname, value):
|
def _set_foreign(cls, fname, value):
|
||||||
"Setter only used on foreign key relations, allows setting with #dbref"
|
"Setter only used on foreign key relations, allows setting with #dbref"
|
||||||
|
if _GA(cls, "_is_deleted"):
|
||||||
|
raise ObjectDoesNotExist("Cannot set %s to %s: Hosting object was already deleted!" % (fname, value))
|
||||||
try:
|
try:
|
||||||
value = _GA(value, "dbobj")
|
value = _GA(value, "dbobj")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
@ -348,7 +356,7 @@ def flush_cached_instance(sender, instance, **kwargs):
|
||||||
# XXX: Is this the best way to make sure we can flush?
|
# XXX: Is this the best way to make sure we can flush?
|
||||||
if not hasattr(instance, 'flush_cached_instance'):
|
if not hasattr(instance, 'flush_cached_instance'):
|
||||||
return
|
return
|
||||||
sender.flush_cached_instance(instance)
|
sender.flush_cached_instance(instance, force=True)
|
||||||
pre_delete.connect(flush_cached_instance)
|
pre_delete.connect(flush_cached_instance)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue