Compare commits

..

No commits in common. "66ac7863b5b61160152b352f1cf28e303012b2de" and "70c7e334a773617dbcaa69380252ab4e07412fb7" have entirely different histories.

8 changed files with 4 additions and 180 deletions

View File

@ -1,92 +0,0 @@
'''
Regression tests for the cold package import surface.
'''
import json
import subprocess
import sys
from typing import (
Any,
get_type_hints,
)
from tractor.discovery import (
_addr,
_multiaddr,
)
from tractor.ipc import (
_tcp,
_uds,
)
def run_cold_import(code: str) -> dict[str, object]:
result = subprocess.run(
[
sys.executable,
'-c',
code,
],
check=True,
capture_output=True,
text=True,
)
return json.loads(result.stdout)
def test_lazy_to_asyncio_package_api():
'''
Keep the public lazy submodule discoverable without eagerly
importing it.
Before the lazy conversion, package import side effects exposed
`to_asyncio` to `dir()` and wildcard imports. Exercise those APIs
in cold interpreters so this test proves normal `import tractor`
leaves `asyncio` unloaded, while discovery and wildcard access
still advertise and resolve the public submodule.
'''
cold = run_cold_import(
'import json, sys, tractor; '
'print(json.dumps({'
'"advertised": "to_asyncio" in dir(tractor), '
'"asyncio_loaded": "asyncio" in sys.modules}))'
)
assert cold == {
'advertised': True,
'asyncio_loaded': False,
}
wildcard = run_cold_import(
'import json; '
'from tractor import *; '
'print(json.dumps({'
'"module": to_asyncio.__name__}))'
)
assert wildcard == {
'module': 'tractor.to_asyncio',
}
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

@ -2,11 +2,9 @@
`tractor.log`-wrapping unit tests.
'''
import importlib
import logging
from pathlib import Path
import shutil
import sys
from types import ModuleType
import pytest
@ -167,53 +165,6 @@ def test_implicit_mod_name_applied_for_child(
assert submod.log.logger in sub_logs
def test_implicit_mod_name_from_unregistered_namespace(
tmp_path: Path,
):
'''
Preserve implicit logger naming for dynamic module namespaces.
The fast `sys.modules` caller lookup cannot resolve `runpy`,
plugin-loader, or `exec()` namespaces that are not registered.
Compile a real package file under an unregistered module name so
the rare filename fallback must recover its imported package and
retain the same package-level logger name.
'''
pkg_name = 'dynamic_logger_pkg'
pkg_dir = tmp_path / pkg_name
pkg_dir.mkdir()
init_path = pkg_dir / '__init__.py'
init_path.write_text('')
mod_path = pkg_dir / 'plugin.py'
mod_path.write_text('')
sys.path.insert(0, str(tmp_path))
try:
importlib.import_module(pkg_name)
namespace = {
'__name__': f'{pkg_name}.unregistered',
'__package__': pkg_name,
'tractor': tractor,
}
exec(
compile(
'log = tractor.log.get_logger('
f'pkg_name={pkg_name!r})',
str(mod_path),
'exec',
),
namespace,
)
dynamic_log = namespace.get('log')
finally:
sys.path.remove(str(tmp_path))
sys.modules.pop(pkg_name, None)
assert dynamic_log is not None
assert dynamic_log.name == pkg_name
def test_io_custom_level_registered():
'''
The `IO`(21) level (registered via `add_log_level()` at

View File

@ -76,19 +76,6 @@ from .discovery._registry import (
# from . import hilevel as hilevel
__all__: tuple[str, ...] = tuple(
name
for name in globals()
if not name.startswith('_')
) + (
'to_asyncio',
)
def __dir__() -> list[str]:
return sorted(set(globals()) | set(__all__))
def __getattr__(name: str):
'''
PEP 562 lazy sub-module loading, presently only for

View File

@ -16,7 +16,6 @@
from __future__ import annotations
from uuid import uuid4
from typing import (
Any,
Protocol,
ClassVar,
Type,
@ -37,9 +36,8 @@ 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()
@ -174,7 +172,7 @@ class Address(Protocol):
...
_address_types: dict[str, Type[Address]] = {
_address_types: bidict[str, Type[Address]] = {
'tcp': TCPAddress,
'uds': UDSAddress
}

View File

@ -27,10 +27,7 @@ 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
if TYPE_CHECKING:
# NOTE, `multiaddr` is lazy-imported at first use
@ -38,9 +35,6 @@ 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,7 +20,6 @@ TCP implementation of tractor.ipc._transport.MsgTransport protocol
from __future__ import annotations
import ipaddress
from typing import (
Any,
ClassVar,
TYPE_CHECKING,
)
@ -47,8 +46,6 @@ 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,7 +32,6 @@ from socket import (
)
import struct
from typing import (
Any,
Type,
TYPE_CHECKING,
ClassVar,
@ -67,9 +66,6 @@ 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

View File

@ -463,14 +463,7 @@ def get_logger(
if mod_name is None:
return None
if caller_mod := sys.modules.get(mod_name):
return caller_mod
# Preserve caller discovery for `runpy`, plugin loaders,
# and `exec()` namespaces not registered in `sys.modules`.
# Import `inspect` only on this rare fallback path.
from inspect import getmodule
return getmodule(caller_frame)
return sys.modules.get(mod_name)
# --- Auto--naming-CASE ---
# -------------------------