From ddf04a6287dc7ae36fdcac76bc4e1e61fc45de2c Mon Sep 17 00:00:00 2001 From: timrid <6593626+timrid@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:50:06 +0100 Subject: [PATCH 1/4] Add support for Android and iOS platforms in CI/CD workflow --- .github/workflows/ci-cd.yml | 12 ++++++++++-- CHANGES/11750.feature.rst | 1 + pyproject.toml | 6 ++++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 CHANGES/11750.feature.rst diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index a6bb5e70bc4..66680e97f53 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -492,7 +492,7 @@ jobs: permissions: contents: read # to fetch code (actions/checkout) - name: Build wheels on ${{ matrix.os }} ${{ matrix.qemu }} ${{ matrix.musl }} + name: Build wheels on ${{ matrix.os }} ${{ matrix.qemu }} ${{ matrix.musl }} ${{ matrix.platform }} runs-on: ${{ matrix.os }} needs: pre-deploy strategy: @@ -500,6 +500,7 @@ jobs: os: ["ubuntu-latest", "windows-latest", "windows-11-arm", "macos-latest", "ubuntu-24.04-arm"] qemu: [''] musl: [""] + platform: [""] include: # Split ubuntu/musl jobs for the sake of speed-up - os: ubuntu-latest @@ -530,6 +531,10 @@ jobs: musl: musllinux - os: ubuntu-24.04-arm musl: musllinux + - os: ubuntu-latest + platform: android + - os: macos-latest + platform: ios steps: - name: Checkout uses: actions/checkout@v6 @@ -575,6 +580,7 @@ jobs: - name: Build wheels uses: pypa/cibuildwheel@v3.4.0 env: + CIBW_PLATFORM: ${{ matrix.platform || 'auto' }} CIBW_SKIP: pp* ${{ matrix.musl == 'musllinux' && '*manylinux*' || '*musllinux*' }} CIBW_ARCHS_MACOS: x86_64 arm64 universal2 - name: Upload wheels @@ -582,7 +588,9 @@ jobs: with: name: >- dist-${{ matrix.os }}-${{ matrix.musl }}-${{ - matrix.qemu + matrix.platform + && matrix.platform + || matrix.qemu && matrix.qemu || 'native' }} diff --git a/CHANGES/11750.feature.rst b/CHANGES/11750.feature.rst new file mode 100644 index 00000000000..af3f5dda0ec --- /dev/null +++ b/CHANGES/11750.feature.rst @@ -0,0 +1 @@ +Added wheels for Android and iOS platforms -- by :user:`timrid`. \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 7ad4f54b0c3..0e453a45127 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -163,6 +163,12 @@ test-command = "" # don't build PyPy wheels, install from source instead skip = "pp*" +[tool.cibuildwheel.ios] +archs = ["arm64_iphoneos", "arm64_iphonesimulator", "x86_64_iphonesimulator"] + +[tool.cibuildwheel.android] +archs = ["arm64_v8a", "x86_64"] + [tool.codespell] skip = '.git,*.pdf,*.svg,Makefile,CONTRIBUTORS.txt,venvs,_build' ignore-words-list = 'te,assertIn' From 110399217bcdcc3121ed12915953509ac9393c25 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 20:05:42 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CHANGES/11750.feature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES/11750.feature.rst b/CHANGES/11750.feature.rst index af3f5dda0ec..2af5e11cf45 100644 --- a/CHANGES/11750.feature.rst +++ b/CHANGES/11750.feature.rst @@ -1 +1 @@ -Added wheels for Android and iOS platforms -- by :user:`timrid`. \ No newline at end of file +Added wheels for Android and iOS platforms -- by :user:`timrid`. From 165104f8c61e04a4d6e790c03888eb082ad7976e Mon Sep 17 00:00:00 2001 From: timrid <6593626+timrid@users.noreply.github.com> Date: Sat, 14 Mar 2026 01:32:31 +0100 Subject: [PATCH 3/4] Add unittests in CI for android and ios --- .github/workflows/ci-cd.yml | 57 +++++++++++++++++- pyproject.toml | 12 +--- requirements/base-ft.txt | 6 +- requirements/base.in | 2 +- requirements/base.txt | 8 +-- requirements/constraints.txt | 63 ++++++++++++-------- requirements/dev.txt | 69 +++++++++++++--------- requirements/runtime-deps.in | 6 +- requirements/runtime-deps.txt | 6 +- requirements/test-common.in | 25 +++++--- requirements/test-common.txt | 58 +++++++++++------- requirements/test-ft.txt | 67 ++++++++++++--------- requirements/test.txt | 69 +++++++++++++--------- setup.cfg | 2 + tests/conftest.py | 9 ++- tests/test_benchmarks_client.py | 2 + tests/test_benchmarks_client_request.py | 4 ++ tests/test_benchmarks_client_ws.py | 2 + tests/test_benchmarks_cookiejar.py | 3 + tests/test_benchmarks_http_websocket.py | 2 + tests/test_benchmarks_http_writer.py | 3 + tests/test_benchmarks_web_fileresponse.py | 2 + tests/test_benchmarks_web_middleware.py | 2 + tests/test_benchmarks_web_response.py | 2 + tests/test_benchmarks_web_urldispatcher.py | 2 + tests/test_circular_imports.py | 2 +- tests/test_client_functional.py | 7 ++- tests/test_cookiejar.py | 1 + tests/test_http_parser.py | 1 + tests/test_leaks.py | 1 + tests/test_loop.py | 3 + tests/test_multipart.py | 2 +- tests/test_proxy_functional.py | 4 +- tests/test_run_app.py | 2 + tests/test_urldispatch.py | 4 +- tests/test_web_functional.py | 7 ++- tests/test_web_sendfile_functional.py | 17 +++++- 37 files changed, 360 insertions(+), 174 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 66680e97f53..a10fb79b56f 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -280,6 +280,58 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} + test-mobile: + permissions: + contents: read # to fetch code (actions/checkout) + + name: Test (${{ matrix.config.platform }}, ${{ matrix.pyver }}, ${{ matrix.config.os }}) + runs-on: ${{ matrix.config.os }} + needs: gen_llhttp + strategy: + matrix: + pyver: ["cp313", "cp314"] + config: + - os: ubuntu-latest + platform: android + archs: x86_64 + - os: macos-14 + platform: ios + archs: arm64_iphonesimulator + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + submodules: true + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: 3.x + - name: Update pip, wheel, setuptools, build, twine + run: | + python -m pip install -U pip wheel setuptools build twine + - name: Install cython + run: >- + python -m + pip install -r requirements/cython.in -c requirements/cython.txt + - name: Restore llhttp generated files + uses: actions/download-artifact@v8 + with: + name: llhttp + path: vendor/llhttp/build/ + - name: Cythonize + run: | + make cythonize + - name: Build wheels and test + uses: pypa/cibuildwheel@v3.4.0 + env: + CIBW_BUILD: ${{ matrix.pyver }}-* + CIBW_PLATFORM: ${{ matrix.config.platform }} + CIBW_ARCHS: ${{ matrix.config.archs }} + CIBW_TEST_REQUIRES: -r requirements/test.txt + CIBW_TEST_SOURCES: setup.cfg tests + CIBW_TEST_COMMAND: >- + python -m pytest -o 'addopts='-v -ra --showlocals -m 'not dev_mode and not autobahn' + autobahn: permissions: contents: read # to fetch code (actions/checkout) @@ -422,6 +474,7 @@ jobs: needs: - lint - test + - test-mobile - autobahn runs-on: ubuntu-latest @@ -533,7 +586,7 @@ jobs: musl: musllinux - os: ubuntu-latest platform: android - - os: macos-latest + - os: macos-14 platform: ios steps: - name: Checkout @@ -583,6 +636,8 @@ jobs: CIBW_PLATFORM: ${{ matrix.platform || 'auto' }} CIBW_SKIP: pp* ${{ matrix.musl == 'musllinux' && '*manylinux*' || '*musllinux*' }} CIBW_ARCHS_MACOS: x86_64 arm64 universal2 + CIBW_ARCHS_IOS: arm64_iphoneos arm64_iphonesimulator x86_64_iphonesimulator + CIBW_ARCHS_ANDROID: arm64_v8a x86_64 - name: Upload wheels uses: actions/upload-artifact@v6 with: diff --git a/pyproject.toml b/pyproject.toml index 0e453a45127..d1d9e782f42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,10 +48,10 @@ dynamic = [ [project.optional-dependencies] speedups = [ - "aiodns >= 3.3.0", - "Brotli >= 1.2; platform_python_implementation == 'CPython'", + "aiodns >= 3.3.0; sys_platform != 'android' and sys_platform != 'ios'", + "Brotli >= 1.2; platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios'", "brotlicffi >= 1.2; platform_python_implementation != 'CPython'", - "backports.zstd; platform_python_implementation == 'CPython' and python_version < '3.14'", + "backports.zstd; platform_python_implementation == 'CPython' and python_version < '3.14' and sys_platform != 'android' and sys_platform != 'ios'", ] [[project.maintainers]] @@ -163,12 +163,6 @@ test-command = "" # don't build PyPy wheels, install from source instead skip = "pp*" -[tool.cibuildwheel.ios] -archs = ["arm64_iphoneos", "arm64_iphonesimulator", "x86_64_iphonesimulator"] - -[tool.cibuildwheel.android] -archs = ["arm64_v8a", "x86_64"] - [tool.codespell] skip = '.git,*.pdf,*.svg,Makefile,CONTRIBUTORS.txt,venvs,_build' ignore-words-list = 'te,assertIn' diff --git a/requirements/base-ft.txt b/requirements/base-ft.txt index f28172ab3c3..1bd397a85d3 100644 --- a/requirements/base-ft.txt +++ b/requirements/base-ft.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/base-ft.txt --strip-extras requirements/base-ft.in # -aiodns==4.0.0 +aiodns==4.0.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in aiohappyeyeballs==2.6.1 # via -r requirements/runtime-deps.in @@ -12,9 +12,9 @@ aiosignal==1.4.0 # via -r requirements/runtime-deps.in async-timeout==5.0.1 ; python_version < "3.11" # via -r requirements/runtime-deps.in -backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" +backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in -brotli==1.2.0 ; platform_python_implementation == "CPython" +brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in cffi==2.0.0 # via pycares diff --git a/requirements/base.in b/requirements/base.in index 816a4e84026..f4cb3743528 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -1,5 +1,5 @@ -r runtime-deps.in gunicorn -uvloop; platform_system != "Windows" and implementation_name == "cpython" # MagicStack/uvloop#14 +uvloop; platform_system != "Windows" and implementation_name == "cpython" and sys_platform != 'android' and sys_platform != 'ios' # MagicStack/uvloop#14 winloop; platform_system == "Windows" and implementation_name == "cpython" diff --git a/requirements/base.txt b/requirements/base.txt index d637bda2d6d..f491760e616 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/base.txt --strip-extras requirements/base.in # -aiodns==4.0.0 +aiodns==4.0.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in aiohappyeyeballs==2.6.1 # via -r requirements/runtime-deps.in @@ -12,9 +12,9 @@ aiosignal==1.4.0 # via -r requirements/runtime-deps.in async-timeout==5.0.1 ; python_version < "3.11" # via -r requirements/runtime-deps.in -backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" +backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in -brotli==1.2.0 ; platform_python_implementation == "CPython" +brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in cffi==2.0.0 # via pycares @@ -45,7 +45,7 @@ typing-extensions==4.15.0 ; python_version < "3.13" # -r requirements/runtime-deps.in # aiosignal # multidict -uvloop==0.21.0 ; platform_system != "Windows" and implementation_name == "cpython" +uvloop==0.21.0 ; platform_system != "Windows" and implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/base.in yarl==1.22.0 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 92352aa0219..b2c2ccb5488 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/constraints.txt --strip-extras requirements/constraints.in # -aiodns==4.0.0 +aiodns==4.0.0 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/runtime-deps.in @@ -28,18 +28,19 @@ backports-zstd==1.3.0 ; implementation_name == "cpython" # via # -r requirements/lint.in # -r requirements/runtime-deps.in -blockbuster==1.5.26 +blockbuster==1.5.26 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in -brotli==1.2.0 ; platform_python_implementation == "CPython" +brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in build==1.4.0 # via pip-tools certifi==2026.2.25 # via requests -cffi==2.0.0 +cffi==2.0.0 ; sys_platform != "android" and sys_platform != "ios" # via + # -r requirements/test-common.in # cryptography # pycares # pytest-codspeed @@ -54,11 +55,11 @@ click==8.3.1 # towncrier # wait-for-it coverage==7.13.4 + # via pytest-cov +cryptography==46.0.5 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/test-common.in - # pytest-cov -cryptography==46.0.5 - # via trustme + # trustme cython==3.2.4 # via -r requirements/cython.in distlib==0.4.0 @@ -73,8 +74,10 @@ filelock==3.24.3 # via # python-discovery # virtualenv -forbiddenfruit==0.1.4 - # via blockbuster +forbiddenfruit==0.1.4 ; implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # blockbuster freezegun==1.5.5 # via # -r requirements/lint.in @@ -96,7 +99,7 @@ imagesize==2.0.0 # via sphinx iniconfig==2.3.0 # via pytest -isal==1.7.2 ; python_version < "3.14" +isal==1.7.2 ; python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in @@ -104,8 +107,10 @@ jinja2==3.1.6 # via # sphinx # towncrier -librt==0.8.0 - # via mypy +librt==0.8.0 ; platform_python_implementation != "PyPy" and sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # mypy markdown-it-py==4.0.0 # via rich markupsafe==3.0.3 @@ -121,8 +126,10 @@ mypy==1.19.1 ; implementation_name == "cpython" # via # -r requirements/lint.in # -r requirements/test-common.in -mypy-extensions==1.1.0 - # via mypy +mypy-extensions==1.1.0 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # mypy nodeenv==1.10.0 # via pre-commit packaging==26.0 @@ -156,14 +163,20 @@ proxy-py==2.4.10 # via # -r requirements/lint.in # -r requirements/test-common.in -pycares==5.0.1 - # via aiodns +pycares==5.0.1 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # aiodns pycparser==3.0 # via cffi -pydantic==2.12.5 - # via python-on-whales -pydantic-core==2.41.5 - # via pydantic +pydantic==2.12.5 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # python-on-whales +pydantic-core==2.41.5 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # pydantic pyenchant==3.3.0 # via sphinxcontrib-spelling pygments==2.19.2 @@ -183,7 +196,7 @@ pytest==9.0.2 # pytest-cov # pytest-mock # pytest-xdist -pytest-codspeed==4.3.0 +pytest-codspeed==4.3.0 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in @@ -193,13 +206,13 @@ pytest-mock==3.15.1 # via # -r requirements/lint.in # -r requirements/test-common.in -pytest-xdist==3.8.0 +pytest-xdist==3.8.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in python-dateutil==2.9.0.post0 # via freezegun python-discovery==1.1.0 # via virtualenv -python-on-whales==0.80.0 +python-on-whales==0.80.0 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in @@ -254,7 +267,7 @@ towncrier==25.8.0 # via # -r requirements/doc.in # sphinxcontrib-towncrier -trustme==1.2.1 ; platform_machine != "i686" +trustme==1.2.1 ; platform_machine != "i686" and sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in @@ -289,7 +302,7 @@ wheel==0.46.3 # via pip-tools yarl==1.22.0 # via -r requirements/runtime-deps.in -zlib-ng==1.0.0 +zlib-ng==1.0.0 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in diff --git a/requirements/dev.txt b/requirements/dev.txt index eb266b1ee7b..f7414d59077 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/dev.txt --strip-extras requirements/dev.in # -aiodns==4.0.0 +aiodns==4.0.0 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/runtime-deps.in @@ -24,22 +24,23 @@ async-timeout==5.0.1 ; python_version < "3.11" # valkey babel==2.18.0 # via sphinx -backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" +backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/runtime-deps.in -blockbuster==1.5.26 +blockbuster==1.5.26 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in -brotli==1.2.0 ; platform_python_implementation == "CPython" +brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in build==1.4.0 # via pip-tools certifi==2026.2.25 # via requests -cffi==2.0.0 +cffi==2.0.0 ; sys_platform != "android" and sys_platform != "ios" # via + # -r requirements/test-common.in # cryptography # pycares # pytest-codspeed @@ -54,11 +55,11 @@ click==8.3.1 # towncrier # wait-for-it coverage==7.13.4 + # via pytest-cov +cryptography==46.0.5 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/test-common.in - # pytest-cov -cryptography==46.0.5 - # via trustme + # trustme distlib==0.4.0 # via virtualenv docutils==0.21.2 @@ -71,8 +72,10 @@ filelock==3.24.3 # via # python-discovery # virtualenv -forbiddenfruit==0.1.4 - # via blockbuster +forbiddenfruit==0.1.4 ; implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # blockbuster freezegun==1.5.5 # via # -r requirements/lint.in @@ -94,7 +97,7 @@ imagesize==2.0.0 # via sphinx iniconfig==2.3.0 # via pytest -isal==1.7.2 ; python_version < "3.14" +isal==1.7.2 ; python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in @@ -102,8 +105,10 @@ jinja2==3.1.6 # via # sphinx # towncrier -librt==0.8.0 - # via mypy +librt==0.8.0 ; platform_python_implementation != "PyPy" and sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # mypy markdown-it-py==4.0.0 # via rich markupsafe==3.0.3 @@ -114,12 +119,14 @@ multidict==6.7.1 # via # -r requirements/runtime-deps.in # yarl -mypy==1.19.1 ; implementation_name == "cpython" +mypy==1.19.1 ; implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in -mypy-extensions==1.1.0 - # via mypy +mypy-extensions==1.1.0 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # mypy nodeenv==1.10.0 # via pre-commit packaging==26.0 @@ -153,14 +160,20 @@ proxy-py==2.4.10 # via # -r requirements/lint.in # -r requirements/test-common.in -pycares==5.0.1 - # via aiodns +pycares==5.0.1 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # aiodns pycparser==3.0 # via cffi -pydantic==2.12.5 - # via python-on-whales -pydantic-core==2.41.5 - # via pydantic +pydantic==2.12.5 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # python-on-whales +pydantic-core==2.41.5 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # pydantic pygments==2.19.2 # via # pytest @@ -178,7 +191,7 @@ pytest==9.0.2 # pytest-cov # pytest-mock # pytest-xdist -pytest-codspeed==4.3.0 +pytest-codspeed==4.3.0 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in @@ -188,13 +201,13 @@ pytest-mock==3.15.1 # via # -r requirements/lint.in # -r requirements/test-common.in -pytest-xdist==3.8.0 +pytest-xdist==3.8.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in python-dateutil==2.9.0.post0 # via freezegun python-discovery==1.1.0 # via virtualenv -python-on-whales==0.80.0 +python-on-whales==0.80.0 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in @@ -244,7 +257,7 @@ towncrier==25.8.0 # via # -r requirements/doc.in # sphinxcontrib-towncrier -trustme==1.2.1 ; platform_machine != "i686" +trustme==1.2.1 ; platform_machine != "i686" and sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in @@ -265,7 +278,7 @@ typing-inspection==0.4.2 # via pydantic urllib3==2.6.3 # via requests -uvloop==0.21.0 ; platform_system != "Windows" and implementation_name == "cpython" +uvloop==0.21.0 ; platform_system != "Windows" and implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" # via # -r requirements/base.in # -r requirements/lint.in @@ -279,7 +292,7 @@ wheel==0.46.3 # via pip-tools yarl==1.22.0 # via -r requirements/runtime-deps.in -zlib-ng==1.0.0 +zlib-ng==1.0.0 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/lint.in # -r requirements/test-common.in diff --git a/requirements/runtime-deps.in b/requirements/runtime-deps.in index 3e3c4d05313..d70fc5a9dbc 100644 --- a/requirements/runtime-deps.in +++ b/requirements/runtime-deps.in @@ -1,11 +1,11 @@ # Extracted from `pyproject.toml` via `make sync-direct-runtime-deps` -aiodns >= 3.3.0 +aiodns >= 3.3.0; sys_platform != 'android' and sys_platform != 'ios' aiohappyeyeballs >= 2.5.0 aiosignal >= 1.4.0 async-timeout >= 4.0, < 6.0 ; python_version < '3.11' -backports.zstd; platform_python_implementation == 'CPython' and python_version < '3.14' -Brotli >= 1.2; platform_python_implementation == 'CPython' +backports.zstd; platform_python_implementation == 'CPython' and python_version < '3.14' and sys_platform != 'android' and sys_platform != 'ios' +Brotli >= 1.2; platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' brotlicffi >= 1.2; platform_python_implementation != 'CPython' frozenlist >= 1.1.1 multidict >=4.5, < 7.0 diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 7b6983e8467..80ef7bf9ab3 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/runtime-deps.txt --strip-extras requirements/runtime-deps.in # -aiodns==4.0.0 +aiodns==4.0.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in aiohappyeyeballs==2.6.1 # via -r requirements/runtime-deps.in @@ -12,9 +12,9 @@ aiosignal==1.4.0 # via -r requirements/runtime-deps.in async-timeout==5.0.1 ; python_version < "3.11" # via -r requirements/runtime-deps.in -backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" +backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in -brotli==1.2.0 ; platform_python_implementation == "CPython" +brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in cffi==2.0.0 # via pycares diff --git a/requirements/test-common.in b/requirements/test-common.in index c010f61fa8a..e11815259e7 100644 --- a/requirements/test-common.in +++ b/requirements/test-common.in @@ -1,17 +1,24 @@ -blockbuster -coverage +blockbuster; sys_platform != 'android' and sys_platform != 'ios' +cffi; sys_platform != 'android' and sys_platform != 'ios' # used by cryptography, pycares and pytest-codspeedcoverage +cryptography; sys_platform != 'android' and sys_platform != 'ios' # used by trustme +forbiddenfruit; implementation_name == "cpython" and sys_platform != 'android' and sys_platform != 'ios' # used by blockbuster freezegun -isal; python_version < "3.14" # no wheel for 3.14 -mypy; implementation_name == "cpython" +isal; python_version < "3.14" and sys_platform != 'android' and sys_platform != 'ios' # no wheel for 3.14 +librt; platform_python_implementation != 'PyPy' and sys_platform != 'android' and sys_platform != 'ios' # used by mypy +mypy; implementation_name == "cpython" and sys_platform != 'android' and sys_platform != 'ios' +mypy-extensions; sys_platform != 'android' and sys_platform != 'ios' # used by mypy pkgconfig proxy.py >= 2.4.4rc5 +pycares; sys_platform != 'android' and sys_platform != 'ios' # used by aiodns +pydantic; sys_platform != 'android' and sys_platform != 'ios' # used by python-on-whales +pydantic-core; sys_platform != 'android' and sys_platform != 'ios' # used by pydantic pytest pytest-cov pytest-mock -pytest-xdist -pytest_codspeed -python-on-whales +pytest-xdist; sys_platform != 'android' and sys_platform != 'ios' +pytest_codspeed; sys_platform != 'android' and sys_platform != 'ios' +python-on-whales; sys_platform != 'android' and sys_platform != 'ios' setuptools-git -trustme; platform_machine != "i686" # no 32-bit wheels +trustme; platform_machine != "i686" and sys_platform != 'android' and sys_platform != 'ios' # no 32-bit and android and ios wheels wait-for-it -zlib_ng +zlib_ng; sys_platform != 'android' and sys_platform != 'ios' diff --git a/requirements/test-common.txt b/requirements/test-common.txt index 8ec6de57f4f..9dc931cd2cb 100644 --- a/requirements/test-common.txt +++ b/requirements/test-common.txt @@ -6,44 +6,52 @@ # annotated-types==0.7.0 # via pydantic -blockbuster==1.5.26 +blockbuster==1.5.26 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in -cffi==2.0.0 +cffi==2.0.0 ; sys_platform != "android" and sys_platform != "ios" # via + # -r requirements/test-common.in # cryptography + # pycares # pytest-codspeed click==8.3.1 # via wait-for-it coverage==7.13.4 + # via pytest-cov +cryptography==46.0.5 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/test-common.in - # pytest-cov -cryptography==46.0.5 - # via trustme + # trustme exceptiongroup==1.3.1 # via pytest execnet==2.1.2 # via pytest-xdist -forbiddenfruit==0.1.4 - # via blockbuster +forbiddenfruit==0.1.4 ; implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # blockbuster freezegun==1.5.5 # via -r requirements/test-common.in idna==3.11 # via trustme iniconfig==2.3.0 # via pytest -isal==1.8.0 ; python_version < "3.14" +isal==1.8.0 ; python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in -librt==0.8.0 - # via mypy +librt==0.8.0 ; platform_python_implementation != "PyPy" and sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # mypy markdown-it-py==4.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -mypy==1.19.1 ; implementation_name == "cpython" +mypy==1.19.1 ; implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in -mypy-extensions==1.1.0 - # via mypy +mypy-extensions==1.1.0 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # mypy packaging==26.0 # via pytest pathspec==1.0.4 @@ -56,12 +64,18 @@ pluggy==1.6.0 # pytest-cov proxy-py==2.4.10 # via -r requirements/test-common.in +pycares==5.0.1 ; sys_platform != "android" and sys_platform != "ios" + # via -r requirements/test-common.in pycparser==3.0 # via cffi -pydantic==2.12.5 - # via python-on-whales -pydantic-core==2.41.5 - # via pydantic +pydantic==2.12.5 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # python-on-whales +pydantic-core==2.41.5 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # pydantic pygments==2.19.2 # via # pytest @@ -73,17 +87,17 @@ pytest==9.0.2 # pytest-cov # pytest-mock # pytest-xdist -pytest-codspeed==4.3.0 +pytest-codspeed==4.3.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in pytest-cov==7.0.0 # via -r requirements/test-common.in pytest-mock==3.15.1 # via -r requirements/test-common.in -pytest-xdist==3.8.0 +pytest-xdist==3.8.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in python-dateutil==2.9.0.post0 # via freezegun -python-on-whales==0.80.0 +python-on-whales==0.80.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in rich==14.3.3 # via pytest-codspeed @@ -96,7 +110,7 @@ tomli==2.4.0 # coverage # mypy # pytest -trustme==1.2.1 ; platform_machine != "i686" +trustme==1.2.1 ; platform_machine != "i686" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in typing-extensions==4.15.0 # via @@ -111,5 +125,5 @@ typing-inspection==0.4.2 # via pydantic wait-for-it==2.3.0 # via -r requirements/test-common.in -zlib-ng==1.0.0 +zlib-ng==1.0.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt index 7e1671bad0e..b56dd876035 100644 --- a/requirements/test-ft.txt +++ b/requirements/test-ft.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/test-ft.txt --strip-extras requirements/test-ft.in # -aiodns==4.0.0 +aiodns==4.0.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in aiohappyeyeballs==2.6.1 # via -r requirements/runtime-deps.in @@ -14,31 +14,34 @@ annotated-types==0.7.0 # via pydantic async-timeout==5.0.1 ; python_version < "3.11" # via -r requirements/runtime-deps.in -backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" +backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in -blockbuster==1.5.26 +blockbuster==1.5.26 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in -brotli==1.2.0 ; platform_python_implementation == "CPython" +brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in -cffi==2.0.0 +cffi==2.0.0 ; sys_platform != "android" and sys_platform != "ios" # via + # -r requirements/test-common.in # cryptography # pycares # pytest-codspeed click==8.3.1 # via wait-for-it coverage==7.13.4 + # via pytest-cov +cryptography==46.0.5 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/test-common.in - # pytest-cov -cryptography==46.0.5 - # via trustme + # trustme exceptiongroup==1.3.1 # via pytest execnet==2.1.2 # via pytest-xdist -forbiddenfruit==0.1.4 - # via blockbuster +forbiddenfruit==0.1.4 ; implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # blockbuster freezegun==1.5.5 # via -r requirements/test-common.in frozenlist==1.8.0 @@ -53,10 +56,12 @@ idna==3.11 # yarl iniconfig==2.3.0 # via pytest -isal==1.8.0 ; python_version < "3.14" +isal==1.8.0 ; python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in -librt==0.8.0 - # via mypy +librt==0.8.0 ; platform_python_implementation != "PyPy" and sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # mypy markdown-it-py==4.0.0 # via rich mdurl==0.1.2 @@ -65,10 +70,12 @@ multidict==6.7.1 # via # -r requirements/runtime-deps.in # yarl -mypy==1.19.1 ; implementation_name == "cpython" +mypy==1.19.1 ; implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in -mypy-extensions==1.1.0 - # via mypy +mypy-extensions==1.1.0 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # mypy packaging==26.0 # via # gunicorn @@ -87,14 +94,20 @@ propcache==0.4.1 # yarl proxy-py==2.4.10 # via -r requirements/test-common.in -pycares==5.0.1 - # via aiodns +pycares==5.0.1 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # aiodns pycparser==3.0 # via cffi -pydantic==2.12.5 - # via python-on-whales -pydantic-core==2.41.5 - # via pydantic +pydantic==2.12.5 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # python-on-whales +pydantic-core==2.41.5 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # pydantic pygments==2.19.2 # via # pytest @@ -106,17 +119,17 @@ pytest==9.0.2 # pytest-cov # pytest-mock # pytest-xdist -pytest-codspeed==4.3.0 +pytest-codspeed==4.3.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in pytest-cov==7.0.0 # via -r requirements/test-common.in pytest-mock==3.15.1 # via -r requirements/test-common.in -pytest-xdist==3.8.0 +pytest-xdist==3.8.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in python-dateutil==2.9.0.post0 # via freezegun -python-on-whales==0.80.0 +python-on-whales==0.80.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in rich==14.3.3 # via pytest-codspeed @@ -129,7 +142,7 @@ tomli==2.4.0 # coverage # mypy # pytest -trustme==1.2.1 ; platform_machine != "i686" +trustme==1.2.1 ; platform_machine != "i686" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in typing-extensions==4.15.0 ; python_version < "3.13" # via @@ -149,5 +162,5 @@ wait-for-it==2.3.0 # via -r requirements/test-common.in yarl==1.22.0 # via -r requirements/runtime-deps.in -zlib-ng==1.0.0 +zlib-ng==1.0.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in diff --git a/requirements/test.txt b/requirements/test.txt index 3ba827a4860..43b02a3dcf9 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/test.txt --strip-extras requirements/test.in # -aiodns==4.0.0 +aiodns==4.0.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in aiohappyeyeballs==2.6.1 # via -r requirements/runtime-deps.in @@ -14,31 +14,34 @@ annotated-types==0.7.0 # via pydantic async-timeout==5.0.1 ; python_version < "3.11" # via -r requirements/runtime-deps.in -backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" +backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in -blockbuster==1.5.26 +blockbuster==1.5.26 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in -brotli==1.2.0 ; platform_python_implementation == "CPython" +brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/runtime-deps.in -cffi==2.0.0 +cffi==2.0.0 ; sys_platform != "android" and sys_platform != "ios" # via + # -r requirements/test-common.in # cryptography # pycares # pytest-codspeed click==8.3.1 # via wait-for-it coverage==7.13.4 + # via pytest-cov +cryptography==46.0.5 ; sys_platform != "android" and sys_platform != "ios" # via # -r requirements/test-common.in - # pytest-cov -cryptography==46.0.5 - # via trustme + # trustme exceptiongroup==1.3.1 # via pytest execnet==2.1.2 # via pytest-xdist -forbiddenfruit==0.1.4 - # via blockbuster +forbiddenfruit==0.1.4 ; implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # blockbuster freezegun==1.5.5 # via -r requirements/test-common.in frozenlist==1.8.0 @@ -53,10 +56,12 @@ idna==3.11 # yarl iniconfig==2.3.0 # via pytest -isal==1.7.2 ; python_version < "3.14" +isal==1.7.2 ; python_version < "3.14" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in -librt==0.8.0 - # via mypy +librt==0.8.0 ; platform_python_implementation != "PyPy" and sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # mypy markdown-it-py==4.0.0 # via rich mdurl==0.1.2 @@ -65,10 +70,12 @@ multidict==6.7.1 # via # -r requirements/runtime-deps.in # yarl -mypy==1.19.1 ; implementation_name == "cpython" +mypy==1.19.1 ; implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in -mypy-extensions==1.1.0 - # via mypy +mypy-extensions==1.1.0 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # mypy packaging==26.0 # via # gunicorn @@ -87,14 +94,20 @@ propcache==0.4.1 # yarl proxy-py==2.4.10 # via -r requirements/test-common.in -pycares==5.0.1 - # via aiodns +pycares==5.0.1 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # aiodns pycparser==3.0 # via cffi -pydantic==2.12.5 - # via python-on-whales -pydantic-core==2.41.5 - # via pydantic +pydantic==2.12.5 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # python-on-whales +pydantic-core==2.41.5 ; sys_platform != "android" and sys_platform != "ios" + # via + # -r requirements/test-common.in + # pydantic pygments==2.19.2 # via # pytest @@ -106,17 +119,17 @@ pytest==9.0.2 # pytest-cov # pytest-mock # pytest-xdist -pytest-codspeed==4.3.0 +pytest-codspeed==4.3.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in pytest-cov==7.0.0 # via -r requirements/test-common.in pytest-mock==3.15.1 # via -r requirements/test-common.in -pytest-xdist==3.8.0 +pytest-xdist==3.8.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in python-dateutil==2.9.0.post0 # via freezegun -python-on-whales==0.80.0 +python-on-whales==0.80.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in rich==14.3.3 # via pytest-codspeed @@ -129,7 +142,7 @@ tomli==2.4.0 # coverage # mypy # pytest -trustme==1.2.1 ; platform_machine != "i686" +trustme==1.2.1 ; platform_machine != "i686" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in typing-extensions==4.15.0 ; python_version < "3.13" # via @@ -145,11 +158,11 @@ typing-extensions==4.15.0 ; python_version < "3.13" # typing-inspection typing-inspection==0.4.2 # via pydantic -uvloop==0.21.0 ; platform_system != "Windows" and implementation_name == "cpython" +uvloop==0.21.0 ; platform_system != "Windows" and implementation_name == "cpython" and sys_platform != "android" and sys_platform != "ios" # via -r requirements/base.in wait-for-it==2.3.0 # via -r requirements/test-common.in yarl==1.22.0 # via -r requirements/runtime-deps.in -zlib-ng==1.0.0 +zlib-ng==1.0.0 ; sys_platform != "android" and sys_platform != "ios" # via -r requirements/test-common.in diff --git a/setup.cfg b/setup.cfg index 2d7e24b0374..7e50e3fc490 100644 --- a/setup.cfg +++ b/setup.cfg @@ -88,6 +88,8 @@ filterwarnings = # https://github.com/spulec/freezegun/issues/508 # https://github.com/spulec/freezegun/pull/511 ignore:datetime.*utcnow\(\) is deprecated and scheduled for removal:DeprecationWarning:freezegun.api + # coverage's C tracer is not available on iOS/Android (pure-Python fallback is used instead) + ignore:Couldn't import C tracer:coverage.exceptions.CoverageWarning junit_suite_name = aiohttp_test_suite norecursedirs = dist docs build .tox .eggs minversion = 3.8.2 diff --git a/tests/conftest.py b/tests/conftest.py index 336c0e14d40..28535b9e072 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,10 +19,14 @@ from uuid import uuid4 import pytest -import trustme from multidict import CIMultiDict from yarl import URL +try: + import trustme +except ImportError: # pragma: no cover + pass + try: from blockbuster import blockbuster_ctx @@ -113,7 +117,8 @@ def blockbuster(request: pytest.FixtureRequest) -> Iterator[None]: @pytest.fixture def tls_certificate_authority() -> trustme.CA: - return trustme.CA() + trustme = pytest.importorskip("trustme") + return trustme.CA() # type: ignore[no-any-return] @pytest.fixture diff --git a/tests/test_benchmarks_client.py b/tests/test_benchmarks_client.py index 5e205549e9c..5beea505db8 100644 --- a/tests/test_benchmarks_client.py +++ b/tests/test_benchmarks_client.py @@ -3,6 +3,8 @@ import asyncio import pytest + +pytest.importorskip("pytest_codspeed") from pytest_codspeed import BenchmarkFixture from yarl import URL diff --git a/tests/test_benchmarks_client_request.py b/tests/test_benchmarks_client_request.py index 0a37087380f..5d71153e281 100644 --- a/tests/test_benchmarks_client_request.py +++ b/tests/test_benchmarks_client_request.py @@ -2,6 +2,10 @@ import asyncio import sys + +import pytest + +pytest.importorskip("pytest_codspeed") from collections.abc import Callable from http.cookies import BaseCookie from typing import Any diff --git a/tests/test_benchmarks_client_ws.py b/tests/test_benchmarks_client_ws.py index 0338b52fb9d..72b40e92bf9 100644 --- a/tests/test_benchmarks_client_ws.py +++ b/tests/test_benchmarks_client_ws.py @@ -3,6 +3,8 @@ import asyncio import pytest + +pytest.importorskip("pytest_codspeed") from pytest_codspeed import BenchmarkFixture from aiohttp import web diff --git a/tests/test_benchmarks_cookiejar.py b/tests/test_benchmarks_cookiejar.py index 78566151ef4..c2dd294fd03 100644 --- a/tests/test_benchmarks_cookiejar.py +++ b/tests/test_benchmarks_cookiejar.py @@ -2,6 +2,9 @@ from http.cookies import BaseCookie +import pytest + +pytest.importorskip("pytest_codspeed") from pytest_codspeed import BenchmarkFixture from yarl import URL diff --git a/tests/test_benchmarks_http_websocket.py b/tests/test_benchmarks_http_websocket.py index 2aa9b8294bd..61630d14fda 100644 --- a/tests/test_benchmarks_http_websocket.py +++ b/tests/test_benchmarks_http_websocket.py @@ -3,6 +3,8 @@ import asyncio import pytest + +pytest.importorskip("pytest_codspeed") from pytest_codspeed import BenchmarkFixture from aiohttp._websocket.helpers import MSG_SIZE, PACK_LEN3 diff --git a/tests/test_benchmarks_http_writer.py b/tests/test_benchmarks_http_writer.py index 0d52ca875e6..617f02efdba 100644 --- a/tests/test_benchmarks_http_writer.py +++ b/tests/test_benchmarks_http_writer.py @@ -1,5 +1,8 @@ """codspeed benchmarks for http writer.""" +import pytest + +pytest.importorskip("pytest_codspeed") from multidict import CIMultiDict from pytest_codspeed import BenchmarkFixture diff --git a/tests/test_benchmarks_web_fileresponse.py b/tests/test_benchmarks_web_fileresponse.py index 01aa7448c86..27fe2263c07 100644 --- a/tests/test_benchmarks_web_fileresponse.py +++ b/tests/test_benchmarks_web_fileresponse.py @@ -2,7 +2,9 @@ import asyncio import pathlib +import pytest +pytest.importorskip("pytest_codspeed") from multidict import CIMultiDict from pytest_codspeed import BenchmarkFixture diff --git a/tests/test_benchmarks_web_middleware.py b/tests/test_benchmarks_web_middleware.py index 497da1819c9..fa02159bc5c 100644 --- a/tests/test_benchmarks_web_middleware.py +++ b/tests/test_benchmarks_web_middleware.py @@ -1,7 +1,9 @@ """codspeed benchmarks for web middlewares.""" import asyncio +import pytest +pytest.importorskip("pytest_codspeed") from pytest_codspeed import BenchmarkFixture from aiohttp import web diff --git a/tests/test_benchmarks_web_response.py b/tests/test_benchmarks_web_response.py index fbf1fadf1e1..5351a47a06c 100644 --- a/tests/test_benchmarks_web_response.py +++ b/tests/test_benchmarks_web_response.py @@ -1,5 +1,7 @@ """codspeed benchmarks for the web responses.""" +import pytest +pytest.importorskip("pytest_codspeed") from pytest_codspeed import BenchmarkFixture from aiohttp import web diff --git a/tests/test_benchmarks_web_urldispatcher.py b/tests/test_benchmarks_web_urldispatcher.py index 339eaef8a0e..627a92bcc98 100644 --- a/tests/test_benchmarks_web_urldispatcher.py +++ b/tests/test_benchmarks_web_urldispatcher.py @@ -10,6 +10,8 @@ from unittest import mock import pytest + +pytest.importorskip("pytest_codspeed") from multidict import CIMultiDict, CIMultiDictProxy from pytest_codspeed import BenchmarkFixture from yarl import URL diff --git a/tests/test_circular_imports.py b/tests/test_circular_imports.py index 9b5d7ed2697..ce30204e719 100644 --- a/tests/test_circular_imports.py +++ b/tests/test_circular_imports.py @@ -84,7 +84,7 @@ def _discover_path_importables( ) ) - +@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not supported") @pytest.mark.parametrize( "import_path", _mark_aiohttp_worker_for_skipping(_find_all_importables(aiohttp)), diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 971e7576d52..706487957a0 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -1,4 +1,5 @@ # HTTP client functional tests against aiohttp.web server +from __future__ import annotations # TODO(PY311): Remove import asyncio import datetime @@ -19,6 +20,11 @@ from typing import Any, NoReturn from unittest import mock +try: + import trustme +except ImportError: # pragma: no cover + pass + try: try: import brotlicffi as brotli @@ -33,7 +39,6 @@ ZstdCompressor = None # type: ignore[assignment,misc] # pragma: no cover import pytest -import trustme from multidict import MultiDict from pytest_mock import MockerFixture from yarl import URL, Query diff --git a/tests/test_cookiejar.py b/tests/test_cookiejar.py index 24c1f73284c..ea4240b57ca 100644 --- a/tests/test_cookiejar.py +++ b/tests/test_cookiejar.py @@ -1569,6 +1569,7 @@ async def test_shared_cookie_with_multiple_domains() -> None: # === Security tests for restricted unpickler and JSON save/load === +@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="os.system not supported") def test_load_rejects_malicious_pickle(tmp_path: Path) -> None: """Verify CookieJar.load() blocks arbitrary code execution via pickle. diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py index 28fc3aac03d..dde990d25b7 100644 --- a/tests/test_http_parser.py +++ b/tests/test_http_parser.py @@ -2040,6 +2040,7 @@ async def test_feed_eof_no_err_gzip(self, protocol: BaseProtocol) -> None: dbuf.feed_eof() assert [b"line"] == list(buf._buffer) + @pytest.mark.skipif(sys.platform in ("android", "ios"), reason="brotli not available") async def test_feed_eof_no_err_brotli(self, protocol: BaseProtocol) -> None: buf = aiohttp.StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) dbuf = DeflateBuffer(buf, "br") diff --git a/tests/test_leaks.py b/tests/test_leaks.py index 07b506bdb99..fcbb345df82 100644 --- a/tests/test_leaks.py +++ b/tests/test_leaks.py @@ -8,6 +8,7 @@ IS_PYPY = platform.python_implementation() == "PyPy" +@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not supported") @pytest.mark.skipif(IS_PYPY, reason="gc.DEBUG_LEAK not available on PyPy") @pytest.mark.parametrize( ("script", "message"), diff --git a/tests/test_loop.py b/tests/test_loop.py index 0feaf5ce1a5..77ad0fa4830 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -1,5 +1,6 @@ import asyncio import platform +import sys import threading import pytest @@ -8,6 +9,7 @@ from aiohttp.test_utils import AioHTTPTestCase, loop_context +@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not available") @pytest.mark.skipif( platform.system() == "Windows", reason="the test is not valid for Windows" ) @@ -40,6 +42,7 @@ def test_default_loop(loop: asyncio.AbstractEventLoop) -> None: assert asyncio.get_event_loop() is loop +@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not available") def test_setup_loop_non_main_thread() -> None: child_exc = None diff --git a/tests/test_multipart.py b/tests/test_multipart.py index 9422a68fbb6..99cc67be5f1 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -122,7 +122,7 @@ async def read(self, size: int | None = None) -> bytes: class TestMultipartResponseWrapper: - def test_at_eof(self) -> None: + async def test_at_eof(self) -> None: m_resp = mock.create_autospec(aiohttp.ClientResponse, spec_set=True) m_stream = mock.create_autospec(MultipartReader, spec_set=True) wrapper = MultipartResponseWrapper(m_resp, m_stream) diff --git a/tests/test_proxy_functional.py b/tests/test_proxy_functional.py index 7c92b840790..dfab406cbcf 100644 --- a/tests/test_proxy_functional.py +++ b/tests/test_proxy_functional.py @@ -11,8 +11,10 @@ from unittest import mock from uuid import uuid4 -import proxy import pytest + +pytest.importorskip("proxy") +import proxy from pytest_mock import MockerFixture from yarl import URL diff --git a/tests/test_run_app.py b/tests/test_run_app.py index 9e1fdb63176..e781a59c15f 100644 --- a/tests/test_run_app.py +++ b/tests/test_run_app.py @@ -688,6 +688,7 @@ def test_run_app_multiple_preexisting_sockets( """ +@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not supported") def test_sigint() -> None: skip_if_on_windows() @@ -700,6 +701,7 @@ def test_sigint() -> None: assert proc.wait() == 0 +@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not supported") def test_sigterm() -> None: skip_if_on_windows() diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py index 6603389f16d..33ee6d8f894 100644 --- a/tests/test_urldispatch.py +++ b/tests/test_urldispatch.py @@ -1063,7 +1063,7 @@ def test_static_route_user_home(router: web.UrlDispatcher) -> None: except ValueError: pytest.skip("aiohttp folder is not placed in user's HOME") route = router.add_static("/st", str(static_dir)) - assert here == route.get_info()["directory"] + assert here.resolve() == route.get_info()["directory"] def test_static_route_points_to_file(router: web.UrlDispatcher) -> None: @@ -1087,7 +1087,7 @@ async def test_405_for_resource_adapter(router: web.UrlDispatcher) -> None: @pytest.mark.skipif(platform.system() == "Windows", reason="Different path formats") async def test_static_resource_outside_traversal(router: web.UrlDispatcher) -> None: """Test relative path traversing outside root does not resolve.""" - static_file = pathlib.Path(aiohttp.__file__) + static_file = pathlib.Path(aiohttp.__file__).resolve() request_path = "/st" + "/.." * (len(static_file.parts) - 2) + str(static_file) assert pathlib.Path(request_path).resolve() == static_file diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 730d662ced4..154210a5d91 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -33,7 +33,10 @@ try: import brotlicffi as brotli except ImportError: - import brotli + try: + import brotli + except ImportError: + brotli = None try: import ssl @@ -1132,9 +1135,11 @@ async def handler(request: web.Request) -> web.Response: resp.release() +@pytest.mark.skipif(brotli is None, reason="brotli not available") async def test_response_with_precompressed_body_brotli( aiohttp_client: AiohttpClient, ) -> None: + assert brotli is not None async def handler(request: web.Request) -> web.Response: headers = {"Content-Encoding": "br"} return web.Response(body=brotli.compress(b"mydata"), headers=headers) diff --git a/tests/test_web_sendfile_functional.py b/tests/test_web_sendfile_functional.py index 87be2db182b..bc3348b5e23 100644 --- a/tests/test_web_sendfile_functional.py +++ b/tests/test_web_sendfile_functional.py @@ -4,6 +4,7 @@ import pathlib import socket from collections.abc import Iterable, Iterator +import sys from typing import Protocol from unittest import mock @@ -19,7 +20,10 @@ try: import brotlicffi as brotli except ImportError: - import brotli + try: + import brotli + except ImportError: + brotli = None try: import ssl @@ -54,9 +58,12 @@ def hello_txt( } # Uncompressed file is not actually written to test it is not required. hello["gzip"].write_bytes(gzip.compress(HELLO_AIOHTTP)) - hello["br"].write_bytes(brotli.compress(HELLO_AIOHTTP)) + if brotli is not None: + hello["br"].write_bytes(brotli.compress(HELLO_AIOHTTP)) hello["bzip2"].write_bytes(bz2.compress(HELLO_AIOHTTP)) encoding = getattr(request, "param", None) + if encoding == "br" and brotli is None: + pytest.skip("brotli not available") return hello[encoding] @@ -275,6 +282,9 @@ async def test_static_file_custom_content_type_compress( ) -> None: """Test that custom type with encoding is returned for unencoded requests.""" + if expect_encoding == "br" and brotli is None: + pytest.skip("brotli not available") + async def handler(request: web.Request) -> web.FileResponse: resp = sender(hello_txt, chunk_size=16) resp.content_type = "application/pdf" @@ -309,6 +319,8 @@ async def test_static_file_with_encoding_and_enable_compression( forced_compression: web.ContentCoding | None, ) -> None: """Test that enable_compression does not double compress when an encoded file is also present.""" + if expect_encoding == "br" and brotli is None: + pytest.skip("brotli not available") async def handler(request: web.Request) -> web.FileResponse: resp = sender(hello_txt) @@ -614,6 +626,7 @@ async def test_static_file_ssl( await conn.close() +@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="README.md not supported") async def test_static_file_directory_traversal_attack( aiohttp_client: AiohttpClient, ) -> None: From 257947ddfe05e6bf75177a06b4d9ced38ecbd043 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 14 Mar 2026 00:58:39 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_benchmarks_web_fileresponse.py | 1 + tests/test_benchmarks_web_middleware.py | 1 + tests/test_benchmarks_web_response.py | 1 + tests/test_circular_imports.py | 5 ++++- tests/test_cookiejar.py | 4 +++- tests/test_http_parser.py | 4 +++- tests/test_leaks.py | 4 +++- tests/test_loop.py | 8 ++++++-- tests/test_run_app.py | 8 ++++++-- tests/test_web_functional.py | 1 + tests/test_web_sendfile_functional.py | 6 ++++-- 11 files changed, 33 insertions(+), 10 deletions(-) diff --git a/tests/test_benchmarks_web_fileresponse.py b/tests/test_benchmarks_web_fileresponse.py index 27fe2263c07..4d42cef9c37 100644 --- a/tests/test_benchmarks_web_fileresponse.py +++ b/tests/test_benchmarks_web_fileresponse.py @@ -2,6 +2,7 @@ import asyncio import pathlib + import pytest pytest.importorskip("pytest_codspeed") diff --git a/tests/test_benchmarks_web_middleware.py b/tests/test_benchmarks_web_middleware.py index fa02159bc5c..c08a33cc7ea 100644 --- a/tests/test_benchmarks_web_middleware.py +++ b/tests/test_benchmarks_web_middleware.py @@ -1,6 +1,7 @@ """codspeed benchmarks for web middlewares.""" import asyncio + import pytest pytest.importorskip("pytest_codspeed") diff --git a/tests/test_benchmarks_web_response.py b/tests/test_benchmarks_web_response.py index 5351a47a06c..d80e4d3cf64 100644 --- a/tests/test_benchmarks_web_response.py +++ b/tests/test_benchmarks_web_response.py @@ -1,4 +1,5 @@ """codspeed benchmarks for the web responses.""" + import pytest pytest.importorskip("pytest_codspeed") diff --git a/tests/test_circular_imports.py b/tests/test_circular_imports.py index ce30204e719..227da10e53b 100644 --- a/tests/test_circular_imports.py +++ b/tests/test_circular_imports.py @@ -84,7 +84,10 @@ def _discover_path_importables( ) ) -@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not supported") + +@pytest.mark.skipif( + sys.platform in ("android", "ios"), reason="subprocess not supported" +) @pytest.mark.parametrize( "import_path", _mark_aiohttp_worker_for_skipping(_find_all_importables(aiohttp)), diff --git a/tests/test_cookiejar.py b/tests/test_cookiejar.py index ea4240b57ca..f267104d34c 100644 --- a/tests/test_cookiejar.py +++ b/tests/test_cookiejar.py @@ -1569,7 +1569,9 @@ async def test_shared_cookie_with_multiple_domains() -> None: # === Security tests for restricted unpickler and JSON save/load === -@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="os.system not supported") +@pytest.mark.skipif( + sys.platform in ("android", "ios"), reason="os.system not supported" +) def test_load_rejects_malicious_pickle(tmp_path: Path) -> None: """Verify CookieJar.load() blocks arbitrary code execution via pickle. diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py index b2a76a2f77f..7be1e86d7f6 100644 --- a/tests/test_http_parser.py +++ b/tests/test_http_parser.py @@ -2050,7 +2050,9 @@ async def test_feed_eof_no_err_gzip(self, protocol: BaseProtocol) -> None: dbuf.feed_eof() assert [b"line"] == list(buf._buffer) - @pytest.mark.skipif(sys.platform in ("android", "ios"), reason="brotli not available") + @pytest.mark.skipif( + sys.platform in ("android", "ios"), reason="brotli not available" + ) async def test_feed_eof_no_err_brotli(self, protocol: BaseProtocol) -> None: buf = aiohttp.StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) dbuf = DeflateBuffer(buf, "br") diff --git a/tests/test_leaks.py b/tests/test_leaks.py index fcbb345df82..742d6f9aa65 100644 --- a/tests/test_leaks.py +++ b/tests/test_leaks.py @@ -8,7 +8,9 @@ IS_PYPY = platform.python_implementation() == "PyPy" -@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not supported") +@pytest.mark.skipif( + sys.platform in ("android", "ios"), reason="subprocess not supported" +) @pytest.mark.skipif(IS_PYPY, reason="gc.DEBUG_LEAK not available on PyPy") @pytest.mark.parametrize( ("script", "message"), diff --git a/tests/test_loop.py b/tests/test_loop.py index 77ad0fa4830..ef1a67b854e 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -9,7 +9,9 @@ from aiohttp.test_utils import AioHTTPTestCase, loop_context -@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not available") +@pytest.mark.skipif( + sys.platform in ("android", "ios"), reason="subprocess not available" +) @pytest.mark.skipif( platform.system() == "Windows", reason="the test is not valid for Windows" ) @@ -42,7 +44,9 @@ def test_default_loop(loop: asyncio.AbstractEventLoop) -> None: assert asyncio.get_event_loop() is loop -@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not available") +@pytest.mark.skipif( + sys.platform in ("android", "ios"), reason="subprocess not available" +) def test_setup_loop_non_main_thread() -> None: child_exc = None diff --git a/tests/test_run_app.py b/tests/test_run_app.py index e781a59c15f..e3b6a3fed53 100644 --- a/tests/test_run_app.py +++ b/tests/test_run_app.py @@ -688,7 +688,9 @@ def test_run_app_multiple_preexisting_sockets( """ -@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not supported") +@pytest.mark.skipif( + sys.platform in ("android", "ios"), reason="subprocess not supported" +) def test_sigint() -> None: skip_if_on_windows() @@ -701,7 +703,9 @@ def test_sigint() -> None: assert proc.wait() == 0 -@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="subprocess not supported") +@pytest.mark.skipif( + sys.platform in ("android", "ios"), reason="subprocess not supported" +) def test_sigterm() -> None: skip_if_on_windows() diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 154210a5d91..81da4969c80 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -1140,6 +1140,7 @@ async def test_response_with_precompressed_body_brotli( aiohttp_client: AiohttpClient, ) -> None: assert brotli is not None + async def handler(request: web.Request) -> web.Response: headers = {"Content-Encoding": "br"} return web.Response(body=brotli.compress(b"mydata"), headers=headers) diff --git a/tests/test_web_sendfile_functional.py b/tests/test_web_sendfile_functional.py index bc3348b5e23..901da56cf35 100644 --- a/tests/test_web_sendfile_functional.py +++ b/tests/test_web_sendfile_functional.py @@ -3,8 +3,8 @@ import gzip import pathlib import socket -from collections.abc import Iterable, Iterator import sys +from collections.abc import Iterable, Iterator from typing import Protocol from unittest import mock @@ -626,7 +626,9 @@ async def test_static_file_ssl( await conn.close() -@pytest.mark.skipif(sys.platform in ("android", "ios"), reason="README.md not supported") +@pytest.mark.skipif( + sys.platform in ("android", "ios"), reason="README.md not supported" +) async def test_static_file_directory_traversal_attack( aiohttp_client: AiohttpClient, ) -> None: