Lazy-import optional 3rd-party deps (gh #470)

Move every import-time-only-by-accident dep off the eager
`import tractor` path so cold child-actor boots only pay for
what they actually use:

- `bidict` -> `TYPE_CHECKING` in `discovery._addr`
  (annotation-only; `_address_types` is a plain `dict`
  literal).
- `multiaddr` -> `TYPE_CHECKING` + fn-local imports in
  `discovery._multiaddr.mk_maddr()`/`parse_maddr()`; also
  `TYPE_CHECKING` the `Multiaddr` annots in `ipc._tcp`/`._uds`
  (adds future-annots to `._multiaddr`).
- `colorlog` -> fn-local in `log.get_console_log()`.
- `pdbp` + `wrapt` -> fn-local in
  `devx._frame_stack.hide_runtime_frames()`/`api_frame()`.
- `platformdirs` -> fn-local in `runtime._state.get_rt_dir()`.

Still eager (documented follow-ups),
- `pdbp` via `devx.debug._repl` class-bases
  (`PdbREPL(pdbp.Pdb)`) + the module-lvl `@pdbp.hideframe` in
  `._tty_lock`; needs a `._repl` restructure.
- `platformdirs` via the `UDSAddress.def_bindspace: ClassVar`
  class-body eval of `get_rt_dir()`; needs an `Address`-proto
  rework.
- `stackscope` is already fn-local; `setproctitle` is not
  imported anywhere.

Prompt-IO: ai/prompt-io/claude/20260702T155626Z_65bf9df5_prompt_io.md
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
boot_latency_470
Gud Boi 2026-07-02 12:19:27 -04:00
parent d653cd2c26
commit 4f811a87f9
7 changed files with 38 additions and 9 deletions

View File

@ -39,14 +39,15 @@ from typing import (
Type,
)
import pdbp
# NOTE, `pdbp` + `wrapt` are lazy-imported at their
# single use-sites below to keep them off the eager
# `import tractor` path (gh #470).
from tractor.log import get_logger
import trio
from tractor.msg import (
pretty_struct,
NamespacePath,
)
import wrapt
log = get_logger()
@ -257,6 +258,7 @@ def api_frame(
caller_frames_up: int = 1,
) -> Callable:
import wrapt
# handle the decorator called WITHOUT () case,
# i.e. just @api_frame, NOT @api_frame(extra=<blah>)
@ -320,6 +322,8 @@ def hide_runtime_frames() -> dict[FunctionType, CodeType]:
as possible, particularly from inside a `PdbREPL`.
'''
import pdbp
# XXX HACKZONE XXX
# hide exit stack frames on nurseries and cancel-scopes!
# |_ so avoid seeing it when the `pdbp` REPL is first engaged from

View File

@ -22,7 +22,6 @@ from typing import (
TYPE_CHECKING,
)
from bidict import bidict
from trio import (
SocketListener,
)
@ -35,6 +34,9 @@ from ..ipc._tcp import TCPAddress
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
log = get_logger()

View File

@ -24,13 +24,16 @@ Multiaddress support using the upstream `py-multiaddr` lib
- https://github.com/multiformats/multiaddr/blob/master/protocols/unix.md
'''
from __future__ import annotations
import ipaddress
from pathlib import Path
from typing import TYPE_CHECKING
from multiaddr import Multiaddr
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
# map from tractor-internal `proto_key` identifiers
@ -56,6 +59,8 @@ def mk_maddr(
multiaddr-spec-compliant protocol path.
'''
from multiaddr import Multiaddr
proto_key: str = addr.proto_key
maddr_proto: str|None = _tpt_proto_to_maddr.get(proto_key)
if maddr_proto is None:
@ -98,6 +103,7 @@ def parse_maddr(
'''
# lazy imports to avoid circular deps
from multiaddr import Multiaddr
from tractor.ipc._tcp import TCPAddress
from tractor.ipc._uds import UDSAddress

View File

@ -21,6 +21,7 @@ from __future__ import annotations
import ipaddress
from typing import (
ClassVar,
TYPE_CHECKING,
)
# from contextlib import (
# asynccontextmanager as acm,
@ -33,7 +34,6 @@ from trio import (
open_tcp_listeners,
)
from multiaddr import Multiaddr
from tractor.msg import MsgCodec
from tractor.log import get_logger
from tractor.discovery._multiaddr import mk_maddr
@ -42,6 +42,11 @@ from tractor.ipc._transport import (
MsgpackTransport,
)
if TYPE_CHECKING:
# ONLY type-annots, the eager import costs
# `import tractor` wall-time (gh #470).
from multiaddr import Multiaddr
log = get_logger()

View File

@ -49,7 +49,6 @@ from trio._highlevel_open_unix_stream import (
has_unix,
)
from multiaddr import Multiaddr
from tractor.msg import MsgCodec
from tractor.log import get_logger
from tractor.discovery._multiaddr import mk_maddr
@ -63,6 +62,9 @@ from tractor.runtime._state import (
)
if TYPE_CHECKING:
# ONLY type-annots, the eager import costs
# `import tractor` wall-time (gh #470).
from multiaddr import Multiaddr
from tractor.runtime._runtime import Actor

View File

@ -39,7 +39,10 @@ from types import (
)
import warnings
import colorlog # type: ignore
# NOTE, `colorlog` is lazy-imported in
# `get_console_log()` to keep it off the eager
# `import tractor` path (gh #470).
#
# ?TODO, some other (modern) alt libs?
# import coloredlogs
# import colored_traceback.auto # ?TODO, need better config?
@ -797,6 +800,10 @@ def get_console_log(
None,
)
):
# lazy-imported to keep it off the eager
# `import tractor` path (gh #470).
import colorlog # type: ignore
fmt: str = LOG_FORMAT # always apply our format?
handler = StreamHandler()
formatter = colorlog.ColoredFormatter(

View File

@ -30,7 +30,6 @@ from typing import (
TYPE_CHECKING,
)
import platformdirs
from trio.lowlevel import current_task
from msgspec import (
@ -332,6 +331,10 @@ def get_rt_dir(
the lovely `platformdirs` lib.
'''
# lazy-imported to keep it off the eager
# `import tractor` path (gh #470).
import platformdirs
rt_dir: Path = Path(
platformdirs.user_runtime_dir(
appname=appname,