Skip to content

[asyncio] Support IPv6 addr type in DatagramProtocol.datagram_received#15543

Closed
sedat4ras wants to merge 1 commit intopython:mainfrom
sedat4ras:fix/asyncio-datagram-ipv6-addr-type
Closed

[asyncio] Support IPv6 addr type in DatagramProtocol.datagram_received#15543
sedat4ras wants to merge 1 commit intopython:mainfrom
sedat4ras:fix/asyncio-datagram-ipv6-addr-type

Conversation

@sedat4ras
Copy link

Fixes #15169

Changes

DatagramProtocol.datagram_received currently types the addr parameter as tuple[str | Any, int] — a 2-tuple — which handles IPv4 ((host, port)) and unusual protocols like AF_NETLINK. However, when using IPv6, the addr is a 4-tuple (host, port, flowinfo, scope_id), i.e. tuple[str, int, int, int].

This adds the IPv6 4-tuple to the union type so that datagram protocols using IPv6 can be properly type-checked without resorting to Any or ignoring the error.

Before:

def datagram_received(self, data: bytes, addr: tuple[str | Any, int]) -> None: ...

After:

def datagram_received(self, data: bytes, addr: tuple[str | Any, int] | tuple[str, int, int, int]) -> None: ...

This is consistent with how IPv6 addresses are represented elsewhere in typeshed — for example, socket.getaddrinfo() already uses tuple[str, int, int, int] for IPv6 in its return type.

When using IPv6, the `addr` parameter passed to `datagram_received` is a
4-tuple `(host, port, flowinfo, scope_id)` of type `tuple[str, int, int, int]`,
not a 2-tuple. Add this case to the union type so IPv6 datagram protocols
can be properly typed without resorting to `Any`.

Fixes python#15169
@github-actions
Copy link
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

anyio (https://github.com/agronholm/anyio)
+ src/anyio/_backends/_asyncio.py:1228: error: Argument 2 of "datagram_received" is incompatible with supertype "asyncio.protocols.DatagramProtocol"; supertype defines the argument type as "tuple[str | Any, int] | tuple[str, int, int, int]"  [override]
+ src/anyio/_backends/_asyncio.py:1228: note: This violates the Liskov substitution principle
+ src/anyio/_backends/_asyncio.py:1228: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides

@sedat4ras
Copy link
Author

The mypy_primer result shows that this change exposes a pre-existing Liskov Substitution Principle violation in anyio: its datagram_received override uses a narrower addr type that doesn't account for IPv6 4-tuples.

This is arguably the correct behavior — widening the base class type correctly reveals that anyio's implementation is not actually IPv6-compatible. However, I understand this may be considered too breaking for a typeshed change.

A few possible approaches if this regression is not acceptable:

  1. Keep this PR as-is and let anyio update their stub/implementation
  2. Use tuple[Any, ...] for addr (broadest, loses precision)
  3. Revert and document the IPv6 limitation in the existing comment

Happy to take any direction the maintainers prefer!

@srittau
Copy link
Collaborator

srittau commented Mar 23, 2026

Please see the discussion in #15184 for why just widening the annotation won't work and what has been tried previously.

@sedat4ras
Copy link
Author

Thanks for the pointer to #15184 — after reading the full discussion it's clear this hits the same fundamental limitation: subclasses like anyio and home-assistant legitimately narrow the addr type because they have protocol-specific knowledge, so widening the base class annotation unavoidably causes LSP violations in those overrides.

Closing since there's no clean solution until AnyOf (python/typing#566) lands. I'll leave this to be revisited at that point.

@sedat4ras sedat4ras closed this Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

asyncio: DatagramProtocol.datagram_received has incorrect annotation for addr parameter

2 participants