Skip to content

Commit ebb3a58

Browse files
authored
fix: do not write lazyjson twice when in context manager (#5268)
* fix: do not write lazyjson twice when in context manager * tests: simpler use of tempdir * test: more test simplification * test: add test for double write * test: ensure data is written
1 parent 06b1295 commit ebb3a58

File tree

3 files changed

+55
-17
lines changed

3 files changed

+55
-17
lines changed

conda_forge_tick/lazy_json_backends.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,8 +1023,15 @@ def _load(self) -> None:
10231023
data_str = file_backend.hget(self.hashmap, self.node)
10241024
else:
10251025
backend = LAZY_JSON_BACKENDS[CF_TICK_GRAPH_DATA_PRIMARY_BACKEND]()
1026-
backend.hsetnx(self.hashmap, self.node, dumps({}))
1027-
data_str = backend.hget(self.hashmap, self.node)
1026+
if backend.hexists(self.hashmap, self.node):
1027+
data_str = backend.hget(self.hashmap, self.node)
1028+
else:
1029+
data_str = dumps({})
1030+
if not self._in_context:
1031+
# need to push file to backend since will not get
1032+
# done at end of context manager
1033+
backend.hset(self.hashmap, self.node, data_str)
1034+
10281035
if isinstance(data_str, bytes): # type: ignore[unreachable]
10291036
data_str = data_str.decode("utf-8") # type: ignore[unreachable]
10301037

tests/test_lazy_json_backends.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,3 +945,29 @@ def _sleep():
945945
raise e
946946
else:
947947
pass
948+
949+
950+
def test_lazy_json_backends_contexts_double_write():
951+
with tempfile.TemporaryDirectory() as tmpdir, pushd(tmpdir):
952+
with lazy_json_override_backends(["file"], use_file_cache=False):
953+
lzj = LazyJson("test.json")
954+
# old behavior always makes a file on init
955+
# we remove it here so that we can test
956+
assert os.path.exists(lzj.sharded_path)
957+
os.remove(lzj.sharded_path)
958+
959+
with lzj:
960+
# file should not yet exist
961+
assert not os.path.exists(lzj.sharded_path)
962+
963+
# even if we get the data, the file should not yet exist
964+
assert lzj.data == {}
965+
assert not os.path.exists(lzj.sharded_path)
966+
967+
# setting data doesn't make the file
968+
lzj["hi"] = "world"
969+
assert not os.path.exists(lzj.sharded_path)
970+
971+
# now the file exists at the end of the context block
972+
assert os.path.exists(lzj.sharded_path)
973+
assert lzj.data == {"hi": "world"}

tests/test_utils.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import contextlib
2+
import tempfile
23
import textwrap
34
from io import StringIO
45
from unittest import mock
56
from unittest.mock import MagicMock, mock_open
67

78
import pytest
89

10+
from conda_forge_tick.lazy_json_backends import LazyJson
11+
from conda_forge_tick.os_utils import pushd
912
from conda_forge_tick.utils import (
1013
DEFAULT_GRAPH_FILENAME,
1114
_munge_dict_repr,
@@ -93,24 +96,26 @@ def test_get_keys_default_none():
9396

9497

9598
def test_load_graph():
96-
with mock.patch("builtins.open", mock_open(read_data=DEMO_GRAPH)) as mock_file:
99+
with tempfile.TemporaryDirectory() as tmpdir, pushd(tmpdir):
100+
with open(LazyJson(DEFAULT_GRAPH_FILENAME).sharded_path, "w") as fp:
101+
fp.write(DEMO_GRAPH)
102+
97103
gx = load_graph()
98104

99105
assert gx is not None
100106

101107
assert gx.nodes.keys() == {"package1", "package2"}
102108

103-
mock_file.assert_has_calls([mock.call(DEFAULT_GRAPH_FILENAME)])
104-
105109

106110
def test_load_graph_empty_graph():
107-
with mock.patch("builtins.open", mock_open(read_data=EMPTY_JSON)) as mock_file:
111+
with tempfile.TemporaryDirectory() as tmpdir, pushd(tmpdir):
112+
with open(LazyJson(DEFAULT_GRAPH_FILENAME).sharded_path, "w") as fp:
113+
fp.write(EMPTY_JSON)
114+
108115
gx = load_graph()
109116

110117
assert gx is None
111118

112-
mock_file.assert_has_calls([mock.call(DEFAULT_GRAPH_FILENAME)])
113-
114119

115120
@mock.patch("os.path.exists")
116121
def test_load_graph_file_does_not_exist(exists_mock: MagicMock):
@@ -122,24 +127,24 @@ def test_load_graph_file_does_not_exist(exists_mock: MagicMock):
122127
mock_file.assert_has_calls([mock.call(DEFAULT_GRAPH_FILENAME, "w")])
123128

124129

125-
@mock.patch("os.path.isfile")
126-
def test_load_existing_graph(isfile_mock: MagicMock):
127-
isfile_mock.return_value = True
128-
with mock.patch("builtins.open", mock_open(read_data=DEMO_GRAPH)) as mock_file:
130+
def test_load_existing_graph():
131+
with tempfile.TemporaryDirectory() as tmpdir, pushd(tmpdir):
132+
with open(LazyJson(DEFAULT_GRAPH_FILENAME).sharded_path, "w") as fp:
133+
fp.write(DEMO_GRAPH)
134+
129135
gx = load_existing_graph()
130136

131137
assert gx.nodes.keys() == {"package1", "package2"}
132138

133-
mock_file.assert_has_calls([mock.call(DEFAULT_GRAPH_FILENAME)])
134-
135139

136140
def test_load_existing_graph_empty_graph():
137-
with mock.patch("builtins.open", mock_open(read_data=EMPTY_JSON)) as mock_file:
141+
with tempfile.TemporaryDirectory() as tmpdir, pushd(tmpdir):
142+
with open(LazyJson(DEFAULT_GRAPH_FILENAME).sharded_path, "w") as fp:
143+
fp.write(EMPTY_JSON)
144+
138145
with pytest.raises(ValueError, match="empty JSON"):
139146
load_existing_graph()
140147

141-
mock_file.assert_has_calls([mock.call(DEFAULT_GRAPH_FILENAME)])
142-
143148

144149
@mock.patch("os.path.exists")
145150
def test_load_existing_graph_file_does_not_exist(exists_mock: MagicMock):

0 commit comments

Comments
 (0)