httpr 0.4.8__tar.gz → 0.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. {httpr-0.4.8 → httpr-0.5.2}/.github/workflows/CI.yml +8 -69
  2. {httpr-0.4.8 → httpr-0.5.2}/.github/workflows/mkdocs.yml +0 -9
  3. {httpr-0.4.8 → httpr-0.5.2}/CLAUDE.md +2 -1
  4. {httpr-0.4.8 → httpr-0.5.2}/Cargo.lock +76 -93
  5. {httpr-0.4.8 → httpr-0.5.2}/Cargo.toml +4 -4
  6. {httpr-0.4.8 → httpr-0.5.2}/PKG-INFO +24 -6
  7. {httpr-0.4.8 → httpr-0.5.2}/README.md +23 -5
  8. {httpr-0.4.8 → httpr-0.5.2}/docs/api/index.md +4 -0
  9. {httpr-0.4.8 → httpr-0.5.2}/docs/api/response.md +101 -0
  10. {httpr-0.4.8 → httpr-0.5.2}/docs/tutorial/making-requests.md +12 -3
  11. {httpr-0.4.8 → httpr-0.5.2}/docs/tutorial/response-handling.md +47 -11
  12. {httpr-0.4.8 → httpr-0.5.2}/httpr/__init__.py +56 -2
  13. {httpr-0.4.8 → httpr-0.5.2}/httpr/httpr.pyi +85 -1
  14. {httpr-0.4.8 → httpr-0.5.2}/pyproject.toml +1 -1
  15. {httpr-0.4.8 → httpr-0.5.2}/src/exceptions.rs +1 -1
  16. {httpr-0.4.8 → httpr-0.5.2}/src/lib.rs +23 -0
  17. {httpr-0.4.8 → httpr-0.5.2}/src/response.rs +150 -2
  18. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/test_client.py +45 -0
  19. httpr-0.5.2/tests/unit/test_response.py +69 -0
  20. {httpr-0.4.8 → httpr-0.5.2}/uv.lock +139 -144
  21. httpr-0.4.8/benchmark/generate_image.py +0 -149
  22. {httpr-0.4.8 → httpr-0.5.2}/.github/actions/set-version/action.yml +0 -0
  23. {httpr-0.4.8 → httpr-0.5.2}/.github/copilot-instructions.md +0 -0
  24. {httpr-0.4.8 → httpr-0.5.2}/.github/workflows/CI.yml.backup +0 -0
  25. {httpr-0.4.8 → httpr-0.5.2}/.github/workflows/benchmark.yml +0 -0
  26. {httpr-0.4.8 → httpr-0.5.2}/.github/workflows/copilot-setup-steps.yml +0 -0
  27. {httpr-0.4.8 → httpr-0.5.2}/.github/workflows/set_version.py +0 -0
  28. {httpr-0.4.8 → httpr-0.5.2}/.gitignore +0 -0
  29. {httpr-0.4.8 → httpr-0.5.2}/.pre-commit-config.yaml +0 -0
  30. {httpr-0.4.8 → httpr-0.5.2}/LICENSE +0 -0
  31. {httpr-0.4.8 → httpr-0.5.2}/Taskfile.yaml +0 -0
  32. {httpr-0.4.8 → httpr-0.5.2}/benchmark/README.md +0 -0
  33. {httpr-0.4.8 → httpr-0.5.2}/benchmark/__init__.py +0 -0
  34. {httpr-0.4.8 → httpr-0.5.2}/benchmark/benchmark.py +0 -0
  35. {httpr-0.4.8 → httpr-0.5.2}/benchmark/benchmark_cbor.py +0 -0
  36. {httpr-0.4.8 → httpr-0.5.2}/benchmark/server.py +0 -0
  37. {httpr-0.4.8 → httpr-0.5.2}/benchmark.jpg +0 -0
  38. {httpr-0.4.8 → httpr-0.5.2}/docs/advanced/cookies.md +0 -0
  39. {httpr-0.4.8 → httpr-0.5.2}/docs/advanced/index.md +0 -0
  40. {httpr-0.4.8 → httpr-0.5.2}/docs/advanced/proxy.md +0 -0
  41. {httpr-0.4.8 → httpr-0.5.2}/docs/advanced/ssl-tls.md +0 -0
  42. {httpr-0.4.8 → httpr-0.5.2}/docs/api/async-client.md +0 -0
  43. {httpr-0.4.8 → httpr-0.5.2}/docs/api/client.md +0 -0
  44. {httpr-0.4.8 → httpr-0.5.2}/docs/api/functions.md +0 -0
  45. {httpr-0.4.8 → httpr-0.5.2}/docs/index.md +0 -0
  46. {httpr-0.4.8 → httpr-0.5.2}/docs/quickstart.md +0 -0
  47. {httpr-0.4.8 → httpr-0.5.2}/docs/tutorial/async.md +0 -0
  48. {httpr-0.4.8 → httpr-0.5.2}/docs/tutorial/authentication.md +0 -0
  49. {httpr-0.4.8 → httpr-0.5.2}/docs/tutorial/index.md +0 -0
  50. {httpr-0.4.8 → httpr-0.5.2}/docs/writings/index.md +0 -0
  51. {httpr-0.4.8 → httpr-0.5.2}/docs/writings/posts/2025-02-24-python-http-clients-suck.md +0 -0
  52. {httpr-0.4.8 → httpr-0.5.2}/httpr/py.typed +0 -0
  53. {httpr-0.4.8 → httpr-0.5.2}/httpr.code-workspace +0 -0
  54. {httpr-0.4.8 → httpr-0.5.2}/mkdocs.yml +0 -0
  55. {httpr-0.4.8 → httpr-0.5.2}/scripts/generate_certs.py +0 -0
  56. {httpr-0.4.8 → httpr-0.5.2}/src/traits.rs +0 -0
  57. {httpr-0.4.8 → httpr-0.5.2}/src/utils.rs +0 -0
  58. {httpr-0.4.8 → httpr-0.5.2}/tests/benchmark/__init__.py +0 -0
  59. {httpr-0.4.8 → httpr-0.5.2}/tests/benchmark/test_performance.py +0 -0
  60. {httpr-0.4.8 → httpr-0.5.2}/tests/conftest.py +0 -0
  61. {httpr-0.4.8 → httpr-0.5.2}/tests/e2e/__init__.py +0 -0
  62. {httpr-0.4.8 → httpr-0.5.2}/tests/e2e/test_async.py +0 -0
  63. {httpr-0.4.8 → httpr-0.5.2}/tests/e2e/test_auth.py +0 -0
  64. {httpr-0.4.8 → httpr-0.5.2}/tests/e2e/test_redirects.py +0 -0
  65. {httpr-0.4.8 → httpr-0.5.2}/tests/e2e/test_ssl.py +0 -0
  66. {httpr-0.4.8 → httpr-0.5.2}/tests/e2e/test_streaming.py +0 -0
  67. {httpr-0.4.8 → httpr-0.5.2}/tests/e2e/test_uploads.py +0 -0
  68. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/__init__.py +0 -0
  69. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/cbor_test_server.py +0 -0
  70. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/httpx_conns.py +0 -0
  71. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/test_asyncclient.py +0 -0
  72. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/test_cbor.py +0 -0
  73. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/test_defs.py +0 -0
  74. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/test_docs.py +0 -0
  75. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/test_exceptions.py +0 -0
  76. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/test_ssl.py +0 -0
  77. {httpr-0.4.8 → httpr-0.5.2}/tests/unit/test_streaming.py +0 -0
@@ -4,8 +4,6 @@ on:
4
4
  push:
5
5
  branches:
6
6
  - main
7
- paths-ignore:
8
- - "*.jpg" # Prevents the benchmark image update from triggering CI
9
7
  tags:
10
8
  - "*"
11
9
  pull_request:
@@ -176,7 +174,7 @@ jobs:
176
174
  uses: PyO3/maturin-action@v1
177
175
  with:
178
176
  target: ${{ matrix.platform.target }}
179
- args: --release --out dist -i python3.13t -i python3.14t
177
+ args: --release --out dist -i python3.14t
180
178
  sccache: true
181
179
  manylinux: auto
182
180
  - name: Upload wheels
@@ -231,7 +229,7 @@ jobs:
231
229
  uses: PyO3/maturin-action@v1
232
230
  with:
233
231
  target: ${{ matrix.platform.target }}
234
- args: --release --out dist -i python3.13t -i python3.14t
232
+ args: --release --out dist -i python3.14t
235
233
  sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
236
234
  manylinux: musllinux_1_2
237
235
  - name: Upload wheels
@@ -287,7 +285,7 @@ jobs:
287
285
  uses: PyO3/maturin-action@v1
288
286
  with:
289
287
  target: ${{ matrix.platform.target }}
290
- args: --release --out dist -i python3.13t -i python3.14t
288
+ args: --release --out dist -i python3.14t
291
289
  sccache: true
292
290
  - name: Upload wheels
293
291
  uses: actions/upload-artifact@v4
@@ -295,6 +293,10 @@ jobs:
295
293
  name: wheels-windows-${{ matrix.platform.target }}
296
294
  path: dist
297
295
  - name: pytest
296
+ # Skip pytest on x86 - cryptography (pulled in via the trustme dev
297
+ # dependency) no longer ships 32-bit Windows wheels and fails to build
298
+ # from source. The x64 job provides test coverage for Windows.
299
+ if: ${{ matrix.platform.target == 'x64' }}
298
300
  shell: bash
299
301
  env:
300
302
  RUSTC_WRAPPER: ""
@@ -333,7 +335,7 @@ jobs:
333
335
  uses: PyO3/maturin-action@v1
334
336
  with:
335
337
  target: ${{ matrix.platform.target }}
336
- args: --release --out dist -i python3.13t -i python3.14t
338
+ args: --release --out dist -i python3.14t
337
339
  sccache: true
338
340
  - name: Upload wheels
339
341
  uses: actions/upload-artifact@v4
@@ -401,66 +403,3 @@ jobs:
401
403
  with:
402
404
  command: upload
403
405
  args: --non-interactive --skip-existing wheels-*/*
404
-
405
- benchmark:
406
- permissions:
407
- contents: write
408
- runs-on: ubuntu-latest
409
- needs: [linux]
410
- if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
411
- steps:
412
- - uses: actions/checkout@v4
413
- with:
414
- fetch-depth: 0
415
- - uses: astral-sh/setup-uv@v7
416
- with:
417
- python-version: "3.12"
418
- - name: Download wheels
419
- uses: actions/download-artifact@v4
420
- with:
421
- name: wheels-linux-x86_64
422
- - name: Start Uvicorn server
423
- env:
424
- BENCHMARK_HOST: 0.0.0.0
425
- BENCHMARK_PORT: "8000"
426
- run: |
427
- uv run --script benchmark/server.py &
428
- sleep 10
429
- - name: Run benchmark
430
- run: |
431
- # Extract version from tag (v0.3.0 -> 0.3.0) or use 'dev' for manual runs
432
- if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
433
- HTTPR_VERSION=${GITHUB_REF#refs/tags/v}
434
- else
435
- HTTPR_VERSION="dev"
436
- fi
437
- # The script declares `httpr` in its PEP 723 deps; --find-links lets uv
438
- # pick the freshly-built wheel from the workspace instead of PyPI.
439
- uv run \
440
- --find-links ./ \
441
- --refresh-package httpr \
442
- --script benchmark/benchmark.py --httpr-version "$HTTPR_VERSION"
443
- - name: Generate and deploy benchmark image to gh-pages
444
- if: startsWith(github.ref, 'refs/tags/')
445
- run: |
446
- uv run --script benchmark/generate_image.py
447
- git config --global user.name 'github-actions[bot]'
448
- git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
449
-
450
- # Save the generated image
451
- cp benchmark.jpg /tmp/benchmark.jpg
452
-
453
- # Fetch and checkout gh-pages
454
- git fetch origin gh-pages:gh-pages
455
- git checkout gh-pages
456
-
457
- # Copy the benchmark image
458
- cp /tmp/benchmark.jpg benchmark.jpg
459
-
460
- git add benchmark.jpg
461
- if ! git diff --quiet --cached; then
462
- git commit -m "Update benchmark image for ${{ github.ref_name }}"
463
- git push origin gh-pages
464
- else
465
- echo "No changes to commit"
466
- fi
@@ -50,10 +50,6 @@ jobs:
50
50
  if git show gh-pages:dev/bench/index.html > /tmp/bench-backup/index.html 2>/dev/null; then
51
51
  echo "Found existing benchmark index.html, will preserve it"
52
52
  fi
53
- # Save benchmark image if it exists
54
- if git show gh-pages:benchmark.jpg > /tmp/bench-backup/benchmark.jpg 2>/dev/null; then
55
- echo "Found existing benchmark.jpg, will preserve it"
56
- fi
57
53
 
58
54
  # Switch to gh-pages branch
59
55
  git checkout gh-pages || git checkout --orphan gh-pages
@@ -75,11 +71,6 @@ jobs:
75
71
  [ -f /tmp/bench-backup/index.html ] && cp /tmp/bench-backup/index.html dev/bench/
76
72
  echo "Restored benchmark chart files"
77
73
  fi
78
- # Restore benchmark image if it existed
79
- if [ -f /tmp/bench-backup/benchmark.jpg ]; then
80
- cp /tmp/bench-backup/benchmark.jpg benchmark.jpg
81
- echo "Restored benchmark.jpg"
82
- fi
83
74
 
84
75
  # Add all files and commit
85
76
  git add -A
@@ -151,7 +151,8 @@ uv run python benchmark.py # Terminal 2: Run benchmarks
151
151
 
152
152
  ### Headers Behavior
153
153
  - Headers are lowercased internally (HTTP/2 spec)
154
- - `client.headers` getter excludes `Cookie` header
154
+ - `client.headers` getter returns a live `_ClientHeaders` view; in-place mutations (`__setitem__`, `del`, `update`, `pop`, `popitem`, `setdefault`, `clear`) mirror to the client via Rust `set_header`/`del_header`
155
+ - `client.headers` getter excludes `Cookie` header; `set_headers` preserves the existing `Cookie` header so header mutation never clobbers cookies
155
156
  - `client.cookies` getter/setter extracts from `Cookie` header
156
157
 
157
158
  ### Request Body
@@ -25,9 +25,9 @@ dependencies = [
25
25
 
26
26
  [[package]]
27
27
  name = "anyhow"
28
- version = "1.0.102"
28
+ version = "1.0.103"
29
29
  source = "registry+https://github.com/rust-lang/crates.io-index"
30
- checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
30
+ checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
31
31
 
32
32
  [[package]]
33
33
  name = "arc-swap"
@@ -56,12 +56,6 @@ version = "1.1.2"
56
56
  source = "registry+https://github.com/rust-lang/crates.io-index"
57
57
  checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
58
58
 
59
- [[package]]
60
- name = "autocfg"
61
- version = "1.5.0"
62
- source = "registry+https://github.com/rust-lang/crates.io-index"
63
- checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
64
-
65
59
  [[package]]
66
60
  name = "base64"
67
61
  version = "0.22.1"
@@ -131,6 +125,17 @@ version = "0.2.1"
131
125
  source = "registry+https://github.com/rust-lang/crates.io-index"
132
126
  checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
133
127
 
128
+ [[package]]
129
+ name = "chacha20"
130
+ version = "0.10.1"
131
+ source = "registry+https://github.com/rust-lang/crates.io-index"
132
+ checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
133
+ dependencies = [
134
+ "cfg-if",
135
+ "cpufeatures",
136
+ "rand_core 0.10.1",
137
+ ]
138
+
134
139
  [[package]]
135
140
  name = "compression-codecs"
136
141
  version = "0.4.38"
@@ -180,6 +185,15 @@ dependencies = [
180
185
  "url",
181
186
  ]
182
187
 
188
+ [[package]]
189
+ name = "cpufeatures"
190
+ version = "0.3.0"
191
+ source = "registry+https://github.com/rust-lang/crates.io-index"
192
+ checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
193
+ dependencies = [
194
+ "libc",
195
+ ]
196
+
183
197
  [[package]]
184
198
  name = "crc32fast"
185
199
  version = "1.5.0"
@@ -376,10 +390,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
376
390
  checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
377
391
  dependencies = [
378
392
  "cfg-if",
379
- "js-sys",
380
393
  "libc",
381
- "r-efi",
394
+ "r-efi 5.3.0",
382
395
  "wasip2",
396
+ ]
397
+
398
+ [[package]]
399
+ name = "getrandom"
400
+ version = "0.4.3"
401
+ source = "registry+https://github.com/rust-lang/crates.io-index"
402
+ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
403
+ dependencies = [
404
+ "cfg-if",
405
+ "js-sys",
406
+ "libc",
407
+ "r-efi 6.0.0",
408
+ "rand_core 0.10.1",
383
409
  "wasm-bindgen",
384
410
  ]
385
411
 
@@ -492,7 +518,7 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
492
518
 
493
519
  [[package]]
494
520
  name = "httpr"
495
- version = "0.4.8"
521
+ version = "0.5.2"
496
522
  dependencies = [
497
523
  "anyhow",
498
524
  "bytes",
@@ -688,15 +714,6 @@ dependencies = [
688
714
  "serde_core",
689
715
  ]
690
716
 
691
- [[package]]
692
- name = "indoc"
693
- version = "2.0.7"
694
- source = "registry+https://github.com/rust-lang/crates.io-index"
695
- checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
696
- dependencies = [
697
- "rustversion",
698
- ]
699
-
700
717
  [[package]]
701
718
  name = "ipnet"
702
719
  version = "2.12.0"
@@ -807,15 +824,6 @@ version = "2.8.0"
807
824
  source = "registry+https://github.com/rust-lang/crates.io-index"
808
825
  checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
809
826
 
810
- [[package]]
811
- name = "memoffset"
812
- version = "0.9.1"
813
- source = "registry+https://github.com/rust-lang/crates.io-index"
814
- checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
815
- dependencies = [
816
- "autocfg",
817
- ]
818
-
819
827
  [[package]]
820
828
  name = "mime"
821
829
  version = "0.3.17"
@@ -987,15 +995,6 @@ version = "0.2.0"
987
995
  source = "registry+https://github.com/rust-lang/crates.io-index"
988
996
  checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
989
997
 
990
- [[package]]
991
- name = "ppv-lite86"
992
- version = "0.2.21"
993
- source = "registry+https://github.com/rust-lang/crates.io-index"
994
- checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
995
- dependencies = [
996
- "zerocopy",
997
- ]
998
-
999
998
  [[package]]
1000
999
  name = "precomputed-hash"
1001
1000
  version = "0.1.1"
@@ -1029,38 +1028,34 @@ dependencies = [
1029
1028
 
1030
1029
  [[package]]
1031
1030
  name = "pyo3"
1032
- version = "0.27.2"
1031
+ version = "0.29.0"
1033
1032
  source = "registry+https://github.com/rust-lang/crates.io-index"
1034
- checksum = "ab53c047fcd1a1d2a8820fe84f05d6be69e9526be40cb03b73f86b6b03e6d87d"
1033
+ checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
1035
1034
  dependencies = [
1036
1035
  "anyhow",
1037
1036
  "indexmap",
1038
- "indoc",
1039
1037
  "libc",
1040
- "memoffset",
1041
1038
  "once_cell",
1042
1039
  "portable-atomic",
1043
1040
  "pyo3-build-config",
1044
1041
  "pyo3-ffi",
1045
1042
  "pyo3-macros",
1046
- "unindent",
1047
1043
  ]
1048
1044
 
1049
1045
  [[package]]
1050
1046
  name = "pyo3-build-config"
1051
- version = "0.27.2"
1047
+ version = "0.29.0"
1052
1048
  source = "registry+https://github.com/rust-lang/crates.io-index"
1053
- checksum = "b455933107de8642b4487ed26d912c2d899dec6114884214a0b3bb3be9261ea6"
1049
+ checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
1054
1050
  dependencies = [
1055
- "python3-dll-a",
1056
1051
  "target-lexicon",
1057
1052
  ]
1058
1053
 
1059
1054
  [[package]]
1060
1055
  name = "pyo3-ffi"
1061
- version = "0.27.2"
1056
+ version = "0.29.0"
1062
1057
  source = "registry+https://github.com/rust-lang/crates.io-index"
1063
- checksum = "1c85c9cbfaddf651b1221594209aed57e9e5cff63c4d11d1feead529b872a089"
1058
+ checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
1064
1059
  dependencies = [
1065
1060
  "libc",
1066
1061
  "pyo3-build-config",
@@ -1068,9 +1063,9 @@ dependencies = [
1068
1063
 
1069
1064
  [[package]]
1070
1065
  name = "pyo3-log"
1071
- version = "0.13.3"
1066
+ version = "0.13.4"
1072
1067
  source = "registry+https://github.com/rust-lang/crates.io-index"
1073
- checksum = "26c2ec80932c5c3b2d4fbc578c9b56b2d4502098587edb8bef5b6bfcad43682e"
1068
+ checksum = "f64083bd3a16a353d9d62335808e8e13d0552d2a2b83fdb084496192dcfa9fcd"
1074
1069
  dependencies = [
1075
1070
  "arc-swap",
1076
1071
  "log",
@@ -1079,9 +1074,9 @@ dependencies = [
1079
1074
 
1080
1075
  [[package]]
1081
1076
  name = "pyo3-macros"
1082
- version = "0.27.2"
1077
+ version = "0.29.0"
1083
1078
  source = "registry+https://github.com/rust-lang/crates.io-index"
1084
- checksum = "0a5b10c9bf9888125d917fb4d2ca2d25c8df94c7ab5a52e13313a07e050a3b02"
1079
+ checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
1085
1080
  dependencies = [
1086
1081
  "proc-macro2",
1087
1082
  "pyo3-macros-backend",
@@ -1091,31 +1086,21 @@ dependencies = [
1091
1086
 
1092
1087
  [[package]]
1093
1088
  name = "pyo3-macros-backend"
1094
- version = "0.27.2"
1089
+ version = "0.29.0"
1095
1090
  source = "registry+https://github.com/rust-lang/crates.io-index"
1096
- checksum = "03b51720d314836e53327f5871d4c0cfb4fb37cc2c4a11cc71907a86342c40f9"
1091
+ checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
1097
1092
  dependencies = [
1098
1093
  "heck",
1099
1094
  "proc-macro2",
1100
- "pyo3-build-config",
1101
1095
  "quote",
1102
1096
  "syn",
1103
1097
  ]
1104
1098
 
1105
- [[package]]
1106
- name = "python3-dll-a"
1107
- version = "0.2.15"
1108
- source = "registry+https://github.com/rust-lang/crates.io-index"
1109
- checksum = "d80ba7540edb18890d444c5aa8e1f1f99b1bdf26fb26ae383135325f4a36042b"
1110
- dependencies = [
1111
- "cc",
1112
- ]
1113
-
1114
1099
  [[package]]
1115
1100
  name = "pythonize"
1116
- version = "0.27.0"
1101
+ version = "0.29.0"
1117
1102
  source = "registry+https://github.com/rust-lang/crates.io-index"
1118
- checksum = "a3a8f29db331e28c332c63496cfcbb822aca3d7320bc08b655d7fd0c29c50ede"
1103
+ checksum = "6ec376e1216e0c929a74964ce2020012a1a39f32d80e78aa688721219ea7fb89"
1119
1104
  dependencies = [
1120
1105
  "pyo3",
1121
1106
  "serde",
@@ -1143,14 +1128,15 @@ dependencies = [
1143
1128
 
1144
1129
  [[package]]
1145
1130
  name = "quinn-proto"
1146
- version = "0.11.14"
1131
+ version = "0.11.16"
1147
1132
  source = "registry+https://github.com/rust-lang/crates.io-index"
1148
- checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
1133
+ checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560"
1149
1134
  dependencies = [
1150
1135
  "bytes",
1151
- "getrandom 0.3.4",
1136
+ "getrandom 0.4.3",
1152
1137
  "lru-slab",
1153
- "rand 0.9.4",
1138
+ "rand 0.10.2",
1139
+ "rand_pcg",
1154
1140
  "ring",
1155
1141
  "rustc-hash",
1156
1142
  "rustls",
@@ -1191,6 +1177,12 @@ version = "5.3.0"
1191
1177
  source = "registry+https://github.com/rust-lang/crates.io-index"
1192
1178
  checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
1193
1179
 
1180
+ [[package]]
1181
+ name = "r-efi"
1182
+ version = "6.0.0"
1183
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1184
+ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
1185
+
1194
1186
  [[package]]
1195
1187
  name = "rand"
1196
1188
  version = "0.8.6"
@@ -1202,22 +1194,13 @@ dependencies = [
1202
1194
 
1203
1195
  [[package]]
1204
1196
  name = "rand"
1205
- version = "0.9.4"
1206
- source = "registry+https://github.com/rust-lang/crates.io-index"
1207
- checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
1208
- dependencies = [
1209
- "rand_chacha",
1210
- "rand_core 0.9.5",
1211
- ]
1212
-
1213
- [[package]]
1214
- name = "rand_chacha"
1215
- version = "0.9.0"
1197
+ version = "0.10.2"
1216
1198
  source = "registry+https://github.com/rust-lang/crates.io-index"
1217
- checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
1199
+ checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
1218
1200
  dependencies = [
1219
- "ppv-lite86",
1220
- "rand_core 0.9.5",
1201
+ "chacha20",
1202
+ "getrandom 0.4.3",
1203
+ "rand_core 0.10.1",
1221
1204
  ]
1222
1205
 
1223
1206
  [[package]]
@@ -1228,11 +1211,17 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
1228
1211
 
1229
1212
  [[package]]
1230
1213
  name = "rand_core"
1231
- version = "0.9.5"
1214
+ version = "0.10.1"
1232
1215
  source = "registry+https://github.com/rust-lang/crates.io-index"
1233
- checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
1216
+ checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
1217
+
1218
+ [[package]]
1219
+ name = "rand_pcg"
1220
+ version = "0.10.2"
1221
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1222
+ checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a"
1234
1223
  dependencies = [
1235
- "getrandom 0.3.4",
1224
+ "rand_core 0.10.1",
1236
1225
  ]
1237
1226
 
1238
1227
  [[package]]
@@ -1803,12 +1792,6 @@ version = "0.2.2"
1803
1792
  source = "registry+https://github.com/rust-lang/crates.io-index"
1804
1793
  checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
1805
1794
 
1806
- [[package]]
1807
- name = "unindent"
1808
- version = "0.2.4"
1809
- source = "registry+https://github.com/rust-lang/crates.io-index"
1810
- checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
1811
-
1812
1795
  [[package]]
1813
1796
  name = "untrusted"
1814
1797
  version = "0.9.0"
@@ -2,7 +2,7 @@
2
2
  name = "httpr"
3
3
  # Version is set dynamically by CI from git tags (e.g., v1.2.3 -> 1.2.3).
4
4
  # Do not edit manually. See .github/actions/set-version/ for details.
5
- version = "0.4.8"
5
+ version = "0.5.2"
6
6
  edition = "2021"
7
7
  description = "Fast HTTP client for python"
8
8
  authors = ["thomasht86"]
@@ -15,7 +15,7 @@ name = "httpr"
15
15
  crate-type = ["cdylib"]
16
16
 
17
17
  [dependencies]
18
- pyo3 = { version = "0.27.0", features = [
18
+ pyo3 = { version = "0.29.0", features = [
19
19
  "extension-module",
20
20
  "abi3-py39",
21
21
  "indexmap",
@@ -24,7 +24,7 @@ pyo3 = { version = "0.27.0", features = [
24
24
  ] }
25
25
  anyhow = "1.0.95"
26
26
  tracing = { version = "0.1.41", features = ["log-always"] }
27
- pyo3-log = "0.13.2"
27
+ pyo3-log = "0.13.4"
28
28
  reqwest = { version = "0.12", default-features = false, features = [
29
29
  "cookies",
30
30
  "json",
@@ -44,7 +44,7 @@ tokio = { version = "1.43.0", features = ["full"] }
44
44
  tokio-util = { version = "0.7.13", features = ["codec"] } # for multipart
45
45
  html2text = "0.13.6"
46
46
  bytes = "1.10.0"
47
- pythonize = "0.27.0"
47
+ pythonize = "0.29.0"
48
48
  serde_json = "1.0.138"
49
49
  serde_cbor_2 = "0.13"
50
50
  webpki-root-certs = "0.26.8"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: httpr
3
- Version: 0.4.8
3
+ Version: 0.5.2
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -107,7 +107,11 @@ pip install -U httpr
107
107
 
108
108
  ## Benchmark
109
109
 
110
- ![](https://thomasht86.github.io/httpr/benchmark.jpg)
110
+ Performance is tracked continuously with [github-action-benchmark](https://github.com/benchmark-action/github-action-benchmark): on every push to `main`, the benchmark suite (`tests/benchmark/`) runs in CI and the results are published as interactive charts.
111
+
112
+ **📈 [View live benchmark results](https://thomasht86.github.io/httpr/dev/bench/)**
113
+
114
+ To run the benchmarks locally, see [benchmark/README.md](benchmark/README.md).
111
115
 
112
116
  ## Usage
113
117
 
@@ -209,6 +213,15 @@ resp.encoding
209
213
  resp.headers
210
214
  resp.json()
211
215
  resp.status_code
216
+ resp.reason_phrase # e.g. "OK", "Not Found"
217
+ resp.raise_for_status() # raise HTTPStatusError on non-2xx (returns self)
218
+ resp.is_informational # 1xx
219
+ resp.is_success # 2xx
220
+ resp.is_redirect # 3xx
221
+ resp.is_client_error # 4xx
222
+ resp.is_server_error # 5xx
223
+ resp.is_error # 4xx or 5xx
224
+ resp.has_redirect_location # 3xx with a Location header
212
225
  resp.text
213
226
  resp.text_markdown # html is converted to markdown text using html2text-rs
214
227
  resp.text_plain # html is converted to plain text
@@ -216,6 +229,9 @@ resp.text_rich # html is converted to rich text
216
229
  resp.url
217
230
  ```
218
231
 
232
+ `raise_for_status()` follows `httpx` semantics: any non-2xx status raises
233
+ `HTTPStatusError` (not just 4xx/5xx as in `requests`).
234
+
219
235
  #### Streaming responses
220
236
 
221
237
  The `Client` class supports streaming responses for efficient memory usage when handling large payloads. Use the `stream()` context manager to iterate over response data without buffering the entire response in memory.
@@ -245,6 +261,9 @@ with client.stream("GET", url) as response:
245
261
 
246
262
  **StreamingResponse attributes:**
247
263
  - `status_code` - HTTP status code
264
+ - `reason_phrase` - Canonical reason phrase (e.g. "OK")
265
+ - `raise_for_status()` - Raise `HTTPStatusError` on a non-2xx status (returns self)
266
+ - `is_success` / `is_error` / `is_redirect` / ... - Status-class helpers (same as `Response`)
248
267
  - `headers` - Response headers (case-insensitive)
249
268
  - `cookies` - Response cookies
250
269
  - `url` - Final URL after redirects
@@ -449,12 +468,11 @@ task httpbun:logs # Show container logs
449
468
  | `docs` (build) | ✓ | | | |
450
469
  | `linux`, `musllinux`, `windows`, `macos`, `sdist` | | | ✓ | ✓ |
451
470
  | `release` (PyPI publish) | | | ✓ | ✓ |
452
- | `benchmark` | | | ✓ | ✓ |
453
471
 
454
472
  - **PRs**: Run lint, tests across Python 3.10-3.14 matrix, and verify docs build
455
- - **Push to main**: Run tests only
456
- - **Tags**: Run tests, build wheels, publish stable release to PyPI, run benchmarks
457
- - **Manual**: Full multi-platform wheel builds with release and benchmarks
473
+ - **Push to main**: Run tests, then the separate `Benchmark` workflow runs the benchmark suite and publishes results to the [live benchmark charts](https://thomasht86.github.io/httpr/dev/bench/)
474
+ - **Tags**: Run tests, build wheels, publish stable release to PyPI
475
+ - **Manual**: Full multi-platform wheel builds with release
458
476
 
459
477
  ## Acknowledgements
460
478
 
@@ -63,7 +63,11 @@ pip install -U httpr
63
63
 
64
64
  ## Benchmark
65
65
 
66
- ![](https://thomasht86.github.io/httpr/benchmark.jpg)
66
+ Performance is tracked continuously with [github-action-benchmark](https://github.com/benchmark-action/github-action-benchmark): on every push to `main`, the benchmark suite (`tests/benchmark/`) runs in CI and the results are published as interactive charts.
67
+
68
+ **📈 [View live benchmark results](https://thomasht86.github.io/httpr/dev/bench/)**
69
+
70
+ To run the benchmarks locally, see [benchmark/README.md](benchmark/README.md).
67
71
 
68
72
  ## Usage
69
73
 
@@ -165,6 +169,15 @@ resp.encoding
165
169
  resp.headers
166
170
  resp.json()
167
171
  resp.status_code
172
+ resp.reason_phrase # e.g. "OK", "Not Found"
173
+ resp.raise_for_status() # raise HTTPStatusError on non-2xx (returns self)
174
+ resp.is_informational # 1xx
175
+ resp.is_success # 2xx
176
+ resp.is_redirect # 3xx
177
+ resp.is_client_error # 4xx
178
+ resp.is_server_error # 5xx
179
+ resp.is_error # 4xx or 5xx
180
+ resp.has_redirect_location # 3xx with a Location header
168
181
  resp.text
169
182
  resp.text_markdown # html is converted to markdown text using html2text-rs
170
183
  resp.text_plain # html is converted to plain text
@@ -172,6 +185,9 @@ resp.text_rich # html is converted to rich text
172
185
  resp.url
173
186
  ```
174
187
 
188
+ `raise_for_status()` follows `httpx` semantics: any non-2xx status raises
189
+ `HTTPStatusError` (not just 4xx/5xx as in `requests`).
190
+
175
191
  #### Streaming responses
176
192
 
177
193
  The `Client` class supports streaming responses for efficient memory usage when handling large payloads. Use the `stream()` context manager to iterate over response data without buffering the entire response in memory.
@@ -201,6 +217,9 @@ with client.stream("GET", url) as response:
201
217
 
202
218
  **StreamingResponse attributes:**
203
219
  - `status_code` - HTTP status code
220
+ - `reason_phrase` - Canonical reason phrase (e.g. "OK")
221
+ - `raise_for_status()` - Raise `HTTPStatusError` on a non-2xx status (returns self)
222
+ - `is_success` / `is_error` / `is_redirect` / ... - Status-class helpers (same as `Response`)
204
223
  - `headers` - Response headers (case-insensitive)
205
224
  - `cookies` - Response cookies
206
225
  - `url` - Final URL after redirects
@@ -405,12 +424,11 @@ task httpbun:logs # Show container logs
405
424
  | `docs` (build) | ✓ | | | |
406
425
  | `linux`, `musllinux`, `windows`, `macos`, `sdist` | | | ✓ | ✓ |
407
426
  | `release` (PyPI publish) | | | ✓ | ✓ |
408
- | `benchmark` | | | ✓ | ✓ |
409
427
 
410
428
  - **PRs**: Run lint, tests across Python 3.10-3.14 matrix, and verify docs build
411
- - **Push to main**: Run tests only
412
- - **Tags**: Run tests, build wheels, publish stable release to PyPI, run benchmarks
413
- - **Manual**: Full multi-platform wheel builds with release and benchmarks
429
+ - **Push to main**: Run tests, then the separate `Benchmark` workflow runs the benchmark suite and publishes results to the [live benchmark charts](https://thomasht86.github.io/httpr/dev/bench/)
430
+ - **Tags**: Run tests, build wheels, publish stable release to PyPI
431
+ - **Manual**: Full multi-platform wheel builds with release
414
432
 
415
433
  ## Acknowledgements
416
434
 
@@ -83,6 +83,10 @@ response = client.get("https://api.example.com")
83
83
 
84
84
  # Status
85
85
  response.status_code # int: 200, 404, etc.
86
+ response.reason_phrase # str: "OK", "Not Found", etc.
87
+ response.is_success # bool: True for 2xx
88
+ response.is_error # bool: True for 4xx/5xx
89
+ response.raise_for_status() # raise HTTPStatusError on non-2xx (returns self)
86
90
 
87
91
  # Body
88
92
  response.text # str: decoded text