fix(tx): correct _tx_loop to pop _TxItem and send frames contiguously - #13
Conversation
- pop `_TxItem` (not raw bytes) and check `item is None` - avoid variable shadowing; use `frame_bytes` in send loop - on writer=None: requeue whole item, clear `_connected`, yield - on write error: requeue remaining frames as one atomic item - preserves non-interleaving batches and prevents tight re-loops
There was a problem hiding this comment.
Pull Request Overview
This PR refactors the transmission loop in WaveShareCANClient to properly handle atomic frame batches and fix several issues with frame ordering and error recovery. The changes introduce a new _TxItem data structure to group frames atomically and ensure they are sent contiguously without interleaving.
Key changes:
- Introduces
_TxItemstructure to encapsulate atomic frame groups with metadata - Refactors
_tx_loopto pop entire items and send all frames within an item back-to-back - Adds proper error handling that re-queues remaining frames atomically on write failures
- Implements atomic context manager for creating non-interleaved frame sequences
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| caneth/client.py | Core refactoring of transmission system with new _TxItem structure, updated TX loop logic, and atomic batching functionality |
| tests/test_atomic_context.py | Comprehensive test coverage for atomic frame transmission and mid-batch disconnect scenarios |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| for idx, frame_bytes in enumerate(item.frames): | ||
| writer.write(frame_bytes) |
There was a problem hiding this comment.
The variable name frame_bytes shadows the outer scope variable mentioned in the PR description. Consider using a more descriptive name like frame_data or encoded_frame to avoid confusion and improve code clarity.
| for idx, frame_bytes in enumerate(item.frames): | |
| writer.write(frame_bytes) | |
| for idx, frame_data in enumerate(item.frames): | |
| writer.write(frame_data) |
| remaining = item.frames[sent:] | ||
| if remaining: | ||
| async with self._tx_cv: | ||
| self._tx_buf.appendleft(_TxItem(frames=remaining, atomic=True, can_id=item.can_id)) |
There was a problem hiding this comment.
When re-queuing remaining frames, the code always sets atomic=True regardless of the original item's atomic flag. This could change the behavior for non-atomic items. Consider preserving the original atomic value: _TxItem(frames=remaining, atomic=item.atomic, can_id=item.can_id).
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
_TxItem(not raw bytes) and checkitem is Noneframe_bytesin send loop_connected, yieldWhat’s changed
Checklist
ruff checkandruff formatpassmypy canethpasses