From 33d74da8c2c491fbfd60b553999667d506d3238d Mon Sep 17 00:00:00 2001 From: goodboy Date: Thu, 20 Aug 2026 11:57:39 -0400 Subject: [PATCH] Fix send-side `MsgTypeError` rendering Once `pformat_caller_frame()` renders successfully, the default `_mk_send_mte()` path still fails while formatting the valid IPC msg spec and then constructs its error message as a one-element tuple. Pass `MsgCodec` to `pformat_msgspec()`, keep the assembled message a string and exercise the complete path through a printable `MsgTypeError` regression. Prompt-IO: ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`)) --- .../20260820T150250Z_9afda1c6_prompt_io.md | 33 +++++++++++++++++++ ...20260820T150250Z_9afda1c6_prompt_io.raw.md | 25 ++++++++++++++ tests/devx/test_pformat.py | 32 ++++++++++++++++++ tractor/_exceptions.py | 2 +- tractor/msg/_codec.py | 2 +- 5 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.md create mode 100644 ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.raw.md diff --git a/ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.md b/ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.md new file mode 100644 index 00000000..10731392 --- /dev/null +++ b/ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.md @@ -0,0 +1,33 @@ +--- +model: openai/gpt-5.6-sol +service: opencode +session: 7b9c97c4-fff7-4ac4-97fb-35720453308e +timestamp: 2026-08-20T15:02:50Z +git_ref: pformat_caller_frame_render_guard +scope: code +substantive: true +raw_file: 20260820T150250Z_9afda1c6_prompt_io.raw.md +--- + +## Prompt + +Fix both newly exposed send-side `MsgTypeError` formatting failures +and pin them with an end-to-end regression in PR #503. + +## Response summary + +Corrected codec-spec formatting and default error-message assembly so +`_mk_send_mte()` returns a printable error instead of raising another +formatter exception. + +## Files changed + +- `tractor/msg/_codec.py` - pass the codec to its supported formatter. +- `tractor/_exceptions.py` - assemble the default message as `str`. +- `tests/devx/test_pformat.py` - render the complete default error. + +## Human edits + +The human selected both one-line fixes and the single end-to-end test +as coherent additions to PR #503, while leaving broader formatter +cleanup out of scope. diff --git a/ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.raw.md b/ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.raw.md new file mode 100644 index 00000000..4f0a1e37 --- /dev/null +++ b/ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.raw.md @@ -0,0 +1,25 @@ +--- +model: openai/gpt-5.6-sol +service: opencode +timestamp: 2026-08-20T15:02:50Z +git_ref: pformat_caller_frame_render_guard +diff_cmd: git diff HEAD~1..HEAD +--- + +## Prompt + +After reviewing additional `tractor.devx.pformat` work suitable for +PR #503, the user approved fixing both send-side `MsgTypeError` +formatting failures and adding an end-to-end regression. + +## Response + +The generated code corrects the `MsgCodec.msg_spec_str` formatter +input, keeps `_mk_send_mte()`'s assembled default message a string, +and tests that the resulting `MsgTypeError` can be rendered: + +> `git diff HEAD~1..HEAD -- tractor/msg/_codec.py tractor/_exceptions.py tests/devx/test_pformat.py` + +These failures were hidden behind the original +`pformat_caller_frame()` keyword error addressed by the first two +commits on the branch. diff --git a/tests/devx/test_pformat.py b/tests/devx/test_pformat.py index 103e2556..401fae45 100644 --- a/tests/devx/test_pformat.py +++ b/tests/devx/test_pformat.py @@ -6,10 +6,12 @@ from __future__ import annotations import pytest +from tractor._exceptions import _mk_send_mte from tractor.devx.pformat import ( pformat_boxed_tb, pformat_caller_frame, ) +from tractor.msg._codec import _def_tractor_codec @pytest.mark.parametrize( @@ -50,3 +52,33 @@ def test_pformat_boxed_tb_rejects_unknown_kwargs(): tb_str='doggy\n', indent='', ) + + +def test_send_mte_default_message_renders(): + ''' + The default send-side `MsgTypeError` must remain printable. + + Once `pformat_caller_frame()` stopped failing first, this path + exposed two more formatter errors: `MsgCodec.msg_spec_str` passed + a type union where `pformat_msgspec()` requires a codec/decoder, + then `_mk_send_mte()` wrapped its message in a one-element tuple. + + Construct the error without an override message to execute that + complete default path. Requiring a `str` message with the bad + value and valid spec, then rendering the exception, proves the + original IPC violation survives every formatter layer. + + ''' + bad_msg: dict[str, bool] = {'bad': True} + mte = _mk_send_mte( + msg=bad_msg, + codec=_def_tractor_codec, + ) + + assert isinstance(mte.message, str) + assert f'invalid msg -> {bad_msg}' in mte.message + assert 'Valid IPC msgs are:' in mte.message + + report: str = repr(mte) + assert 'MsgTypeError' in report + assert f'invalid msg -> {bad_msg}' in report diff --git a/tractor/_exceptions.py b/tractor/_exceptions.py index d838544b..6a0af0df 100644 --- a/tractor/_exceptions.py +++ b/tractor/_exceptions.py @@ -1509,7 +1509,7 @@ def _mk_send_mte( f'invalid msg -> {msg}: {type(msg)}\n\n' f'{tb_fmt}\n' f'Valid IPC msgs are:\n\n' - f'{codec.msg_spec_str}\n', + f'{codec.msg_spec_str}\n' ) elif src_type_error: src_message: str = str(src_type_error) diff --git a/tractor/msg/_codec.py b/tractor/msg/_codec.py index 26f8e24e..4f09b35e 100644 --- a/tractor/msg/_codec.py +++ b/tractor/msg/_codec.py @@ -430,7 +430,7 @@ class MsgCodec(Struct): # wrapped field over the `.msg_spec` one? @property def msg_spec_str(self) -> str: - return pformat_msgspec(self.msg_spec) + return pformat_msgspec(self) lib: ModuleType = msgspec