Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .coveragerc-cython.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ omit = [
]

[report]
partial_also = [
'if not TYPE_CHECKING',
]
exclude_also = [
'if TYPE_CHECKING',
'assert False',
Expand Down
3 changes: 3 additions & 0 deletions .coveragerc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ omit = [
]

[report]
partial_also = [
'if not TYPE_CHECKING',
]
exclude_also = [
'if TYPE_CHECKING',
'assert False',
Expand Down
68 changes: 66 additions & 2 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,59 @@ jobs:
report_type: test_results
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 ${{ matrix.pyver }}
id: python-install
# important: do not use system python
env:
UV_PYTHON_PREFERENCE: only-managed
uses: astral-sh/setup-uv@v8.1.0
with:
python-version: ${{ matrix.pyver }}
activate-environment: true
enable-cache: true
- name: Install build tooling and cython
run: |
uv pip install -U pip wheel setuptools build twine -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.1
env:
CIBW_BUILD: ${{ matrix.pyver }}-*
CIBW_PLATFORM: ${{ matrix.config.platform }}
CIBW_ARCHS: ${{ matrix.config.archs }}
CIBW_TEST_REQUIRES: -r requirements/test-mobile.txt
CIBW_TEST_SOURCES: setup.cfg README.rst tests
CIBW_TEST_COMMAND: python -m pytest

autobahn:
permissions:
contents: read # to fetch code (actions/checkout)
Expand Down Expand Up @@ -451,6 +504,7 @@ jobs:
needs:
- lint
- test
- test-mobile
- autobahn

runs-on: ubuntu-latest
Expand Down Expand Up @@ -518,14 +572,15 @@ 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:
matrix:
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
Expand Down Expand Up @@ -561,6 +616,10 @@ jobs:
musl: musllinux
- os: ubuntu-24.04-arm
musl: musllinux
- os: ubuntu-latest
platform: android
- os: macos-14
platform: ios
steps:
- name: Checkout
uses: actions/checkout@v6
Expand Down Expand Up @@ -615,14 +674,19 @@ jobs:
# for those QEMU matrix cells.
extras: uv
env:
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@v7
with:
name: >-
dist-${{ matrix.os }}-${{ matrix.musl }}-${{
matrix.qemu
matrix.platform
&& matrix.platform
|| matrix.qemu
&& matrix.qemu
|| 'native'
}}
Expand Down
1 change: 1 addition & 0 deletions CHANGES/11750.packaging.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added wheels for Android and iOS platforms -- by :user:`timrid`.
1 change: 1 addition & 0 deletions CHANGES/12722.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed :py:meth:`~aiohttp.FormData.add_field` accepting invalid bytes in ``name`` and ``filename`` -- by :user:`Dreamsorcerer`.
1 change: 1 addition & 0 deletions CHANGES/12727.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed websocket upgrade occurring when header contained a value like `notupgrade` -- by :user:`Dreamsorcerer`.
9 changes: 4 additions & 5 deletions aiohttp/formdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from . import hdrs, multipart, payload
from .helpers import guess_filename
from .http_writer import _safe_header
from .payload import Payload

__all__ = ("FormData",)
Expand Down Expand Up @@ -56,12 +57,14 @@ def add_field(
if isinstance(value, (io.IOBase, bytes, bytearray, memoryview)):
self._is_multipart = True

_safe_header(name)
type_options: MultiDict[str] = MultiDict({"name": name})
if filename is not None and not isinstance(filename, str):
raise TypeError("filename must be an instance of str. Got: %s" % filename)
if filename is None and isinstance(value, io.IOBase):
filename = guess_filename(value, name)
if filename is not None:
_safe_header(filename)
type_options["filename"] = filename
self._is_multipart = True

Expand All @@ -71,11 +74,7 @@ def add_field(
raise TypeError(
"content_type must be an instance of str. Got: %s" % content_type
)
if "\r" in content_type or "\n" in content_type:
raise ValueError(
"Newline or carriage return detected in headers. "
"Potential header injection attack."
)
_safe_header(content_type)
headers[hdrs.CONTENT_TYPE] = content_type
self._is_multipart = True

Expand Down
5 changes: 4 additions & 1 deletion aiohttp/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,9 @@ def make_mocked_request(
raw_hdrs = ()

chunked = "chunked" in headers.get(hdrs.TRANSFER_ENCODING, "").lower()
upgrade = headers.get(hdrs.CONNECTION, "").lower() == "upgrade" and bool(
headers.get(hdrs.UPGRADE)
)

message = RawRequestMessage(
method,
Expand All @@ -615,7 +618,7 @@ def make_mocked_request(
raw_hdrs,
closing,
None,
False,
upgrade,
chunked,
URL(path),
)
Expand Down
2 changes: 1 addition & 1 deletion aiohttp/web_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ def _handshake(
)
)

if "upgrade" not in headers.get(hdrs.CONNECTION, "").lower():
if not request._message.upgrade:
raise HTTPBadRequest(
text=f"No CONNECTION upgrade hdr: {headers.get(hdrs.CONNECTION)}"
)
Expand Down
10 changes: 7 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down Expand Up @@ -170,6 +170,10 @@ test-command = ""
# don't build PyPy wheels, install from source instead
skip = "pp*"

[tool.cibuildwheel.ios]
# iOS currently does not support build[uv]
build-frontend = "build"

[tool.codespell]
skip = '.git,*.pdf,*.svg,Makefile,CONTRIBUTORS.txt,venvs,_build'
ignore-words-list = 'te,assertIn'
Expand Down
6 changes: 3 additions & 3 deletions requirements/base-ft.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
#
# pip-compile --allow-unsafe --output-file=requirements/base-ft.txt --strip-extras requirements/base-ft.in
#
aiodns==4.0.4
aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
aiohappyeyeballs==2.6.2
# via -r requirements/runtime-deps.in
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
Expand Down
6 changes: 3 additions & 3 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
#
# pip-compile --allow-unsafe --output-file=requirements/base.txt --strip-extras requirements/base.in
#
aiodns==4.0.4
aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
aiohappyeyeballs==2.6.2
# via -r requirements/runtime-deps.in
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
Expand Down
33 changes: 16 additions & 17 deletions requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --allow-unsafe --output-file=requirements/constraints.txt --strip-extras requirements/constraints.in
# pip-compile --output-file=requirements/constraints.txt --strip-extras --unsafe-package=aiohttp requirements/constraints.in
#
aiodns==4.0.4
aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via
# -r requirements/lint.in
# -r requirements/runtime-deps.in
aiohappyeyeballs==2.6.2
# via
# -r requirements/runtime-deps.in
# aiohttp
aiohttp==3.13.5
# via pytest-aiohttp
aiohttp-theme==0.1.7
# via -r requirements/doc.in
aiosignal==1.4.0
Expand Down Expand Up @@ -45,7 +43,7 @@ blockbuster==1.5.26
# 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.5.0
# via pip-tools
Expand Down Expand Up @@ -92,7 +90,7 @@ forbiddenfruit==0.1.4
freezegun==1.5.5
# via
# -r requirements/lint.in
# -r requirements/test-common.in
# -r requirements/test-common-base.in
frozenlist==1.8.0
# via
# -r requirements/runtime-deps.in
Expand Down Expand Up @@ -159,6 +157,8 @@ packaging==26.2
# wheel
pathspec==1.1.1
# via mypy
pip==26.1.1
# via pip-tools
pip-tools==7.5.3
# via -r requirements/dev.in
pkgconfig==1.6.0
Expand All @@ -181,7 +181,7 @@ propcache==0.5.2
proxy-py==2.4.10
# via
# -r requirements/lint.in
# -r requirements/test-common.in
# -r requirements/test-common-base.in
pycares==5.0.1
# via aiodns
pycparser==3.0
Expand All @@ -204,7 +204,7 @@ pyproject-hooks==1.2.0
pytest==9.0.3
# via
# -r requirements/lint.in
# -r requirements/test-common.in
# -r requirements/test-common-base.in
# pytest-aiohttp
# pytest-asyncio
# pytest-codspeed
Expand All @@ -223,13 +223,13 @@ pytest-codspeed==5.0.3
# -r requirements/lint.in
# -r requirements/test-common.in
pytest-cov==7.1.0
# via -r requirements/test-common.in
# via -r requirements/test-common-base.in
pytest-mock==3.15.1
# via
# -r requirements/lint.in
# -r requirements/test-common.in
# -r requirements/test-common-base.in
pytest-timeout==2.4.0
# via -r requirements/test-common.in
# via -r requirements/test-common-base.in
pytest-xdist==3.8.0
# via -r requirements/test-common.in
python-dateutil==2.9.0.post0
Expand All @@ -251,8 +251,10 @@ requests==2.34.2
# sphinxcontrib-spelling
rich==15.0.0
# via pytest-codspeed
setuptools==82.0.1
# via pip-tools
setuptools-git==1.2
# via -r requirements/test-common.in
# via -r requirements/test-common-base.in
six==1.17.0
# via python-dateutil
slotscheck==0.19.1
Expand Down Expand Up @@ -329,7 +331,7 @@ valkey==6.1.1
virtualenv==21.4.1
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common.in
# via -r requirements/test-common-base.in
wheel==0.47.0
# via pip-tools
yarl==1.24.2
Expand All @@ -342,7 +344,4 @@ zlib-ng==1.0.0
# -r requirements/test-common.in

# The following packages are considered to be unsafe in a requirements file:
pip==26.1.1
# via pip-tools
setuptools==82.0.1
# via pip-tools
# aiohttp
Loading
Loading