Compare commits
No commits in common. "66ac7863b5b61160152b352f1cf28e303012b2de" and "70c7e334a773617dbcaa69380252ab4e07412fb7" have entirely different histories.
66ac7863b5
...
70c7e334a7
|
|
@ -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')
|
|
||||||
|
|
@ -2,11 +2,9 @@
|
||||||
`tractor.log`-wrapping unit tests.
|
`tractor.log`-wrapping unit tests.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
import importlib
|
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
@ -167,53 +165,6 @@ def test_implicit_mod_name_applied_for_child(
|
||||||
assert submod.log.logger in sub_logs
|
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():
|
def test_io_custom_level_registered():
|
||||||
'''
|
'''
|
||||||
The `IO`(21) level (registered via `add_log_level()` at
|
The `IO`(21) level (registered via `add_log_level()` at
|
||||||
|
|
|
||||||
|
|
@ -76,19 +76,6 @@ from .discovery._registry import (
|
||||||
# from . import hilevel as hilevel
|
# 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):
|
def __getattr__(name: str):
|
||||||
'''
|
'''
|
||||||
PEP 562 lazy sub-module loading, presently only for
|
PEP 562 lazy sub-module loading, presently only for
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
|
||||||
Protocol,
|
Protocol,
|
||||||
ClassVar,
|
ClassVar,
|
||||||
Type,
|
Type,
|
||||||
|
|
@ -37,9 +36,8 @@ from ..ipc._uds import UDSAddress
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
# ONLY type-annots, the eager import costs ~4.5ms
|
# ONLY type-annots, the eager import costs ~4.5ms
|
||||||
# of `import tractor` wall-time (gh #470).
|
# of `import tractor` wall-time (gh #470).
|
||||||
|
from bidict import bidict
|
||||||
from ..runtime._runtime import Actor
|
from ..runtime._runtime import Actor
|
||||||
else:
|
|
||||||
Actor = Any
|
|
||||||
|
|
||||||
log = get_logger()
|
log = get_logger()
|
||||||
|
|
||||||
|
|
@ -174,7 +172,7 @@ class Address(Protocol):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
_address_types: dict[str, Type[Address]] = {
|
_address_types: bidict[str, Type[Address]] = {
|
||||||
'tcp': TCPAddress,
|
'tcp': TCPAddress,
|
||||||
'uds': UDSAddress
|
'uds': UDSAddress
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,7 @@ Multiaddress support using the upstream `py-multiaddr` lib
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import ipaddress
|
import ipaddress
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import (
|
from typing import TYPE_CHECKING
|
||||||
Any,
|
|
||||||
TYPE_CHECKING,
|
|
||||||
)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
# NOTE, `multiaddr` is lazy-imported at first use
|
# NOTE, `multiaddr` is lazy-imported at first use
|
||||||
|
|
@ -38,9 +35,6 @@ if TYPE_CHECKING:
|
||||||
# `import tractor` path (gh #470).
|
# `import tractor` path (gh #470).
|
||||||
from multiaddr import Multiaddr
|
from multiaddr import Multiaddr
|
||||||
from tractor.discovery._addr import Address
|
from tractor.discovery._addr import Address
|
||||||
else:
|
|
||||||
Multiaddr = Any
|
|
||||||
Address = Any
|
|
||||||
|
|
||||||
# map from tractor-internal `proto_key` identifiers
|
# map from tractor-internal `proto_key` identifiers
|
||||||
# to the standard multiaddr protocol name strings.
|
# to the standard multiaddr protocol name strings.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ TCP implementation of tractor.ipc._transport.MsgTransport protocol
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import ipaddress
|
import ipaddress
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
|
||||||
ClassVar,
|
ClassVar,
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
|
|
@ -47,8 +46,6 @@ if TYPE_CHECKING:
|
||||||
# ONLY type-annots, the eager import costs
|
# ONLY type-annots, the eager import costs
|
||||||
# `import tractor` wall-time (gh #470).
|
# `import tractor` wall-time (gh #470).
|
||||||
from multiaddr import Multiaddr
|
from multiaddr import Multiaddr
|
||||||
else:
|
|
||||||
Multiaddr = Any
|
|
||||||
|
|
||||||
|
|
||||||
log = get_logger()
|
log = get_logger()
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ from socket import (
|
||||||
)
|
)
|
||||||
import struct
|
import struct
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
|
||||||
Type,
|
Type,
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
ClassVar,
|
ClassVar,
|
||||||
|
|
@ -67,9 +66,6 @@ if TYPE_CHECKING:
|
||||||
# `import tractor` wall-time (gh #470).
|
# `import tractor` wall-time (gh #470).
|
||||||
from multiaddr import Multiaddr
|
from multiaddr import Multiaddr
|
||||||
from tractor.runtime._runtime import Actor
|
from tractor.runtime._runtime import Actor
|
||||||
else:
|
|
||||||
Multiaddr = Any
|
|
||||||
Actor = Any
|
|
||||||
|
|
||||||
|
|
||||||
# Platform-specific credential passing constants
|
# Platform-specific credential passing constants
|
||||||
|
|
|
||||||
|
|
@ -463,14 +463,7 @@ def get_logger(
|
||||||
if mod_name is None:
|
if mod_name is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if caller_mod := sys.modules.get(mod_name):
|
return 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)
|
|
||||||
|
|
||||||
# --- Auto--naming-CASE ---
|
# --- Auto--naming-CASE ---
|
||||||
# -------------------------
|
# -------------------------
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue