Closed mgorny closed 1 month ago
Thanks for reporting.
It looks like a mocking problem as follow_symlinks
is new in python 3.13 and the mock_stat
doesn't accept the kwarg tests/test_web_urldispatcher.py: def mock_stat(self: pathlib.Path) -> os.stat_result:
It doesn't look urgent to fix, as it won't affect production code, but something we do need to handle before 3.13 goes stable.
Steve is on holiday right now so it will likely get fixed next week when he returns. Ideally the fix would get included in 3.10.1 with https://github.com/aio-libs/aiohttp/pull/8546 and any other fixups needed from 3.10.0 feedback
I can submit a PR if that helps. I'm thinking it should be sufficient to add the optional kwarg here — since it's a mock purely for testing, I don't think we really need to handle or pass the value through.
Steve let me know he was going to take care of it over the weekend. Its very unlikely we would do .1 before next week unless there is something that needs to be addressed right away in .0
Hmm, there's another regression if I skip this test:
$ python -m pytest tests/test_web_urldispatcher.py::test_access_symlink_loop
========================================================= test session starts =========================================================
platform linux -- Python 3.13.0b4, pytest-8.3.2, pluggy-1.5.0 -- /tmp/aiohttp/.venv/bin/python
cachedir: .pytest_cache
rootdir: /tmp/aiohttp
configfile: setup.cfg
plugins: cov-5.0.0
collected 1 item
tests/test_web_urldispatcher.py::test_access_symlink_loop[pyloop] FAILED [100%]
============================================================== FAILURES ===============================================================
__________________________________________________ test_access_symlink_loop[pyloop] ___________________________________________________
tmp_path = PosixPath('/tmp/pytest-of-mgorny/pytest-12/test_access_symlink_loop_pyloo0')
aiohttp_client = <function aiohttp_client.<locals>.go at 0x7f09ac327740>
async def test_access_symlink_loop(
tmp_path: pathlib.Path, aiohttp_client: AiohttpClient
) -> None:
# Tests the access to a looped symlink, which could not be resolved.
my_dir_path = tmp_path / "my_symlink"
pathlib.Path(str(my_dir_path)).symlink_to(str(my_dir_path), True)
app = web.Application()
# Register global static route:
app.router.add_static("/", str(tmp_path), show_index=True)
client = await aiohttp_client(app)
# Request the root of the static directory.
> r = await client.get("/" + my_dir_path.name)
aiohttp_client = <function aiohttp_client.<locals>.go at 0x7f09ac327740>
app = <Application 0x7f09ac7d0250>
client = <aiohttp.test_utils.TestClient object at 0x7f09ac781fd0>
my_dir_path = PosixPath('/tmp/pytest-of-mgorny/pytest-12/test_access_symlink_loop_pyloo0/my_symlink')
tmp_path = PosixPath('/tmp/pytest-of-mgorny/pytest-12/test_access_symlink_loop_pyloo0')
tests/test_web_urldispatcher.py:513:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
aiohttp/test_utils.py:309: in _request
resp = await self._session.request(method, self.make_url(path), **kwargs)
kwargs = {}
method = 'GET'
path = '/my_symlink'
self = <aiohttp.test_utils.TestClient object at 0x7f09ac781fd0>
aiohttp/client.py:616: in _request
await resp.start(conn)
all_cookies = <SimpleCookie: >
allow_redirects = True
auth = None
auth_from_url = None
auto_decompress = True
chunked = None
compress = None
conn = Connection<ConnectionKey(host='127.0.0.1', port=33655, is_ssl=False, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
cookies = None
data = None
expect100 = False
handle = None
headers = <CIMultiDict()>
history = []
json = None
max_field_size = 8190
max_line_size = 8190
max_redirects = 10
method = 'GET'
params = {}
proxy = None
proxy_auth = None
proxy_headers = <CIMultiDict()>
raise_for_status = None
read_bufsize = 65536
read_until_eof = True
real_timeout = ClientTimeout(total=300, connect=None, sock_read=None, sock_connect=None, ceil_threshold=5)
redirects = 0
req = <aiohttp.client_reqrep.ClientRequest object at 0x7f09ac7a79d0>
resp = <ClientResponse(http://127.0.0.1:33655/my_symlink) [None None]>
None
retry_persistent_connection = False
self = <aiohttp.client.ClientSession object at 0x7f09ac31e880>
server_hostname = None
skip_auto_headers = None
skip_headers = set()
ssl = True
str_or_url = URL('http://127.0.0.1:33655/my_symlink')
timeout = <_SENTINEL.sentinel: 1>
timer = <aiohttp.helpers.TimerContext object at 0x7f09ac782f90>
tm = <aiohttp.helpers.TimeoutHandle object at 0x7f09ac782e40>
trace_request_ctx = None
traces = []
url = URL('http://127.0.0.1:33655/my_symlink')
version = HttpVersion(major=1, minor=1)
aiohttp/client_reqrep.py:926: in start
message, payload = await protocol.read() # type: ignore[union-attr]
connection = Connection<ConnectionKey(host='127.0.0.1', port=33655, is_ssl=False, ssl=True, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
protocol = <aiohttp.client_proto.ResponseHandler object at 0x7f09ac482ff0>
self = <ClientResponse(http://127.0.0.1:33655/my_symlink) [None None]>
None
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <aiohttp.client_proto.ResponseHandler object at 0x7f09ac482ff0>
async def read(self) -> _SizedT:
if not self._buffer and not self._eof:
assert not self._waiter
self._waiter = self._loop.create_future()
try:
> await self._waiter
E aiohttp.client_exceptions.ServerDisconnectedError: Server disconnected
self = <aiohttp.client_proto.ResponseHandler object at 0x7f09ac482ff0>
aiohttp/streams.py:626: ServerDisconnectedError
---------------------------------------------------------- Captured log call ----------------------------------------------------------
ERROR aiohttp.server:web_protocol.py:442 Unhandled exception
Traceback (most recent call last):
File "/tmp/aiohttp/aiohttp/web_protocol.py", line 545, in start
resp, reset = await task
^^^^^^^^^^
File "/tmp/aiohttp/aiohttp/web_protocol.py", line 491, in _handle_request
reset = await self.finish_response(request, resp, start_time)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/aiohttp/aiohttp/web_protocol.py", line 647, in finish_response
await prepare_meth(request)
File "/tmp/aiohttp/aiohttp/web_fileresponse.py", line 188, in prepare
file_path, st, file_encoding = await loop.run_in_executor(
^^^^^^^^^^^^^^^^^^^^^^^^^^^
None, self._get_file_path_stat_encoding, accept_encoding
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/usr/lib/python3.13/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "/tmp/aiohttp/aiohttp/web_fileresponse.py", line 180, in _get_file_path_stat_encoding
return file_path, file_path.stat(), None
~~~~~~~~~~~~~~^^
File "/usr/lib/python3.13/pathlib/_local.py", line 515, in stat
return os.stat(self, follow_symlinks=follow_symlinks)
~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OSError: [Errno 40] Too many levels of symbolic links: '/tmp/pytest-of-mgorny/pytest-12/test_access_symlink_loop_pyloo0/my_symlink'
ERROR aiohttp.server:web_protocol.py:442 Unhandled exception
Traceback (most recent call last):
File "/tmp/aiohttp/aiohttp/web_protocol.py", line 545, in start
resp, reset = await task
^^^^^^^^^^
File "/tmp/aiohttp/aiohttp/web_protocol.py", line 491, in _handle_request
reset = await self.finish_response(request, resp, start_time)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/aiohttp/aiohttp/web_protocol.py", line 647, in finish_response
await prepare_meth(request)
File "/tmp/aiohttp/aiohttp/web_fileresponse.py", line 188, in prepare
file_path, st, file_encoding = await loop.run_in_executor(
^^^^^^^^^^^^^^^^^^^^^^^^^^^
None, self._get_file_path_stat_encoding, accept_encoding
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/usr/lib/python3.13/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "/tmp/aiohttp/aiohttp/web_fileresponse.py", line 180, in _get_file_path_stat_encoding
return file_path, file_path.stat(), None
~~~~~~~~~~~~~~^^
File "/usr/lib/python3.13/pathlib/_local.py", line 515, in stat
return os.stat(self, follow_symlinks=follow_symlinks)
~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OSError: [Errno 40] Too many levels of symbolic links: '/tmp/pytest-of-mgorny/pytest-12/test_access_symlink_loop_pyloo0/my_symlink'
----------- coverage: platform linux, python 3.13.0-beta-4 -----------
Name Stmts Miss Branch BrPart Cover
--------------------------------------------------------------------------
aiohttp/__init__.py 26 10 2 0 57%
aiohttp/abc.py 97 2 66 0 99%
aiohttp/base_protocol.py 67 36 24 3 40%
aiohttp/client.py 530 251 234 43 47%
aiohttp/client_exceptions.py 138 55 36 1 61%
aiohttp/client_proto.py 170 80 68 11 46%
aiohttp/client_reqrep.py 657 299 328 59 48%
aiohttp/client_ws.py 220 172 84 1 20%
aiohttp/compression_utils.py 70 37 22 0 42%
aiohttp/connector.py 700 407 329 45 36%
aiohttp/cookiejar.py 254 187 128 3 19%
aiohttp/formdata.py 86 69 40 0 15%
aiohttp/hdrs.py 90 0 0 0 100%
aiohttp/helpers.py 558 299 231 21 39%
aiohttp/http.py 8 0 0 0 100%
aiohttp/http_exceptions.py 50 20 4 0 56%
aiohttp/http_parser.py 490 273 208 33 37%
aiohttp/http_websocket.py 381 288 152 0 18%
aiohttp/http_writer.py 118 40 54 14 57%
aiohttp/locks.py 24 16 4 0 29%
aiohttp/log.py 7 0 0 0 100%
aiohttp/multipart.py 608 501 272 0 14%
aiohttp/payload.py 220 108 74 1 47%
aiohttp/pytest_plugin.py 159 65 70 8 59%
aiohttp/resolver.py 63 42 18 0 26%
aiohttp/streams.py 395 292 140 2 20%
aiohttp/tcp_helpers.py 19 2 8 3 81%
aiohttp/test_utils.py 301 117 78 15 58%
aiohttp/tracing.py 191 69 64 0 73%
aiohttp/typedefs.py 23 0 0 0 100%
aiohttp/web.py 121 84 52 0 23%
aiohttp/web_app.py 247 84 93 19 63%
aiohttp/web_exceptions.py 214 37 42 6 80%
aiohttp/web_fileresponse.py 161 109 52 2 29%
aiohttp/web_log.py 102 39 40 0 64%
aiohttp/web_middlewares.py 54 38 22 0 21%
aiohttp/web_protocol.py 355 166 157 26 43%
aiohttp/web_request.py 450 267 210 7 41%
aiohttp/web_response.py 439 309 240 2 27%
aiohttp/web_routedef.py 104 44 18 2 57%
aiohttp/web_runner.py 221 71 70 12 67%
aiohttp/web_server.py 45 8 14 4 80%
aiohttp/web_urldispatcher.py 723 354 235 20 50%
aiohttp/web_ws.py 341 273 130 1 17%
aiohttp/worker.py 123 123 32 0 0%
tests/conftest.py 130 74 59 0 48%
tests/test_base_protocol.py 198 198 8 0 0%
tests/test_circular_imports.py 29 29 13 0 0%
tests/test_classbasedview.py 39 39 4 0 0%
tests/test_client_connection.py 93 93 12 0 0%
tests/test_client_exceptions.py 180 180 14 0 0%
tests/test_client_fingerprint.py 24 24 4 0 0%
tests/test_client_functional.py 2399 2399 456 0 0%
tests/test_client_proto.py 100 100 0 0 0%
tests/test_client_request.py 759 759 108 0 0%
tests/test_client_response.py 461 461 36 0 0%
tests/test_client_session.py 492 492 97 0 0%
tests/test_client_ws.py 468 468 154 0 0%
tests/test_client_ws_functional.py 673 673 54 0 0%
tests/test_connector.py 1653 1653 218 0 0%
tests/test_cookiejar.py 358 358 52 0 0%
tests/test_flowcontrol_streams.py 101 101 6 0 0%
tests/test_formdata.py 77 77 24 0 0%
tests/test_helpers.py 541 541 162 0 0%
tests/test_http_exceptions.py 109 109 10 0 0%
tests/test_http_parser.py 1053 1053 208 0 0%
tests/test_http_writer.py 183 183 14 0 0%
tests/test_imports.py 34 34 12 0 0%
tests/test_locks.py 40 40 4 0 0%
tests/test_loop.py 36 36 4 0 0%
tests/test_multipart.py 759 759 220 0 0%
tests/test_multipart_helpers.py 446 446 82 0 0%
tests/test_payload.py 77 77 8 0 0%
tests/test_proxy.py 299 299 92 0 0%
tests/test_proxy_functional.py 456 456 106 0 0%
tests/test_pytest_plugin.py 47 47 2 0 0%
tests/test_resolver.py 179 179 52 0 0%
tests/test_route_def.py 211 211 26 0 0%
tests/test_run_app.py 554 554 100 0 0%
tests/test_streams.py 1058 1058 128 0 0%
tests/test_tcp_helpers.py 52 52 14 0 0%
tests/test_test_utils.py 230 230 54 0 0%
tests/test_tracing.py 49 49 2 0 0%
tests/test_urldispatch.py 859 859 98 0 0%
tests/test_web_app.py 381 381 30 0 0%
tests/test_web_cli.py 76 76 20 0 0%
tests/test_web_exceptions.py 274 274 42 0 0%
tests/test_web_functional.py 1487 1487 152 0 0%
tests/test_web_log.py 143 143 12 0 0%
tests/test_web_middleware.py 230 230 26 0 0%
tests/test_web_request.py 543 543 40 0 0%
tests/test_web_request_handler.py 42 42 2 0 0%
tests/test_web_response.py 813 813 78 0 0%
tests/test_web_runner.py 175 175 36 0 0%
tests/test_web_sendfile.py 85 85 0 0 0%
tests/test_web_sendfile_functional.py 656 656 70 0 0%
tests/test_web_server.py 178 178 18 0 0%
tests/test_web_urldispatcher.py 468 406 78 0 17%
tests/test_web_websocket.py 383 383 68 0 0%
tests/test_web_websocket_functional.py 682 682 34 0 0%
tests/test_websocket_handshake.py 153 153 26 0 0%
tests/test_websocket_parser.py 293 293 44 0 0%
tests/test_websocket_writer.py 95 95 16 0 0%
tests/test_worker.py 190 190 20 0 0%
--------------------------------------------------------------------------
TOTAL 33273 28478 7674 364 15%
======================================================== slowest 10 durations =========================================================
0.01s call tests/test_web_urldispatcher.py::test_access_symlink_loop[pyloop]
0.01s teardown tests/test_web_urldispatcher.py::test_access_symlink_loop[pyloop]
(1 durations < 0.005s hidden. Use -vv to show these durations.)
======================================================= short test summary info =======================================================
FAILED tests/test_web_urldispatcher.py::test_access_symlink_loop[pyloop] - aiohttp.client_exceptions.ServerDisconnectedError: Server disconnected
========================================================== 1 failed in 2.99s ==========================================================
Should I report a separate bug or is it ok to handle it here? FWICS this one affects runtime too.
Since this one is about the test, a separate issue for the runtime one would be preferred so we don't forget to fix both.
Since this one is about the test, a separate issue for the runtime one would be preferred so we don't forget to fix both.
Moving to 3.10.2 since 3.13 isn't released yet and we don't have the ability to test it in the CI yet
Thanks!
Describe the bug
In 3.10.0rc0, the
tests/test_web_urldispatcher.py::test_access_mock_special_resource
is failing on Python 3.13.0b4.The problem was introduced in 4f834b646c23f74962e181170a40d872e76f4602 (CC @steverep). The previous version passes all tests on 3.13.
To Reproduce
In a venv, inside a directory with the git checkout:
Expected behavior
Tests passing :-).
Logs/tracebacks
Python Version
aiohttp Version
multidict Version
yarl Version
OS
Gentoo Linux amd64
Related component
Server, Client
Additional context
No response
Code of Conduct