bpo-35545: Fix asyncio discarding IPv6 scopes by lepaperwan · Pull Request #11271 · python/cpython (original) (raw)

I hadn't thought this through the other night while answering so I'll try to make this as complete as possible.

Testing that the scope is properly kept through the internals of asyncio actually requires that the test host has a usable scoped IP address (which is unusual on the loopback interface).

Should it be the case, the test would have to either dynamically acquire that IP and use it to bind the server, or know it beforehand and have it hardcoded. Binding to an invalid address, of course, fails.

import socket

s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) s.bind(("fe80::1", 80, 0, 1))

Traceback (most recent call last): File "", line 1, in OSError: [Errno 99] Cannot assign requested address

Another solution is to rely on scoped IPs failing to be used if the scope ID is not explicitly given.

import socket

s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) s.connect(("fe80::1", 80))

Traceback (most recent call last): File "", line 1, in OSError: [Errno 22] Invalid argument

However if there is no server to answer, as expected, the call fails as well.

import socket

s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) s.connect(("fe80::1", 80, 0, 1))

Traceback (most recent call last): File "", line 1, in ConnectionRefusedError: [Errno 111] Connection refused

And if that interface doesn't have a scoped address, it fails as well.

import socket

s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) s.connect(("fe80::1", 80, 0, 1))

Traceback (most recent call last): File "", line 1, in OSError: [Errno 101] Network is unreachable

The Invalid argument method works with asyncio as well. (This is on 3.7.2)

import asyncio

asyncio.run(asyncio.open_connection("fe80::1%1", 80)) Traceback (most recent call last): File "", line 1, in File "/usr/local/lib/python3.7/asyncio/runners.py", line 43, in run return loop.run_until_complete(main) File "/usr/local/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete return future.result() File "/usr/local/lib/python3.7/asyncio/streams.py", line 77, in open_connection lambda: protocol, host, port, **kwds) File "/usr/local/lib/python3.7/asyncio/base_events.py", line 959, in create_connection raise exceptions[0] File "/usr/local/lib/python3.7/asyncio/base_events.py", line 946, in create_connection await self.sock_connect(sock, address) File "/usr/local/lib/python3.7/asyncio/selector_events.py", line 464, in sock_connect return await fut File "/usr/local/lib/python3.7/asyncio/selector_events.py", line 469, in _sock_connect sock.connect(address) OSError: [Errno 22] Invalid argument

Imposing additional constraints on the test host isn't ideal because while it might be possible to setup the CIs appropriately, it isn't desirable and contributors might not have identical setups.

I'm not certain what Python's test policy is at this stage. I'd assume expecting errors for anything other than error detection/handling isn't great, constraining the test host isn't great, and mocks aren't great either.

If anyone has ideas on how to best go about testing this please chime in :)