Skip to content

Commit 14fd85f

Browse files
committed
json_db: fix StoredDict.__delitem__() to work similarly to .pop()
follow-up #10233 ("jsondb pointers")
1 parent afc87fe commit 14fd85f

File tree

2 files changed

+44
-26
lines changed

2 files changed

+44
-26
lines changed

electrum/json_db.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,11 @@ def __setitem__(self, key: _FLEX_KEY, v) -> None:
222222
@locked
223223
def __delitem__(self, key: _FLEX_KEY) -> None:
224224
assert isinstance(key, _FLEX_KEY), repr(key)
225+
r = self.get(key, None)
225226
dict.__delitem__(self, key)
226227
self.db_remove(key)
228+
if isinstance(r, StoredDict):
229+
r._parent = None
227230

228231
@locked
229232
def pop(self, key: _FLEX_KEY, v=_RaiseKeyError) -> Any:

tests/test_jsondb.py

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import copy
33
import traceback
44
import json
5+
from typing import Any
56

67
import jsonpatch
78
from jsonpatch import JsonPatchException
@@ -88,6 +89,16 @@ def fail_if_leaking_secret(ctx) -> None:
8889
fail_if_leaking_secret(ctx)
8990

9091

92+
def pop1_from_dict(d: dict, key: str) -> Any:
93+
return d.pop(key)
94+
95+
96+
def pop2_from_dict(d: dict, key: str) -> Any:
97+
val = d[key]
98+
del d[key]
99+
return val
100+
101+
91102
class TestJsonDB(ElectrumTestCase):
92103

93104
async def test_jsonpatch_replace_after_remove(self):
@@ -109,31 +120,35 @@ async def test_jsonpatch_replace_after_remove(self):
109120
data = jpatch.apply(data)
110121

111122
async def test_jsondb_replace_after_remove(self):
112-
data = { 'a': {'b': {'c': 0}}, 'd': 3}
113-
db = JsonDB(repr(data))
114-
a = db.get_dict('a')
115-
# remove
116-
b = a.pop('b')
117-
self.assertEqual(len(db.pending_changes), 1)
118-
# replace item. this must not been written to db
119-
b['c'] = 42
120-
self.assertEqual(len(db.pending_changes), 1)
121-
patches = json.loads('[' + ','.join(db.pending_changes) + ']')
122-
jpatch = jsonpatch.JsonPatch(patches)
123-
data = jpatch.apply(data)
124-
self.assertEqual(data, {'a': {}, 'd': 3})
123+
for pop_from_dict in [pop1_from_dict, pop2_from_dict]:
124+
with self.subTest(pop_from_dict):
125+
data = { 'a': {'b': {'c': 0}}, 'd': 3}
126+
db = JsonDB(repr(data))
127+
a = db.get_dict('a')
128+
# remove
129+
b = pop_from_dict(a, 'b')
130+
self.assertEqual(len(db.pending_changes), 1)
131+
# replace item. this must not been written to db
132+
b['c'] = 42
133+
self.assertEqual(len(db.pending_changes), 1)
134+
patches = json.loads('[' + ','.join(db.pending_changes) + ']')
135+
jpatch = jsonpatch.JsonPatch(patches)
136+
data = jpatch.apply(data)
137+
self.assertEqual(data, {'a': {}, 'd': 3})
125138

126139
async def test_jsondb_replace_after_remove_nested(self):
127-
data = { 'a': {'b': {'c': 0}}, 'd': 3}
128-
db = JsonDB(repr(data))
129-
# remove
130-
a = db.data.pop('a')
131-
self.assertEqual(len(db.pending_changes), 1)
132-
b = a['b']
133-
# replace item. this must not be written to db
134-
b['c'] = 42
135-
self.assertEqual(len(db.pending_changes), 1)
136-
patches = json.loads('[' + ','.join(db.pending_changes) + ']')
137-
jpatch = jsonpatch.JsonPatch(patches)
138-
data = jpatch.apply(data)
139-
self.assertEqual(data, {'d': 3})
140+
for pop_from_dict in [pop1_from_dict, pop2_from_dict]:
141+
with self.subTest(pop_from_dict):
142+
data = { 'a': {'b': {'c': 0}}, 'd': 3}
143+
db = JsonDB(repr(data))
144+
# remove
145+
a = pop_from_dict(db.data, "a")
146+
self.assertEqual(len(db.pending_changes), 1)
147+
b = a['b']
148+
# replace item. this must not be written to db
149+
b['c'] = 42
150+
self.assertEqual(len(db.pending_changes), 1)
151+
patches = json.loads('[' + ','.join(db.pending_changes) + ']')
152+
jpatch = jsonpatch.JsonPatch(patches)
153+
data = jpatch.apply(data)
154+
self.assertEqual(data, {'d': 3})

0 commit comments

Comments
 (0)