From bc6e18d7b4b87d0a20ed7f702c679b6e089ff8be Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 10 Jun 2026 13:11:05 -0400 Subject: [PATCH] Port service+tests to latest `tractor` APIs Continue the `repair_tests`-branch mission (already merged in this stack's ancestry, see f4c4f1e2 which ported `conftest.py`) by fixing the remaining drift breakage vs. `tractor` git `main`; without these NOTHING boots since the `tractor.Address` port in 604e5fcf. Deats, - normalize reg addrs via `wrap_address()` in `open_pikerd()` before `.unwrap()`-ing; entries may be raw `tuple`s when passed in from (test) client code. - port `check_for_service()` to `query_actor(regaddr=)` (was `arbiter_sockaddr=`) incl. its 2-tuple yield and the now-required `open_registry(addrs=)` arg. - `wait_for_actor(registry_addr=)` + `.chan.raddr.unwrap()` raw-tuple compares in `test_runtime_boot` and `ensure_service()`. - update `run_test_w_cancel_method()` for modern `tractor` cancel semantics: self-requested sub-service cancels are absorbed (no `ContextCancelled` raised to the opener) and single-exc groups collapse to a bare KBI. - `RemoteActorError.boxed_type` (was `.type`) and `Position.cumsize` (was `.size`) renames in tests. - bump the paper-EMS startup budget 9 -> 19s; it includes a live (kraken) symbology fetch so needs net headroom. - woops, add the missing comma in `.deribit.api`'s `tractor.trionics` import tuple.. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code Co-Authored-By: Claude Fable 5 Prompt-IO: ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.md --- .../20260610T171022Z_4485f2b9_prompt_io.md | 53 +++++++++++++ ...20260610T171022Z_4485f2b9_prompt_io.raw.md | 56 ++++++++++++++ piker/brokers/deribit/api.py | 2 +- piker/service/_actor_runtime.py | 8 +- piker/service/_registry.py | 9 ++- tests/test_ems.py | 4 +- tests/test_services.py | 74 +++++++++++-------- 7 files changed, 168 insertions(+), 38 deletions(-) create mode 100644 ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.md create mode 100644 ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.raw.md diff --git a/ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.md b/ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.md new file mode 100644 index 00000000..9d8072fa --- /dev/null +++ b/ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.md @@ -0,0 +1,53 @@ +--- +model: claude-fable-5[1m] +service: claude +session: 32d15f9a-b2d3-4c26-bdc9-190219141a25 +timestamp: 2026-06-10T17:10:22Z +git_ref: datad_service +diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.md +scope: code +substantive: true +raw_file: 20260610T171022Z_4485f2b9_prompt_io.raw.md +--- + +## Prompt + +Same session-initiating `brokerd`-split instruction (see +`20260610T170859Z_75cefe10_prompt_io.md`). Proximate +driver: the approved plan's per-stage test gates could +not run AT ALL — the branch base was broken vs. +`tractor` git `main` (`AttributeError: 'tuple' object +has no attribute 'unwrap'` at `pikerd` boot, stale +`arbiter_sockaddr`/`.type`/`.size` API refs). The agent +fixed forward autonomously to (re)establish the gate +baseline, continuing the `repair_tests` branch lineage +(verified already merged in ancestry via `git cherry` +during a user-requested branch-overlap survey). + +## Response summary + +Port the service layer + test suites to current +`tractor` APIs: addr-type normalization in +`open_pikerd()`, `query_actor()`/`wait_for_actor()` +kwarg renames, modern self-cancel absorption semantics +in the cancel-method test harness, exc/position attr +renames, a paper-EMS startup-budget bump and a syntax +fix in `.deribit.api`. + +## Files changed + +- `piker/service/_actor_runtime.py` — `wrap_address()` + normalize before `.unwrap()` in `open_pikerd()` +- `piker/service/_registry.py` — `check_for_service()` + -> `query_actor(regaddr=)` + 2-tuple yield + + `open_registry(addrs=)` +- `piker/brokers/deribit/api.py` — missing comma in + `tractor.trionics` import tuple +- `tests/test_services.py` — `registry_addr=` kwarg, + raddr unwraps, cancel-semantics harness rewrite, + `fail_after` 9 -> 19s +- `tests/test_ems.py` — `.boxed_type`, `pp.cumsize` + +## Human edits + +None — committed as generated. diff --git a/ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.raw.md b/ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.raw.md new file mode 100644 index 00000000..9b0b40d6 --- /dev/null +++ b/ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.raw.md @@ -0,0 +1,56 @@ +--- +model: claude-fable-5[1m] +service: claude +timestamp: 2026-06-10T17:10:22Z +git_ref: datad_service +diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.md +--- + +NOTE: diff-ref mode entry (code committed in the same +commit as this log); backfilled from the live dev +session transcript per the `/prompt-io` skill rules. + +> `git log -1 -p --follow -- piker/service/_actor_runtime.py` + +Generated: normalize each registry addr via +`tractor.discovery._addr.wrap_address()` before +`.unwrap()`-ing for the `accept_addrs` bind check — +entries may be raw `tuple`s when passed in from (test) +client code. Import-path precedent taken from +`piker/cli/__init__.py:336`. + +> `git log -1 -p --follow -- piker/service/_registry.py` + +Generated: `check_for_service()` ported to +`tractor.query_actor(name, regaddr=...)` (kwarg was +`arbiter_sockaddr=`), unpacking the new +`(sockaddr, portal)` yield, and passing the +now-required `open_registry(addrs=Registry.addrs)`. + +> `git log -1 -p --follow -- tests/test_services.py` +> `git log -1 -p --follow -- tests/test_ems.py` +> `git log -1 -p --follow -- piker/brokers/deribit/api.py` + +Key diagnostic reasoning (verbatim from session): + +- the "DID NOT RAISE ContextCancelled" failure: in this + test the client actor IS pikerd (in-proc), and + current `tractor` main absorbs a `ContextCancelled` + whose canceller is your own actor — self-requested + cancels now exit cleanly instead of raising; the + 'sigint' variant propagates a bare collapsed + `KeyboardInterrupt` rather than a + `BaseExceptionGroup`. +- the hard-coded `trio.fail_after(9)` startup budget is + marginal — full stack boot (pikerd -> emsd -> + brokerd.kraken -> paperboi + live kraken symbology + fetch) occasionally exceeds 9s -> bumped to 19s. +- `RemoteActorError.type` -> `.boxed_type`; + `Position.size` -> `.cumsize` (the paper engine + populates `BrokerdPosition.size` from `pp.cumsize`). +- overlap survey (user-requested): all of the + `repair_tests` branch commits are already in this + stack's ancestry; this commit finishes that branch's + port mission (its f4c4f1e2 fixed `conftest.py`'s + `arbiter_sockaddr` usage; this fixes the remaining + `test_services.py` + `check_for_service()` sites). diff --git a/piker/brokers/deribit/api.py b/piker/brokers/deribit/api.py index eb70bfa0..85d490be 100644 --- a/piker/brokers/deribit/api.py +++ b/piker/brokers/deribit/api.py @@ -37,7 +37,7 @@ from rapidfuzz import process as fuzzy import numpy as np from tractor.trionics import ( broadcast_receiver, - maybe_open_context + maybe_open_context, collapse_eg, ) from tractor import to_asyncio diff --git a/piker/service/_actor_runtime.py b/piker/service/_actor_runtime.py index 5bb67c5b..b0f180e4 100644 --- a/piker/service/_actor_runtime.py +++ b/piker/service/_actor_runtime.py @@ -214,7 +214,13 @@ async def open_pikerd( trio.open_nursery() as service_tn, ): for addr in reg_addrs: - uaddr: tuple = addr.unwrap() + # normalize to a wrapped `tractor` addr-type; + # entries may be raw `tuple`s when passed in + # from (test) client code. + wladdr = tractor.discovery._addr.wrap_address( + addr, + ) + uaddr: tuple = wladdr.unwrap() if ( uaddr not in root_actor.accept_addrs ): diff --git a/piker/service/_registry.py b/piker/service/_registry.py index e24539e2..2d787d7c 100644 --- a/piker/service/_registry.py +++ b/piker/service/_registry.py @@ -225,10 +225,13 @@ async def check_for_service( ''' async with ( - open_registry(ensure_exists=False) as reg_addr, + open_registry( + addrs=Registry.addrs, + ensure_exists=False, + ) as reg_addrs, tractor.query_actor( service_name, - arbiter_sockaddr=reg_addr, - ) as sockaddr, + regaddr=reg_addrs[0], + ) as (sockaddr, _), ): return sockaddr diff --git a/tests/test_ems.py b/tests/test_ems.py index 07e28c33..a6adbbf8 100644 --- a/tests/test_ems.py +++ b/tests/test_ems.py @@ -151,7 +151,7 @@ def load_and_check_pos( # is the same the fqme. pp: Position = table.pps[ppmsg.symbol] - assert ppmsg.size == pp.size + assert ppmsg.size == pp.cumsize assert ppmsg.avg_price == pp.ppu yield pp @@ -179,7 +179,7 @@ def test_ems_err_on_bad_broker( # NOTE: emsd should error on the actor's enabled modules # import phase, when looking for a backend named `doggy`. except tractor.RemoteActorError as re: - assert re.type is ModuleNotFoundError + assert re.boxed_type is ModuleNotFoundError run_and_tollerate_cancels(load_bad_fqme) diff --git a/tests/test_services.py b/tests/test_services.py index 433e97f3..69771c09 100644 --- a/tests/test_services.py +++ b/tests/test_services.py @@ -53,11 +53,12 @@ def test_runtime_boot( tractor.wait_for_actor( 'pikerd', - arbiter_sockaddr=daemon_addr, + registry_addr=daemon_addr, ) as portal, ): - assert pikerd_portal.channel.raddr == daemon_addr - assert pikerd_portal.channel.raddr == portal.channel.raddr + uw_raddr: tuple = pikerd_portal.chan.raddr.unwrap() + assert uw_raddr == daemon_addr + assert uw_raddr == portal.chan.raddr.unwrap() # no service tasks should be started assert not services.service_tasks @@ -108,7 +109,7 @@ async def ensure_service( sockaddr: tuple[str, int] | None = None, ) -> None: async with find_service(name) as portal: - remote_sockaddr = portal.channel.raddr + remote_sockaddr: tuple = portal.chan.raddr.unwrap() print(f'FOUND `{name}` @ {remote_sockaddr}') if sockaddr: @@ -131,40 +132,49 @@ def run_test_w_cancel_method( "was remotely cancelled by remote actor (\'pikerd\'") if cancel_method == 'sigint': - with pytest.raises( + # XXX: with modern `tractor` the (single-exc) + # group is collapsed so a bare KBI normally + # propagates; tolerate either form. + with pytest.raises(( + KeyboardInterrupt, BaseExceptionGroup, - ) as exc_info: + )) as exc_info: trio.run(main) - multi = exc_info.value + err = exc_info.value + match err: + case BaseExceptionGroup(): + for suberr in err.exceptions: + match suberr: + # ensure we receive a remote + # cancellation error caused by the + # pikerd root actor. + case tractor.ContextCancelled(): + assert ( + cancelled_msg + in + suberr.args[0] + ) - for suberr in multi.exceptions: - match suberr: - # ensure we receive a remote cancellation error caused - # by the pikerd root actor since we used the - # `.cancel_service()` API above B) - case tractor.ContextCancelled(): - assert cancelled_msg in suberr.args[0] + case KeyboardInterrupt(): + pass - case KeyboardInterrupt(): - pass + case _: + pytest.fail( + f'Unexpected error {suberr}' + ) - case _: - pytest.fail(f'Unexpected error {suberr}') + case KeyboardInterrupt(): + pass elif cancel_method == 'services': - - # XXX NOTE: oddly, when you pass --pdb to pytest, i think since - # we also use that to enable the underlying tractor debug mode, - # it causes this to not raise for some reason? So if you see - # that while changing this test.. it's prolly that. - - with pytest.raises( - tractor.ContextCancelled - ) as exc_info: - trio.run(main) - - assert cancelled_msg in exc_info.value.args[0] + # XXX: cancelling our own sub-service via + # `Services.cancel_service()` is a *self* + # requested cancel: modern `tractor` absorbs the + # resulting `ContextCancelled` (canceller is our + # own actor) so the runtime tears down gracefully + # with NO error raised to the opener. + trio.run(main) else: pytest.fail(f'Test is broken due to {cancel_method}') @@ -197,7 +207,9 @@ def test_ensure_ems_in_paper_actors( # ensure we timeout after is startup is too slow. # TODO: something like this should be our start point for # benchmarking end-to-end startup B) - with trio.fail_after(9): + # NOTE: includes a live (kraken) symbology fetch so + # the budget needs some headroom for net latency.. + with trio.fail_after(19): async with ( open_test_pikerd() as (_, _, _, services),