Skip to content
Closed
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
203 changes: 103 additions & 100 deletions libp2p/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,107 @@ async def listen_close(self, network: "INetwork", multiaddr: Multiaddr) -> None:
"""


class IMultiselectCommunicator(ABC):
"""
Communicator helper for multiselect.

Ensures that both the client and multistream module follow the same
multistream protocol.
"""

@abstractmethod
async def write(self, msg_str: str) -> None:
"""
Write a message to the stream.

Parameters
----------
msg_str : str
The message string to write.

"""

@abstractmethod
async def read(self) -> str:
"""
Read a message from the stream until EOF.

Returns
-------
str
The message read from the stream.

"""


# -------------------------- multiselect_muxer interface.py --------------------------


class IMultiselectMuxer(ABC):
"""
Multiselect module for protocol negotiation.

Responsible for responding to a multiselect client by selecting a protocol
and its corresponding handler for communication.
"""

handlers: dict[TProtocol | None, StreamHandlerFn | None]

@abstractmethod
def add_handler(self, protocol: TProtocol, handler: StreamHandlerFn) -> None:
"""
Store a handler for the specified protocol.

Parameters
----------
protocol : TProtocol
The protocol name.
handler : StreamHandlerFn
The handler function associated with the protocol.

"""

@abstractmethod # Ensure this is present if it was implicitly removed
def get_protocols(self) -> tuple[TProtocol, ...]:
"""
Retrieve the protocols for which handlers have been registered.

Returns
-------
tuple[TProtocol, ...]
A tuple of registered protocol names.

"""
# For an abstract method, the body might be empty or a simple `pass`.
# If it was `return tuple(self.handlers.keys())`, it should remain unchanged.
# The key is the type annotation above.
pass # Or whatever the abstract method's body was

@abstractmethod
async def negotiate(
self, communicator: IMultiselectCommunicator
) -> tuple[TProtocol | None, StreamHandlerFn | None]:
"""
Negotiate a protocol selection with a multiselect client.

Parameters
----------
communicator : IMultiselectCommunicator
The communicator used to negotiate the protocol.

Returns
-------
tuple[TProtocol, StreamHandlerFn]
A tuple containing the selected protocol and its handler.

Raises
------
Exception
If negotiation fails.

"""


# -------------------------- host interface.py --------------------------


Expand Down Expand Up @@ -1545,15 +1646,14 @@ def get_network(self) -> INetworkService:

"""

# FIXME: Replace with correct return type
@abstractmethod
def get_mux(self) -> Any:
def get_mux(self) -> IMultiselectMuxer:
"""
Retrieve the muxer instance for the host.

Returns
-------
Any
IMultiselectMuxer
The muxer instance of the host.

"""
Expand Down Expand Up @@ -2016,39 +2116,6 @@ def is_expired(self) -> bool:
# ------------------ multiselect_communicator interface.py ------------------


class IMultiselectCommunicator(ABC):
"""
Communicator helper for multiselect.

Ensures that both the client and multistream module follow the same
multistream protocol.
"""

@abstractmethod
async def write(self, msg_str: str) -> None:
"""
Write a message to the stream.

Parameters
----------
msg_str : str
The message string to write.

"""

@abstractmethod
async def read(self) -> str:
"""
Read a message from the stream until EOF.

Returns
-------
str
The message read from the stream.

"""


# -------------------------- multiselect_client interface.py --------------------------


Expand Down Expand Up @@ -2131,70 +2198,6 @@ async def try_select(
"""


# -------------------------- multiselect_muxer interface.py --------------------------


class IMultiselectMuxer(ABC):
"""
Multiselect module for protocol negotiation.

Responsible for responding to a multiselect client by selecting a protocol
and its corresponding handler for communication.
"""

handlers: dict[TProtocol | None, StreamHandlerFn | None]

@abstractmethod
def add_handler(self, protocol: TProtocol, handler: StreamHandlerFn) -> None:
"""
Store a handler for the specified protocol.

Parameters
----------
protocol : TProtocol
The protocol name.
handler : StreamHandlerFn
The handler function associated with the protocol.

"""

def get_protocols(self) -> tuple[TProtocol | None, ...]:
"""
Retrieve the protocols for which handlers have been registered.

Returns
-------
tuple[TProtocol, ...]
A tuple of registered protocol names.

"""
return tuple(self.handlers.keys())

@abstractmethod
async def negotiate(
self, communicator: IMultiselectCommunicator
) -> tuple[TProtocol | None, StreamHandlerFn | None]:
"""
Negotiate a protocol selection with a multiselect client.

Parameters
----------
communicator : IMultiselectCommunicator
The communicator used to negotiate the protocol.

Returns
-------
tuple[TProtocol, StreamHandlerFn]
A tuple containing the selected protocol and its handler.

Raises
------
Exception
If negotiation fails.

"""


# -------------------------- routing interface.py --------------------------


Expand Down
3 changes: 2 additions & 1 deletion libp2p/host/basic_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from libp2p.abc import (
IHost,
IMultiselectMuxer,
INetConn,
INetStream,
INetworkService,
Expand Down Expand Up @@ -130,7 +131,7 @@ def get_peerstore(self) -> IPeerStore:
"""
return self.peerstore

def get_mux(self) -> Multiselect:
def get_mux(self) -> IMultiselectMuxer:
"""
:return: mux instance of host
"""
Expand Down
13 changes: 13 additions & 0 deletions libp2p/protocol_muxer/multiselect.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ def add_handler(
"""
self.handlers[protocol] = handler

def get_protocols(self) -> tuple[TProtocol, ...]:
"""
Retrieve the protocols for which handlers have been registered.

Returns
-------
tuple[TProtocol, ...]
A tuple of registered protocol names.

"""
# Filter out None values, as they are not considered valid "protocols"
return tuple(p for p in self.handlers.keys() if p is not None)

# FIXME: Make TProtocol Optional[TProtocol] to keep types consistent
async def negotiate(
self,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -279,4 +279,4 @@ project_excludes = [
"**/*pb2.py",
"**/*.pyi",
".venv/**",
]
]
5 changes: 5 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import pytest

pytest_plugins = ["trio"]


@pytest.fixture
def security_protocol():
return None


pytest_plugins = ["trio"]
Loading
Loading