Register `tipc` in the tpt tables + test harness
Wire the backend through every registration site (contract §2)
so `--tpt-proto tipc` is a first-class suite mode,
- `_state.TransportProtocolKey` gains the key
- `_addr._address_types` + `._default_lo_addrs`
- `_addr.wrap_address()` gets a `case ('tipc', *_)`; being a
4-elem seq it can't collide w/ `tcp`s or `uds`s 2-tuple
cases, so NO ordering hazard (and a bare seq-pattern matches
the `list` form `msgpack` decodes to).
- `_types`: the `Address` union, `_msg_transports`,
`_key_to_transport`, `_addr_to_transport` and the
`transport_from_stream()` family match. That last one keys
off `._tipc.AF_TIPC` (which carries the uapi fallback) NOT
`socket.AF_TIPC` which is linux-only.
Test-harness side,
- `get_rando_addr()` gains a `tipc` branch; `.get_random()`
already salts w/ `uuid4`+pid so both within- and cross-proc
isolation come for free.
- the `tpt_protos` fixture calls an addr-type's optional
`.is_available()` and `pytest.fail()`s w/ its reason. Keeps
a module-less box from turning `--tpt-proto tipc` into a few
hundred confusing connect-timeouts. Generic on purpose —
plans 02/03 need the same hook.
- the discovery `daemon` fixture's readiness probe learns to
dial a TIPC service name (it previously assumed tcp-or-uds
and blew up on the 4-tuple).
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
wkt/pr493_review
parent
2204979492
commit
e0f66616cd
|
|
@ -84,6 +84,35 @@ def _wait_for_daemon_ready(
|
|||
timeout=poll_interval,
|
||||
):
|
||||
return
|
||||
|
||||
elif tpt_proto == 'tipc':
|
||||
# TIPC — `reg_addr` is the proto-keyed
|
||||
# `('tipc', stype, instance, scope)` per
|
||||
# `tractor.ipc._tipc.TIPCAddress.unwrap()`.
|
||||
#
|
||||
# NOTE, connecting *by name* IS the readiness
|
||||
# probe: until the daemon `.bind()`s (i.e.
|
||||
# publishes) the name, the kernel answers
|
||||
# `EHOSTUNREACH` immediately — no timeout wait.
|
||||
from tractor.ipc._tipc import (
|
||||
AF_TIPC,
|
||||
TIPC_ADDR_NAME,
|
||||
)
|
||||
_, stype, instance, scope = reg_addr
|
||||
sock = socket.socket(AF_TIPC, socket.SOCK_STREAM)
|
||||
try:
|
||||
sock.settimeout(poll_interval)
|
||||
sock.connect((
|
||||
TIPC_ADDR_NAME,
|
||||
stype,
|
||||
instance,
|
||||
0, # domain: 0 == "anywhere in scope"
|
||||
scope,
|
||||
))
|
||||
return
|
||||
finally:
|
||||
sock.close()
|
||||
|
||||
else:
|
||||
# UDS — `reg_addr` is a `(filedir, sockname)`
|
||||
# tuple per `tractor.ipc._uds.UDSAddress.unwrap`.
|
||||
|
|
|
|||
|
|
@ -99,6 +99,20 @@ def get_rando_addr(
|
|||
assert addr.sockpath.resolve()
|
||||
testrun_reg_addr = addr.unwrap()
|
||||
|
||||
# NOTE, `.get_random()` already derives the service
|
||||
# *instance* from a `uuid4`+pid-salted seed, so both the
|
||||
# within- and cross-proc isolation the other 2 protos
|
||||
# hand-roll above comes for free.
|
||||
#
|
||||
# XXX matters MORE here than for tcp/uds: a TIPC name
|
||||
# clash doesn't raise `EADDRINUSE`, it silently
|
||||
# round-robins connects between both publishers.
|
||||
case 'tipc':
|
||||
from tractor.ipc._tipc import TIPCAddress
|
||||
addr: TIPCAddress = addr_type.get_random()
|
||||
assert addr.is_valid
|
||||
testrun_reg_addr = addr.unwrap()
|
||||
|
||||
# XXX, as sanity it should never the same as the default for the
|
||||
# host-singleton registry actor.
|
||||
assert def_reg_addr != testrun_reg_addr
|
||||
|
|
|
|||
|
|
@ -498,14 +498,14 @@ def pytest_configure(
|
|||
)
|
||||
config.addinivalue_line(
|
||||
'markers',
|
||||
'tipc: test targets the `AF_TIPC` tpt backend; the kernel- '
|
||||
'touching cases self-skip unless the `tipc` module is loaded '
|
||||
'(`sudo modprobe tipc`).'
|
||||
'trio: legacy mark for tests meant to run under the `trio` '
|
||||
'spawn backend (e.g. `test_local.py`).'
|
||||
)
|
||||
config.addinivalue_line(
|
||||
'markers',
|
||||
'trio: legacy mark for tests meant to run under the `trio` '
|
||||
'spawn backend (e.g. `test_local.py`).'
|
||||
'tipc: test targets the `AF_TIPC` tpt backend; the kernel- '
|
||||
'touching cases self-skip unless the `tipc` module is loaded '
|
||||
'(`sudo modprobe tipc`).'
|
||||
)
|
||||
|
||||
# `--enable-stackscope`: install SIGUSR1 → trio task-tree
|
||||
|
|
@ -803,6 +803,27 @@ def tpt_protos(
|
|||
addr_type = _addr._address_types[proto_key]
|
||||
assert addr_type.proto_key == proto_key
|
||||
|
||||
# XXX, generic capability gate: an env-dependent tpt
|
||||
# whose backing kernel-mod/lib/netns isn't present here
|
||||
# must fail LOUDLY and EARLY rather than as a few hundred
|
||||
# confusing connect-timeouts downstream.
|
||||
#
|
||||
# Any `Address` type MAY expose `.is_available()`
|
||||
# returning `(ok, why_not)`; absence means "always
|
||||
# available" (i.e. tcp/uds).
|
||||
is_avail = getattr(
|
||||
addr_type,
|
||||
'is_available',
|
||||
None,
|
||||
)
|
||||
if is_avail:
|
||||
avail, why_not = is_avail()
|
||||
if not avail:
|
||||
pytest.fail(
|
||||
f'--tpt-proto={proto_key!r} is NOT usable here!\n'
|
||||
f'{why_not}\n'
|
||||
)
|
||||
|
||||
yield proto_keys
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ from ..runtime._state import (
|
|||
)
|
||||
from ..ipc._tcp import TCPAddress
|
||||
from ..ipc._uds import UDSAddress
|
||||
from ..ipc._tipc import TIPCAddress
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..runtime._runtime import Actor
|
||||
|
|
@ -197,6 +198,7 @@ class Address(Protocol):
|
|||
_address_types: bidict[str, Type[Address]] = {
|
||||
'tcp': TCPAddress,
|
||||
'uds': UDSAddress,
|
||||
'tipc': TIPCAddress,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -208,6 +210,9 @@ _default_lo_addrs: dict[
|
|||
] = {
|
||||
'tcp': TCPAddress.get_root().unwrap(),
|
||||
'uds': UDSAddress.get_root().unwrap(),
|
||||
# NOTE, pure/cheap: a service-name pair, no kernel module
|
||||
# nor I/O required at import time.
|
||||
'tipc': TIPCAddress.get_root().unwrap(),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -253,6 +258,16 @@ def wrap_address(
|
|||
# import pdbp; pdbp.set_trace()
|
||||
match addr:
|
||||
|
||||
# XXX, the explicitly proto-keyed form (spelled with the
|
||||
# `multiaddr` proto name) which is where ALL backends
|
||||
# should eventually land per the `UnwrappedAddress`
|
||||
# migration note above.
|
||||
#
|
||||
# NOTE, a bare seq-pattern matches `list` too, which is
|
||||
# what `msgpack` decodes our tuples back to.
|
||||
case ('tipc', *_):
|
||||
cls = TIPCAddress
|
||||
|
||||
# classic network socket-address as tuple/list
|
||||
case (
|
||||
(str(), int())
|
||||
|
|
|
|||
|
|
@ -38,17 +38,23 @@ from tractor.ipc._uds import (
|
|||
UDSAddress,
|
||||
MsgpackUDSStream,
|
||||
)
|
||||
from tractor.ipc._tipc import (
|
||||
AF_TIPC,
|
||||
TIPCAddress,
|
||||
MsgpackTIPCStream,
|
||||
)
|
||||
|
||||
# if TYPE_CHECKING:
|
||||
# from tractor._addr import Address
|
||||
|
||||
|
||||
Address = TCPAddress|UDSAddress
|
||||
Address = TCPAddress|UDSAddress|TIPCAddress
|
||||
|
||||
# manually updated list of all supported msg transport types
|
||||
_msg_transports = [
|
||||
MsgpackTCPStream,
|
||||
MsgpackUDSStream
|
||||
MsgpackUDSStream,
|
||||
MsgpackTIPCStream,
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -59,15 +65,17 @@ _key_to_transport: dict[
|
|||
] = {
|
||||
('msgpack', 'tcp'): MsgpackTCPStream,
|
||||
('msgpack', 'uds'): MsgpackUDSStream,
|
||||
('msgpack', 'tipc'): MsgpackTIPCStream,
|
||||
}
|
||||
|
||||
# convert an Address wrapper to its corresponding transport type
|
||||
_addr_to_transport: dict[
|
||||
Type[TCPAddress|UDSAddress],
|
||||
Type[TCPAddress|UDSAddress|TIPCAddress],
|
||||
Type[MsgTransport]
|
||||
] = {
|
||||
TCPAddress: MsgpackTCPStream,
|
||||
UDSAddress: MsgpackUDSStream,
|
||||
TIPCAddress: MsgpackTIPCStream,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -108,6 +116,12 @@ def transport_from_stream(
|
|||
case socket.AF_UNIX:
|
||||
transport = 'uds'
|
||||
|
||||
# NOTE, `AF_TIPC` is linux-only in CPython so we
|
||||
# match the `._tipc` constant (which carries a uapi
|
||||
# fallback) rather than `socket.AF_TIPC`.
|
||||
case _ if sock.family == AF_TIPC:
|
||||
transport = 'tipc'
|
||||
|
||||
case _:
|
||||
raise NotImplementedError(
|
||||
f'Unsupported socket family: {sock.family}'
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ if TYPE_CHECKING:
|
|||
TransportProtocolKey = Literal[
|
||||
'tcp',
|
||||
'uds',
|
||||
'tipc',
|
||||
]
|
||||
_def_tpt_proto: TransportProtocolKey = 'tcp'
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue