diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 454a1bc146e..7bc9283f10d 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -254,6 +254,47 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} + benchmark: + name: Benchmark + needs: gen_llhttp + + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout project + uses: actions/checkout@v4 + with: + submodules: true + - name: Setup Python 3.12 + id: python-install + uses: actions/setup-python@v5 + with: + python-version: 3.12 + cache: pip + cache-dependency-path: requirements/*.txt + - name: Update pip, wheel, setuptools, build, twine + run: | + python -m pip install -U pip wheel setuptools build twine + - name: Install dependencies + run: | + python -m pip install -r requirements/test.in -c requirements/test.txt + - name: Restore llhttp generated files + uses: actions/download-artifact@v3 + with: + name: llhttp + path: vendor/llhttp/build/ + - name: Cythonize + run: | + make cythonize + - name: Install self + run: python -m pip install -e . + - name: Run benchmarks + uses: CodSpeedHQ/action@v3 + with: + token: ${{ secrets.CODSPEED_TOKEN }} + run: python -Im pytest --no-cov -vvvvv --codspeed + + check: # This job does nothing and is only used for the branch protection if: always() diff --git a/.mypy.ini b/.mypy.ini index 70261107033..7eb7908efe3 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -34,3 +34,17 @@ ignore_missing_imports = True [mypy-gunicorn.*] ignore_missing_imports = True + +# Benchmark configuration is because pytest_codspeed is missing +# a py.typed file. Can be removed once the following PR is merged +# and released: +# https://github.com/CodSpeedHQ/pytest-codspeed/pull/53 +[mypy-test_benchmarks_client_request] +disable_error_code = + no-any-unimported, + misc + +[mypy-test_benchmarks_cookiejar] +disable_error_code = + no-any-unimported, + misc diff --git a/requirements/lint.in b/requirements/lint.in index 29c5810916a..999f41b7a40 100644 --- a/requirements/lint.in +++ b/requirements/lint.in @@ -5,6 +5,7 @@ pre-commit proxy.py pytest pytest-mock +pytest_codspeed python-on-whales slotscheck trustme diff --git a/requirements/lint.txt b/requirements/lint.txt index 2912c944a84..614f10e7bbf 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -16,6 +16,7 @@ cffi==1.17.1 # via # cryptography # pycares + # pytest-codspeed cfgv==3.4.0 # via pre-commit charset-normalizer==3.4.0 @@ -31,7 +32,9 @@ distlib==0.3.9 exceptiongroup==1.2.2 # via pytest filelock==3.16.1 - # via virtualenv + # via + # pytest-codspeed + # virtualenv freezegun==1.5.1 # via -r requirements/lint.in identify==2.6.1 @@ -75,7 +78,10 @@ pygments==2.18.0 pytest==8.1.1 # via # -r requirements/lint.in + # pytest-codspeed # pytest-mock +pytest-codspeed==2.2.1 + # via -r requirements/lint.in pytest-mock==3.14.0 # via -r requirements/lint.in python-dateutil==2.9.0.post0 diff --git a/requirements/test.in b/requirements/test.in index 18423f3743c..a98d9edf28d 100644 --- a/requirements/test.in +++ b/requirements/test.in @@ -7,6 +7,7 @@ proxy.py >= 2.4.4rc4 pytest pytest-cov pytest-mock +pytest_codspeed python-on-whales setuptools-git trustme; platform_machine != "i686" # no 32-bit wheels diff --git a/requirements/test.txt b/requirements/test.txt index b7d3c7db665..1c994962aba 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -22,6 +22,7 @@ cffi==1.17.1 # via # cryptography # pycares + # pytest-codspeed charset-normalizer==3.4.0 # via requests click==8.1.7 @@ -36,6 +37,8 @@ cryptography==43.0.3 # via trustme exceptiongroup==1.2.2 # via pytest +filelock==3.16.1 + # via pytest-codspeed freezegun==1.5.1 # via -r requirements/test.in frozenlist==1.5.0 @@ -88,8 +91,11 @@ pygments==2.18.0 pytest==8.1.1 # via # -r requirements/test.in + # pytest-codspeed # pytest-cov # pytest-mock +pytest-codspeed==2.2.1 + # via -r requirements/test.in pytest-cov==5.0.0 # via -r requirements/test.in pytest-mock==3.14.0 diff --git a/tests/test_benchmarks_client_request.py b/tests/test_benchmarks_client_request.py new file mode 100644 index 00000000000..63c77dfcdc8 --- /dev/null +++ b/tests/test_benchmarks_client_request.py @@ -0,0 +1,22 @@ +"""codspeed benchmarks for client requests.""" + +import asyncio +from http.cookies import Morsel + +from pytest_codspeed import BenchmarkFixture # type: ignore[import-untyped] +from yarl import URL + +from aiohttp.client_reqrep import ClientRequest + + +def test_client_request_update_cookies( + loop: asyncio.AbstractEventLoop, benchmark: BenchmarkFixture +) -> None: + req = ClientRequest("get", URL("http://python.org"), loop=loop) + morsel: "Morsel[str]" = Morsel() + morsel.set(key="string", val="Another string", coded_val="really") + morsel_cookie = {"str": morsel} + + @benchmark + def _run() -> None: + req.update_cookies(cookies=morsel_cookie) diff --git a/tests/test_benchmarks_cookiejar.py b/tests/test_benchmarks_cookiejar.py new file mode 100644 index 00000000000..508b49f68cb --- /dev/null +++ b/tests/test_benchmarks_cookiejar.py @@ -0,0 +1,26 @@ +"""codspeed benchmarks for cookies.""" + +from http.cookies import BaseCookie + +from pytest_codspeed import BenchmarkFixture # type: ignore[import-untyped] +from yarl import URL + +from aiohttp.cookiejar import CookieJar + + +async def test_load_cookies_into_temp_cookiejar(benchmark: BenchmarkFixture) -> None: + """Benchmark for creating a temp CookieJar and filtering by URL. + + This benchmark matches what the client request does when cookies + are passed to the request. + """ + all_cookies: BaseCookie[str] = BaseCookie() + url = URL("http://example.com") + cookies = {"cookie1": "value1", "cookie2": "value2"} + + @benchmark + def _run() -> None: + tmp_cookie_jar = CookieJar() + tmp_cookie_jar.update_cookies(cookies) + req_cookies = tmp_cookie_jar.filter_cookies(url) + all_cookies.load(req_cookies)