From 6e44d3635d7e1f66d6ab95c72f18006831860e7c Mon Sep 17 00:00:00 2001 From: abebus Date: Sun, 27 Jul 2025 20:15:04 +0300 Subject: [PATCH 1/2] adding benchmarking, currently only for http funcs --- tests/test_benchmark_http.py | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 tests/test_benchmark_http.py diff --git a/tests/test_benchmark_http.py b/tests/test_benchmark_http.py new file mode 100644 index 0000000..d278bf5 --- /dev/null +++ b/tests/test_benchmark_http.py @@ -0,0 +1,58 @@ +from collections import OrderedDict + +import pytest + +from w3lib.http import headers_dict_to_raw, headers_raw_to_dict + +pytestmark = pytest.mark.benchmark + + +def _header_case_long_headers(): + dct = OrderedDict( + [ + (b"X-Custom-Header", [b"a" * 1_000]), + (b"X-Custom-Header-2", [b"b" * 1_000]), + ] + ) + raw = ( + b"X-Custom-Header: " + b"a" * 1_000 + b"\r\n" + b"X-Custom-Header-2: " + b"b" * 1_000 + ) + return "long_headers", dct, raw + + +def _header_case_many_unique_headers(): + dct = OrderedDict( + [(f"Header-{i}".encode(), [f"value-{i}".encode()]) for i in range(100)] + ) + raw = b"\r\n".join([f"Header-{i}: value-{i}".encode() for i in range(100)]) + return "many_unique_headers", dct, raw + + +def _header_case_many_repeated_headers(): + values = [f"id={i}".encode() for i in range(100)] + dct = OrderedDict([(b"Set-Cookie", values)]) + raw = b"\r\n".join([b"Set-Cookie: " + val for val in values]) + return "many_repeated_headers", dct, raw + + +header_cases = [ + _header_case_long_headers(), + _header_case_many_unique_headers(), + _header_case_many_repeated_headers(), +] + + +@pytest.mark.parametrize( + ("_id", "dct", "raw"), + header_cases, + ids=[case[0] for case in header_cases], +) +class TestBenchmarkHttp: + def test_bench_dict_to_raw(self, benchmark, _id, dct, raw): # noqa: PT019 + result = benchmark(lambda: headers_dict_to_raw(dct)) + assert result == raw + + def test_bench_raw_to_dict(self, benchmark, _id, dct, raw): # noqa: PT019 + result = benchmark(lambda: headers_raw_to_dict(raw)) + assert result == dct From d8f400b69989fdcd31a3bfa92e9a4c3e7fdeb849 Mon Sep 17 00:00:00 2001 From: abebus Date: Sun, 27 Jul 2025 23:00:35 +0300 Subject: [PATCH 2/2] wip, will add new workflow --- .gitignore | 2 ++ pyproject.toml | 1 + tests/test_benchmark_http.py | 4 +++- tests/test_http.py | 5 ++++- tox.ini | 35 ++++++++++++++++++++++++++++++++++- 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 3306f6b..6dc3315 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ coverage.xml .dmypy.json .hypothesis/ .idea/ +.codspeed +uv.lock diff --git a/pyproject.toml b/pyproject.toml index 105017d..c59fe25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,6 +83,7 @@ allow_untyped_defs = false # Allow test functions to be untyped module = "tests.*" allow_untyped_defs = true +disallow_untyped_calls = false [tool.pylint.MASTER] persistent = "no" diff --git a/tests/test_benchmark_http.py b/tests/test_benchmark_http.py index d278bf5..f578832 100644 --- a/tests/test_benchmark_http.py +++ b/tests/test_benchmark_http.py @@ -4,6 +4,8 @@ from w3lib.http import headers_dict_to_raw, headers_raw_to_dict +pytest.importorskip("pytest_codspeed", reason="Benchmark tests require pytest-codspeed") + pytestmark = pytest.mark.benchmark @@ -17,7 +19,7 @@ def _header_case_long_headers(): raw = ( b"X-Custom-Header: " + b"a" * 1_000 + b"\r\n" b"X-Custom-Header-2: " + b"b" * 1_000 - ) + ) # fmt: off return "long_headers", dct, raw diff --git a/tests/test_http.py b/tests/test_http.py index e0ece39..a212c65 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -55,7 +55,10 @@ def test_headers_dict_to_raw(self): def test_headers_dict_to_raw_listtuple(self): dct: HeadersDictInput = OrderedDict( - [(b"Content-type", [b"text/html"]), (b"Accept", [b"gzip"])] + [ + (b"Content-type", [b"text/html"]), + (b"Accept", [b"gzip"]), + ] ) assert headers_dict_to_raw(dct) == b"Content-type: text/html\r\nAccept: gzip" diff --git a/tox.ini b/tox.ini index 9a2de58..4e092dd 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,20 @@ # and then run "tox" from this directory. [tox] -envlist = py39, py310, py311, py312, py313, py314, pypy3.10, pypy3.11, docs, pylint, typing, pre-commit, twinecheck +envlist = + py39 + py310 + py311 + py312 + py313 + py314 + pypy3.10 + pypy3.11 + docs + pylint + typing + pre-commit + twinecheck [testenv] deps = @@ -51,3 +64,23 @@ deps = commands = python -m build --sdist twine check dist/* + +[testenv:benchmark-{py39,py310,py311,py312,py313,py314,pypy310,pypy311}] +basepython = + py39: python3.9 + py310: python3.10 + py311: python3.11 + py312: python3.12 + py313: python3.13 + py314: python3.14 + pypy310: pypy3.10 + pypy311: pypy3.11 +deps = + pytest + pytest-codspeed +commands = + python -m pytest \ + --codspeed \ + --codspeed-warmup-time=1 \ + --codspeed-max-rounds=10000 \ + --codspeed-max-time=10