Fix bug in 'Attribute-with-hidden-object' deserializer

This commit is contained in:
Griatch 2022-06-12 00:00:12 +02:00
parent f8e29f6f10
commit ff877b9671
2 changed files with 64 additions and 8 deletions

View file

@ -23,7 +23,7 @@ from collections import deque, OrderedDict, defaultdict
from collections.abc import MutableSequence, MutableSet, MutableMapping
try:
from pickle import dumps, loads
from pickle import dumps, loads, UnpicklingError
except ImportError:
from pickle import dumps, loads
from django.core.exceptions import ObjectDoesNotExist
@ -633,12 +633,12 @@ def to_pickle(data):
# not one of the base types
if hasattr(item, "__serialize_dbobjs__"):
# Allows custom serialization of any dbobjects embedded in
# the item that Evennia will otherwise not found (these would
# the item that Evennia will otherwise not find (these would
# otherwise lead to an error). Use the dbserialize helper from
# this method.
try:
item.__serialize_dbobjs__()
except TypeError:
except TypeError as err:
# we catch typerrors so we can handle both classes (requiring
# classmethods) and instances
pass
@ -725,9 +725,13 @@ def from_pickle(data, db_obj=None):
# use the dbunserialize helper in this module.
try:
item.__deserialize_dbobjs__()
except TypeError:
except (TypeError, UnpicklingError):
# handle recoveries both of classes (requiring classmethods
# or instances
# or instances. Unpickling errors can happen when re-loading the
# data from cache (because the hidden entity was already
# deserialized and stored back on the object, unpickling it
# again fails). TODO: Maybe one could avoid this retry in a
# more graceful way?
pass
return item

View file

@ -15,9 +15,7 @@ class TestDbSerialize(TestCase):
"""
def setUp(self):
self.obj = DefaultObject(
db_key="Tester",
)
self.obj = DefaultObject(db_key="Tester")
self.obj.save()
def test_constants(self):
@ -117,3 +115,57 @@ class TestDbSerialize(TestCase):
self.assertEqual(self.obj.db.test, {"a": [1, 2, 3]})
self.obj.db.test |= {"b": [5, 6]}
self.assertEqual(self.obj.db.test, {"a": [1, 2, 3], "b": [5, 6]})
class _InvalidContainer:
"""Container not saveable in Attribute (if obj is dbobj, it 'hides' it)"""
def __init__(self, obj):
self.hidden_obj = obj
class _ValidContainer(_InvalidContainer):
"""Container possible to save in Attribute (handles hidden dbobj explicitly)"""
def __serialize_dbobjs__(self):
self.hidden_obj = dbserialize.dbserialize(self.hidden_obj)
def __deserialize_dbobjs__(self):
self.hidden_obj = dbserialize.dbunserialize(self.hidden_obj)
class DbObjWrappers(TestCase):
"""
Test the `__serialize_dbobjs__` and `__deserialize_dbobjs__` methods.
"""
def setUp(self):
super().setUp()
self.dbobj1 = DefaultObject(db_key="Tester1")
self.dbobj1.save()
self.dbobj2 = DefaultObject(db_key="Tester2")
self.dbobj2.save()
def test_dbobj_hidden_obj__fail(self):
with self.assertRaises(TypeError):
self.dbobj1.db.testarg = _InvalidContainer(self.dbobj1)
def test_consecutive_fetch(self):
con =_ValidContainer(self.dbobj2)
self.dbobj1.db.testarg = con
attrobj = self.dbobj1.attributes.get("testarg", return_obj=True)
self.assertEqual(attrobj.value, con)
self.assertEqual(attrobj.value, con)
self.assertEqual(attrobj.value.hidden_obj, self.dbobj2)
def test_dbobj_hidden_obj__success(self):
con =_ValidContainer(self.dbobj2)
self.dbobj1.db.testarg = con
# accessing the same data twice
res1 = self.dbobj1.db.testarg
res2 = self.dbobj1.db.testarg
self.assertEqual(res1, res2)
self.assertEqual(res1, con)
self.assertEqual(res2, con)
self.assertEqual(res1.hidden_obj, self.dbobj2)
self.assertEqual(res2.hidden_obj, self.dbobj2)