Skip to content
Merged
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: 41 additions & 0 deletions .github/workflows/tox.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Run tox

on:
pull_request:
push:
branches:
- main
- github-actions

defaults:
run:
shell: bash

jobs:
tox:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
toxenv: [core, lint]
include:
- python-version: "3.10"
toxenv: docs
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: |
python_version="${{ matrix.python-version }}"
toxenv="${{ matrix.toxenv }}"
if [[ "$toxenv" == "docs" ]]; then
echo "TOXENV=docs" | tee -a "$GITHUB_ENV"
else
echo "TOXENV=py${python_version}-${toxenv}" | tr -d '.' | tee -a "$GITHUB_ENV"
fi
python -m pip install --upgrade pip
python -m pip install "tox>=4.10"
- run: |
python -m tox run -r
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ repos:
rev: v3.15.0
hooks:
- id: pyupgrade
args: [--py39-plus]
args: [--py310-plus]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.10
hooks:
Expand Down
21 changes: 21 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: 2

build:
os: ubuntu-24.04
tools:
python: "3.10"

sphinx:
configuration: docs/conf.py
fail_on_warning: true

python:
install:
- method: pip
path: .
extra_requirements:
- dev

formats:
- epub
- htmlzip
2 changes: 1 addition & 1 deletion COPYRIGHT
Original file line number Diff line number Diff line change
@@ -1 +1 @@
This library is dual-licensed under Apache 2.0 and MIT terms.
This library is dual-licensed under Apache 2.0 and MIT terms.
14 changes: 5 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,7 @@ setup:
pip install -r requirements_dev.txt

lint:
python -m ruff check --fix

fix:
python -m ruff check --fix

typecheck:
pre-commit run pyright-pretty --all-files
pre-commit run --all-files

test:
python -m pytest tests
Expand All @@ -56,12 +50,14 @@ coverage:
coverage html
$(BROWSER) htmlcov/index.html

docs:
docs-ci:
rm -f docs/multiaddr.rst
rm -f docs/modules.rst
sphinx-apidoc -o docs/ multiaddr
$(MAKE) -C docs clean
$(MAKE) -C docs html
$(MAKE) -C docs html SPHINXOPTS="-W"

docs: docs-ci
$(BROWSER) docs/_build/html/index.html

servedocs: docs
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
# here, relative to this directory. They are copied after the builtin
# static files, so a file named "default.css" will overwrite the builtin
# "default.css".
html_static_path = ['_static']
# html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page
# bottom, using the given strftime format.
Expand Down
2 changes: 1 addition & 1 deletion docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Installation
============

Requirements:
- Python 3.9 or newer
- Python 3.10 or newer

Install from PyPI:

Expand Down
2 changes: 1 addition & 1 deletion examples/dns/dns_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@

## Requirements

- Python 3.9+
- Python 3.10+
- py-multiaddr library
- trio library
- Internet connection for DNS resolution
Expand Down
2 changes: 1 addition & 1 deletion examples/thin_waist/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ result = get_thin_waist_addresses(addr, port=9000)

## Requirements

- Python 3.9+
- Python 3.10+
- `multiaddr` library with `psutil` dependency
- Network interfaces to demonstrate wildcard expansion

Expand Down
6 changes: 3 additions & 3 deletions multiaddr/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .multiaddr import Multiaddr # NOQA

__author__ = 'Steven Buss'
__email__ = '[email protected]'
__version__ = '0.0.9'
__author__ = "Steven Buss"
__email__ = "[email protected]"
__version__ = "0.0.9"
6 changes: 3 additions & 3 deletions multiaddr/codecs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import importlib
from typing import Any, Dict, Union
from typing import Any

# These are special sizes
LENGTH_PREFIXED_VAR_SIZE = -1
Expand Down Expand Up @@ -27,10 +27,10 @@ def to_bytes(self, proto: Any, string: str) -> bytes:
return b""


CODEC_CACHE: Dict[str, CodecBase] = {}
CODEC_CACHE: dict[str, CodecBase] = {}


def codec_by_name(name: Union[str, None]) -> CodecBase:
def codec_by_name(name: str | None) -> CodecBase:
if name is None: # Special "do nothing - expect nothing" pseudo-codec
return NoneCodec()
codec = CODEC_CACHE.get(name)
Expand Down
2 changes: 1 addition & 1 deletion multiaddr/codecs/ip4.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ def to_bytes(self, proto, string):

def to_string(self, proto, buf):
try:
return str(netaddr.IPAddress(int.from_bytes(buf, byteorder='big'), version=4))
return str(netaddr.IPAddress(int.from_bytes(buf, byteorder="big"), version=4))
except (ValueError, netaddr.AddrFormatError):
raise BinaryParseError("Invalid IPv4 address bytes", buf, "ip4")
2 changes: 1 addition & 1 deletion multiaddr/codecs/ip6.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ def to_bytes(self, proto, string):
return netaddr.IPAddress(string, version=6).packed

def to_string(self, proto, buf):
return str(netaddr.IPAddress(int.from_bytes(buf, byteorder='big'), version=6))
return str(netaddr.IPAddress(int.from_bytes(buf, byteorder="big"), version=6))
12 changes: 6 additions & 6 deletions multiaddr/codecs/onion.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ class Codec(CodecBase):

def to_bytes(self, proto, string):
try:
addr, port = string.split(':', 1)
if addr.endswith('.onion'):
addr, port = string.split(":", 1)
if addr.endswith(".onion"):
addr = addr[:-6]
if len(addr) != 16:
raise ValueError("Invalid onion address length")
Expand All @@ -28,7 +28,7 @@ def to_bytes(self, proto, string):
addr_bytes = base64.b32decode(addr.upper())
if len(addr_bytes) != 10:
raise ValueError("Decoded onion address must be 10 bytes")
return addr_bytes + port_num.to_bytes(2, byteorder='big')
return addr_bytes + port_num.to_bytes(2, byteorder="big")
except (ValueError, UnicodeEncodeError, binascii.Error) as e:
raise BinaryParseError(str(e), string.encode(), proto)

Expand All @@ -38,14 +38,14 @@ def to_string(self, proto, buf):
raise ValueError("Invalid onion address length")
# Base32 encode the address
try:
addr = base64.b32encode(buf[:10]).decode('ascii').lower()
addr = base64.b32encode(buf[:10]).decode("ascii").lower()
except binascii.Error:
raise ValueError("Invalid base32 encoding")
# Remove padding
addr = addr.rstrip('=')
addr = addr.rstrip("=")
if len(addr) != 16:
raise ValueError("Invalid onion address length")
port = str(int.from_bytes(buf[10:12], byteorder='big'))
port = str(int.from_bytes(buf[10:12], byteorder="big"))
if not 1 <= int(port) <= 65535:
raise ValueError("Invalid onion port range")
return f"{addr}:{port}"
Expand Down
12 changes: 6 additions & 6 deletions multiaddr/codecs/onion3.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ class Codec(CodecBase):

def to_bytes(self, proto, string):
try:
addr, port = string.split(':', 1)
if addr.endswith('.onion'):
addr, port = string.split(":", 1)
if addr.endswith(".onion"):
addr = addr[:-6]
if len(addr) != 56:
raise ValueError("Invalid onion3 address length")
Expand All @@ -31,7 +31,7 @@ def to_bytes(self, proto, string):
raise ValueError("Invalid base32 encoding")
if len(addr_bytes) != 35:
raise ValueError("Decoded onion3 address must be 35 bytes")
return addr_bytes + port_num.to_bytes(2, byteorder='big')
return addr_bytes + port_num.to_bytes(2, byteorder="big")
except (ValueError, UnicodeEncodeError, binascii.Error) as e:
raise BinaryParseError(str(e), string.encode(), proto)

Expand All @@ -40,14 +40,14 @@ def to_string(self, proto, buf):
if len(buf) != 37:
raise ValueError("Invalid onion3 address length")
try:
addr = base64.b32encode(buf[:35]).decode('ascii').lower()
addr = base64.b32encode(buf[:35]).decode("ascii").lower()
except binascii.Error:
raise ValueError("Invalid base32 encoding")
# Remove padding
addr = addr.rstrip('=')
addr = addr.rstrip("=")
if len(addr) != 56:
raise ValueError("Invalid onion3 address length")
port = str(int.from_bytes(buf[35:], byteorder='big'))
port = str(int.from_bytes(buf[35:], byteorder="big"))
if not 1 <= int(port) <= 65535:
raise ValueError("Invalid onion3 port range")
return f"{addr}:{port}"
Expand Down
4 changes: 2 additions & 2 deletions multiaddr/codecs/uint16be.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ def to_bytes(self, proto, string):
raise ValueError("invalid base 10 integer")
if n < 0 or n >= 65536:
raise ValueError("integer not in range [0, 65536)")
return n.to_bytes(2, byteorder='big')
return n.to_bytes(2, byteorder="big")

def to_string(self, proto, buf):
if len(buf) != 2:
raise ValueError("buffer length must be 2 bytes")
return str(int.from_bytes(buf, byteorder='big'))
return str(int.from_bytes(buf, byteorder="big"))
14 changes: 7 additions & 7 deletions multiaddr/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Optional, Union
from typing import Any


class Error(Exception):
Expand Down Expand Up @@ -34,16 +34,16 @@ def __init__(
self,
message: str,
string: str,
protocol: Optional[str] = None,
original: Optional[Exception] = None,
protocol: str | None = None,
original: Exception | None = None,
) -> None:
self.message = message
self.string = string
self.protocol = protocol
self.original = original

if protocol:
message = "Invalid MultiAddr {!r} protocol {}: {}".format(string, protocol, message)
message = f"Invalid MultiAddr {string!r} protocol {protocol}: {message}"
else:
message = f"Invalid MultiAddr {string!r}: {message}"

Expand All @@ -69,8 +69,8 @@ def __init__(
self,
message: str,
binary: bytes,
protocol: Union[str, int],
original: Optional[Exception] = None,
protocol: str | int,
original: Exception | None = None,
) -> None:
self.message = message
self.binary = binary
Expand Down Expand Up @@ -119,7 +119,7 @@ def __init__(self, proto: Any, kind: str = "name") -> None:
class ProtocolNotFoundError(ProtocolRegistryError):
"""No protocol with the given name or code found"""

def __init__(self, value: Union[str, int], kind: str = "name") -> None:
def __init__(self, value: str | int, kind: str = "name") -> None:
self.value = value
self.kind = kind

Expand Down
14 changes: 6 additions & 8 deletions multiaddr/multiaddr.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import collections.abc
from collections.abc import Iterator, Sequence
from typing import Any, Optional, TypeVar, Union, overload
from typing import Any, TypeVar, Union, overload

import varint

Expand All @@ -24,7 +24,7 @@ def __contains__(self, proto: object) -> bool:
proto = self._mapping.registry.find(proto)
return collections.abc.Sequence.__contains__(self, proto)

def __getitem__(self, idx: Union[int, slice]) -> Union[Any, Sequence[Any]]:
def __getitem__(self, idx: int | slice) -> Any | Sequence[Any]:
if isinstance(idx, slice):
return list(self)[idx]
if idx < 0:
Expand Down Expand Up @@ -62,9 +62,7 @@ def __getitem__(self, idx: int) -> tuple[Any, Any]: ...
@overload
def __getitem__(self, idx: slice) -> Sequence[tuple[Any, Any]]: ...

def __getitem__(
self, idx: Union[int, slice]
) -> Union[tuple[Any, Any], Sequence[tuple[Any, Any]]]:
def __getitem__(self, idx: int | slice) -> tuple[Any, Any] | Sequence[tuple[Any, Any]]:
if isinstance(idx, slice):
return list(self)[idx]
if idx < 0:
Expand Down Expand Up @@ -101,7 +99,7 @@ def __init__(self, mapping: "Multiaddr") -> None:
def __contains__(self, value: object) -> bool:
return collections.abc.Sequence.__contains__(self, value)

def __getitem__(self, idx: Union[int, slice]) -> Union[Any, Sequence[Any]]:
def __getitem__(self, idx: int | slice) -> Any | Sequence[Any]:
if isinstance(idx, slice):
return list(self)[idx]
if idx < 0:
Expand Down Expand Up @@ -272,7 +270,7 @@ def decapsulate_code(self, code: int) -> "Multiaddr":
return self.__class__("")
return self.__class__(self._bytes[:cut_offset])

def value_for_protocol(self, proto: Any) -> Optional[Any]:
def value_for_protocol(self, proto: Any) -> Any | None:
"""Return the value (if any) following the specified protocol

Returns
Expand Down Expand Up @@ -465,7 +463,7 @@ def _from_bytes(self, addr: bytes) -> None:

self._bytes = addr

def get_peer_id(self) -> Optional[str]:
def get_peer_id(self) -> str | None:
"""Get the peer ID from the multiaddr.

For circuit addresses, returns the target peer ID, not the relay peer ID.
Expand Down
Loading