Skip to content
Merged
7 changes: 6 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,12 @@ Multiaddr provides thin waist address validation functionality to process multia
.. code-block:: python

from multiaddr import Multiaddr
from multiaddr.utils import get_thin_waist_addresses, get_network_addrs
from multiaddr.utils import get_thin_waist_addresses, get_network_addrs, interface_multiaddrs

# List local interfaces as Multiaddr objects
for ma in interface_multiaddrs():
print(ma)
# Multiaddr('/ip4/192.168.1.10'), ...

# Network interface discovery
ipv4_addrs = get_network_addrs(4)
Expand Down
7 changes: 6 additions & 1 deletion examples/thin_waist/thin_waist_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import sys

from multiaddr import Multiaddr
from multiaddr.utils import get_network_addrs, get_thin_waist_addresses
from multiaddr.utils import get_network_addrs, get_thin_waist_addresses, interface_multiaddrs


def show_network_info():
Expand All @@ -28,6 +28,11 @@ def show_network_info():
print(f"Available IPv6 addresses: {ipv6_addrs}")
print()

print("=== interface_multiaddrs() ===")
for ma in interface_multiaddrs():
print(f" {ma}")
print()


def basic_examples():
"""Show basic thin waist address validation examples."""
Expand Down
2 changes: 2 additions & 0 deletions multiaddr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
get_multiaddr_options,
get_network_addrs,
get_thin_waist_addresses,
interface_multiaddrs,
is_ip6_link_local,
is_ip_loopback,
is_ip_unspecified,
Expand Down Expand Up @@ -86,6 +87,7 @@
"get_multiaddr_options",
"get_network_addrs",
"get_thin_waist_addresses",
"interface_multiaddrs",
"is_ip6_link_local",
"is_ip_loopback",
"is_ip_unspecified",
Expand Down
14 changes: 14 additions & 0 deletions multiaddr/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,17 @@ def get_thin_waist_addresses(
# Return the specific address
addr_str = f"/{ip_proto}/{options['host']}/{options['transport']}/{target_port}"
return [Multiaddr(addr_str)]


def interface_multiaddrs() -> list[Multiaddr]:
"""Return local interface addresses as Multiaddr objects.

Uses :func:`get_network_addrs` for IPv4 and IPv6. Loopback and link-local
addresses are excluded (same filtering as ``get_network_addrs``).
"""
result: list[Multiaddr] = []
for host in get_network_addrs(4):
result.append(Multiaddr(f"/ip4/{host}"))
for host in get_network_addrs(6):
result.append(Multiaddr(f"/ip6/{host}"))
return result
1 change: 1 addition & 0 deletions newsfragments/122.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ``interface_multiaddrs()`` to return local interface addresses as Multiaddr objects.
18 changes: 18 additions & 0 deletions tests/test_interface_multiaddrs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from unittest.mock import patch

from multiaddr import Multiaddr, interface_multiaddrs


def test_interface_multiaddrs_builds_multiaddrs():
with patch("multiaddr.utils.get_network_addrs") as mock_addrs:
mock_addrs.side_effect = lambda family: (["192.0.2.10"] if family == 4 else ["2001:db8::1"])
addrs = interface_multiaddrs()
assert addrs == [
Multiaddr("/ip4/192.0.2.10"),
Multiaddr("/ip6/2001:db8::1"),
]


def test_interface_multiaddrs_empty_when_no_interfaces():
with patch("multiaddr.utils.get_network_addrs", return_value=[]):
assert interface_multiaddrs() == []
1 change: 1 addition & 0 deletions tests/test_package_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"get_multiaddr_options",
"get_network_addrs",
"get_thin_waist_addresses",
"interface_multiaddrs",
"is_ip6_link_local",
"is_ip_loopback",
"is_ip_unspecified",
Expand Down
Loading