Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the error messages by including command sent #94

Merged
merged 1 commit into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions src/pandablocks/_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,27 @@ def __init__(self, to_send: Union[str, list[str]]):
self.received: list[str] = []
self.is_multiline = False

def _error_message(self) -> str:
sent = "\n".join(self.to_send)
received = "\n".join(self.received)
return f"{sent!r} -> {received!r}"

def check_ok(self):
assert self.received == ["OK"], self._error_message()

@property
def line(self) -> str:
"""Check received is not multiline and return the line"""
assert not self.is_multiline
return self.received[0]
assert not self.is_multiline and self.received[0].startswith(
"OK ="
), self._error_message()
# Remove the OK= header
return self.received[0][4:]

@property
def multiline(self) -> list[str]:
"""Return the multiline received lines, processed to remove markup"""
assert self.is_multiline
assert self.is_multiline, self._error_message()
# Remove the ! and . markup
return [line[1:] for line in self.received[:-1]]

Expand Down
18 changes: 6 additions & 12 deletions src/pandablocks/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,7 @@
if ex.is_multiline:
return ex.multiline
else:
# We got OK =value
line = ex.line
assert line.startswith("OK =")
return line[4:]
return ex.line


@dataclass
Expand All @@ -232,10 +229,7 @@
def execute(self) -> ExchangeGenerator[str]:
ex = Exchange(f"{self.field}?")
yield ex
# Expect "OK =value"
line = ex.line
assert line.startswith("OK =")
return line[4:]
return ex.line


@dataclass
Expand Down Expand Up @@ -285,7 +279,7 @@
else:
ex = Exchange(f"{self.field}={self.value}")
yield ex
assert ex.line == "OK"
ex.check_ok()


@dataclass
Expand All @@ -308,7 +302,7 @@
# Multiline table with blank line to terminate
ex = Exchange([f"{self.field}<<"] + self.value + [""])
yield ex
assert ex.line == "OK"
ex.check_ok()


class Arm(Command[None]):
Expand All @@ -317,7 +311,7 @@
def execute(self) -> ExchangeGenerator[None]:
ex = Exchange("*PCAP.ARM=")
yield ex
assert ex.line == "OK"
ex.check_ok()


class Disarm(Command[None]):
Expand All @@ -326,7 +320,7 @@
def execute(self) -> ExchangeGenerator[None]:
ex = Exchange("*PCAP.DISARM=")
yield ex
assert ex.line == "OK"
ex.check_ok()

Check warning on line 323 in src/pandablocks/commands.py

View check run for this annotation

Codecov / codecov/patch

src/pandablocks/commands.py#L323

Added line #L323 was not covered by tests


@dataclass
Expand Down
11 changes: 1 addition & 10 deletions src/pandablocks/connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,7 @@ class _ExchangeContext:
generator: Optional[ExchangeGenerator[Any]] = None

def exception(self, e: Exception) -> CommandException:
"""Return a `CommandException` with the sent and received strings
in the text"""
msg = f"{self.command} ->"
if self.exchange.is_multiline:
for line in self.exchange.multiline:
msg += "\n " + line
else:
msg += " " + self.exchange.line
if e.args:
msg += f"\n{type(e).__name__}:{e}"
msg = f"{self.command} raised error:\n{type(e).__name__}: {e}"
return CommandException(msg).with_traceback(e.__traceback__)


Expand Down
5 changes: 4 additions & 1 deletion tests/test_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ async def test_asyncio_bad_put_raises(dummy_server_async):
async with AsyncioClient("localhost") as client:
with pytest.raises(CommandException) as cm:
await asyncio.wait_for(client.send(Put("PCAP.thing", 1)), timeout=1)
assert str(cm.value) == "Put(field='PCAP.thing', value=1) -> ERR no such field"
assert (
str(cm.value) == "Put(field='PCAP.thing', value=1) raised error:\n"
"AssertionError: 'PCAP.thing=1' -> 'ERR no such field'"
)
assert dummy_server_async.received == ["PCAP.thing=1"]


Expand Down
5 changes: 4 additions & 1 deletion tests/test_blocking.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ def test_blocking_bad_put_raises(dummy_server_in_thread):
with BlockingClient("localhost") as client:
with pytest.raises(CommandException) as cm:
client.send(Put("PCAP.thing", 1), timeout=1)
assert str(cm.value) == "Put(field='PCAP.thing', value=1) -> ERR no such field"
assert (
str(cm.value) == "Put(field='PCAP.thing', value=1) raised error:\n"
"AssertionError: 'PCAP.thing=1' -> 'ERR no such field'"
)
assert dummy_server_in_thread.received == ["PCAP.thing=1"]


Expand Down
35 changes: 23 additions & 12 deletions tests/test_pandablocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ def test_get_line_error_when_multiline():
assert get_responses(conn, b"!ACTIVE 5 bit_out\n.\n") == [
(
cmd,
ACommandException("GetLine(field='PCAP.ACTIVE') ->\n ACTIVE 5 bit_out"),
ACommandException(
"GetLine(field='PCAP.ACTIVE') raised error:\n"
"AssertionError: 'PCAP.ACTIVE?' -> '!ACTIVE 5 bit_out\\n.'"
),
)
]

Expand All @@ -95,7 +98,10 @@ def test_get_line_error_no_ok():
assert get_responses(conn, b"NOT OK\n") == [
(
cmd,
ACommandException("GetLine(field='PCAP.ACTIVE') -> NOT OK"),
ACommandException(
"GetLine(field='PCAP.ACTIVE') raised error:\n"
"AssertionError: 'PCAP.ACTIVE?' -> 'NOT OK'"
),
)
]

Expand All @@ -117,7 +123,10 @@ def test_get_multiline_error_when_single_line():
assert get_responses(conn, b"1\n") == [
(
cmd,
ACommandException("GetMultiline(field='PCAP.*') -> 1"),
ACommandException(
"GetMultiline(field='PCAP.*') raised error:\n"
"AssertionError: 'PCAP.*?' -> '1'"
),
)
]

Expand All @@ -143,7 +152,8 @@ def test_put_fails_with_single_line_exception():
(
cmd,
ACommandException(
"Put(field='PCAP.blah', value='something') -> ERR No such field"
"Put(field='PCAP.blah', value='something') raised error:\n"
"AssertionError: 'PCAP.blah=something' -> 'ERR No such field'"
),
)
]
Expand All @@ -157,11 +167,9 @@ def test_put_fails_with_multiline_exception():
(
cmd,
ACommandException(
"""\
Put(field='PCAP.blah', value='something') ->
This is bad
Very bad
Don't do this"""
"Put(field='PCAP.blah', value='something') raised error:\n"
"AssertionError: 'PCAP.blah=something' -> "
'"!This is bad\\n!Very bad\\n!Don\'t do this\\n."'
),
)
]
Expand Down Expand Up @@ -267,7 +275,8 @@ def test_get_block_info_error():
(
cmd,
ACommandException(
"GetBlockInfo(skip_description=False) -> ERR Cannot read blocks"
"GetBlockInfo(skip_description=False) raised error:\n"
"AssertionError: '*BLOCKS?' -> 'ERR Cannot read blocks'"
),
)
]
Expand All @@ -291,7 +300,8 @@ def test_get_block_info_desc_err():
(
cmd,
ACommandException(
"GetBlockInfo(skip_description=False) -> ERR could not get description"
"GetBlockInfo(skip_description=False) raised error:\n"
"AssertionError: '*DESC.PCAP?' -> 'ERR could not get description'"
),
)
]
Expand Down Expand Up @@ -416,7 +426,8 @@ def test_get_fields_non_existant_block():
(
cmd,
ACommandException(
"GetFieldInfo(block='FOO', extended_metadata=True) -> ERR No such block"
"GetFieldInfo(block='FOO', extended_metadata=True) raised error:\n"
"AssertionError: 'FOO.*?' -> 'ERR No such block'"
),
)
]
Expand Down