Fix nesting errors when serializing hidden dbobjs with de/serializer magic methods

This commit is contained in:
Griatch 2022-07-14 00:01:18 +02:00
parent ae21036a34
commit 2fd8afa2fa
2 changed files with 50 additions and 10 deletions

View file

@ -18,19 +18,20 @@ in-situ, e.g `obj.db.mynestedlist[3][5] = 3` would never be saved and
be out of sync with the database. be out of sync with the database.
""" """
from collections import OrderedDict, defaultdict, deque
from collections.abc import MutableMapping, MutableSequence, MutableSet
from functools import update_wrapper from functools import update_wrapper
from collections import deque, OrderedDict, defaultdict
from collections.abc import MutableSequence, MutableSet, MutableMapping
try: try:
from pickle import dumps, loads, UnpicklingError from pickle import UnpicklingError, dumps, loads
except ImportError: except ImportError:
from pickle import dumps, loads from pickle import dumps, loads
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.utils.safestring import SafeString from django.utils.safestring import SafeString
from evennia.utils.utils import uses_database, is_iter, to_bytes
from evennia.utils import logger from evennia.utils import logger
from evennia.utils.utils import is_iter, to_bytes, uses_database
__all__ = ("to_pickle", "from_pickle", "do_pickle", "do_unpickle", "dbserialize", "dbunserialize") __all__ = ("to_pickle", "from_pickle", "do_pickle", "do_unpickle", "dbserialize", "dbunserialize")
@ -786,6 +787,13 @@ def from_pickle(data, db_obj=None):
dat = _SaverList(_parent=parent) dat = _SaverList(_parent=parent)
dat._data.extend(process_tree(val, dat) for val in item) dat._data.extend(process_tree(val, dat) for val in item)
return dat return dat
if hasattr(item, "__deserialize_dbobjs__"):
try:
item.__deserialize_dbobjs__()
except (TypeError, UnpicklingError):
pass
return item return item
if db_obj: if db_obj:

View file

@ -2,10 +2,11 @@
Tests for dbserialize module Tests for dbserialize module
""" """
from collections import deque from collections import defaultdict, deque
from django.test import TestCase from django.test import TestCase
from evennia.utils import dbserialize
from evennia.objects.objects import DefaultObject from evennia.objects.objects import DefaultObject
from evennia.utils import dbserialize
from parameterized import parameterized from parameterized import parameterized
@ -93,8 +94,6 @@ class TestDbSerialize(TestCase):
self.assertEqual(self.obj.db.test, {"a": True, "b": False}) self.assertEqual(self.obj.db.test, {"a": True, "b": False})
def test_defaultdict(self): def test_defaultdict(self):
from collections import defaultdict
# baseline behavior for a defaultdict # baseline behavior for a defaultdict
_dd = defaultdict(list) _dd = defaultdict(list)
_dd["a"] _dd["a"]
@ -164,12 +163,45 @@ class DbObjWrappers(TestCase):
con = _ValidContainer(self.dbobj2) con = _ValidContainer(self.dbobj2)
self.dbobj1.db.testarg = con self.dbobj1.db.testarg = con
# accessing the same data twice # accessing the same data multiple times
res1 = self.dbobj1.db.testarg res1 = self.dbobj1.db.testarg
res2 = self.dbobj1.db.testarg res2 = self.dbobj1.db.testarg
res3 = self.dbobj1.db.testarg
self.assertEqual(res1, res2) self.assertEqual(res1, res2)
self.assertEqual(res1, res3)
self.assertEqual(res1, con) self.assertEqual(res1, con)
self.assertEqual(res2, con) self.assertEqual(res2, con)
self.assertEqual(res1.hidden_obj, self.dbobj2) self.assertEqual(res1.hidden_obj, self.dbobj2)
self.assertEqual(res2.hidden_obj, self.dbobj2) self.assertEqual(res2.hidden_obj, self.dbobj2)
self.assertEqual(res3.hidden_obj, self.dbobj2)
def test_dbobj_hidden_dict(self):
con1 = _ValidContainer(self.dbobj2)
con2 = _ValidContainer(self.dbobj2)
self.dbobj1.db.dict = {}
self.dbobj1.db.dict["key1"] = con1
self.dbobj1.db.dict["key2"] = con2
self.assertEqual(self.dbobj1.db.dict["key1"].hidden_obj, self.dbobj2)
self.assertEqual(self.dbobj1.db.dict["key1"].hidden_obj, self.dbobj2)
self.assertEqual(self.dbobj1.db.dict["key2"].hidden_obj, self.dbobj2)
self.assertEqual(self.dbobj1.db.dict["key2"].hidden_obj, self.dbobj2)
def test_dbobj_hidden_defaultdict(self):
con1 = _ValidContainer(self.dbobj2)
con2 = _ValidContainer(self.dbobj2)
self.dbobj1.db.dfdict = defaultdict(dict)
self.dbobj1.db.dfdict["key"]["con1"] = con1
self.dbobj1.db.dfdict["key"]["con2"] = con2
self.assertEqual(self.dbobj1.db.dfdict["key"]["con1"].hidden_obj, self.dbobj2)
self.assertEqual(self.dbobj1.db.dfdict["key"]["con1"].hidden_obj, self.dbobj2)
self.assertEqual(self.dbobj1.db.dfdict["key"]["con2"].hidden_obj, self.dbobj2)
self.assertEqual(self.dbobj1.db.dfdict["key"]["con2"].hidden_obj, self.dbobj2)