Port service+tests to latest `tractor` APIs
Continue the `repair_tests`-branch mission (already merged in this stack's ancestry, seedatad_servicef4c4f1e2which ported `conftest.py`) by fixing the remaining drift breakage vs. `tractor` git `main`; without these NOTHING boots since the `tractor.Address` port in604e5fcf. 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 <noreply@anthropic.com> Prompt-IO: ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.md
parent
4485f2b9ce
commit
bc6e18d7b4
|
|
@ -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.
|
||||||
|
|
@ -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).
|
||||||
|
|
@ -37,7 +37,7 @@ from rapidfuzz import process as fuzzy
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from tractor.trionics import (
|
from tractor.trionics import (
|
||||||
broadcast_receiver,
|
broadcast_receiver,
|
||||||
maybe_open_context
|
maybe_open_context,
|
||||||
collapse_eg,
|
collapse_eg,
|
||||||
)
|
)
|
||||||
from tractor import to_asyncio
|
from tractor import to_asyncio
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,13 @@ async def open_pikerd(
|
||||||
trio.open_nursery() as service_tn,
|
trio.open_nursery() as service_tn,
|
||||||
):
|
):
|
||||||
for addr in reg_addrs:
|
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 (
|
if (
|
||||||
uaddr not in root_actor.accept_addrs
|
uaddr not in root_actor.accept_addrs
|
||||||
):
|
):
|
||||||
|
|
|
||||||
|
|
@ -225,10 +225,13 @@ async def check_for_service(
|
||||||
|
|
||||||
'''
|
'''
|
||||||
async with (
|
async with (
|
||||||
open_registry(ensure_exists=False) as reg_addr,
|
open_registry(
|
||||||
|
addrs=Registry.addrs,
|
||||||
|
ensure_exists=False,
|
||||||
|
) as reg_addrs,
|
||||||
tractor.query_actor(
|
tractor.query_actor(
|
||||||
service_name,
|
service_name,
|
||||||
arbiter_sockaddr=reg_addr,
|
regaddr=reg_addrs[0],
|
||||||
) as sockaddr,
|
) as (sockaddr, _),
|
||||||
):
|
):
|
||||||
return sockaddr
|
return sockaddr
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ def load_and_check_pos(
|
||||||
# is the same the fqme.
|
# is the same the fqme.
|
||||||
pp: Position = table.pps[ppmsg.symbol]
|
pp: Position = table.pps[ppmsg.symbol]
|
||||||
|
|
||||||
assert ppmsg.size == pp.size
|
assert ppmsg.size == pp.cumsize
|
||||||
assert ppmsg.avg_price == pp.ppu
|
assert ppmsg.avg_price == pp.ppu
|
||||||
|
|
||||||
yield pp
|
yield pp
|
||||||
|
|
@ -179,7 +179,7 @@ def test_ems_err_on_bad_broker(
|
||||||
# NOTE: emsd should error on the actor's enabled modules
|
# NOTE: emsd should error on the actor's enabled modules
|
||||||
# import phase, when looking for a backend named `doggy`.
|
# import phase, when looking for a backend named `doggy`.
|
||||||
except tractor.RemoteActorError as re:
|
except tractor.RemoteActorError as re:
|
||||||
assert re.type is ModuleNotFoundError
|
assert re.boxed_type is ModuleNotFoundError
|
||||||
|
|
||||||
run_and_tollerate_cancels(load_bad_fqme)
|
run_and_tollerate_cancels(load_bad_fqme)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,12 @@ def test_runtime_boot(
|
||||||
|
|
||||||
tractor.wait_for_actor(
|
tractor.wait_for_actor(
|
||||||
'pikerd',
|
'pikerd',
|
||||||
arbiter_sockaddr=daemon_addr,
|
registry_addr=daemon_addr,
|
||||||
) as portal,
|
) as portal,
|
||||||
):
|
):
|
||||||
assert pikerd_portal.channel.raddr == daemon_addr
|
uw_raddr: tuple = pikerd_portal.chan.raddr.unwrap()
|
||||||
assert pikerd_portal.channel.raddr == portal.channel.raddr
|
assert uw_raddr == daemon_addr
|
||||||
|
assert uw_raddr == portal.chan.raddr.unwrap()
|
||||||
|
|
||||||
# no service tasks should be started
|
# no service tasks should be started
|
||||||
assert not services.service_tasks
|
assert not services.service_tasks
|
||||||
|
|
@ -108,7 +109,7 @@ async def ensure_service(
|
||||||
sockaddr: tuple[str, int] | None = None,
|
sockaddr: tuple[str, int] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
async with find_service(name) as portal:
|
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}')
|
print(f'FOUND `{name}` @ {remote_sockaddr}')
|
||||||
|
|
||||||
if sockaddr:
|
if sockaddr:
|
||||||
|
|
@ -131,40 +132,49 @@ def run_test_w_cancel_method(
|
||||||
"was remotely cancelled by remote actor (\'pikerd\'")
|
"was remotely cancelled by remote actor (\'pikerd\'")
|
||||||
|
|
||||||
if cancel_method == 'sigint':
|
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,
|
BaseExceptionGroup,
|
||||||
) as exc_info:
|
)) as exc_info:
|
||||||
trio.run(main)
|
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:
|
case KeyboardInterrupt():
|
||||||
match suberr:
|
pass
|
||||||
# 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():
|
case _:
|
||||||
pass
|
pytest.fail(
|
||||||
|
f'Unexpected error {suberr}'
|
||||||
|
)
|
||||||
|
|
||||||
case _:
|
case KeyboardInterrupt():
|
||||||
pytest.fail(f'Unexpected error {suberr}')
|
pass
|
||||||
|
|
||||||
elif cancel_method == 'services':
|
elif cancel_method == 'services':
|
||||||
|
# XXX: cancelling our own sub-service via
|
||||||
# XXX NOTE: oddly, when you pass --pdb to pytest, i think since
|
# `Services.cancel_service()` is a *self*
|
||||||
# we also use that to enable the underlying tractor debug mode,
|
# requested cancel: modern `tractor` absorbs the
|
||||||
# it causes this to not raise for some reason? So if you see
|
# resulting `ContextCancelled` (canceller is our
|
||||||
# that while changing this test.. it's prolly that.
|
# own actor) so the runtime tears down gracefully
|
||||||
|
# with NO error raised to the opener.
|
||||||
with pytest.raises(
|
trio.run(main)
|
||||||
tractor.ContextCancelled
|
|
||||||
) as exc_info:
|
|
||||||
trio.run(main)
|
|
||||||
|
|
||||||
assert cancelled_msg in exc_info.value.args[0]
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
pytest.fail(f'Test is broken due to {cancel_method}')
|
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.
|
# ensure we timeout after is startup is too slow.
|
||||||
# TODO: something like this should be our start point for
|
# TODO: something like this should be our start point for
|
||||||
# benchmarking end-to-end startup B)
|
# 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 (
|
async with (
|
||||||
open_test_pikerd() as (_, _, _, services),
|
open_test_pikerd() as (_, _, _, services),
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue