Skip to content

Commit 472e211

Browse files
authored
Merge pull request #548 from avinxshKD/fix/read-backward-compat
fix: make read() backward compatible again
2 parents a850269 + a5722dc commit 472e211

7 files changed

Lines changed: 55 additions & 71 deletions

File tree

concore.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -223,25 +223,19 @@ def unchanged():
223223
def read(port_identifier, name, initstr_val):
224224
"""Read data from a ZMQ port or file-based port.
225225
226-
Returns:
227-
tuple: (data, success_flag) where success_flag is True if real
228-
data was received, False if a fallback/default was used.
229-
Also sets ``concore.last_read_status`` to one of:
230-
SUCCESS, FILE_NOT_FOUND, TIMEOUT, PARSE_ERROR,
231-
EMPTY_DATA, RETRIES_EXCEEDED.
232-
233-
Backward compatibility:
234-
Legacy callers that do ``value = concore.read(...)`` will
235-
receive a tuple. They can adapt with::
236-
237-
result = concore.read(...)
238-
if isinstance(result, tuple):
239-
value, ok = result
240-
else:
241-
value, ok = result, True
226+
Returns only the data payload for backward compatibility.
227+
Use ``read_with_status()`` for explicit ``(data, success_flag)``.
228+
"""
229+
global last_read_status
230+
result, _ok = concore_base.read(_mod, port_identifier, name, initstr_val)
231+
last_read_status = concore_base.last_read_status
232+
return result
233+
234+
235+
def read_with_status(port_identifier, name, initstr_val):
236+
"""Read data and return ``(data, success_flag)``.
242237
243-
Alternatively, check ``concore.last_read_status`` after the
244-
call.
238+
Also updates ``concore.last_read_status``.
245239
"""
246240
global last_read_status
247241
result = concore_base.read(_mod, port_identifier, name, initstr_val)

concore_base.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -249,18 +249,11 @@ def read(mod, port_identifier, name, initstr_val):
249249
SUCCESS, FILE_NOT_FOUND, TIMEOUT, PARSE_ERROR,
250250
EMPTY_DATA, RETRIES_EXCEEDED.
251251
252-
Backward compatibility:
253-
Legacy callers that do ``value = concore.read(...)`` will
254-
receive a tuple. They can adapt with::
255-
256-
result = concore.read(...)
257-
if isinstance(result, tuple):
258-
value, ok = result
259-
else:
260-
value, ok = result, True
261-
262-
Alternatively, check ``concore.last_read_status`` after the
263-
call.
252+
Notes:
253+
This low-level helper always returns ``(data, success_flag)``.
254+
The wrapper modules expose:
255+
- ``read()`` for backward-compatible data-only reads.
256+
- ``read_with_status()`` for explicit ``(data, success_flag)``.
264257
"""
265258
global last_read_status
266259

concoredocker.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ def unchanged():
9292
# I/O Handling (File + ZMQ)
9393
# ===================================================================
9494
def read(port_identifier, name, initstr_val):
95+
global last_read_status
96+
result, _ok = concore_base.read(_mod, port_identifier, name, initstr_val)
97+
last_read_status = concore_base.last_read_status
98+
return result
99+
100+
def read_with_status(port_identifier, name, initstr_val):
95101
global last_read_status
96102
result = concore_base.read(_mod, port_identifier, name, initstr_val)
97103
last_read_status = concore_base.last_read_status

tests/test_concore.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ def recv_json_with_retry(self):
300300
concore.write("roundtrip_test", "data", original_data)
301301

302302
# Read should return original data (simtime stripped) plus success flag
303-
result, ok = concore.read("roundtrip_test", "data", "[]")
303+
result, ok = concore.read_with_status("roundtrip_test", "data", "[]")
304304
assert result == original_data
305305
assert ok is True
306306

@@ -520,7 +520,7 @@ def recv_json_with_retry(self):
520520
concore.zmq_ports["t_in"] = TimeoutPort()
521521
concore.simtime = 0
522522

523-
result, ok = concore.read("t_in", "x", "[0.0]")
523+
result, ok = concore.read_with_status("t_in", "x", "[0.0]")
524524

525525
assert result == [0.0]
526526
assert ok is False

tests/test_concoredocker.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def test_reads_and_parses_data(self, temp_dir):
136136

137137
concoredocker.s = ""
138138
concoredocker.simtime = 0
139-
result, ok = concoredocker.read(1, "data", "[0, 0, 0]")
139+
result, ok = concoredocker.read_with_status(1, "data", "[0, 0, 0]")
140140

141141
assert result == [100, 200]
142142
assert ok is True
@@ -156,7 +156,7 @@ def test_returns_default_when_file_missing(self, temp_dir):
156156

157157
concoredocker.s = ""
158158
concoredocker.simtime = 0
159-
result, ok = concoredocker.read(1, "nofile", "[0, 5, 5]")
159+
result, ok = concoredocker.read_with_status(1, "nofile", "[0, 5, 5]")
160160

161161
assert result == [5, 5]
162162
assert ok is False
@@ -204,7 +204,7 @@ def recv_json_with_retry(self):
204204
concoredocker.zmq_ports["test_zmq"] = DummyPort()
205205
concoredocker.simtime = 0
206206

207-
result, ok = concoredocker.read("test_zmq", "data", "[]")
207+
result, ok = concoredocker.read_with_status("test_zmq", "data", "[]")
208208

209209
assert result == [4.0, 5.0]
210210
assert ok is True
@@ -243,7 +243,7 @@ def recv_json_with_retry(self):
243243

244244
original = [1.5, 2.5, 3.5]
245245
concoredocker.write("roundtrip", "data", original)
246-
result, ok = concoredocker.read("roundtrip", "data", "[]")
246+
result, ok = concoredocker.read_with_status("roundtrip", "data", "[]")
247247

248248
assert result == original
249249
assert ok is True
@@ -278,7 +278,7 @@ def recv_json_with_retry(self):
278278
concoredocker.zmq_ports["t_in"] = TimeoutPort()
279279
concoredocker.simtime = 0
280280

281-
result, ok = concoredocker.read("t_in", "x", "[0.0]")
281+
result, ok = concoredocker.read_with_status("t_in", "x", "[0.0]")
282282

283283
assert result == [0.0]
284284
assert ok is False

tests/test_protocol_conformance.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def _run_read_file_case(case):
116116
) as f:
117117
f.write(case["input"]["file_content"])
118118

119-
result, ok = concore.read(
119+
result, ok = concore.read_with_status(
120120
case["input"]["port"],
121121
case["input"]["name"],
122122
case["input"]["initstr_val"],

tests/test_read_status.py

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Tests for read() error signalling (Issue #390).
22
3-
read() now returns (data, success_flag) and sets
4-
concore.last_read_status / concore_base.last_read_status.
3+
read() returns data for backward compatibility.
4+
read_with_status() returns (data, success_flag).
5+
Both update concore.last_read_status / concore_base.last_read_status.
56
"""
67

78
import os
@@ -35,7 +36,7 @@ def recv_json_with_retry(self):
3536

3637

3738
class TestReadFileSuccess:
38-
"""read() on a valid file returns (data, True) with SUCCESS status."""
39+
"""read_with_status() on a valid file returns (data, True)."""
3940

4041
@pytest.fixture(autouse=True)
4142
def setup(self, temp_dir, monkeypatch):
@@ -53,7 +54,7 @@ def setup(self, temp_dir, monkeypatch):
5354
monkeypatch.setattr(concore, "inpath", os.path.join(temp_dir, "in"))
5455

5556
def test_returns_data_and_true(self):
56-
data, ok = self.concore.read(1, "ym", "[0, 0.0]")
57+
data, ok = self.concore.read_with_status(1, "ym", "[0, 0.0]")
5758
assert ok is True
5859
assert data == [3.14]
5960

@@ -63,7 +64,7 @@ def test_last_read_status_is_success(self):
6364

6465

6566
class TestReadFileMissing:
66-
"""read() on a missing file returns (default, False) with FILE_NOT_FOUND."""
67+
"""read_with_status() on missing file returns (default, False)."""
6768

6869
@pytest.fixture(autouse=True)
6970
def setup(self, temp_dir, monkeypatch):
@@ -75,7 +76,7 @@ def setup(self, temp_dir, monkeypatch):
7576
monkeypatch.setattr(concore, "inpath", os.path.join(temp_dir, "in"))
7677

7778
def test_returns_default_and_false(self):
78-
data, ok = self.concore.read(1, "nonexistent", "[0, 0.0]")
79+
data, ok = self.concore.read_with_status(1, "nonexistent", "[0, 0.0]")
7980
assert ok is False
8081

8182
def test_last_read_status_is_file_not_found(self):
@@ -84,7 +85,7 @@ def test_last_read_status_is_file_not_found(self):
8485

8586

8687
class TestReadFileParseError:
87-
"""read() returns (default, False) with PARSE_ERROR on malformed content."""
88+
"""read_with_status() returns (default, False) on parse errors."""
8889

8990
@pytest.fixture(autouse=True)
9091
def setup(self, temp_dir, monkeypatch):
@@ -101,7 +102,7 @@ def setup(self, temp_dir, monkeypatch):
101102
monkeypatch.setattr(concore, "inpath", os.path.join(temp_dir, "in"))
102103

103104
def test_returns_default_and_false(self):
104-
data, ok = self.concore.read(1, "ym", "[0, 0.0]")
105+
data, ok = self.concore.read_with_status(1, "ym", "[0, 0.0]")
105106
assert ok is False
106107

107108
def test_last_read_status_is_parse_error(self):
@@ -110,7 +111,7 @@ def test_last_read_status_is_parse_error(self):
110111

111112

112113
class TestReadFileTraversalBlocked:
113-
"""read() rejects traversal names and returns PARSE_ERROR."""
114+
"""read_with_status() rejects traversal names and returns PARSE_ERROR."""
114115

115116
@pytest.fixture(autouse=True)
116117
def setup(self, temp_dir, monkeypatch):
@@ -126,7 +127,7 @@ def setup(self, temp_dir, monkeypatch):
126127
f.write("[10, 3.14]")
127128

128129
def test_returns_default_and_false_on_traversal_name(self):
129-
data, ok = self.concore.read(1, "../ym", "[0, 0.0]")
130+
data, ok = self.concore.read_with_status(1, "../ym", "[0, 0.0]")
130131
assert ok is False
131132
assert data == [0, 0.0]
132133

@@ -136,7 +137,7 @@ def test_last_read_status_is_parse_error_for_traversal_name(self):
136137

137138

138139
class TestReadFileRetriesExceeded:
139-
"""read() returns (default, False) with RETRIES_EXCEEDED when file is empty."""
140+
"""read_with_status() returns (default, False) on retry exhaustion."""
140141

141142
@pytest.fixture(autouse=True)
142143
def setup(self, temp_dir, monkeypatch):
@@ -154,7 +155,7 @@ def setup(self, temp_dir, monkeypatch):
154155
monkeypatch.setattr(concore, "inpath", os.path.join(temp_dir, "in"))
155156

156157
def test_returns_default_and_false(self):
157-
data, ok = self.concore.read(1, "ym", "[0, 0.0]")
158+
data, ok = self.concore.read_with_status(1, "ym", "[0, 0.0]")
158159
assert ok is False
159160

160161
def test_last_read_status_is_retries_exceeded(self):
@@ -168,7 +169,7 @@ def test_last_read_status_is_retries_exceeded(self):
168169

169170

170171
class TestReadZMQSuccess:
171-
"""Successful ZMQ read returns (data, True)."""
172+
"""Successful ZMQ read_with_status() returns (data, True)."""
172173

173174
@pytest.fixture(autouse=True)
174175
def setup(self, monkeypatch):
@@ -185,14 +186,14 @@ def test_zmq_read_returns_data_and_true(self):
185186
self.concore.zmq_ports["test_port"] = dummy
186187
self.concore.simtime = 0
187188

188-
data, ok = self.concore.read("test_port", "ym", "[]")
189+
data, ok = self.concore.read_with_status("test_port", "ym", "[]")
189190
assert ok is True
190191
assert data == [1.1, 2.2]
191192
assert self.concore.last_read_status == "SUCCESS"
192193

193194

194195
class TestReadZMQTimeout:
195-
"""ZMQ read that returns None (timeout) yields (default, False)."""
196+
"""ZMQ read timeout yields (default, False) via read_with_status()."""
196197

197198
@pytest.fixture(autouse=True)
198199
def setup(self, monkeypatch):
@@ -210,13 +211,13 @@ def test_zmq_timeout_returns_default_and_false(self):
210211
)
211212
self.concore.zmq_ports["test_port"] = dummy
212213

213-
data, ok = self.concore.read("test_port", "ym", "[]")
214+
data, ok = self.concore.read_with_status("test_port", "ym", "[]")
214215
assert ok is False
215216
assert self.concore.last_read_status == "TIMEOUT"
216217

217218

218219
class TestReadZMQError:
219-
"""ZMQ read that raises ZMQError yields (default, False)."""
220+
"""ZMQ read ZMQError yields (default, False) via read_with_status()."""
220221

221222
@pytest.fixture(autouse=True)
222223
def setup(self, monkeypatch):
@@ -234,7 +235,7 @@ def test_zmq_error_returns_default_and_false(self):
234235
dummy = DummyZMQPort(raise_on_recv=zmq.error.ZMQError("test error"))
235236
self.concore.zmq_ports["test_port"] = dummy
236237

237-
data, ok = self.concore.read("test_port", "ym", "[]")
238+
data, ok = self.concore.read_with_status("test_port", "ym", "[]")
238239
assert ok is False
239240
assert self.concore.last_read_status == "TIMEOUT"
240241

@@ -245,7 +246,7 @@ def test_zmq_error_returns_default_and_false(self):
245246

246247

247248
class TestReadBackwardCompatibility:
248-
"""Legacy callers can use isinstance check on the result."""
249+
"""Legacy callers get plain data from read()."""
249250

250251
@pytest.fixture(autouse=True)
251252
def setup(self, temp_dir, monkeypatch):
@@ -261,22 +262,12 @@ def setup(self, temp_dir, monkeypatch):
261262

262263
monkeypatch.setattr(concore, "inpath", os.path.join(temp_dir, "in"))
263264

264-
def test_legacy_unpack_pattern(self):
265-
"""The recommended migration pattern works correctly."""
266-
result = self.concore.read(1, "ym", "[0, 0.0]")
267-
268-
if isinstance(result, tuple):
269-
value, ok = result
270-
else:
271-
value = result
272-
ok = True
273-
265+
def test_read_returns_data_only(self):
266+
value = self.concore.read(1, "ym", "[0, 0.0]")
274267
assert value == [42.0]
275-
assert ok is True
276268

277-
def test_tuple_unpack(self):
278-
"""New-style callers can unpack directly."""
279-
value, ok = self.concore.read(1, "ym", "[0, 0.0]")
269+
def test_read_with_status_returns_tuple(self):
270+
value, ok = self.concore.read_with_status(1, "ym", "[0, 0.0]")
280271
assert value == [42.0]
281272
assert ok is True
282273

0 commit comments

Comments
 (0)