Keep lazy annotations runtime-resolvable

Provide import-free runtime aliases for annotation-only actor and
multiaddr types so `typing.get_type_hints()` remains usable without
eagerly loading optional dependencies.

Correct `_address_types` to its actual `dict` shape and cover the
affected discovery and transport APIs.

Caught-during: review remediation
Found-via: `/run-tests` test_lazy_annotation_names_resolve

Review: PR #478 (goodboy)
https://github.com/goodboy/tractor/pull/478#pullrequestreview-4922213201

(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
wkt/pr478_review
Gud Boi 2026-08-12 20:52:15 -04:00
parent 70c7e334a7
commit 7987f6b8d7
5 changed files with 59 additions and 3 deletions

View File

@ -0,0 +1,41 @@
'''
Regression tests for the cold package import surface.
'''
from typing import (
Any,
get_type_hints,
)
from tractor.discovery import (
_addr,
_multiaddr,
)
from tractor.ipc import (
_tcp,
_uds,
)
def test_lazy_annotation_names_resolve():
'''
Resolve annotations without importing optional dependencies.
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.
'''
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 _addr.__annotations__['_address_types'].startswith('dict')

View File

@ -16,6 +16,7 @@
from __future__ import annotations
from uuid import uuid4
from typing import (
Any,
Protocol,
ClassVar,
Type,
@ -36,8 +37,9 @@ from ..ipc._uds import UDSAddress
if TYPE_CHECKING:
# ONLY type-annots, the eager import costs ~4.5ms
# of `import tractor` wall-time (gh #470).
from bidict import bidict
from ..runtime._runtime import Actor
else:
Actor = Any
log = get_logger()
@ -172,7 +174,7 @@ class Address(Protocol):
...
_address_types: bidict[str, Type[Address]] = {
_address_types: dict[str, Type[Address]] = {
'tcp': TCPAddress,
'uds': UDSAddress
}

View File

@ -27,7 +27,10 @@ Multiaddress support using the upstream `py-multiaddr` lib
from __future__ import annotations
import ipaddress
from pathlib import Path
from typing import TYPE_CHECKING
from typing import (
Any,
TYPE_CHECKING,
)
if TYPE_CHECKING:
# NOTE, `multiaddr` is lazy-imported at first use
@ -35,6 +38,9 @@ if TYPE_CHECKING:
# `import tractor` path (gh #470).
from multiaddr import Multiaddr
from tractor.discovery._addr import Address
else:
Multiaddr = Any
Address = Any
# map from tractor-internal `proto_key` identifiers
# to the standard multiaddr protocol name strings.

View File

@ -20,6 +20,7 @@ TCP implementation of tractor.ipc._transport.MsgTransport protocol
from __future__ import annotations
import ipaddress
from typing import (
Any,
ClassVar,
TYPE_CHECKING,
)
@ -46,6 +47,8 @@ if TYPE_CHECKING:
# ONLY type-annots, the eager import costs
# `import tractor` wall-time (gh #470).
from multiaddr import Multiaddr
else:
Multiaddr = Any
log = get_logger()

View File

@ -32,6 +32,7 @@ from socket import (
)
import struct
from typing import (
Any,
Type,
TYPE_CHECKING,
ClassVar,
@ -66,6 +67,9 @@ if TYPE_CHECKING:
# `import tractor` wall-time (gh #470).
from multiaddr import Multiaddr
from tractor.runtime._runtime import Actor
else:
Multiaddr = Any
Actor = Any
# Platform-specific credential passing constants