Move `Address` types to `discovery.types`

Keep address descriptors and structural protocols independent of
concrete IPC, actor runtime, tunnel and optional dependency mods.

Use `AddressDeclaration` for plain and tunnelled address values,
while concrete TCP/UDS factory dispatch remains in `._addr`. Drop
runtime model-name aliases to `Any` and the self-referential
`get_type_hints()` regression.

Also,
- preserve lazy `multiaddr` and net imports
- verify canonical type identity across discovery, IPC and tunnels
- route random and bound address reconstruction through
  concrete types

Prompt-IO: ai/prompt-io/opencode/20260903T201530Z_d097090f_discovery_types.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
wkt/runtime_net_scopes
Gud Boi 2026-09-06 21:12:41 -04:00
parent 41f34de3a4
commit 212a8f82ac
14 changed files with 403 additions and 277 deletions

View File

@ -0,0 +1,70 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: dependency-neutral-discovery-types
timestamp: 2026-09-03T20:15:30Z
git_ref: d097090f
scope: code
substantive: true
raw_file: 20260903T201530Z_d097090f_discovery_types.raw.md
---
## Prompt
The maintainer rejected annotation-only model names rebound to `Any`,
questioned the self-referential `get_type_hints()` regression, selected a
dependency-neutral `tractor.discovery.types` module as the preferred
direction, and then requested implementation of that first design layer.
The maintainer separately asked how to handle the disliked staged
`tractor/net/_bootstrap.py` implementation. That file was excluded from
this implementation boundary and retained as review evidence.
## Response summary
Added a dependency-neutral module for canonical primitive address
descriptors and separate structural contracts for concrete transport
addresses and tunnel-capable address declarations. Migrated discovery,
IPC, root, runtime, and tunnel annotations to import those real types,
removing every runtime model-name assignment to `Any`.
Kept parser table aliases beside `parse_endpoints()` instead of turning
the new module into a general alias registry. Kept transport-specific
factories on TCP and UDS classes because their accepted descriptor and
bindspace types differ. Replaced the old `get_type_hints()` assertions
with cold-import and canonical-identity coverage.
## Files changed
- `tractor/discovery/types.py` - dependency-neutral descriptors and
address protocols.
- `tractor/discovery/_addr.py` - imports and re-exports canonical types;
retains concrete factory registry.
- `tractor/discovery/_multiaddr.py` - uses the structural declaration
type and runtime-safe parsed table alias.
- `tractor/ipc/_tcp.py` - imports the real TCP descriptor alias.
- `tractor/ipc/_uds.py` - imports the real Unix descriptor alias.
- `tractor/ipc/_transport.py` - imports the concrete-address protocol
without cycling through `_addr`.
- `tractor/ipc/_chan.py` - accepts structural address declarations
without a `TunnelledAddress = Any` fallback.
- `tractor/ipc/_server.py` - retains declaration metadata structurally
and routes reconstruction through the concrete address registry.
- `tractor/net/_tunnel.py` - imports real dependency-neutral address
types while retaining dynamic `Any` only for external netlink data.
- `tractor/_root.py` - removes the `Bindspace = Any` fallback and uses
concrete address-factory dispatch.
- `tractor/runtime/_runtime.py` - infers concrete registry address types
for random listener allocation.
- `tests/test_lazy_imports.py` - replaces workaround introspection with
dependency and canonical-identity checks.
## Human edits
The maintainer identified the `Any` aliases and their introspection test
as an unjustified circular contract, directed investigation of the real
cycle/import-performance cause, selected `tractor.discovery.types`, and
approved beginning implementation. The maintainer also required that the
current bootstrap implementation not be treated as acceptable design and
provided direct review annotations which remain preserved in the
worktree.

View File

@ -0,0 +1,34 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-09-03T20:15:30Z
git_ref: d097090f
diff_cmd: git diff HEAD~1..HEAD
---
Implemented a dependency-neutral discovery typing layer after reviewing
the address, transport, tunnel, and lazy-import dependency graph.
> `git diff HEAD~1..HEAD -- tractor/discovery/types.py tractor/discovery/_addr.py tractor/discovery/_multiaddr.py tractor/ipc/_tcp.py tractor/ipc/_uds.py tractor/ipc/_transport.py tractor/ipc/_chan.py tractor/ipc/_server.py tractor/net/_tunnel.py tractor/_root.py tractor/runtime/_runtime.py tests/test_lazy_imports.py`
The generated changes move primitive address descriptors and structural
address protocols into `tractor.discovery.types`. Concrete transport
factories remain outside the shared protocol because TCP and UDS accept
different descriptor and bindspace inputs. Runtime names are no longer
rebound to `Any` merely to satisfy `typing.get_type_hints()`.
The old annotation-introspection regression was replaced with coverage
that checks dependency-neutral cold imports and canonical type identity
across discovery, IPC, and tunnel modules.
Verification completed with the worktree-local Python 3.13 environment:
- focused address/lazy/tunnel coverage: 69 passed;
- discovery, IPC, and local coverage: 77 passed, 2 xpassed;
- root/runtime coverage: 6 passed with 24 multiprocessing fork
deprecation warnings.
Ruff was unavailable in the existing `py313` environment. Scoped
`git diff --check` passed. The global whitespace check reports only a
pre-existing maintainer annotation in `tractor/net/_bootstrap.py`, which
was deliberately left untouched.

View File

@ -7,21 +7,6 @@ import os
from statistics import median
import subprocess
import sys
from typing import (
Any,
get_type_hints,
)
import tractor
from tractor.discovery import (
_addr,
_multiaddr,
)
from tractor.ipc import (
_tcp,
_uds,
)
def run_cold_import(code: str) -> dict[str, object]:
result = subprocess.run(
@ -260,28 +245,56 @@ def test_net_root_export_and_old_discovery_surface():
}
def test_lazy_annotation_names_resolve():
def test_discovery_types_are_dependency_neutral():
'''
Resolve annotations without importing optional dependencies.
Keep canonical address types independent of runtime implementations.
Moving annotation-only third-party names under `TYPE_CHECKING`
left their runtime globals undefined, causing
`typing.get_type_hints()` to raise `NameError`. Resolve every
affected API and prove the lazy aliases retain import-free runtime
introspection.
Annotation-only imports previously rebound unavailable model names
to `Any` so `get_type_hints()` could resolve them. That test only
verified its own workaround and hid the `_addr`/transport import
cycle. Import the canonical declarations in a cold interpreter and
prove they do not pull in optional networking modules. Then prove
each implementation imports the same real declarations instead of
substituting `Any` under the model names.
'''
assert get_type_hints(_multiaddr.mk_maddr)['return'] is Any
assert get_type_hints(_tcp.MsgpackTCPStream.maddr.fget)[
'return'
] is Any
assert get_type_hints(_uds.MsgpackUDSStream.maddr.fget)[
'return'
] == Any|str
assert get_type_hints(_addr.Address.get_random)[
'current_actor'
] is Any
assert get_type_hints(tractor.open_root_actor)[
'bindspace'
] == Any|None
assert _addr.__annotations__['_address_types'].startswith('dict')
cold = run_cold_import(
'import json, sys; '
'from tractor.discovery import types; '
'blocked = ('
'"multiaddr", "multibase", "pyroute2", '
'"tractor.discovery._multiaddr", "tractor.net", '
'"tractor.net._bindspace", "tractor.net._tunnel"); '
'print(json.dumps({'
'"address_module": types.Address.__module__, '
'"blocked": [name for name in blocked '
'if name in sys.modules]}))'
)
assert cold == {
'address_module': 'tractor.discovery.types',
'blocked': [],
}
from tractor.discovery import (
_addr,
_multiaddr,
types,
)
from tractor.ipc import (
_chan,
_server,
_tcp,
_transport,
_uds,
)
from tractor.net import _tunnel
assert _addr.Address is types.Address
assert _addr.UnwrappedAddress is types.UnwrappedAddress
assert _multiaddr.AddressDeclaration is types.AddressDeclaration
assert _tcp.TaggedTCPAddress is types.TaggedTCPAddress
assert _uds.TaggedUnixAddress is types.TaggedUnixAddress
assert _transport.Address is types.Address
assert _chan.AddressDeclaration is types.AddressDeclaration
assert _server.AddressDeclaration is types.AddressDeclaration
assert _tunnel.AddressDeclaration is types.AddressDeclaration

View File

@ -53,12 +53,15 @@ from .runtime import _state
from . import log
from .discovery._api import _probe_registry_addrs
from .discovery._addr import (
Address,
UnwrappedAddress,
default_lo_addrs,
get_address_cls,
mk_uuid,
wrap_address,
)
from .discovery.types import (
Address,
UnwrappedAddress,
)
from .trionics import (
is_multi_cancelled,
collapse_eg,
@ -69,8 +72,6 @@ from ._exceptions import (
if TYPE_CHECKING:
from .net._bindspace import Bindspace
else:
Bindspace = Any
logger = log.get_logger('tractor')
@ -187,7 +188,7 @@ async def open_root_actor(
bindspace: Bindspace|None = None,
tpt_bind_addrs: list[
Address # `Address.get_random()` case
Address # concrete transport address case
|UnwrappedAddress # registrar case `= uw_reg_addrs`
]|None = None,
@ -561,6 +562,9 @@ async def open_root_actor(
for addr in ponged_addrs:
bindable_addr: Address = strip_tunnels(addr)
address_type = get_address_cls(
bindable_addr.proto_key
)
tpt_bind_addrs.append(
# XXX, these are `Address` NOT `UnwrappedAddress`.
#
@ -568,7 +572,7 @@ async def open_root_actor(
# protos we allocate port=0 such that the system
# allocates a random value at bind time; this
# happens in the `.ipc.*` stack's backend.
bindable_addr.get_random(
address_type.get_random(
bindspace=bindable_addr.bindspace,
)
)

View File

@ -15,19 +15,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations
from uuid import uuid4
from typing import (
Any,
Protocol,
ClassVar,
Literal,
Type,
TypeAlias,
TYPE_CHECKING,
)
from trio import (
SocketListener,
)
from typing import TypeAlias
from ..log import get_logger
from ..runtime._state import (
@ -38,70 +26,19 @@ from ..ipc._uds import (
UDSAddress,
HAS_UDS,
)
if TYPE_CHECKING:
# ONLY type-annots, the eager import costs ~4.5ms
# of `import tractor` wall-time (gh #470).
from tractor.net._tunnel import (
TunnelledAddress,
)
from ..runtime._runtime import Actor
else:
Actor = Any
TunnelledAddress = Any
from .types import (
Address as Address,
AddressDeclaration,
LegacyUnwrappedAddress,
TaggedAddress,
TaggedUDSAlias,
UnwrappedAddress,
)
log = get_logger()
# TODO, maybe breakout the netns key to a struct?
# class NetNs(Struct)[str, int]:
# ...
_AddressType: TypeAlias = type[TCPAddress]|type[UDSAddress]
# TODO, can't we just use a type alias
# for this? namely just some `tuple[str, int, str, str]`?
#
# -[ ] would also just be simpler to keep this as SockAddr[tuple]
# or something, implying it's just a simple pair of values which can
# presumably be mapped to all transports?
# -[ ] `pydoc socket.socket.getsockname()` delivers a 4-tuple for
# ipv6 `(hostaddr, port, flowinfo, scope_id)`.. so how should we
# handle that?
# -[ ] as a further alternative to this wrap()/unwrap() approach we
# could just implement `enc/dec_hook()`s for the `Address`-types
# and just deal with our internal objs directly and always and
# leave it to the codec layer to figure out marshalling?
# |_ would mean only one spot to do the `.unwrap()` (which we may
# end up needing to call from the hook()s anyway?)
# -[x] rename to `UnwrappedAddress[Descriptor]` ??
# seems like the right name as per,
# https://www.geeksforgeeks.org/introduction-to-address-descriptor/
#
TaggedTCPAddress: TypeAlias = tuple[
Literal['tcp'],
str,
int,
]
TaggedUnixAddress: TypeAlias = tuple[
Literal['unix'],
str,
]
TaggedUDSAlias: TypeAlias = tuple[
Literal['uds'],
str,
]
TaggedAddress: TypeAlias = (
TaggedTCPAddress
|TaggedUnixAddress
)
# Input-only compatibility forms retained for older callers and
# serialized payloads.
LegacyTCPAddress: TypeAlias = tuple[str, int]
LegacyUDSAddress: TypeAlias = tuple[str, str]
LegacyUnwrappedAddress: TypeAlias = (
LegacyTCPAddress
|LegacyUDSAddress
)
UnwrappedAddress = TaggedAddress
# ?TODO? should we also include another 2 fields from our `Aid` msg
# such that we include the runtime `Actor.uid` of `.name` and `.uuid`?
# - would ensure uniqueness across entire net?
@ -109,106 +46,14 @@ UnwrappedAddress = TaggedAddress
# name"
# TODO, maybe rename to `SocketAddress`?
class Address(Protocol):
proto_key: ClassVar[str]
unwrapped_type: ClassVar[type]
# TODO, i feel like an `.is_bound()` is a better thing to
# support?
# Lke, what use does this have besides a noop and if it's not
# valid why aren't we erroring on creation/use?
@property
def is_valid(self) -> bool:
...
# TODO, maybe `.netns` is a better name?
@property
def namespace(self) -> tuple[str, str|int]|None:
'''
The if-available, OS-specific "network namespace" key.
'''
...
@property
def bindspace(self) -> str:
'''
Deliver the socket address' "bindable space" from
a `socket.socket.bind()` and thus from the perspective of
specific transport protocol domain.
I.e. for most (layer-4) network-socket protocols this is
normally the ipv4/6 address, for UDS this is normally
a filesystem (sub-directory).
For (distributed) network protocols this is normally the routing
layer's domain/(ip-)address, though it might also include a "network namespace"
key different then the default.
For local-host-only transports this is either an explicit
namespace (with types defined by the OS: netns, Cgroup, IPC,
pid, etc. on linux) or failing that the sub-directory in the
filesys in which socket/shm files are located *under*.
'''
...
@classmethod
def from_addr(cls, addr: UnwrappedAddress) -> Address:
...
def unwrap(self) -> UnwrappedAddress:
'''
Deliver the underying minimum field set in
a primitive python data type-structure.
'''
...
@classmethod
def get_random(
cls,
current_actor: Actor,
bindspace: str|None = None,
) -> Address:
...
# TODO, this should be something like a `.get_def_registar_addr()`
# or similar since,
# - it should be a **host singleton** (not root/tree singleton)
# - we **only need this value** when one isn't provided to the
# runtime at boot and we want to implicitly provide a host-wide
# registrar.
# - each rooted-actor-tree should likely have its own
# micro-registry (likely the root being it), also see
@classmethod
def get_root(cls) -> Address:
...
def __repr__(self) -> str:
...
def __eq__(self, other) -> bool:
...
async def open_listener(
self,
**kwargs,
) -> SocketListener:
...
async def close_listener(self):
...
# the address types available on this host: TCP always, UDS only
# where usable (`HAS_UDS`). Both registries derive from this single
# list via each type's `proto_key`.
_address_protos: list[Type[Address]] = [TCPAddress]
_address_protos: list[_AddressType] = [TCPAddress]
if HAS_UDS:
_address_protos.append(UDSAddress)
_address_types: dict[str, Type[Address]] = {
_address_types: dict[str, _AddressType] = {
cls.proto_key: cls
for cls in _address_protos
}
@ -216,13 +61,22 @@ _address_types: dict[str, Type[Address]] = {
# TODO! really these are discovery sys default addrs ONLY useful for
# when none is provided to a root actor on first boot.
#
# TODO, this should be something like a `.get_def_registar_addr()`
# or similar since,
# - it should be a **host singleton** (not root/tree singleton)
# - we **only need this value** when one isn't provided to the
# runtime at boot and we want to implicitly provide a host-wide
# registrar.
# - each rooted-actor-tree should likely have its own
# micro-registry (likely the root being it), also see
_default_lo_addrs: dict[str, UnwrappedAddress] = {
cls.proto_key: cls.get_root().unwrap()
for cls in _address_protos
}
def get_address_cls(name: str) -> Type[Address]:
def get_address_cls(name: str) -> _AddressType:
try:
return _address_types[name]
except KeyError:
@ -233,7 +87,7 @@ def get_address_cls(name: str) -> Type[Address]:
)
def is_wrapped_addr(addr: any) -> bool:
def is_wrapped_addr(addr: object) -> bool:
# XXX NOTE, a `TunnelledAddress` is genuinely "wrapped" but is
# deliberately NOT in `_address_types`: it has no
# `MsgTransport` of its own (a tunnel is transparent to
@ -264,10 +118,9 @@ def wrap_address(
|LegacyUnwrappedAddress
|list[str|int]
|str
|Address
|TunnelledAddress
|AddressDeclaration
),
) -> Address|TunnelledAddress:
) -> AddressDeclaration:
'''
Wrap an `UnwrappedAddress` as an `Address`-type based
on matching builtin python data-structures which we adhoc
@ -284,7 +137,7 @@ def wrap_address(
if is_wrapped_addr(addr):
return addr
cls: Type|None = None
cls: _AddressType|None = None
# if 'sock' in addr[0]:
# import pdbp; pdbp.set_trace()
match addr:
@ -328,7 +181,7 @@ def wrap_address(
|
[None, None]
):
cls: Type[Address] = get_address_cls(_def_tpt_proto)
cls = get_address_cls(_def_tpt_proto)
addr: UnwrappedAddress = cls.get_root().unwrap()
# multiaddr-format string, e.g.
@ -354,7 +207,7 @@ def wrap_address(
def default_lo_addrs(
transports: list[str],
) -> list[Type[Address]]:
) -> list[UnwrappedAddress]:
'''
Return the default, host-singleton, registry address
for an input transport key set.

View File

@ -27,24 +27,15 @@ Multiaddress support using the upstream `py-multiaddr` lib
from __future__ import annotations
import ipaddress
from pathlib import Path
from typing import (
Any,
TYPE_CHECKING,
)
from typing import TYPE_CHECKING
from .types import AddressDeclaration
if TYPE_CHECKING:
# NOTE, `multiaddr` is lazy-imported at first use
# (in the fns below) to keep it off the eager
# `import tractor` path (gh #470).
from multiaddr import Multiaddr
from tractor.discovery._addr import Address
from tractor.net._tunnel import (
TunnelledAddress,
)
else:
Multiaddr = Any
Address = Any
TunnelledAddress = Any
# map from tractor-internal `proto_key` identifiers
# to the standard multiaddr protocol name strings.
@ -61,7 +52,7 @@ _maddr_to_tpt_proto: dict[str, str] = {
def mk_maddr(
addr: 'Address|TunnelledAddress',
addr: AddressDeclaration,
) -> Multiaddr:
'''
Construct a `Multiaddr` from a tractor `Address` instance,
@ -111,7 +102,7 @@ def mk_maddr(
def parse_maddr(
maddr_str: str,
) -> 'Address|TunnelledAddress':
) -> AddressDeclaration:
'''
Parse a multiaddr string into a tractor `Address`.
@ -173,15 +164,15 @@ def parse_maddr(
# or raw unwrapped-address tuples (as accepted by
# `wrap_address()`).
EndpointsTable = dict[
str, # actor/service name
list[str|tuple], # maddr strs or UnwrappedAddress
str, # actor/service name
list[str|tuple|AddressDeclaration],
]
# output table: actor/service name -> list of wrapped address
# declarations ready for bindspace handling.
ParsedEndpoints = dict[
str, # actor/service name
list['Address|TunnelledAddress'],
str, # actor/service name
list[AddressDeclaration],
]

View File

@ -0,0 +1,176 @@
# tractor: structured concurrent "actors".
# Copyright 2018-eternity Tyler Goodlet.
# This program is free software: you can redistribute it and/or
# modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public
# License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
'''
Dependency-neutral address typing declarations.
Keep this module independent of concrete IPC transports, actor
runtime models, network resources, tunnels, and optional
dependencies.
'''
from __future__ import annotations
from pathlib import Path
from typing import (
ClassVar,
Literal,
Protocol,
TypeAlias,
)
# TODO, maybe breakout the netns key to a struct?
# class NetNs(Struct)[str, int]:
# ...
# TODO, can't we just use a type alias
# for this? namely just some `tuple[str, int, str, str]`?
#
# -[ ] would also just be simpler to keep this as
# SockAddr[tuple] or something, implying it's just a simple pair
# of values which can presumably be mapped to all transports?
# -[ ] `pydoc socket.socket.getsockname()` delivers a 4-tuple for
# ipv6 `(hostaddr, port, flowinfo, scope_id)`.. so how should we
# handle that?
# -[ ] as a further alternative to this wrap()/unwrap() approach we
# could just implement `enc/dec_hook()`s for the `Address`-types
# and just deal with our internal objs directly and always and
# leave it to the codec layer to figure out marshalling?
# |_ would mean only one spot to do the `.unwrap()` (which we may
# end up needing to call from the hook()s anyway?)
# -[x] rename to `UnwrappedAddress[Descriptor]` ??
# seems like the right name as per the GeeksForGeeks article,
# "Introduction to Address Descriptor".
#
TaggedTCPAddress: TypeAlias = tuple[
Literal['tcp'],
str,
int,
]
TaggedUnixAddress: TypeAlias = tuple[
Literal['unix'],
str,
]
TaggedUDSAlias: TypeAlias = tuple[
Literal['uds'],
str,
]
TaggedAddress: TypeAlias = (
TaggedTCPAddress
|TaggedUnixAddress
)
# Input-only compatibility forms retained for older callers and
# serialized payloads.
LegacyTCPAddress: TypeAlias = tuple[str, int]
LegacyUDSAddress: TypeAlias = tuple[str, str]
LegacyUnwrappedAddress: TypeAlias = (
LegacyTCPAddress
|LegacyUDSAddress
)
UnwrappedAddress = TaggedAddress
class AddressDeclaration(Protocol):
'''
Common instance shape of plain and tunnelled addresses.
This protocol deliberately excludes transport registration and
listener operations. A tunnel declaration delegates these address
properties to its innermost concrete transport address.
'''
@property
def proto_key(self) -> str:
...
@property
def is_valid(self) -> bool:
...
@property
def namespace(self) -> tuple[str, str|int]|None:
...
@property
def bindspace(self) -> str|Path:
...
def unwrap(self) -> UnwrappedAddress:
...
# TODO, maybe rename to `SocketAddress`?
class Address(Protocol):
'''
Concrete address contract used by IPC transport backends.
Unlike `AddressDeclaration`, transport registries require
class-level protocol metadata. Transport-specific factories stay
on their concrete address classes because their descriptor and
bindspace inputs differ.
'''
proto_key: ClassVar[str]
unwrapped_type: ClassVar[type]
# TODO, i feel like an `.is_bound()` is a better thing to
# support?
# Lke, what use does this have besides a noop and if it's not
# valid why aren't we erroring on creation/use?
@property
def is_valid(self) -> bool:
...
# TODO, maybe `.netns` is a better name?
@property
def namespace(self) -> tuple[str, str|int]|None:
'''
The if-available, OS-specific "network namespace" key.
'''
...
@property
def bindspace(self) -> str|Path:
'''
Deliver the address' transport-specific bindable space.
'''
...
def unwrap(self) -> UnwrappedAddress:
'''
Deliver the underlying primitive address descriptor.
'''
...
__all__ = (
'Address',
'AddressDeclaration',
'LegacyTCPAddress',
'LegacyUDSAddress',
'LegacyUnwrappedAddress',
'TaggedAddress',
'TaggedTCPAddress',
'TaggedUDSAlias',
'TaggedUnixAddress',
'UnwrappedAddress',
)

View File

@ -43,7 +43,10 @@ from ._types import (
from tractor.discovery._addr import (
is_wrapped_addr,
wrap_address,
)
from tractor.discovery.types import (
Address,
AddressDeclaration,
UnwrappedAddress,
)
from tractor.log import get_logger
@ -59,9 +62,6 @@ from tractor.msg import (
if TYPE_CHECKING:
from ._transport import MsgTransport
from tractor.net._tunnel import TunnelledAddress
else:
TunnelledAddress = Any
log = get_logger()
@ -185,7 +185,7 @@ class Channel:
@classmethod
async def from_addr(
cls,
addr: UnwrappedAddress|Address|TunnelledAddress,
addr: UnwrappedAddress|AddressDeclaration,
**kwargs
) -> Channel:
@ -557,7 +557,7 @@ class Channel:
@acm
async def _connect_chan(
addr: UnwrappedAddress|Address|TunnelledAddress,
addr: UnwrappedAddress|AddressDeclaration,
close_timeout: float|None = None,
) -> typing.AsyncGenerator[Channel, None]:
'''

View File

@ -59,8 +59,10 @@ from ..msg import (
from ..trionics import maybe_open_nursery
from ..runtime import _state
from .. import log
from ..discovery._addr import (
from ..discovery._addr import get_address_cls
from ..discovery.types import (
Address,
AddressDeclaration,
UnwrappedAddress,
)
from ._chan import Channel
@ -68,7 +70,6 @@ from ._transport import MsgTransport
if TYPE_CHECKING:
from ..net._tunnel import TunnelledAddress
from ..runtime._runtime import Actor
from ..runtime._supervise import ActorNursery
@ -631,7 +632,7 @@ class Endpoint(Struct):
'''
addr: Address
declared_addr: Address|TunnelledAddress
declared_addr: AddressDeclaration
listen_tn: Nursery
stream_handler_tn: Nursery|None = None
@ -689,7 +690,8 @@ class Endpoint(Struct):
!=
self.addr.unwrap()
):
self.addr=self.addr.from_addr(unwrapped)
address_type = get_address_cls(self.addr.proto_key)
self.addr = address_type.from_addr(unwrapped)
self._listener = lstnr
return lstnr
@ -991,7 +993,7 @@ class Server(Struct):
self,
*,
accept_addrs: list[
UnwrappedAddress|Address|TunnelledAddress
UnwrappedAddress|AddressDeclaration
]|None = None,
stream_handler_nursery: Nursery|None = None,
) -> list[Endpoint]:
@ -1075,7 +1077,7 @@ async def _serve_ipc_eps(
*,
server: IPCServer,
stream_handler_tn: Nursery,
listen_addrs: list[Address|TunnelledAddress],
listen_addrs: list[AddressDeclaration],
task_status: TaskStatus[
Nursery,

View File

@ -20,7 +20,6 @@ TCP implementation of tractor.ipc._transport.MsgTransport protocol
from __future__ import annotations
import ipaddress
from typing import (
Any,
ClassVar,
TYPE_CHECKING,
)
@ -41,15 +40,12 @@ from tractor.ipc._transport import (
MsgTransport,
MsgpackTransport,
)
from tractor.discovery.types import TaggedTCPAddress
if TYPE_CHECKING:
# ONLY type-annots, the eager import costs
# `import tractor` wall-time (gh #470).
from multiaddr import Multiaddr
from tractor.discovery._addr import TaggedTCPAddress
else:
Multiaddr = Any
TaggedTCPAddress = Any
log = get_logger()

View File

@ -55,9 +55,7 @@ from tractor.msg import (
types as msgtypes,
pretty_struct,
)
if TYPE_CHECKING:
from tractor.discovery._addr import Address
from tractor.discovery.types import Address
log = get_logger()

View File

@ -43,7 +43,6 @@ except ImportError:
AF_UNIX = None
import struct
from typing import (
Any,
Type,
TYPE_CHECKING,
ClassVar,
@ -71,17 +70,13 @@ from tractor.runtime._state import (
current_actor,
is_root_process,
)
from tractor.discovery.types import TaggedUnixAddress
if TYPE_CHECKING:
# ONLY type-annots, the eager import costs
# `import tractor` wall-time (gh #470).
from multiaddr import Multiaddr
from tractor.discovery._addr import TaggedUnixAddress
from tractor.runtime._runtime import Actor
else:
Multiaddr = Any
Actor = Any
TaggedUnixAddress = Any
# Platform-specific credential passing constants

View File

@ -93,6 +93,11 @@ import multibase
import trio
from ..msg._local import ProcessLocal
from ..discovery.types import (
Address,
AddressDeclaration,
UnwrappedAddress,
)
from ._bindspace import (
Bindspace,
BindspaceRef,
@ -103,16 +108,6 @@ from ._bindspace import (
if TYPE_CHECKING:
from multiaddr import Multiaddr
from ..discovery._addr import (
Address,
UnwrappedAddress,
)
else:
Address = Any
Multiaddr = Any
UnwrappedAddress = Any
class WGTunnelSpec(
msgspec.Struct,
frozen=True,
@ -879,7 +874,7 @@ class TunnelledAddress(
`strip_tunnels()`.
'''
overlay: Address|TunnelledAddress
overlay: AddressDeclaration
tunnel: TunnelSpec
bindspace_ref: BindspaceRef|None = None
@ -1057,7 +1052,7 @@ def parse_wg_maddr(
match overlay_names:
case [('ip4' | 'ip6'), 'tcp']:
from ..discovery._multiaddr import parse_maddr
overlay: Address|TunnelledAddress = parse_maddr(
overlay: AddressDeclaration = parse_maddr(
str(overlay_ma)
)
@ -1172,7 +1167,7 @@ def mk_wg_maddr(
def strip_tunnels(
addr: Address|TunnelledAddress,
addr: AddressDeclaration,
) -> Address:
'''
Deliver the bindable `Address`, peeling any tunnel
@ -1191,7 +1186,7 @@ def strip_tunnels(
def tunnels_of(
addr: Address|TunnelledAddress,
addr: AddressDeclaration,
) -> tuple[TunnelSpec, ...]:
'''
Deliver every tunnel spec wrapping `addr`, outermost first.

View File

@ -60,7 +60,6 @@ import sys
from typing import (
Any,
Callable,
Type,
TYPE_CHECKING,
)
import uuid
@ -1684,10 +1683,10 @@ async def async_main(
[_state._def_tpt_proto]
)
for transport_key in enable_transports:
transport_cls: Type[Address] = get_address_cls(
address_type = get_address_cls(
transport_key
)
addr: Address = transport_cls.get_random()
addr: Address = address_type.get_random()
accept_addrs.append(addr.unwrap())
# XXX, either passed in by caller or delivered