Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions tractor/_addr.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
TYPE_CHECKING,
)

import logging
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'logging' is not used.

Suggested change
import logging

Copilot uses AI. Check for mistakes.
import platform
import socket


from bidict import bidict
from trio import (
SocketListener,
Expand All @@ -32,14 +37,31 @@
_def_tpt_proto,
)
from .ipc._tcp import TCPAddress
from .ipc._uds import UDSAddress


if TYPE_CHECKING:
from ._runtime import Actor

log = get_logger(__name__)


HAS_AF_UNIX = getattr(socket, "AF_UNIX", None) is not None
IS_WINDOWS = platform.system() == "Windows"

UDSAddress = None # so references exist but do nothing on Windows

if HAS_AF_UNIX and not IS_WINDOWS:
try:
from .ipc._uds import UDSAddress as _UDSAddress
UDSAddress = _UDSAddress
except Exception as e:
log.warning("UDS backend import failed: %s", e)
else:
log.warning("UDS backend disabled on this platform.")

if TYPE_CHECKING:
from ._runtime import Actor

Comment on lines +62 to +64
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duplicate if TYPE_CHECKING: block should be removed. There's already a TYPE_CHECKING block at line 42-43, and this duplicate at lines 62-63 is redundant.

Suggested change
if TYPE_CHECKING:
from ._runtime import Actor

Copilot uses AI. Check for mistakes.
# TODO, maybe breakout the netns key to a struct?
# class NetNs(Struct)[str, int]:
# ...
Expand Down Expand Up @@ -172,20 +194,25 @@ async def close_listener(self):

_address_types: bidict[str, Type[Address]] = {
'tcp': TCPAddress,
'uds': UDSAddress
}

if UDSAddress is not None:
_address_types['uds'] = UDSAddress
else:
log.warning("Skipping UDS address type: no UDS backend available.")


# TODO! really these are discovery sys default addrs ONLY useful for
# when none is provided to a root actor on first boot.
_default_lo_addrs: dict[
str,
UnwrappedAddress
] = {
_default_lo_addrs: dict[str, UnwrappedAddress] = {
'tcp': TCPAddress.get_root().unwrap(),
'uds': UDSAddress.get_root().unwrap(),
}

if UDSAddress is not None:
_default_lo_addrs['uds'] = UDSAddress.get_root().unwrap()
else:
log.warning("Skipping UDS default loopback address: no UDS backend available.")


def get_address_cls(name: str) -> Type[Address]:
return _address_types[name]
Expand Down
14 changes: 12 additions & 2 deletions tractor/devx/_stackscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,22 @@
RLock,
)
import multiprocessing as mp
from signal import (

import platform
import signal
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Module 'signal' is imported with both 'import' and 'import from'.

Suggested change
import signal

Copilot uses AI. Check for mistakes.

from signal import (
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace detected at the end of line 38. This should be removed for code cleanliness.

Suggested change
from signal import (
from signal import (

Copilot uses AI. Check for mistakes.
signal,
getsignal,
SIGUSR1,
SIGINT,
)


if platform.system() != "Windows":
from signal import SIGUSR1
else:
SIGUSR1 = None

# import traceback
from types import ModuleType
from typing import (
Expand Down
13 changes: 11 additions & 2 deletions tractor/ipc/_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
from itertools import chain
import inspect
import textwrap
import platform
import socket
from types import (
ModuleType,
)
Expand Down Expand Up @@ -64,16 +66,23 @@
from .._addr import Address
from ._chan import Channel
from ._transport import MsgTransport
from ._uds import UDSAddress
from ._tcp import TCPAddress


if TYPE_CHECKING:
from .._runtime import Actor
from .._supervise import ActorNursery


from ._tcp import TCPAddress

log = log.get_logger(__name__)

UDSAddress = None

if getattr(socket, "AF_UNIX", None) is not None and platform.system() != "Windows":
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The platform detection pattern here differs from the approach used in _types.py and _addr.py. For consistency, consider defining HAS_AF_UNIX and IS_WINDOWS variables at the module level (like in the other files) and reusing them here: if HAS_AF_UNIX and not IS_WINDOWS:. This would make the codebase more maintainable and ensure consistent behavior across all Windows compatibility checks.

Copilot uses AI. Check for mistakes.
from ._uds import UDSAddress
else:
pass

async def maybe_wait_on_canced_subs(
uid: tuple[str, str],
Expand Down
132 changes: 79 additions & 53 deletions tractor/ipc/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,106 +18,132 @@
IPC subsys type-lookup helpers?

'''
from typing import (
Type,
# TYPE_CHECKING,
)

import trio
from typing import Type
import logging
import platform
import socket
import trio

from tractor.ipc._transport import (
MsgTransportKey,
MsgTransport
MsgTransport,
)
from tractor.ipc._tcp import (
TCPAddress,
MsgpackTCPStream,
)
from tractor.ipc._uds import (
UDSAddress,
MsgpackUDSStream,
)

# if TYPE_CHECKING:
# from tractor._addr import Address
log = logging.getLogger(__name__)

# ------------------------------------------------------------
# Optional UDS backend (Windows / some Pythons may not have AF_UNIX)
# ------------------------------------------------------------
HAS_AF_UNIX = getattr(socket, "AF_UNIX", None) is not None
IS_WINDOWS = platform.system() == "Windows"

Address = TCPAddress|UDSAddress
HAS_UDS = False
UDSAddress = None # type: ignore
MsgpackUDSStream = None # type: ignore

# manually updated list of all supported msg transport types
_msg_transports = [
if HAS_AF_UNIX and not IS_WINDOWS:
try:
from tractor.ipc._uds import ( # type: ignore
UDSAddress as _UDSAddress,
MsgpackUDSStream as _MsgpackUDSStream,
)
UDSAddress = _UDSAddress # type: ignore
MsgpackUDSStream = _MsgpackUDSStream # type: ignore
HAS_UDS = True
except Exception as e:
log.warning("UDS backend unavailable (%s); continuing without it.", e)
else:
if not HAS_AF_UNIX:
log.warning("AF_UNIX not exposed by this Python; disabling UDS backend.")
elif IS_WINDOWS:
# Even if the Windows kernel supports AF_UNIX, CPython may not expose it,
# and this project currently targets POSIX for the UDS backend.
log.warning("Windows detected; disabling UDS backend.")

# ------------------------------------------------------------
# Public types and transport registries
# ------------------------------------------------------------

# Address is TCP-only unless UDS is available.
if HAS_UDS:
Address = TCPAddress | UDSAddress # type: ignore
else:
Address = TCPAddress # type: ignore

# Manually updated list of all supported msg transport types
_msg_transports: list[Type[MsgTransport]] = [
MsgpackTCPStream,
MsgpackUDSStream
]
if HAS_UDS:
_msg_transports.append(MsgpackUDSStream) # type: ignore


# convert a MsgTransportKey to the corresponding transport type
_key_to_transport: dict[
MsgTransportKey,
Type[MsgTransport],
] = {
('msgpack', 'tcp'): MsgpackTCPStream,
('msgpack', 'uds'): MsgpackUDSStream,
# Map MsgTransportKey -> transport type
_key_to_transport: dict[MsgTransportKey, Type[MsgTransport]] = {
("msgpack", "tcp"): MsgpackTCPStream,
}
if HAS_UDS:
_key_to_transport[("msgpack", "uds")] = MsgpackUDSStream # type: ignore

# convert an Address wrapper to its corresponding transport type
_addr_to_transport: dict[
Type[TCPAddress|UDSAddress],
Type[MsgTransport]
] = {
# Map Address wrapper -> transport type
_addr_to_transport: dict[Type[Address], Type[MsgTransport]] = { # type: ignore
TCPAddress: MsgpackTCPStream,
UDSAddress: MsgpackUDSStream,
}
if HAS_UDS:
_addr_to_transport[UDSAddress] = MsgpackUDSStream # type: ignore


# ------------------------------------------------------------
# Helpers
# ------------------------------------------------------------
def transport_from_addr(
addr: Address,
codec_key: str = 'msgpack',
codec_key: str = "msgpack",
) -> Type[MsgTransport]:
'''
"""
Given a destination address and a desired codec, find the
corresponding `MsgTransport` type.

'''
"""
try:
return _addr_to_transport[type(addr)]

return _addr_to_transport[type(addr)] # type: ignore[call-arg]
except KeyError:
raise NotImplementedError(
f'No known transport for address {repr(addr)}'
f"No known transport for address {repr(addr)}"
)


def transport_from_stream(
stream: trio.abc.Stream,
codec_key: str = 'msgpack'
codec_key: str = "msgpack",
) -> Type[MsgTransport]:
'''
"""
Given an arbitrary `trio.abc.Stream` and a desired codec,
find the corresponding `MsgTransport` type.

'''
"""
transport = None

if isinstance(stream, trio.SocketStream):
sock: socket.socket = stream.socket
match sock.family:
case socket.AF_INET | socket.AF_INET6:
transport = 'tcp'
fam = sock.family

if fam in (socket.AF_INET, getattr(socket, "AF_INET6", None)):
transport = "tcp"

case socket.AF_UNIX:
transport = 'uds'
# Only consider AF_UNIX when both Python exposes it and our backend is active
if transport is None and HAS_UDS and HAS_AF_UNIX and fam == socket.AF_UNIX: # type: ignore[attr-defined]
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition checks HAS_AF_UNIX and then accesses socket.AF_UNIX, but HAS_AF_UNIX is already defined as getattr(socket, "AF_UNIX", None) is not None, so the check is redundant. Consider simplifying to just if transport is None and HAS_UDS and fam == socket.AF_UNIX: since HAS_AF_UNIX is already validated by HAS_UDS being True (line 48 ensures UDS is only enabled when HAS_AF_UNIX is True).

Suggested change
if transport is None and HAS_UDS and HAS_AF_UNIX and fam == socket.AF_UNIX: # type: ignore[attr-defined]
if transport is None and HAS_UDS and fam == socket.AF_UNIX: # type: ignore[attr-defined]

Copilot uses AI. Check for mistakes.
transport = "uds"

case _:
raise NotImplementedError(
f'Unsupported socket family: {sock.family}'
)
if transport is None:
raise NotImplementedError(f"Unsupported socket family: {fam}")

if not transport:
raise NotImplementedError(
f'Could not figure out transport type for stream type {type(stream)}'
f"Could not figure out transport type for stream type {type(stream)}"
)

key = (codec_key, transport)

return _key_to_transport[key]

Loading