From 7987f6b8d788646496a5e9b247aa85496e1c1493 Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 12 Aug 2026 20:52:15 -0400 Subject: [PATCH] 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`)) --- tests/test_lazy_imports.py | 41 +++++++++++++++++++++++++++++++++ tractor/discovery/_addr.py | 6 +++-- tractor/discovery/_multiaddr.py | 8 ++++++- tractor/ipc/_tcp.py | 3 +++ tractor/ipc/_uds.py | 4 ++++ 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 tests/test_lazy_imports.py diff --git a/tests/test_lazy_imports.py b/tests/test_lazy_imports.py new file mode 100644 index 00000000..cdafe6a8 --- /dev/null +++ b/tests/test_lazy_imports.py @@ -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') diff --git a/tractor/discovery/_addr.py b/tractor/discovery/_addr.py index ba473b69..8d440bb3 100644 --- a/tractor/discovery/_addr.py +++ b/tractor/discovery/_addr.py @@ -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 } diff --git a/tractor/discovery/_multiaddr.py b/tractor/discovery/_multiaddr.py index 3b7b79f6..d3afdb28 100644 --- a/tractor/discovery/_multiaddr.py +++ b/tractor/discovery/_multiaddr.py @@ -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. diff --git a/tractor/ipc/_tcp.py b/tractor/ipc/_tcp.py index 75519f3b..421afb08 100644 --- a/tractor/ipc/_tcp.py +++ b/tractor/ipc/_tcp.py @@ -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() diff --git a/tractor/ipc/_uds.py b/tractor/ipc/_uds.py index ca9eaca7..6dae10ec 100644 --- a/tractor/ipc/_uds.py +++ b/tractor/ipc/_uds.py @@ -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