diff --git a/tractor/devx/_frame_stack.py b/tractor/devx/_frame_stack.py index 9c722def..cf067a5a 100644 --- a/tractor/devx/_frame_stack.py +++ b/tractor/devx/_frame_stack.py @@ -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=) @@ -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 diff --git a/tractor/discovery/_addr.py b/tractor/discovery/_addr.py index 2697c5c9..ba473b69 100644 --- a/tractor/discovery/_addr.py +++ b/tractor/discovery/_addr.py @@ -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() diff --git a/tractor/discovery/_multiaddr.py b/tractor/discovery/_multiaddr.py index 74076fd4..3b7b79f6 100644 --- a/tractor/discovery/_multiaddr.py +++ b/tractor/discovery/_multiaddr.py @@ -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 diff --git a/tractor/ipc/_tcp.py b/tractor/ipc/_tcp.py index 293ae4be..75519f3b 100644 --- a/tractor/ipc/_tcp.py +++ b/tractor/ipc/_tcp.py @@ -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() diff --git a/tractor/ipc/_uds.py b/tractor/ipc/_uds.py index d1e9d2f6..ca9eaca7 100644 --- a/tractor/ipc/_uds.py +++ b/tractor/ipc/_uds.py @@ -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 diff --git a/tractor/log.py b/tractor/log.py index 53253f83..de07a7bd 100644 --- a/tractor/log.py +++ b/tractor/log.py @@ -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( diff --git a/tractor/runtime/_state.py b/tractor/runtime/_state.py index 0d9a4435..0020fd8d 100644 --- a/tractor/runtime/_state.py +++ b/tractor/runtime/_state.py @@ -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,