Skip to content

gh-151769: Make IPv6Address ordering scope_id-aware#151772

Open
Builder106 wants to merge 1 commit into
python:mainfrom
Builder106:gh-151769-ipv6address-scope-ordering
Open

gh-151769: Make IPv6Address ordering scope_id-aware#151772
Builder106 wants to merge 1 commit into
python:mainfrom
Builder106:gh-151769-ipv6address-scope-ordering

Conversation

@Builder106

@Builder106 Builder106 commented Jun 20, 2026

Copy link
Copy Markdown

IPv6Address.__eq__ and __hash__ fold in the interface scope_id, but IPv6Address inherited the scope-blind _BaseAddress.__lt__. Under @functools.total_ordering — which derives > from < and == — that made ordering inconsistent for addresses differing only by scope: both a > b and b > a could be true, so antisymmetry broke and sorted()/min()/max() were non-deterministic for scoped addresses.

This adds a scope-aware IPv6Address.__lt__ that tie-breaks on scope_id only when the integer address is equal (unscoped sorts before scoped; scope ids compare lexicographically). Because the derived operators dispatch through type(self).__lt__, overriding only __lt__ routes all four comparisons through the scope-aware path — which also repairs scoped IPv6Interface and IPv6Network ordering, since they delegate to address comparison. _BaseAddress.__lt__ is left untouched, so IPv4Address is unaffected.

Verified on a 3.16.0a0 build and on stock 3.14.6: without the fix, antisymmetry fails and the three scoped permutations of one address yield 6 distinct sort orders; with it, ordering is total and deterministic. The new testScopedAddressComparison covers trichotomy, antisymmetry, and stable sorted()/min()/max() for scoped IPv6Address, IPv6Interface, and IPv6Network, with an IPv4 ordering guard.

IPv6Address.__eq__ and __hash__ fold in the interface scope_id, but IPv6Address inherited the scope-blind _BaseAddress.__lt__. Under @functools.total_ordering, which derives > from < and ==, that made ordering inconsistent for addresses differing only by scope: both a > b and b > a could be true, so antisymmetry broke and sorted(), min() and max() were non-deterministic.

Add a scope-aware IPv6Address.__lt__ that tie-breaks on scope_id only when the integer address is equal, so unscoped sorts before scoped. Overriding only __lt__ routes all four total_ordering comparisons through the scope-aware path, which also fixes scoped IPv6Interface and IPv6Network ordering since they delegate to address comparison. _BaseAddress.__lt__ is untouched, so IPv4 is unaffected.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant