We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`BaseDict` does not follow `setdefault`
`mongoengine.base.datastructures.BaseDict` does not follow changes made through `setdefault`.

I have a DictField in a model:

``` python
    success_rates = DictField()
```

I update the field using `serdefault` and the changes are not saved:

``` python
                user_state.success_rates.setdefault(topic_area_group_id, {}).setdefault(
                    task_date, {})[str(task_id)] = action.data['is_solved']
```

I currently do this:

``` python
                user_state._changed_fields.append('success_rates')
```

`BaseDict` does not follow `setdefault`
`mongoengine.base.datastructures.BaseDict` does not follow changes made through `setdefault`.

I have a DictField in a model:

``` python
    success_rates = DictField()
```

I update the field using `serdefault` and the changes are not saved:

``` python
                user_state.success_rates.setdefault(topic_area_group_id, {}).setdefault(
                    task_date, {})[str(task_id)] = action.data['is_solved']
```

I currently do this:

``` python
                user_state._changed_fields.append('success_rates')
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mongoengine/base/datastructures.py`
Content:
```
1 import weakref
2 import functools
3 import itertools
4 from mongoengine.common import _import_class
5 
6 __all__ = ("BaseDict", "BaseList")
7 
8 
9 class BaseDict(dict):
10     """A special dict so we can watch any changes"""
11 
12     _dereferenced = False
13     _instance = None
14     _name = None
15 
16     def __init__(self, dict_items, instance, name):
17         Document = _import_class('Document')
18         EmbeddedDocument = _import_class('EmbeddedDocument')
19 
20         if isinstance(instance, (Document, EmbeddedDocument)):
21             self._instance = weakref.proxy(instance)
22         self._name = name
23         return super(BaseDict, self).__init__(dict_items)
24 
25     def __getitem__(self, key, *args, **kwargs):
26         value = super(BaseDict, self).__getitem__(key)
27 
28         EmbeddedDocument = _import_class('EmbeddedDocument')
29         if isinstance(value, EmbeddedDocument) and value._instance is None:
30             value._instance = self._instance
31         elif not isinstance(value, BaseDict) and isinstance(value, dict):
32             value = BaseDict(value, None, '%s.%s' % (self._name, key))
33             super(BaseDict, self).__setitem__(key, value)
34             value._instance = self._instance
35         elif not isinstance(value, BaseList) and isinstance(value, list):
36             value = BaseList(value, None, '%s.%s' % (self._name, key))
37             super(BaseDict, self).__setitem__(key, value)
38             value._instance = self._instance
39         return value
40 
41     def __setitem__(self, key, value, *args, **kwargs):
42         self._mark_as_changed(key)
43         return super(BaseDict, self).__setitem__(key, value)
44 
45     def __delete__(self, *args, **kwargs):
46         self._mark_as_changed()
47         return super(BaseDict, self).__delete__(*args, **kwargs)
48 
49     def __delitem__(self, key, *args, **kwargs):
50         self._mark_as_changed(key)
51         return super(BaseDict, self).__delitem__(key)
52 
53     def __delattr__(self, key, *args, **kwargs):
54         self._mark_as_changed(key)
55         return super(BaseDict, self).__delattr__(key)
56 
57     def __getstate__(self):
58         self.instance = None
59         self._dereferenced = False
60         return self
61 
62     def __setstate__(self, state):
63         self = state
64         return self
65 
66     def clear(self, *args, **kwargs):
67         self._mark_as_changed()
68         return super(BaseDict, self).clear(*args, **kwargs)
69 
70     def pop(self, *args, **kwargs):
71         self._mark_as_changed()
72         return super(BaseDict, self).pop(*args, **kwargs)
73 
74     def popitem(self, *args, **kwargs):
75         self._mark_as_changed()
76         return super(BaseDict, self).popitem(*args, **kwargs)
77 
78     def update(self, *args, **kwargs):
79         self._mark_as_changed()
80         return super(BaseDict, self).update(*args, **kwargs)
81 
82     def _mark_as_changed(self, key=None):
83         if hasattr(self._instance, '_mark_as_changed'):
84             if key:
85                 self._instance._mark_as_changed('%s.%s' % (self._name, key))
86             else:
87                 self._instance._mark_as_changed(self._name)
88 
89 
90 class BaseList(list):
91     """A special list so we can watch any changes
92     """
93 
94     _dereferenced = False
95     _instance = None
96     _name = None
97 
98     def __init__(self, list_items, instance, name):
99         Document = _import_class('Document')
100         EmbeddedDocument = _import_class('EmbeddedDocument')
101 
102         if isinstance(instance, (Document, EmbeddedDocument)):
103             self._instance = weakref.proxy(instance)
104         self._name = name
105         return super(BaseList, self).__init__(list_items)
106 
107     def __getitem__(self, key, *args, **kwargs):
108         value = super(BaseList, self).__getitem__(key)
109 
110         EmbeddedDocument = _import_class('EmbeddedDocument')
111         if isinstance(value, EmbeddedDocument) and value._instance is None:
112             value._instance = self._instance
113         elif not isinstance(value, BaseDict) and isinstance(value, dict):
114             value = BaseDict(value, None, '%s.%s' % (self._name, key))
115             super(BaseList, self).__setitem__(key, value)
116             value._instance = self._instance
117         elif not isinstance(value, BaseList) and isinstance(value, list):
118             value = BaseList(value, None, '%s.%s' % (self._name, key))
119             super(BaseList, self).__setitem__(key, value)
120             value._instance = self._instance
121         return value
122 
123     def __setitem__(self, key, value, *args, **kwargs):
124         if isinstance(key, slice):
125             self._mark_as_changed()
126         else:
127             self._mark_as_changed(key)
128         return super(BaseList, self).__setitem__(key, value)
129 
130     def __delitem__(self, key, *args, **kwargs):
131         if isinstance(key, slice):
132             self._mark_as_changed()
133         else:
134             self._mark_as_changed(key)
135         return super(BaseList, self).__delitem__(key)
136 
137     def __setslice__(self, *args, **kwargs):
138         self._mark_as_changed()
139         return super(BaseList, self).__setslice__(*args, **kwargs)
140 
141     def __delslice__(self, *args, **kwargs):
142         self._mark_as_changed()
143         return super(BaseList, self).__delslice__(*args, **kwargs)
144 
145     def __getstate__(self):
146         self.instance = None
147         self._dereferenced = False
148         return self
149 
150     def __setstate__(self, state):
151         self = state
152         return self
153 
154     def append(self, *args, **kwargs):
155         self._mark_as_changed()
156         return super(BaseList, self).append(*args, **kwargs)
157 
158     def extend(self, *args, **kwargs):
159         self._mark_as_changed()
160         return super(BaseList, self).extend(*args, **kwargs)
161 
162     def insert(self, *args, **kwargs):
163         self._mark_as_changed()
164         return super(BaseList, self).insert(*args, **kwargs)
165 
166     def pop(self, *args, **kwargs):
167         self._mark_as_changed()
168         return super(BaseList, self).pop(*args, **kwargs)
169 
170     def remove(self, *args, **kwargs):
171         self._mark_as_changed()
172         return super(BaseList, self).remove(*args, **kwargs)
173 
174     def reverse(self, *args, **kwargs):
175         self._mark_as_changed()
176         return super(BaseList, self).reverse(*args, **kwargs)
177 
178     def sort(self, *args, **kwargs):
179         self._mark_as_changed()
180         return super(BaseList, self).sort(*args, **kwargs)
181 
182     def _mark_as_changed(self, key=None):
183         if hasattr(self._instance, '_mark_as_changed'):
184             if key:
185                 self._instance._mark_as_changed('%s.%s' % (self._name, key))
186             else:
187                 self._instance._mark_as_changed(self._name)
188 
189 
190 class StrictDict(object):
191     __slots__ = ()
192     _special_fields = set(['get', 'pop', 'iteritems', 'items', 'keys', 'create'])
193     _classes = {}
194     def __init__(self, **kwargs):
195         for k,v in kwargs.iteritems():
196             setattr(self, k, v)
197     def __getitem__(self, key):
198         key = '_reserved_' + key if key in self._special_fields else key
199         try:
200             return getattr(self, key)
201         except AttributeError:
202             raise KeyError(key)
203     def __setitem__(self, key, value):
204         key = '_reserved_' + key if key in self._special_fields else key
205         return setattr(self, key, value)
206     def __contains__(self, key):
207         return hasattr(self, key)
208     def get(self, key, default=None):
209         try:
210             return self[key]
211         except KeyError:
212             return default
213     def pop(self, key, default=None):
214         v = self.get(key, default)
215         try:
216             delattr(self, key)
217         except AttributeError:
218             pass
219         return v
220     def iteritems(self):
221         for key in self:
222             yield key, self[key]
223     def items(self):
224         return [(k, self[k]) for k in iter(self)]
225     def keys(self):
226         return list(iter(self))
227     def __iter__(self):
228         return (key for key in self.__slots__ if hasattr(self, key))
229     def __len__(self):
230         return len(list(self.iteritems()))
231     def __eq__(self, other):
232         return self.items() == other.items()
233     def __neq__(self, other):
234         return self.items() != other.items()
235 
236     @classmethod
237     def create(cls, allowed_keys):
238         allowed_keys_tuple = tuple(('_reserved_' + k if k in cls._special_fields else k) for k in allowed_keys)
239         allowed_keys = frozenset(allowed_keys_tuple)
240         if allowed_keys not in cls._classes:
241             class SpecificStrictDict(cls):
242                 __slots__ = allowed_keys_tuple
243                 def __repr__(self):
244                     return "{%s}" % ', '.join('"{0!s}": {0!r}'.format(k,v) for (k,v) in self.iteritems())
245             cls._classes[allowed_keys] = SpecificStrictDict
246         return cls._classes[allowed_keys]
247 
248 
249 class SemiStrictDict(StrictDict):
250     __slots__ = ('_extras')
251     _classes = {}
252     def __getattr__(self, attr):
253         try:
254             super(SemiStrictDict, self).__getattr__(attr)
255         except AttributeError:
256             try:
257                 return self.__getattribute__('_extras')[attr]
258             except KeyError as e:
259                 raise AttributeError(e)
260     def __setattr__(self, attr, value):
261         try:
262             super(SemiStrictDict, self).__setattr__(attr, value)
263         except AttributeError:
264             try:
265                 self._extras[attr] = value
266             except AttributeError:
267                 self._extras = {attr: value}
268 
269     def __delattr__(self, attr):
270         try:
271             super(SemiStrictDict, self).__delattr__(attr)
272         except AttributeError:
273             try:
274                 del self._extras[attr]
275             except KeyError as e:
276                 raise AttributeError(e)
277 
278     def __iter__(self):
279         try:
280             extras_iter = iter(self.__getattribute__('_extras'))
281         except AttributeError:
282             extras_iter = ()
283         return itertools.chain(super(SemiStrictDict, self).__iter__(), extras_iter)
284
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mongoengine/base/datastructures.py b/mongoengine/base/datastructures.py
--- a/mongoengine/base/datastructures.py
+++ b/mongoengine/base/datastructures.py
@@ -75,6 +75,10 @@
         self._mark_as_changed()
         return super(BaseDict, self).popitem(*args, **kwargs)
 
+    def setdefault(self, *args, **kwargs):
+        self._mark_as_changed()
+        return super(BaseDict, self).setdefault(*args, **kwargs)
+
     def update(self, *args, **kwargs):
         self._mark_as_changed()
         return super(BaseDict, self).update(*args, **kwargs)

{"golden_diff": "diff --git a/mongoengine/base/datastructures.py b/mongoengine/base/datastructures.py\n--- a/mongoengine/base/datastructures.py\n+++ b/mongoengine/base/datastructures.py\n@@ -75,6 +75,10 @@\n         self._mark_as_changed()\n         return super(BaseDict, self).popitem(*args, **kwargs)\n \n+    def setdefault(self, *args, **kwargs):\n+        self._mark_as_changed()\n+        return super(BaseDict, self).setdefault(*args, **kwargs)\n+\n     def update(self, *args, **kwargs):\n         self._mark_as_changed()\n         return super(BaseDict, self).update(*args, **kwargs)\n", "issue": "`BaseDict` does not follow `setdefault`\n`mongoengine.base.datastructures.BaseDict` does not follow changes made through `setdefault`.\n\nI have a DictField in a model:\n\n``` python\n    success_rates = DictField()\n```\n\nI update the field using `serdefault` and the changes are not saved:\n\n``` python\n                user_state.success_rates.setdefault(topic_area_group_id, {}).setdefault(\n                    task_date, {})[str(task_id)] = action.data['is_solved']\n```\n\nI currently do this:\n\n``` python\n                user_state._changed_fields.append('success_rates')\n```\n\n`BaseDict` does not follow `setdefault`\n`mongoengine.base.datastructures.BaseDict` does not follow changes made through `setdefault`.\n\nI have a DictField in a model:\n\n``` python\n    success_rates = DictField()\n```\n\nI update the field using `serdefault` and the changes are not saved:\n\n``` python\n                user_state.success_rates.setdefault(topic_area_group_id, {}).setdefault(\n                    task_date, {})[str(task_id)] = action.data['is_solved']\n```\n\nI currently do this:\n\n``` python\n                user_state._changed_fields.append('success_rates')\n```\n\n", "before_files": [{"content": "import weakref\nimport functools\nimport itertools\nfrom mongoengine.common import _import_class\n\n__all__ = (\"BaseDict\", \"BaseList\")\n\n\nclass BaseDict(dict):\n    \"\"\"A special dict so we can watch any changes\"\"\"\n\n    _dereferenced = False\n    _instance = None\n    _name = None\n\n    def __init__(self, dict_items, instance, name):\n        Document = _import_class('Document')\n        EmbeddedDocument = _import_class('EmbeddedDocument')\n\n        if isinstance(instance, (Document, EmbeddedDocument)):\n            self._instance = weakref.proxy(instance)\n        self._name = name\n        return super(BaseDict, self).__init__(dict_items)\n\n    def __getitem__(self, key, *args, **kwargs):\n        value = super(BaseDict, self).__getitem__(key)\n\n        EmbeddedDocument = _import_class('EmbeddedDocument')\n        if isinstance(value, EmbeddedDocument) and value._instance is None:\n            value._instance = self._instance\n        elif not isinstance(value, BaseDict) and isinstance(value, dict):\n            value = BaseDict(value, None, '%s.%s' % (self._name, key))\n            super(BaseDict, self).__setitem__(key, value)\n            value._instance = self._instance\n        elif not isinstance(value, BaseList) and isinstance(value, list):\n            value = BaseList(value, None, '%s.%s' % (self._name, key))\n            super(BaseDict, self).__setitem__(key, value)\n            value._instance = self._instance\n        return value\n\n    def __setitem__(self, key, value, *args, **kwargs):\n        self._mark_as_changed(key)\n        return super(BaseDict, self).__setitem__(key, value)\n\n    def __delete__(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).__delete__(*args, **kwargs)\n\n    def __delitem__(self, key, *args, **kwargs):\n        self._mark_as_changed(key)\n        return super(BaseDict, self).__delitem__(key)\n\n    def __delattr__(self, key, *args, **kwargs):\n        self._mark_as_changed(key)\n        return super(BaseDict, self).__delattr__(key)\n\n    def __getstate__(self):\n        self.instance = None\n        self._dereferenced = False\n        return self\n\n    def __setstate__(self, state):\n        self = state\n        return self\n\n    def clear(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).clear(*args, **kwargs)\n\n    def pop(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).pop(*args, **kwargs)\n\n    def popitem(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).popitem(*args, **kwargs)\n\n    def update(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).update(*args, **kwargs)\n\n    def _mark_as_changed(self, key=None):\n        if hasattr(self._instance, '_mark_as_changed'):\n            if key:\n                self._instance._mark_as_changed('%s.%s' % (self._name, key))\n            else:\n                self._instance._mark_as_changed(self._name)\n\n\nclass BaseList(list):\n    \"\"\"A special list so we can watch any changes\n    \"\"\"\n\n    _dereferenced = False\n    _instance = None\n    _name = None\n\n    def __init__(self, list_items, instance, name):\n        Document = _import_class('Document')\n        EmbeddedDocument = _import_class('EmbeddedDocument')\n\n        if isinstance(instance, (Document, EmbeddedDocument)):\n            self._instance = weakref.proxy(instance)\n        self._name = name\n        return super(BaseList, self).__init__(list_items)\n\n    def __getitem__(self, key, *args, **kwargs):\n        value = super(BaseList, self).__getitem__(key)\n\n        EmbeddedDocument = _import_class('EmbeddedDocument')\n        if isinstance(value, EmbeddedDocument) and value._instance is None:\n            value._instance = self._instance\n        elif not isinstance(value, BaseDict) and isinstance(value, dict):\n            value = BaseDict(value, None, '%s.%s' % (self._name, key))\n            super(BaseList, self).__setitem__(key, value)\n            value._instance = self._instance\n        elif not isinstance(value, BaseList) and isinstance(value, list):\n            value = BaseList(value, None, '%s.%s' % (self._name, key))\n            super(BaseList, self).__setitem__(key, value)\n            value._instance = self._instance\n        return value\n\n    def __setitem__(self, key, value, *args, **kwargs):\n        if isinstance(key, slice):\n            self._mark_as_changed()\n        else:\n            self._mark_as_changed(key)\n        return super(BaseList, self).__setitem__(key, value)\n\n    def __delitem__(self, key, *args, **kwargs):\n        if isinstance(key, slice):\n            self._mark_as_changed()\n        else:\n            self._mark_as_changed(key)\n        return super(BaseList, self).__delitem__(key)\n\n    def __setslice__(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).__setslice__(*args, **kwargs)\n\n    def __delslice__(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).__delslice__(*args, **kwargs)\n\n    def __getstate__(self):\n        self.instance = None\n        self._dereferenced = False\n        return self\n\n    def __setstate__(self, state):\n        self = state\n        return self\n\n    def append(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).append(*args, **kwargs)\n\n    def extend(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).extend(*args, **kwargs)\n\n    def insert(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).insert(*args, **kwargs)\n\n    def pop(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).pop(*args, **kwargs)\n\n    def remove(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).remove(*args, **kwargs)\n\n    def reverse(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).reverse(*args, **kwargs)\n\n    def sort(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).sort(*args, **kwargs)\n\n    def _mark_as_changed(self, key=None):\n        if hasattr(self._instance, '_mark_as_changed'):\n            if key:\n                self._instance._mark_as_changed('%s.%s' % (self._name, key))\n            else:\n                self._instance._mark_as_changed(self._name)\n\n\nclass StrictDict(object):\n    __slots__ = ()\n    _special_fields = set(['get', 'pop', 'iteritems', 'items', 'keys', 'create'])\n    _classes = {}\n    def __init__(self, **kwargs):\n        for k,v in kwargs.iteritems():\n            setattr(self, k, v)\n    def __getitem__(self, key):\n        key = '_reserved_' + key if key in self._special_fields else key\n        try:\n            return getattr(self, key)\n        except AttributeError:\n            raise KeyError(key)\n    def __setitem__(self, key, value):\n        key = '_reserved_' + key if key in self._special_fields else key\n        return setattr(self, key, value)\n    def __contains__(self, key):\n        return hasattr(self, key)\n    def get(self, key, default=None):\n        try:\n            return self[key]\n        except KeyError:\n            return default\n    def pop(self, key, default=None):\n        v = self.get(key, default)\n        try:\n            delattr(self, key)\n        except AttributeError:\n            pass\n        return v\n    def iteritems(self):\n        for key in self:\n            yield key, self[key]\n    def items(self):\n        return [(k, self[k]) for k in iter(self)]\n    def keys(self):\n        return list(iter(self))\n    def __iter__(self):\n        return (key for key in self.__slots__ if hasattr(self, key))\n    def __len__(self):\n        return len(list(self.iteritems()))\n    def __eq__(self, other):\n        return self.items() == other.items()\n    def __neq__(self, other):\n        return self.items() != other.items()\n\n    @classmethod\n    def create(cls, allowed_keys):\n        allowed_keys_tuple = tuple(('_reserved_' + k if k in cls._special_fields else k) for k in allowed_keys)\n        allowed_keys = frozenset(allowed_keys_tuple)\n        if allowed_keys not in cls._classes:\n            class SpecificStrictDict(cls):\n                __slots__ = allowed_keys_tuple\n                def __repr__(self):\n                    return \"{%s}\" % ', '.join('\"{0!s}\": {0!r}'.format(k,v) for (k,v) in self.iteritems())\n            cls._classes[allowed_keys] = SpecificStrictDict\n        return cls._classes[allowed_keys]\n\n\nclass SemiStrictDict(StrictDict):\n    __slots__ = ('_extras')\n    _classes = {}\n    def __getattr__(self, attr):\n        try:\n            super(SemiStrictDict, self).__getattr__(attr)\n        except AttributeError:\n            try:\n                return self.__getattribute__('_extras')[attr]\n            except KeyError as e:\n                raise AttributeError(e)\n    def __setattr__(self, attr, value):\n        try:\n            super(SemiStrictDict, self).__setattr__(attr, value)\n        except AttributeError:\n            try:\n                self._extras[attr] = value\n            except AttributeError:\n                self._extras = {attr: value}\n\n    def __delattr__(self, attr):\n        try:\n            super(SemiStrictDict, self).__delattr__(attr)\n        except AttributeError:\n            try:\n                del self._extras[attr]\n            except KeyError as e:\n                raise AttributeError(e)\n\n    def __iter__(self):\n        try:\n            extras_iter = iter(self.__getattribute__('_extras'))\n        except AttributeError:\n            extras_iter = ()\n        return itertools.chain(super(SemiStrictDict, self).__iter__(), extras_iter)\n", "path": "mongoengine/base/datastructures.py"}], "after_files": [{"content": "import weakref\nimport functools\nimport itertools\nfrom mongoengine.common import _import_class\n\n__all__ = (\"BaseDict\", \"BaseList\")\n\n\nclass BaseDict(dict):\n    \"\"\"A special dict so we can watch any changes\"\"\"\n\n    _dereferenced = False\n    _instance = None\n    _name = None\n\n    def __init__(self, dict_items, instance, name):\n        Document = _import_class('Document')\n        EmbeddedDocument = _import_class('EmbeddedDocument')\n\n        if isinstance(instance, (Document, EmbeddedDocument)):\n            self._instance = weakref.proxy(instance)\n        self._name = name\n        return super(BaseDict, self).__init__(dict_items)\n\n    def __getitem__(self, key, *args, **kwargs):\n        value = super(BaseDict, self).__getitem__(key)\n\n        EmbeddedDocument = _import_class('EmbeddedDocument')\n        if isinstance(value, EmbeddedDocument) and value._instance is None:\n            value._instance = self._instance\n        elif not isinstance(value, BaseDict) and isinstance(value, dict):\n            value = BaseDict(value, None, '%s.%s' % (self._name, key))\n            super(BaseDict, self).__setitem__(key, value)\n            value._instance = self._instance\n        elif not isinstance(value, BaseList) and isinstance(value, list):\n            value = BaseList(value, None, '%s.%s' % (self._name, key))\n            super(BaseDict, self).__setitem__(key, value)\n            value._instance = self._instance\n        return value\n\n    def __setitem__(self, key, value, *args, **kwargs):\n        self._mark_as_changed(key)\n        return super(BaseDict, self).__setitem__(key, value)\n\n    def __delete__(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).__delete__(*args, **kwargs)\n\n    def __delitem__(self, key, *args, **kwargs):\n        self._mark_as_changed(key)\n        return super(BaseDict, self).__delitem__(key)\n\n    def __delattr__(self, key, *args, **kwargs):\n        self._mark_as_changed(key)\n        return super(BaseDict, self).__delattr__(key)\n\n    def __getstate__(self):\n        self.instance = None\n        self._dereferenced = False\n        return self\n\n    def __setstate__(self, state):\n        self = state\n        return self\n\n    def clear(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).clear(*args, **kwargs)\n\n    def pop(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).pop(*args, **kwargs)\n\n    def popitem(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).popitem(*args, **kwargs)\n\n    def setdefault(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).setdefault(*args, **kwargs)\n\n    def update(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseDict, self).update(*args, **kwargs)\n\n    def _mark_as_changed(self, key=None):\n        if hasattr(self._instance, '_mark_as_changed'):\n            if key:\n                self._instance._mark_as_changed('%s.%s' % (self._name, key))\n            else:\n                self._instance._mark_as_changed(self._name)\n\n\nclass BaseList(list):\n    \"\"\"A special list so we can watch any changes\n    \"\"\"\n\n    _dereferenced = False\n    _instance = None\n    _name = None\n\n    def __init__(self, list_items, instance, name):\n        Document = _import_class('Document')\n        EmbeddedDocument = _import_class('EmbeddedDocument')\n\n        if isinstance(instance, (Document, EmbeddedDocument)):\n            self._instance = weakref.proxy(instance)\n        self._name = name\n        return super(BaseList, self).__init__(list_items)\n\n    def __getitem__(self, key, *args, **kwargs):\n        value = super(BaseList, self).__getitem__(key)\n\n        EmbeddedDocument = _import_class('EmbeddedDocument')\n        if isinstance(value, EmbeddedDocument) and value._instance is None:\n            value._instance = self._instance\n        elif not isinstance(value, BaseDict) and isinstance(value, dict):\n            value = BaseDict(value, None, '%s.%s' % (self._name, key))\n            super(BaseList, self).__setitem__(key, value)\n            value._instance = self._instance\n        elif not isinstance(value, BaseList) and isinstance(value, list):\n            value = BaseList(value, None, '%s.%s' % (self._name, key))\n            super(BaseList, self).__setitem__(key, value)\n            value._instance = self._instance\n        return value\n\n    def __setitem__(self, key, value, *args, **kwargs):\n        if isinstance(key, slice):\n            self._mark_as_changed()\n        else:\n            self._mark_as_changed(key)\n        return super(BaseList, self).__setitem__(key, value)\n\n    def __delitem__(self, key, *args, **kwargs):\n        if isinstance(key, slice):\n            self._mark_as_changed()\n        else:\n            self._mark_as_changed(key)\n        return super(BaseList, self).__delitem__(key)\n\n    def __setslice__(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).__setslice__(*args, **kwargs)\n\n    def __delslice__(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).__delslice__(*args, **kwargs)\n\n    def __getstate__(self):\n        self.instance = None\n        self._dereferenced = False\n        return self\n\n    def __setstate__(self, state):\n        self = state\n        return self\n\n    def append(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).append(*args, **kwargs)\n\n    def extend(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).extend(*args, **kwargs)\n\n    def insert(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).insert(*args, **kwargs)\n\n    def pop(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).pop(*args, **kwargs)\n\n    def remove(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).remove(*args, **kwargs)\n\n    def reverse(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).reverse(*args, **kwargs)\n\n    def sort(self, *args, **kwargs):\n        self._mark_as_changed()\n        return super(BaseList, self).sort(*args, **kwargs)\n\n    def _mark_as_changed(self, key=None):\n        if hasattr(self._instance, '_mark_as_changed'):\n            if key:\n                self._instance._mark_as_changed('%s.%s' % (self._name, key))\n            else:\n                self._instance._mark_as_changed(self._name)\n\n\nclass StrictDict(object):\n    __slots__ = ()\n    _special_fields = set(['get', 'pop', 'iteritems', 'items', 'keys', 'create'])\n    _classes = {}\n    def __init__(self, **kwargs):\n        for k,v in kwargs.iteritems():\n            setattr(self, k, v)\n    def __getitem__(self, key):\n        key = '_reserved_' + key if key in self._special_fields else key\n        try:\n            return getattr(self, key)\n        except AttributeError:\n            raise KeyError(key)\n    def __setitem__(self, key, value):\n        key = '_reserved_' + key if key in self._special_fields else key\n        return setattr(self, key, value)\n    def __contains__(self, key):\n        return hasattr(self, key)\n    def get(self, key, default=None):\n        try:\n            return self[key]\n        except KeyError:\n            return default\n    def pop(self, key, default=None):\n        v = self.get(key, default)\n        try:\n            delattr(self, key)\n        except AttributeError:\n            pass\n        return v\n    def iteritems(self):\n        for key in self:\n            yield key, self[key]\n    def items(self):\n        return [(k, self[k]) for k in iter(self)]\n    def keys(self):\n        return list(iter(self))\n    def __iter__(self):\n        return (key for key in self.__slots__ if hasattr(self, key))\n    def __len__(self):\n        return len(list(self.iteritems()))\n    def __eq__(self, other):\n        return self.items() == other.items()\n    def __neq__(self, other):\n        return self.items() != other.items()\n\n    @classmethod\n    def create(cls, allowed_keys):\n        allowed_keys_tuple = tuple(('_reserved_' + k if k in cls._special_fields else k) for k in allowed_keys)\n        allowed_keys = frozenset(allowed_keys_tuple)\n        if allowed_keys not in cls._classes:\n            class SpecificStrictDict(cls):\n                __slots__ = allowed_keys_tuple\n                def __repr__(self):\n                    return \"{%s}\" % ', '.join('\"{0!s}\": {0!r}'.format(k,v) for (k,v) in self.iteritems())\n            cls._classes[allowed_keys] = SpecificStrictDict\n        return cls._classes[allowed_keys]\n\n\nclass SemiStrictDict(StrictDict):\n    __slots__ = ('_extras')\n    _classes = {}\n    def __getattr__(self, attr):\n        try:\n            super(SemiStrictDict, self).__getattr__(attr)\n        except AttributeError:\n            try:\n                return self.__getattribute__('_extras')[attr]\n            except KeyError as e:\n                raise AttributeError(e)\n    def __setattr__(self, attr, value):\n        try:\n            super(SemiStrictDict, self).__setattr__(attr, value)\n        except AttributeError:\n            try:\n                self._extras[attr] = value\n            except AttributeError:\n                self._extras = {attr: value}\n\n    def __delattr__(self, attr):\n        try:\n            super(SemiStrictDict, self).__delattr__(attr)\n        except AttributeError:\n            try:\n                del self._extras[attr]\n            except KeyError as e:\n                raise AttributeError(e)\n\n    def __iter__(self):\n        try:\n            extras_iter = iter(self.__getattribute__('_extras'))\n        except AttributeError:\n            extras_iter = ()\n        return itertools.chain(super(SemiStrictDict, self).__iter__(), extras_iter)\n", "path": "mongoengine/base/datastructures.py"}]}