multiaddr implementation in Python
36
stars
369
commits
Python
primary language
Sep 6, 2026
updated
py-multiaddr
==========================
.. image:: https://img.shields.io/pypi/v/multiaddr.svg
:target: https://pypi.python.org/pypi/multiaddr
.. image:: https://github.com/multiformats/py-multiaddr/actions/workflows/ci.yml/badge.svg
:target: https://github.com/multiformats/py-multiaddr/actions
.. image:: https://codecov.io/github/multiformats/py-multiaddr/coverage.svg?branch=master
:target: https://codecov.io/github/multiformats/py-multiaddr?branch=master
.. image:: https://readthedocs.org/projects/multiaddr/badge/?version=latest
:target: https://readthedocs.org/projects/multiaddr/?badge=latest
:alt: Documentation Status
..
multiaddr_ implementation in Python
.. _multiaddr: https://github.com/multiformats/multiaddr
..
.. contents:: :local:
Installation
============
.. code-block:: bash
pip install multiaddr
Requirements
------------
- Python 3.10+
- trio (for async DNS resolution)
Usage
=====
CLI Tool
--------
py-multiaddr provides a command line interface to inspect multiaddrs and decode them into structured JSON.
.. code-block:: bash
$ multiaddr /ip4/1.2.3.4/tcp/80
{
"string": "/ip4/1.2.3.4/tcp/80",
"packed": "0x0401020304060050",
"packedSize": 8,
"components": [
{
"protocol": "ip4",
"code": 4,
"value": "1.2.3.4",
"rawValue": "0x01020304"
},
{
"protocol": "tcp",
"code": 6,
"value": "80",
"rawValue": "0x0050"
}
]
}
You can also pass a hex-encoded multiaddr prefixed with ``0x``, e.g.
``multiaddr 0x0401020304060050``. Use the ``-c`` or ``--compact`` flag to
output on a single line.
IP Classification
-----------------
You can easily classify the IP components of multiaddrs (e.g. check for loopback, private networks, or NAT64):
.. code-block:: python
from multiaddr import Multiaddr, is_private_addr, is_ip_loopback
ma1 = Multiaddr("/ip4/192.168.1.1/tcp/80")
print(is_private_addr(ma1)) # True
print(is_ip_loopback(ma1)) # False
ma2 = Multiaddr("/ip4/127.0.0.1")
print(is_private_addr(ma2)) # True
print(is_ip_loopback(ma2)) # True
Other available helpers include ``is_public_addr``, ``is_ip_unspecified``,
``is_ip6_link_local``, ``is_thin_waist``, and ``is_nat64_ipv4_converted_ipv6_addr``.
These operate on ``Multiaddr`` objects; ``is_wildcard`` / ``is_link_local_ip``
remain available for raw IP strings.
Simple
------
.. code-block:: python
from multiaddr import Multiaddr
# construct from a string
m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")
# construct from bytes
#m2 = Multiaddr(bytes_addr=m1.to_bytes()) # deprecated
m2 = Multiaddr(m1.to_bytes())
assert str(m1) == "/ip4/127.0.0.1/udp/1234"
assert str(m1) == str(m2)
assert m1.to_bytes() == m2.to_bytes()
assert m1 == m2
assert m2 == m1
assert not (m1 != m2)
assert not (m2 != m1)
Protocols
---------
.. code-block:: python
from multiaddr import Multiaddr
m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")
# get the multiaddr protocol description objects
m1.protocols()
# [Protocol(code=4, name='ip4', size=32), Protocol(code=17, name='udp', size=16)]
En/decapsulate
--------------
.. code-block:: python
from multiaddr import Multiaddr
m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")
m1.encapsulate(Multiaddr("/sctp/5678"))
# <Multiaddr /ip4/127.0.0.1/udp/1234/sctp/5678>
m1.decapsulate(Multiaddr("/udp"))
# <Multiaddr /ip4/127.0.0.1>
# Decapsulate by protocol code
m2 = Multiaddr("/ip4/192.168.1.1/tcp/8080/udp/1234")
m2.decapsulate_code(6) # TCP protocol code
# <Multiaddr /ip4/192.168.1.1>
# Decapsulate multiple layers
m3 = Multiaddr("/ip4/10.0.0.1/tcp/443/tls/p2p/QmPeer")
m3.decapsulate_code(6) # Remove TCP and everything after
# <Multiaddr /ip4/10.0.0.1>
Tunneling
---------
Multiaddr allows expressing tunnels very nicely.
.. code-block:: python
printer = Multiaddr("/ip4/192.168.0.13/tcp/80")
proxy = Multiaddr("/ip4/10.20.30.40/tcp/443")
printerOverProxy = proxy.encapsulate(printer)
print(printerOverProxy)
# /ip4/10.20.30.40/tcp/443/ip4/192.168.0.13/tcp/80
proxyAgain = printerOverProxy.decapsulate(printer)
print(proxyAgain)
# /ip4/10.20.30.40/tcp/443
DNS Resolution
--------------
Multiaddr supports DNS-based address resolution using the DNSADDR protocol. This is particularly useful for resolving bootstrap node addresses and maintaining peer IDs during resolution.
.. code-block:: python
from multiaddr import Multiaddr
import trio
# Basic DNS resolution
ma = Multiaddr("/dns/example.com")
resolved = await ma.resolve()
print(resolved)
# [Multiaddr("/ip4/93.184.216.34"), Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946")]
# DNSADDR with peer ID (bootstrap node style)
ma_with_peer = Multiaddr("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")
resolved_with_peer = await ma_with_peer.resolve()
print(resolved_with_peer)
# [Multiaddr("/ip4/147.75.83.83/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")]
# DNS4 and DNS6 resolution (IPv4/IPv6 specific)
ma_dns4 = Multiaddr("/dns4/example.com/tcp/443")
resolved_dns4 = await ma_dns4.resolve()
print(resolved_dns4)
# [Multiaddr("/ip4/93.184.216.34/tcp/443")]
ma_dns6 = Multiaddr("/dns6/example.com/tcp/443")
resolved_dns6 = await ma_dns6.resolve()
print(resolved_dns6)
# [Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946/tcp/443")]
# Using the DNS resolver directly
from multiaddr.resolvers import DNSResolver
resolver = DNSResolver()
resolved = await resolver.resolve(ma)
print(resolved)
# [Multiaddr("/ip4/93.184.216.34"), Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946")]
# Peer ID preservation test
original_peer_id = ma_with_peer.get_peer_id()
print(f"Original peer ID: {original_peer_id}")
# Original peer ID: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN
for resolved_addr in resolved_with_peer:
preserved_peer_id = resolved_addr.get_peer_id()
print(f"Resolved peer ID: {preserved_peer_id}")
# Resolved peer ID: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN
For comprehensive examples including bootstrap node resolution, protocol comparison, and py-libp2p integration, see the `DNS examples <https://github.com/multiformats/py-multiaddr/tree/master/examples/dns>`_ in the examples directory.
IP Filtering
------------
Accept/deny IP ranges for multiaddrs, similar to go-multiaddr ``Filters``:
.. code-block:: python
from multiaddr import Action, Filters, Multiaddr
filters = Filters()
filters.add_filter("10.0.0.0/8", Action.DENY)
assert filters.addr_blocked(Multiaddr("/ip4/10.1.2.3/tcp/80"))
assert not filters.addr_blocked(Multiaddr("/ip4/8.8.8.8/tcp/53"))
See ``examples/filters/filters_example.py`` for a printable demo.
Socket dial arguments
---------------------
Convert thin-waist and unix multiaddrs to ``(network, address)`` pairs suitable
for ``socket.connect()``:
.. code-block:: python
from multiaddr import Multiaddr, dial_args
print(dial_args(Multiaddr("/ip4/1.2.3.4/tcp/80")))
# ('tcp4', '1.2.3.4:80')
print(dial_args(Multiaddr("/ip6/::1/tcp/80")))
# ('tcp6', '[::1]:80')
print(dial_args(Multiaddr("/unix/var/run/docker.sock")))
# ('unix', '/var/run/docker.sock')
See ``examples/dial_args/dial_args_example.py`` for a printable demo.
Socket address conversion
-------------------------
Convert between Python socket address tuples and multiaddrs:
.. code-block:: python
from multiaddr import from_net_addr, to_net_addr
ma = from_net_addr(("1.2.3.4", 80))
print(ma)
# /ip4/1.2.3.4/tcp/80
print(to_net_addr(ma))
# ('1.2.3.4', 80)
print(from_net_addr(("::1", 53), transport="udp"))
# /ip6/::1/udp/53
See ``examples/net_addr/net_addr_example.py`` for a printable demo.
Thin Waist Address Validation
-----------------------------
.. code-block:: python
from multiaddr import Multiaddr
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)
print(f"Available IPv4 addresses: {ipv4_addrs}")
# Available IPv4 addresses: ['192.168.1.12', '10.152.168.99']
# Specific address (no expansion)
addr = Multiaddr("/ip4/192.168.1.100/tcp/8080")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip4/192.168.1.100/tcp/8080>]
# IPv4 wildcard expansion
addr = Multiaddr("/ip4/0.0.0.0/tcp/8080")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip4/192.168.1.12/tcp/8080>, <Multiaddr /ip4/10.152.168.99/tcp/8080>]
# IPv6 wildcard expansion
addr = Multiaddr("/ip6/::/tcp/8080")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip6/::1/tcp/8080>, <Multiaddr /ip6/fd9b:9eba:8224:1:41a1:8939:231a:b414/tcp/8080>]
# Port override
addr = Multiaddr("/ip4/0.0.0.0/tcp/8080")
result = get_thin_waist_addresses(addr, port=9000)
print(result)
# [<Multiaddr /ip4/192.168.1.12/tcp/9000>, <Multiaddr /ip4/10.152.168.99/tcp/9000>]
# UDP transport support
addr = Multiaddr("/ip4/0.0.0.0/udp/1234")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip4/192.168.1.12/udp/1234>, <Multiaddr /ip4/10.152.168.99/udp/1234>]
# Server binding scenario
wildcard = Multiaddr("/ip4/0.0.0.0/tcp/8080")
interfaces = get_thin_waist_addresses(wildcard)
print("Available interfaces for server binding:")
for i, interface in enumerate(interfaces, 1):
print(f" {i}. {interface}")
# Available interfaces for server binding:
# 1. /ip4/192.168.1.12/tcp/8080
# 2. /ip4/10.152.168.99/tcp/8080
For comprehensive examples including error handling, practical usage scenarios, and detailed network interface information, see the `thin waist examples <https://github.com/multiformats/py-multiaddr/tree/master/examples/thin_waist>`_ in the examples directory.
Features
========
- **Multiaddr Protocol Support**: Full support for the multiaddr specification
- **DNS Resolution**: Async DNS and DNSADDR resolution with trio
- **Thin Waist Validation**: Network interface discovery and wildcard expansion
- **Protocol Support**: IPv4, IPv6, TCP, UDP, DNS, DNS4, DNS6, DNSADDR, p2p, p2p-circuit, onion, onion3, quic, tls, and more
- **Type Safety**: Full type hints and mypy support
- **Modern Python**: Python 3.10+ support with modern tooling
Maintainers
===========
Original author: `@sbuss`_.
Current maintainers: `@acul71`_, `@pacrob`_, `@manusheel`_.
Contribute
==========
Contributions welcome. Please check out `the issues`_.
Check out our `contributing document`_ for more information on how we work, and about contributing in general.
Please be aware that all interactions related to multiformats are subject to the IPFS `Code of Conduct`_.
Development
-----------
For development setup, see `py-multiaddr contributing`_.
.. code-block:: bash
# Clone the repository
git clone https://github.com/multiformats/py-multiaddr.git
cd py-multiaddr
# Install in development mode
pip install -e ".[dev]"
# Run the development workflow
make pr
License
=======
Dual-licensed:
- `MIT`_ © 2014 Steven Buss
- `Apache 2`_ © 2014 Steven Buss
.. _the issues: https://github.com/multiformats/py-multiaddr/issues
.. _contributing document: https://github.com/multiformats/multiformats/blob/master/contributing.md
.. _py-multiaddr contributing: https://github.com/multiformats/py-multiaddr/blob/master/CONTRIBUTING.rst
.. _Code of Conduct: https://github.com/ipfs/community/blob/master/code-of-conduct.md
.. _standard-readme: https://github.com/RichardLitt/standard-readme
.. _MIT: LICENSE-MIT
.. _Apache 2: LICENSE-APACHE2
.. _`@sbuss`: https://github.com/sbuss
.. _`@acul71`: https://github.com/acul71
.. _`@pacrob`: https://github.com/pacrob
.. _`@manusheel`: https://github.com/manusheel
Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.
Python
98.4%
Makefile
1.6%
multiaddr implementation in Python
36
stars
369
commits
Python
primary language
Sep 6, 2026
updated
py-multiaddr
==========================
.. image:: https://img.shields.io/pypi/v/multiaddr.svg
:target: https://pypi.python.org/pypi/multiaddr
.. image:: https://github.com/multiformats/py-multiaddr/actions/workflows/ci.yml/badge.svg
:target: https://github.com/multiformats/py-multiaddr/actions
.. image:: https://codecov.io/github/multiformats/py-multiaddr/coverage.svg?branch=master
:target: https://codecov.io/github/multiformats/py-multiaddr?branch=master
.. image:: https://readthedocs.org/projects/multiaddr/badge/?version=latest
:target: https://readthedocs.org/projects/multiaddr/?badge=latest
:alt: Documentation Status
..
multiaddr_ implementation in Python
.. _multiaddr: https://github.com/multiformats/multiaddr
..
.. contents:: :local:
Installation
============
.. code-block:: bash
pip install multiaddr
Requirements
------------
- Python 3.10+
- trio (for async DNS resolution)
Usage
=====
CLI Tool
--------
py-multiaddr provides a command line interface to inspect multiaddrs and decode them into structured JSON.
.. code-block:: bash
$ multiaddr /ip4/1.2.3.4/tcp/80
{
"string": "/ip4/1.2.3.4/tcp/80",
"packed": "0x0401020304060050",
"packedSize": 8,
"components": [
{
"protocol": "ip4",
"code": 4,
"value": "1.2.3.4",
"rawValue": "0x01020304"
},
{
"protocol": "tcp",
"code": 6,
"value": "80",
"rawValue": "0x0050"
}
]
}
You can also pass a hex-encoded multiaddr prefixed with ``0x``, e.g.
``multiaddr 0x0401020304060050``. Use the ``-c`` or ``--compact`` flag to
output on a single line.
IP Classification
-----------------
You can easily classify the IP components of multiaddrs (e.g. check for loopback, private networks, or NAT64):
.. code-block:: python
from multiaddr import Multiaddr, is_private_addr, is_ip_loopback
ma1 = Multiaddr("/ip4/192.168.1.1/tcp/80")
print(is_private_addr(ma1)) # True
print(is_ip_loopback(ma1)) # False
ma2 = Multiaddr("/ip4/127.0.0.1")
print(is_private_addr(ma2)) # True
print(is_ip_loopback(ma2)) # True
Other available helpers include ``is_public_addr``, ``is_ip_unspecified``,
``is_ip6_link_local``, ``is_thin_waist``, and ``is_nat64_ipv4_converted_ipv6_addr``.
These operate on ``Multiaddr`` objects; ``is_wildcard`` / ``is_link_local_ip``
remain available for raw IP strings.
Simple
------
.. code-block:: python
from multiaddr import Multiaddr
# construct from a string
m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")
# construct from bytes
#m2 = Multiaddr(bytes_addr=m1.to_bytes()) # deprecated
m2 = Multiaddr(m1.to_bytes())
assert str(m1) == "/ip4/127.0.0.1/udp/1234"
assert str(m1) == str(m2)
assert m1.to_bytes() == m2.to_bytes()
assert m1 == m2
assert m2 == m1
assert not (m1 != m2)
assert not (m2 != m1)
Protocols
---------
.. code-block:: python
from multiaddr import Multiaddr
m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")
# get the multiaddr protocol description objects
m1.protocols()
# [Protocol(code=4, name='ip4', size=32), Protocol(code=17, name='udp', size=16)]
En/decapsulate
--------------
.. code-block:: python
from multiaddr import Multiaddr
m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")
m1.encapsulate(Multiaddr("/sctp/5678"))
# <Multiaddr /ip4/127.0.0.1/udp/1234/sctp/5678>
m1.decapsulate(Multiaddr("/udp"))
# <Multiaddr /ip4/127.0.0.1>
# Decapsulate by protocol code
m2 = Multiaddr("/ip4/192.168.1.1/tcp/8080/udp/1234")
m2.decapsulate_code(6) # TCP protocol code
# <Multiaddr /ip4/192.168.1.1>
# Decapsulate multiple layers
m3 = Multiaddr("/ip4/10.0.0.1/tcp/443/tls/p2p/QmPeer")
m3.decapsulate_code(6) # Remove TCP and everything after
# <Multiaddr /ip4/10.0.0.1>
Tunneling
---------
Multiaddr allows expressing tunnels very nicely.
.. code-block:: python
printer = Multiaddr("/ip4/192.168.0.13/tcp/80")
proxy = Multiaddr("/ip4/10.20.30.40/tcp/443")
printerOverProxy = proxy.encapsulate(printer)
print(printerOverProxy)
# /ip4/10.20.30.40/tcp/443/ip4/192.168.0.13/tcp/80
proxyAgain = printerOverProxy.decapsulate(printer)
print(proxyAgain)
# /ip4/10.20.30.40/tcp/443
DNS Resolution
--------------
Multiaddr supports DNS-based address resolution using the DNSADDR protocol. This is particularly useful for resolving bootstrap node addresses and maintaining peer IDs during resolution.
.. code-block:: python
from multiaddr import Multiaddr
import trio
# Basic DNS resolution
ma = Multiaddr("/dns/example.com")
resolved = await ma.resolve()
print(resolved)
# [Multiaddr("/ip4/93.184.216.34"), Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946")]
# DNSADDR with peer ID (bootstrap node style)
ma_with_peer = Multiaddr("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")
resolved_with_peer = await ma_with_peer.resolve()
print(resolved_with_peer)
# [Multiaddr("/ip4/147.75.83.83/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")]
# DNS4 and DNS6 resolution (IPv4/IPv6 specific)
ma_dns4 = Multiaddr("/dns4/example.com/tcp/443")
resolved_dns4 = await ma_dns4.resolve()
print(resolved_dns4)
# [Multiaddr("/ip4/93.184.216.34/tcp/443")]
ma_dns6 = Multiaddr("/dns6/example.com/tcp/443")
resolved_dns6 = await ma_dns6.resolve()
print(resolved_dns6)
# [Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946/tcp/443")]
# Using the DNS resolver directly
from multiaddr.resolvers import DNSResolver
resolver = DNSResolver()
resolved = await resolver.resolve(ma)
print(resolved)
# [Multiaddr("/ip4/93.184.216.34"), Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946")]
# Peer ID preservation test
original_peer_id = ma_with_peer.get_peer_id()
print(f"Original peer ID: {original_peer_id}")
# Original peer ID: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN
for resolved_addr in resolved_with_peer:
preserved_peer_id = resolved_addr.get_peer_id()
print(f"Resolved peer ID: {preserved_peer_id}")
# Resolved peer ID: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN
For comprehensive examples including bootstrap node resolution, protocol comparison, and py-libp2p integration, see the `DNS examples <https://github.com/multiformats/py-multiaddr/tree/master/examples/dns>`_ in the examples directory.
IP Filtering
------------
Accept/deny IP ranges for multiaddrs, similar to go-multiaddr ``Filters``:
.. code-block:: python
from multiaddr import Action, Filters, Multiaddr
filters = Filters()
filters.add_filter("10.0.0.0/8", Action.DENY)
assert filters.addr_blocked(Multiaddr("/ip4/10.1.2.3/tcp/80"))
assert not filters.addr_blocked(Multiaddr("/ip4/8.8.8.8/tcp/53"))
See ``examples/filters/filters_example.py`` for a printable demo.
Socket dial arguments
---------------------
Convert thin-waist and unix multiaddrs to ``(network, address)`` pairs suitable
for ``socket.connect()``:
.. code-block:: python
from multiaddr import Multiaddr, dial_args
print(dial_args(Multiaddr("/ip4/1.2.3.4/tcp/80")))
# ('tcp4', '1.2.3.4:80')
print(dial_args(Multiaddr("/ip6/::1/tcp/80")))
# ('tcp6', '[::1]:80')
print(dial_args(Multiaddr("/unix/var/run/docker.sock")))
# ('unix', '/var/run/docker.sock')
See ``examples/dial_args/dial_args_example.py`` for a printable demo.
Socket address conversion
-------------------------
Convert between Python socket address tuples and multiaddrs:
.. code-block:: python
from multiaddr import from_net_addr, to_net_addr
ma = from_net_addr(("1.2.3.4", 80))
print(ma)
# /ip4/1.2.3.4/tcp/80
print(to_net_addr(ma))
# ('1.2.3.4', 80)
print(from_net_addr(("::1", 53), transport="udp"))
# /ip6/::1/udp/53
See ``examples/net_addr/net_addr_example.py`` for a printable demo.
Thin Waist Address Validation
-----------------------------
.. code-block:: python
from multiaddr import Multiaddr
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)
print(f"Available IPv4 addresses: {ipv4_addrs}")
# Available IPv4 addresses: ['192.168.1.12', '10.152.168.99']
# Specific address (no expansion)
addr = Multiaddr("/ip4/192.168.1.100/tcp/8080")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip4/192.168.1.100/tcp/8080>]
# IPv4 wildcard expansion
addr = Multiaddr("/ip4/0.0.0.0/tcp/8080")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip4/192.168.1.12/tcp/8080>, <Multiaddr /ip4/10.152.168.99/tcp/8080>]
# IPv6 wildcard expansion
addr = Multiaddr("/ip6/::/tcp/8080")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip6/::1/tcp/8080>, <Multiaddr /ip6/fd9b:9eba:8224:1:41a1:8939:231a:b414/tcp/8080>]
# Port override
addr = Multiaddr("/ip4/0.0.0.0/tcp/8080")
result = get_thin_waist_addresses(addr, port=9000)
print(result)
# [<Multiaddr /ip4/192.168.1.12/tcp/9000>, <Multiaddr /ip4/10.152.168.99/tcp/9000>]
# UDP transport support
addr = Multiaddr("/ip4/0.0.0.0/udp/1234")
result = get_thin_waist_addresses(addr)
print(result)
# [<Multiaddr /ip4/192.168.1.12/udp/1234>, <Multiaddr /ip4/10.152.168.99/udp/1234>]
# Server binding scenario
wildcard = Multiaddr("/ip4/0.0.0.0/tcp/8080")
interfaces = get_thin_waist_addresses(wildcard)
print("Available interfaces for server binding:")
for i, interface in enumerate(interfaces, 1):
print(f" {i}. {interface}")
# Available interfaces for server binding:
# 1. /ip4/192.168.1.12/tcp/8080
# 2. /ip4/10.152.168.99/tcp/8080
For comprehensive examples including error handling, practical usage scenarios, and detailed network interface information, see the `thin waist examples <https://github.com/multiformats/py-multiaddr/tree/master/examples/thin_waist>`_ in the examples directory.
Features
========
- **Multiaddr Protocol Support**: Full support for the multiaddr specification
- **DNS Resolution**: Async DNS and DNSADDR resolution with trio
- **Thin Waist Validation**: Network interface discovery and wildcard expansion
- **Protocol Support**: IPv4, IPv6, TCP, UDP, DNS, DNS4, DNS6, DNSADDR, p2p, p2p-circuit, onion, onion3, quic, tls, and more
- **Type Safety**: Full type hints and mypy support
- **Modern Python**: Python 3.10+ support with modern tooling
Maintainers
===========
Original author: `@sbuss`_.
Current maintainers: `@acul71`_, `@pacrob`_, `@manusheel`_.
Contribute
==========
Contributions welcome. Please check out `the issues`_.
Check out our `contributing document`_ for more information on how we work, and about contributing in general.
Please be aware that all interactions related to multiformats are subject to the IPFS `Code of Conduct`_.
Development
-----------
For development setup, see `py-multiaddr contributing`_.
.. code-block:: bash
# Clone the repository
git clone https://github.com/multiformats/py-multiaddr.git
cd py-multiaddr
# Install in development mode
pip install -e ".[dev]"
# Run the development workflow
make pr
License
=======
Dual-licensed:
- `MIT`_ © 2014 Steven Buss
- `Apache 2`_ © 2014 Steven Buss
.. _the issues: https://github.com/multiformats/py-multiaddr/issues
.. _contributing document: https://github.com/multiformats/multiformats/blob/master/contributing.md
.. _py-multiaddr contributing: https://github.com/multiformats/py-multiaddr/blob/master/CONTRIBUTING.rst
.. _Code of Conduct: https://github.com/ipfs/community/blob/master/code-of-conduct.md
.. _standard-readme: https://github.com/RichardLitt/standard-readme
.. _MIT: LICENSE-MIT
.. _Apache 2: LICENSE-APACHE2
.. _`@sbuss`: https://github.com/sbuss
.. _`@acul71`: https://github.com/acul71
.. _`@pacrob`: https://github.com/pacrob
.. _`@manusheel`: https://github.com/manusheel
Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.
Python
98.4%
Makefile
1.6%