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 <noreply@anthropic.com>
Gud Boi 2026-06-09 17:20:45 -04:00
parent 75cefe10f1
commit fa98290808
5 changed files with 59 additions and 38 deletions

View File

@ -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

View File

@ -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
): ):

View File

@ -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

View File

@ -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)

View File

@ -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),