vws-python 2026.8.26__tar.gz → 2026.9.7__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 (86) hide show
  1. {vws_python-2026.8.26 → vws_python-2026.9.7}/CHANGELOG.rst +5 -0
  2. {vws_python-2026.8.26/src/vws_python.egg-info → vws_python-2026.9.7}/PKG-INFO +15 -14
  3. {vws_python-2026.8.26 → vws_python-2026.9.7}/docs/source/index.rst +38 -0
  4. {vws_python-2026.8.26 → vws_python-2026.9.7}/pyproject.toml +24 -14
  5. {vws_python-2026.8.26 → vws_python-2026.9.7}/spelling_private_dict.txt +3 -0
  6. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/transports.py +192 -1
  7. {vws_python-2026.8.26 → vws_python-2026.9.7/src/vws_python.egg-info}/PKG-INFO +15 -14
  8. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws_python.egg-info/requires.txt +14 -13
  9. vws_python-2026.9.7/src/vws_python.egg-info/scm_version.json +8 -0
  10. vws_python-2026.9.7/tests/test_transports.py +1111 -0
  11. vws_python-2026.8.26/src/vws_python.egg-info/scm_version.json +0 -8
  12. vws_python-2026.8.26/tests/test_transports.py +0 -407
  13. {vws_python-2026.8.26 → vws_python-2026.9.7}/.git_archival.txt +0 -0
  14. {vws_python-2026.8.26 → vws_python-2026.9.7}/.gitattributes +0 -0
  15. {vws_python-2026.8.26 → vws_python-2026.9.7}/.github/dependabot.yml +0 -0
  16. {vws_python-2026.8.26 → vws_python-2026.9.7}/.github/workflows/autofix.yml +0 -0
  17. {vws_python-2026.8.26 → vws_python-2026.9.7}/.github/workflows/ci.yml +0 -0
  18. {vws_python-2026.8.26 → vws_python-2026.9.7}/.github/workflows/dependabot-merge.yml +0 -0
  19. {vws_python-2026.8.26 → vws_python-2026.9.7}/.github/workflows/lint.yml +0 -0
  20. {vws_python-2026.8.26 → vws_python-2026.9.7}/.github/workflows/publish-site.yml +0 -0
  21. {vws_python-2026.8.26 → vws_python-2026.9.7}/.github/workflows/release.yml +0 -0
  22. {vws_python-2026.8.26 → vws_python-2026.9.7}/.gitignore +0 -0
  23. {vws_python-2026.8.26 → vws_python-2026.9.7}/.prettierrc +0 -0
  24. {vws_python-2026.8.26 → vws_python-2026.9.7}/.vale.ini +0 -0
  25. {vws_python-2026.8.26 → vws_python-2026.9.7}/.vscode/extensions.json +0 -0
  26. {vws_python-2026.8.26 → vws_python-2026.9.7}/.vscode/settings.json +0 -0
  27. {vws_python-2026.8.26 → vws_python-2026.9.7}/CODE_OF_CONDUCT.rst +0 -0
  28. {vws_python-2026.8.26 → vws_python-2026.9.7}/LICENSE +0 -0
  29. {vws_python-2026.8.26 → vws_python-2026.9.7}/README.rst +0 -0
  30. {vws_python-2026.8.26 → vws_python-2026.9.7}/conftest.py +0 -0
  31. {vws_python-2026.8.26 → vws_python-2026.9.7}/docs/source/__init__.py +0 -0
  32. {vws_python-2026.8.26 → vws_python-2026.9.7}/docs/source/api-reference.rst +0 -0
  33. {vws_python-2026.8.26 → vws_python-2026.9.7}/docs/source/changelog.rst +0 -0
  34. {vws_python-2026.8.26 → vws_python-2026.9.7}/docs/source/conf.py +0 -0
  35. {vws_python-2026.8.26 → vws_python-2026.9.7}/docs/source/contributing.rst +0 -0
  36. {vws_python-2026.8.26 → vws_python-2026.9.7}/docs/source/exceptions.rst +0 -0
  37. {vws_python-2026.8.26 → vws_python-2026.9.7}/docs/source/release-process.rst +0 -0
  38. {vws_python-2026.8.26 → vws_python-2026.9.7}/docs/source/unreleased.rst +0 -0
  39. {vws_python-2026.8.26 → vws_python-2026.9.7}/docs/towncrier_template.rst.jinja +0 -0
  40. {vws_python-2026.8.26 → vws_python-2026.9.7}/newsfragments/.gitkeep +0 -0
  41. {vws_python-2026.8.26 → vws_python-2026.9.7}/prek.toml +0 -0
  42. {vws_python-2026.8.26 → vws_python-2026.9.7}/setup.cfg +0 -0
  43. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/__init__.py +0 -0
  44. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/_async_vws_request.py +0 -0
  45. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/_image_utils.py +0 -0
  46. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/_model_targets.py +0 -0
  47. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/_reco_counts.py +0 -0
  48. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/_vws_request.py +0 -0
  49. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/async_model_target_service.py +0 -0
  50. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/async_query.py +0 -0
  51. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/async_vumark_service.py +0 -0
  52. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/async_vws.py +0 -0
  53. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/exceptions/__init__.py +0 -0
  54. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/exceptions/base_exceptions.py +0 -0
  55. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/exceptions/cloud_reco_exceptions.py +0 -0
  56. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/exceptions/custom_exceptions.py +0 -0
  57. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/exceptions/model_target_exceptions.py +0 -0
  58. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/exceptions/vws_exceptions.py +0 -0
  59. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/include_target_data.py +0 -0
  60. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/model_target_datasets.py +0 -0
  61. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/model_target_service.py +0 -0
  62. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/py.typed +0 -0
  63. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/query.py +0 -0
  64. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/reports.py +0 -0
  65. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/response.py +0 -0
  66. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/vumark_accept.py +0 -0
  67. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/vumark_service.py +0 -0
  68. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws/vws.py +0 -0
  69. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws_python.egg-info/SOURCES.txt +0 -0
  70. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws_python.egg-info/dependency_links.txt +0 -0
  71. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws_python.egg-info/not-zip-safe +0 -0
  72. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws_python.egg-info/scm_file_list.json +0 -0
  73. {vws_python-2026.8.26 → vws_python-2026.9.7}/src/vws_python.egg-info/top_level.txt +0 -0
  74. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/__init__.py +0 -0
  75. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/conftest.py +0 -0
  76. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/test_async_cloud_reco_exceptions.py +0 -0
  77. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/test_async_model_targets.py +0 -0
  78. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/test_async_query.py +0 -0
  79. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/test_async_vws.py +0 -0
  80. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/test_async_vws_exceptions.py +0 -0
  81. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/test_cloud_reco_exceptions.py +0 -0
  82. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/test_model_targets.py +0 -0
  83. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/test_query.py +0 -0
  84. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/test_vws.py +0 -0
  85. {vws_python-2026.8.26 → vws_python-2026.9.7}/tests/test_vws_exceptions.py +0 -0
  86. {vws_python-2026.8.26 → vws_python-2026.9.7}/zizmor.yml +0 -0
@@ -3,6 +3,11 @@ Changelog
3
3
 
4
4
  .. towncrier release notes start
5
5
 
6
+ 2026.09.07
7
+ ----------
8
+
9
+ - Add ``HTTPX2Transport`` and ``AsyncHTTPX2Transport``, which make requests with ``httpx2``, the continuation of ``httpx`` maintained by Pydantic. The ``requests`` and ``httpx`` transports are unchanged. ``httpx`` and ``httpx2`` objects are never mixed: the ``httpx`` transports raise ``httpx`` exceptions and the ``httpx2`` transports raise ``httpx2`` exceptions.
10
+
6
11
  2026.08.26
7
12
  ----------
8
13
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vws-python
3
- Version: 2026.8.26
3
+ Version: 2026.9.7
4
4
  Summary: Interact with the Vuforia Web Services (VWS) API.
5
5
  Author-email: Adam Dangoor <adamdangoor@gmail.com>
6
6
  License-Expression: MIT
@@ -18,6 +18,7 @@ Description-Content-Type: text/x-rst
18
18
  License-File: LICENSE
19
19
  Requires-Dist: beartype>=0.22.9
20
20
  Requires-Dist: httpx>=0.28.0
21
+ Requires-Dist: httpx2>=2.12
21
22
  Requires-Dist: requests>=2.32.3
22
23
  Requires-Dist: urllib3>=2.2.3
23
24
  Requires-Dist: vws-auth-tools>=2024.7.12
@@ -26,20 +27,20 @@ Requires-Dist: actionlint-py==1.7.12.24; extra == "dev"
26
27
  Requires-Dist: check-manifest==0.51; extra == "dev"
27
28
  Requires-Dist: deptry==0.25.1; extra == "dev"
28
29
  Requires-Dist: doc8==2.0.0; extra == "dev"
29
- Requires-Dist: doccmd==2026.8.16; extra == "dev"
30
+ Requires-Dist: doccmd==2026.9.1; extra == "dev"
30
31
  Requires-Dist: freezegun==1.5.5; extra == "dev"
31
32
  Requires-Dist: furo==2025.12.19; extra == "dev"
32
33
  Requires-Dist: interrogate==1.7.0; extra == "dev"
33
34
  Requires-Dist: mypy[faster-cache]==2.3.1; extra == "dev"
34
35
  Requires-Dist: mypy-strict-kwargs==2026.8.25.1; extra == "dev"
35
- Requires-Dist: no-defaults==2.1.0; extra == "dev"
36
- Requires-Dist: prek==0.4.14; extra == "dev"
36
+ Requires-Dist: no-defaults==2026.9.1; extra == "dev"
37
+ Requires-Dist: prek==0.5.2; extra == "dev"
37
38
  Requires-Dist: pydocstringformatter==1.0.0; extra == "dev"
38
39
  Requires-Dist: pydocstyle==6.3; extra == "dev"
39
40
  Requires-Dist: pygments==2.21.0; extra == "dev"
40
- Requires-Dist: pylint[spelling]==4.0.7; extra == "dev"
41
+ Requires-Dist: pylint[spelling]==4.0.8; extra == "dev"
41
42
  Requires-Dist: pylint-per-file-ignores==3.2.1; extra == "dev"
42
- Requires-Dist: pyproject-fmt==2.28.0; extra == "dev"
43
+ Requires-Dist: pyproject-fmt==2.29.3; extra == "dev"
43
44
  Requires-Dist: pyrefly==1.2.0; extra == "dev"
44
45
  Requires-Dist: pyright==1.1.411; extra == "dev"
45
46
  Requires-Dist: pyroma==5.0.1; extra == "dev"
@@ -48,9 +49,9 @@ Requires-Dist: pytest-asyncio==1.4.0; extra == "dev"
48
49
  Requires-Dist: pytest-beartype-tests==2026.8.16; extra == "dev"
49
50
  Requires-Dist: pytest-cov==7.1.0; extra == "dev"
50
51
  Requires-Dist: pyyaml==6.0.3; extra == "dev"
51
- Requires-Dist: ruff==0.16.4; extra == "dev"
52
+ Requires-Dist: ruff==0.16.6; extra == "dev"
52
53
  Requires-Dist: shellcheck-py==0.11.0.1; extra == "dev"
53
- Requires-Dist: shfmt-py==4.0.0; extra == "dev"
54
+ Requires-Dist: shfmt-py==4.1.0; extra == "dev"
54
55
  Requires-Dist: sphinx==9.1.0; extra == "dev"
55
56
  Requires-Dist: sphinx-copybutton==0.5.2; extra == "dev"
56
57
  Requires-Dist: sphinx-lint==1.0.2; extra == "dev"
@@ -58,17 +59,17 @@ Requires-Dist: sphinx-pyproject==0.3.0; extra == "dev"
58
59
  Requires-Dist: sphinx-substitution-extensions==2026.8.13.1; extra == "dev"
59
60
  Requires-Dist: sphinxcontrib-spelling==8.0.2; extra == "dev"
60
61
  Requires-Dist: sphinxcontrib-towncrier==0.5.0a0; extra == "dev"
61
- Requires-Dist: strict-kwargs==2026.8.16; extra == "dev"
62
+ Requires-Dist: strict-kwargs==2026.8.28.post2; extra == "dev"
62
63
  Requires-Dist: sybil==10.1.0; extra == "dev"
63
64
  Requires-Dist: towncrier==25.8.0; extra == "dev"
64
- Requires-Dist: ty==0.0.74; extra == "dev"
65
+ Requires-Dist: ty==0.0.78; extra == "dev"
65
66
  Requires-Dist: types-requests==2.33.0.20260712; extra == "dev"
66
- Requires-Dist: vale==3.18.0.0; extra == "dev"
67
+ Requires-Dist: vale==3.20.0.0; extra == "dev"
67
68
  Requires-Dist: vulture==2.16; extra == "dev"
68
- Requires-Dist: vws-python-mock==2026.8.26.1; extra == "dev"
69
- Requires-Dist: vws-test-fixtures==2026.8.23; extra == "dev"
69
+ Requires-Dist: vws-python-mock==2026.9.6; extra == "dev"
70
+ Requires-Dist: vws-test-fixtures==2026.8.26; extra == "dev"
70
71
  Requires-Dist: yamlfix==1.19.1; extra == "dev"
71
- Requires-Dist: zizmor==1.29.0; extra == "dev"
72
+ Requires-Dist: zizmor==1.30.0; extra == "dev"
72
73
  Provides-Extra: release
73
74
  Requires-Dist: check-wheel-contents==0.6.3; extra == "release"
74
75
  Requires-Dist: towncrier==25.8.0; extra == "release"
@@ -186,6 +186,44 @@ Dataset generation happens in the background, and the generated dataset is downl
186
186
  dataset_type=ModelTargetDatasetType.STANDARD,
187
187
  )
188
188
 
189
+ HTTP transports
190
+ ---------------
191
+
192
+ By default, the synchronous clients make requests with `requests`_ and the asynchronous clients make requests with `httpx`_.
193
+ To use another HTTP library, pass a transport from :mod:`vws.transports` to a client.
194
+
195
+ Transports are available for `requests`_, `httpx`_ and `HTTPX2`_.
196
+ ``httpx`` and ``httpx2`` are separate packages with separate client, response and exception classes.
197
+ ``HTTPXTransport`` and ``AsyncHTTPXTransport`` use ``httpx`` and raise ``httpx`` exceptions.
198
+ ``HTTPX2Transport`` and ``AsyncHTTPX2Transport`` use ``httpx2`` and raise ``httpx2`` exceptions.
199
+
200
+ .. clear-namespace
201
+
202
+ .. code-block:: python
203
+
204
+ """List targets using HTTPX2."""
205
+
206
+ import os
207
+
208
+ from vws import VWS
209
+ from vws.transports import HTTPX2Transport
210
+
211
+ server_access_key = os.environ["VWS_SERVER_ACCESS_KEY"]
212
+ server_secret_key = os.environ["VWS_SERVER_SECRET_KEY"]
213
+
214
+ vws_client = VWS(
215
+ server_access_key=server_access_key,
216
+ server_secret_key=server_secret_key,
217
+ transport=HTTPX2Transport(),
218
+ )
219
+
220
+ # This database has no targets.
221
+ assert not vws_client.list_targets()
222
+
223
+ .. _requests: https://pypi.org/project/requests/
224
+ .. _httpx: https://pypi.org/project/httpx/
225
+ .. _HTTPX2: https://httpx2.pydantic.dev/
226
+
189
227
  Testing
190
228
  -------
191
229
 
@@ -33,6 +33,7 @@ dynamic = [
33
33
  dependencies = [
34
34
  "beartype>=0.22.9",
35
35
  "httpx>=0.28.0",
36
+ "httpx2>=2.12",
36
37
  "requests>=2.32.3",
37
38
  "urllib3>=2.2.3",
38
39
  "vws-auth-tools>=2024.7.12",
@@ -42,20 +43,20 @@ optional-dependencies.dev = [
42
43
  "check-manifest==0.51",
43
44
  "deptry==0.25.1",
44
45
  "doc8==2.0.0",
45
- "doccmd==2026.8.16",
46
+ "doccmd==2026.9.1",
46
47
  "freezegun==1.5.5",
47
48
  "furo==2025.12.19",
48
49
  "interrogate==1.7.0",
49
50
  "mypy[faster-cache]==2.3.1",
50
51
  "mypy-strict-kwargs==2026.8.25.1",
51
- "no-defaults==2.1.0",
52
- "prek==0.4.14",
52
+ "no-defaults==2026.9.1",
53
+ "prek==0.5.2",
53
54
  "pydocstringformatter==1.0.0",
54
55
  "pydocstyle==6.3",
55
56
  "pygments==2.21.0",
56
- "pylint[spelling]==4.0.7",
57
+ "pylint[spelling]==4.0.8",
57
58
  "pylint-per-file-ignores==3.2.1",
58
- "pyproject-fmt==2.28.0",
59
+ "pyproject-fmt==2.29.3",
59
60
  "pyrefly==1.2.0",
60
61
  "pyright==1.1.411",
61
62
  "pyroma==5.0.1",
@@ -64,12 +65,12 @@ optional-dependencies.dev = [
64
65
  "pytest-beartype-tests==2026.8.16",
65
66
  "pytest-cov==7.1.0",
66
67
  "pyyaml==6.0.3",
67
- "ruff==0.16.4",
68
+ "ruff==0.16.6",
68
69
  # We add shellcheck-py not only for shell scripts and shell code blocks,
69
70
  # but also because having it installed means that ``actionlint-py`` will
70
71
  # use it to lint shell commands in GitHub workflow files.
71
72
  "shellcheck-py==0.11.0.1",
72
- "shfmt-py==4.0.0",
73
+ "shfmt-py==4.1.0",
73
74
  "sphinx==9.1.0",
74
75
  "sphinx-copybutton==0.5.2",
75
76
  "sphinx-lint==1.0.2",
@@ -79,17 +80,17 @@ optional-dependencies.dev = [
79
80
  # ``sphinxcontrib-towncrier`` renders unreleased news fragments
80
81
  # into docs/source/unreleased.rst during Sphinx builds.
81
82
  "sphinxcontrib-towncrier==0.5.0a0",
82
- "strict-kwargs==2026.8.16",
83
+ "strict-kwargs==2026.8.28.post2",
83
84
  "sybil==10.1.0",
84
85
  "towncrier==25.8.0",
85
- "ty==0.0.74",
86
+ "ty==0.0.78",
86
87
  "types-requests==2.33.0.20260712",
87
- "vale==3.18.0.0",
88
+ "vale==3.20.0.0",
88
89
  "vulture==2.16",
89
- "vws-python-mock==2026.8.26.1",
90
- "vws-test-fixtures==2026.8.23",
90
+ "vws-python-mock==2026.9.6",
91
+ "vws-test-fixtures==2026.8.26",
91
92
  "yamlfix==1.19.1",
92
- "zizmor==1.29.0",
93
+ "zizmor==1.30.0",
93
94
  ]
94
95
  optional-dependencies.release = [ "check-wheel-contents==0.6.3", "towncrier==25.8.0" ]
95
96
  urls.Documentation = "https://vws-python.github.io/vws-python/"
@@ -154,7 +155,9 @@ lint.unfixable = [
154
155
  "ERA001",
155
156
  ]
156
157
  lint.external = [ "NOD" ]
157
- lint.flake8-tidy-imports.banned-api."typing.cast".msg = "typing.cast is banned: use explicit type narrowing or a typed variable instead."
158
+ lint.flake8-tidy-imports.banned-api."typing.cast".msg = """\
159
+ typing.cast is banned: use explicit type narrowing or a typed variable instead.\
160
+ """
158
161
  lint.pydocstyle.convention = "google"
159
162
 
160
163
  [tool.pylint]
@@ -328,6 +331,7 @@ ignore_names = [
328
331
  "ALWAYS",
329
332
  "AR_CONTROLLER",
330
333
  # Public API classes imported by users from vws.transports
334
+ "AsyncHTTPX2Transport",
331
335
  "AsyncHTTPXTransport",
332
336
  "AUTO",
333
337
  # Sphinx
@@ -351,6 +355,7 @@ ignore_names = [
351
355
  "html_theme_options",
352
356
  "html_title",
353
357
  "htmlhelp_basename",
358
+ "HTTPX2Transport",
354
359
  "HTTPXTransport",
355
360
  "IGES",
356
361
  "intersphinx_mapping",
@@ -434,6 +439,11 @@ issue_format = "#{issue}"
434
439
  # version such as ``2026.05.18`` underlined with ``-`` matches every
435
440
  # pre-towncrier entry in CHANGELOG.rst.
436
441
  underlines = [ "-", "~", "^" ]
442
+ # Setting ``ignore`` makes ``towncrier build`` fail on any file in
443
+ # the fragment directory whose suffix is not a declared fragment
444
+ # type, instead of silently dropping it. Only the listed
445
+ # non-fragment files are allowed through.
446
+ ignore = [ ".gitkeep" ]
437
447
  type = [
438
448
  # A single, unnamed fragment type keeps the assembled output as one
439
449
  # flat bullet list, matching the historical changelog (which never
@@ -4,6 +4,7 @@ BadImage
4
4
  ConnectionErrorPossiblyImageTooLarge
5
5
  DateRangeError
6
6
  Falsy
7
+ HTTPX2
7
8
  ImageTooLarge
8
9
  InactiveProject
9
10
  JSONDecodeError
@@ -17,6 +18,7 @@ OopsAnErrorOccurredPossiblyBadNameError
17
18
  ProjectHasNoApiAccess
18
19
  ProjectInactive
19
20
  ProjectSuspended
21
+ Pydantic
20
22
  QuotaExceeded
21
23
  RequestQuotaReached
22
24
  RequestTimeTooSkewed
@@ -65,6 +67,7 @@ html
65
67
  http
66
68
  https
67
69
  httpx
70
+ httpx2
68
71
  iff
69
72
  io
70
73
  issuecomment
@@ -1,8 +1,25 @@
1
- """HTTP transport implementations for VWS clients."""
1
+ """HTTP transport implementations for VWS clients.
2
+
3
+ Three transport families are available:
4
+
5
+ * ``RequestsTransport`` uses ``requests``. It is synchronous only, and
6
+ is the default transport for the synchronous clients.
7
+ * ``HTTPXTransport`` and ``AsyncHTTPXTransport`` use ``httpx``.
8
+ ``AsyncHTTPXTransport`` is the default transport for the
9
+ asynchronous clients.
10
+ * ``HTTPX2Transport`` and ``AsyncHTTPX2Transport`` use ``httpx2``, the
11
+ continuation of ``httpx`` maintained by Pydantic.
12
+
13
+ ``httpx`` and ``httpx2`` are separate packages with separate client,
14
+ request, response, timeout and exception classes. Each transport uses
15
+ exactly one of them: the ``httpx`` transports raise ``httpx``
16
+ exceptions, and the ``httpx2`` transports raise ``httpx2`` exceptions.
17
+ """
2
18
 
3
19
  from typing import TYPE_CHECKING, Protocol, Self, runtime_checkable
4
20
 
5
21
  import httpx
22
+ import httpx2
6
23
  import requests
7
24
  from beartype import BeartypeConf, beartype
8
25
 
@@ -191,6 +208,120 @@ class HTTPXTransport:
191
208
  )
192
209
 
193
210
 
211
+ @beartype(conf=BeartypeConf(is_pep484_tower=True))
212
+ def _httpx2_timeout(
213
+ *,
214
+ request_timeout: float | tuple[float, float],
215
+ ) -> httpx2.Timeout:
216
+ """The ``httpx2`` timeout for a request timeout.
217
+
218
+ Args:
219
+ request_timeout: The timeout for the request. A float sets
220
+ both the connect and read timeouts. A (connect, read)
221
+ tuple sets them individually.
222
+
223
+ Returns:
224
+ The equivalent ``httpx2`` timeout.
225
+ """
226
+ match request_timeout:
227
+ case tuple() as timeout:
228
+ connect_timeout, read_timeout = timeout
229
+ case timeout:
230
+ connect_timeout = timeout
231
+ read_timeout = timeout
232
+
233
+ return httpx2.Timeout(
234
+ connect=connect_timeout,
235
+ read=read_timeout,
236
+ write=None,
237
+ pool=None,
238
+ )
239
+
240
+
241
+ @beartype(conf=BeartypeConf(is_pep484_tower=True))
242
+ def _response_from_httpx2(*, httpx2_response: httpx2.Response) -> Response:
243
+ """Convert an ``httpx2`` response to a ``Response``.
244
+
245
+ Args:
246
+ httpx2_response: The response to convert.
247
+
248
+ Returns:
249
+ A Response populated from the ``httpx2`` response.
250
+ """
251
+ content = bytes(httpx2_response.content)
252
+ request_content = httpx2_response.request.content
253
+
254
+ return Response(
255
+ text=httpx2_response.text,
256
+ url=str(object=httpx2_response.url),
257
+ status_code=httpx2_response.status_code,
258
+ headers=dict(httpx2_response.headers),
259
+ request_body=bytes(request_content) or None,
260
+ tell_position=len(content),
261
+ content=content,
262
+ )
263
+
264
+
265
+ @beartype(conf=BeartypeConf(is_pep484_tower=True))
266
+ class HTTPX2Transport:
267
+ """HTTP transport using the ``httpx2`` library.
268
+
269
+ ``httpx2`` is the continuation of ``httpx`` maintained by
270
+ Pydantic. Its client, request, response, timeout and exception
271
+ classes are distinct from the ``httpx`` ones, so this transport
272
+ raises ``httpx2`` exceptions, not ``httpx`` ones.
273
+ A single ``httpx2.Client`` is reused across requests
274
+ for connection pooling.
275
+ """
276
+
277
+ def __init__(self) -> None:
278
+ """Create an ``HTTPX2Transport``."""
279
+ self._client = httpx2.Client()
280
+
281
+ def close(self) -> None:
282
+ """Close the underlying ``httpx2.Client``."""
283
+ self._client.close()
284
+
285
+ def __enter__(self) -> Self:
286
+ """Enter the context manager."""
287
+ return self
288
+
289
+ def __exit__(self, *_args: object) -> None:
290
+ """Exit the context manager and close the client."""
291
+ self.close()
292
+
293
+ def __call__(
294
+ self,
295
+ *,
296
+ method: str,
297
+ url: str,
298
+ headers: dict[str, str],
299
+ data: bytes,
300
+ request_timeout: float | tuple[float, float],
301
+ ) -> Response:
302
+ """Make an HTTP request using ``httpx2``.
303
+
304
+ Args:
305
+ method: The HTTP method.
306
+ url: The full URL.
307
+ headers: Request headers.
308
+ data: The request body.
309
+ request_timeout: The request timeout.
310
+
311
+ Returns:
312
+ A Response populated from the ``httpx2`` response.
313
+ """
314
+ httpx2_response = self._client.request(
315
+ method=method,
316
+ url=url,
317
+ headers=headers,
318
+ content=data,
319
+ timeout=_httpx2_timeout(request_timeout=request_timeout),
320
+ follow_redirects=True,
321
+ )
322
+ return _response_from_httpx2(httpx2_response=httpx2_response)
323
+
324
+
194
325
  @runtime_checkable
195
326
  class AsyncTransport(Protocol):
196
327
  """Protocol for async HTTP transports used by VWS clients.
@@ -313,3 +444,63 @@ class AsyncHTTPXTransport:
313
444
  tell_position=len(content),
314
445
  content=content,
315
446
  )
447
+
448
+
449
+ @beartype(conf=BeartypeConf(is_pep484_tower=True))
450
+ class AsyncHTTPX2Transport:
451
+ """Async HTTP transport using the ``httpx2`` library.
452
+
453
+ ``httpx2`` is the continuation of ``httpx`` maintained by
454
+ Pydantic. Its client, request, response, timeout and exception
455
+ classes are distinct from the ``httpx`` ones, so this transport
456
+ raises ``httpx2`` exceptions, not ``httpx`` ones.
457
+ A single ``httpx2.AsyncClient`` is reused across requests
458
+ for connection pooling.
459
+ """
460
+
461
+ def __init__(self) -> None:
462
+ """Create an ``AsyncHTTPX2Transport``."""
463
+ self._client = httpx2.AsyncClient()
464
+
465
+ async def aclose(self) -> None:
466
+ """Close the underlying ``httpx2.AsyncClient``."""
467
+ await self._client.aclose()
468
+
469
+ async def __aenter__(self) -> Self:
470
+ """Enter the async context manager."""
471
+ return self
472
+
473
+ async def __aexit__(self, *_args: object) -> None:
474
+ """Exit the async context manager and close the client."""
475
+ await self.aclose()
476
+
477
+ async def __call__(
478
+ self,
479
+ *,
480
+ method: str,
481
+ url: str,
482
+ headers: dict[str, str],
483
+ data: bytes,
484
+ request_timeout: float | tuple[float, float],
485
+ ) -> Response:
486
+ """Make an async HTTP request using ``httpx2``.
487
+
488
+ Args:
489
+ method: The HTTP method.
490
+ url: The full URL.
491
+ headers: Request headers.
492
+ data: The request body.
493
+ request_timeout: The request timeout.
494
+
495
+ Returns:
496
+ A Response populated from the ``httpx2`` response.
497
+ """
498
+ httpx2_response = await self._client.request(
499
+ method=method,
500
+ url=url,
501
+ headers=headers,
502
+ content=data,
503
+ timeout=_httpx2_timeout(request_timeout=request_timeout),
504
+ follow_redirects=True,
505
+ )
506
+ return _response_from_httpx2(httpx2_response=httpx2_response)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vws-python
3
- Version: 2026.8.26
3
+ Version: 2026.9.7
4
4
  Summary: Interact with the Vuforia Web Services (VWS) API.
5
5
  Author-email: Adam Dangoor <adamdangoor@gmail.com>
6
6
  License-Expression: MIT
@@ -18,6 +18,7 @@ Description-Content-Type: text/x-rst
18
18
  License-File: LICENSE
19
19
  Requires-Dist: beartype>=0.22.9
20
20
  Requires-Dist: httpx>=0.28.0
21
+ Requires-Dist: httpx2>=2.12
21
22
  Requires-Dist: requests>=2.32.3
22
23
  Requires-Dist: urllib3>=2.2.3
23
24
  Requires-Dist: vws-auth-tools>=2024.7.12
@@ -26,20 +27,20 @@ Requires-Dist: actionlint-py==1.7.12.24; extra == "dev"
26
27
  Requires-Dist: check-manifest==0.51; extra == "dev"
27
28
  Requires-Dist: deptry==0.25.1; extra == "dev"
28
29
  Requires-Dist: doc8==2.0.0; extra == "dev"
29
- Requires-Dist: doccmd==2026.8.16; extra == "dev"
30
+ Requires-Dist: doccmd==2026.9.1; extra == "dev"
30
31
  Requires-Dist: freezegun==1.5.5; extra == "dev"
31
32
  Requires-Dist: furo==2025.12.19; extra == "dev"
32
33
  Requires-Dist: interrogate==1.7.0; extra == "dev"
33
34
  Requires-Dist: mypy[faster-cache]==2.3.1; extra == "dev"
34
35
  Requires-Dist: mypy-strict-kwargs==2026.8.25.1; extra == "dev"
35
- Requires-Dist: no-defaults==2.1.0; extra == "dev"
36
- Requires-Dist: prek==0.4.14; extra == "dev"
36
+ Requires-Dist: no-defaults==2026.9.1; extra == "dev"
37
+ Requires-Dist: prek==0.5.2; extra == "dev"
37
38
  Requires-Dist: pydocstringformatter==1.0.0; extra == "dev"
38
39
  Requires-Dist: pydocstyle==6.3; extra == "dev"
39
40
  Requires-Dist: pygments==2.21.0; extra == "dev"
40
- Requires-Dist: pylint[spelling]==4.0.7; extra == "dev"
41
+ Requires-Dist: pylint[spelling]==4.0.8; extra == "dev"
41
42
  Requires-Dist: pylint-per-file-ignores==3.2.1; extra == "dev"
42
- Requires-Dist: pyproject-fmt==2.28.0; extra == "dev"
43
+ Requires-Dist: pyproject-fmt==2.29.3; extra == "dev"
43
44
  Requires-Dist: pyrefly==1.2.0; extra == "dev"
44
45
  Requires-Dist: pyright==1.1.411; extra == "dev"
45
46
  Requires-Dist: pyroma==5.0.1; extra == "dev"
@@ -48,9 +49,9 @@ Requires-Dist: pytest-asyncio==1.4.0; extra == "dev"
48
49
  Requires-Dist: pytest-beartype-tests==2026.8.16; extra == "dev"
49
50
  Requires-Dist: pytest-cov==7.1.0; extra == "dev"
50
51
  Requires-Dist: pyyaml==6.0.3; extra == "dev"
51
- Requires-Dist: ruff==0.16.4; extra == "dev"
52
+ Requires-Dist: ruff==0.16.6; extra == "dev"
52
53
  Requires-Dist: shellcheck-py==0.11.0.1; extra == "dev"
53
- Requires-Dist: shfmt-py==4.0.0; extra == "dev"
54
+ Requires-Dist: shfmt-py==4.1.0; extra == "dev"
54
55
  Requires-Dist: sphinx==9.1.0; extra == "dev"
55
56
  Requires-Dist: sphinx-copybutton==0.5.2; extra == "dev"
56
57
  Requires-Dist: sphinx-lint==1.0.2; extra == "dev"
@@ -58,17 +59,17 @@ Requires-Dist: sphinx-pyproject==0.3.0; extra == "dev"
58
59
  Requires-Dist: sphinx-substitution-extensions==2026.8.13.1; extra == "dev"
59
60
  Requires-Dist: sphinxcontrib-spelling==8.0.2; extra == "dev"
60
61
  Requires-Dist: sphinxcontrib-towncrier==0.5.0a0; extra == "dev"
61
- Requires-Dist: strict-kwargs==2026.8.16; extra == "dev"
62
+ Requires-Dist: strict-kwargs==2026.8.28.post2; extra == "dev"
62
63
  Requires-Dist: sybil==10.1.0; extra == "dev"
63
64
  Requires-Dist: towncrier==25.8.0; extra == "dev"
64
- Requires-Dist: ty==0.0.74; extra == "dev"
65
+ Requires-Dist: ty==0.0.78; extra == "dev"
65
66
  Requires-Dist: types-requests==2.33.0.20260712; extra == "dev"
66
- Requires-Dist: vale==3.18.0.0; extra == "dev"
67
+ Requires-Dist: vale==3.20.0.0; extra == "dev"
67
68
  Requires-Dist: vulture==2.16; extra == "dev"
68
- Requires-Dist: vws-python-mock==2026.8.26.1; extra == "dev"
69
- Requires-Dist: vws-test-fixtures==2026.8.23; extra == "dev"
69
+ Requires-Dist: vws-python-mock==2026.9.6; extra == "dev"
70
+ Requires-Dist: vws-test-fixtures==2026.8.26; extra == "dev"
70
71
  Requires-Dist: yamlfix==1.19.1; extra == "dev"
71
- Requires-Dist: zizmor==1.29.0; extra == "dev"
72
+ Requires-Dist: zizmor==1.30.0; extra == "dev"
72
73
  Provides-Extra: release
73
74
  Requires-Dist: check-wheel-contents==0.6.3; extra == "release"
74
75
  Requires-Dist: towncrier==25.8.0; extra == "release"
@@ -1,5 +1,6 @@
1
1
  beartype>=0.22.9
2
2
  httpx>=0.28.0
3
+ httpx2>=2.12
3
4
  requests>=2.32.3
4
5
  urllib3>=2.2.3
5
6
  vws-auth-tools>=2024.7.12
@@ -9,20 +10,20 @@ actionlint-py==1.7.12.24
9
10
  check-manifest==0.51
10
11
  deptry==0.25.1
11
12
  doc8==2.0.0
12
- doccmd==2026.8.16
13
+ doccmd==2026.9.1
13
14
  freezegun==1.5.5
14
15
  furo==2025.12.19
15
16
  interrogate==1.7.0
16
17
  mypy[faster-cache]==2.3.1
17
18
  mypy-strict-kwargs==2026.8.25.1
18
- no-defaults==2.1.0
19
- prek==0.4.14
19
+ no-defaults==2026.9.1
20
+ prek==0.5.2
20
21
  pydocstringformatter==1.0.0
21
22
  pydocstyle==6.3
22
23
  pygments==2.21.0
23
- pylint[spelling]==4.0.7
24
+ pylint[spelling]==4.0.8
24
25
  pylint-per-file-ignores==3.2.1
25
- pyproject-fmt==2.28.0
26
+ pyproject-fmt==2.29.3
26
27
  pyrefly==1.2.0
27
28
  pyright==1.1.411
28
29
  pyroma==5.0.1
@@ -31,9 +32,9 @@ pytest-asyncio==1.4.0
31
32
  pytest-beartype-tests==2026.8.16
32
33
  pytest-cov==7.1.0
33
34
  pyyaml==6.0.3
34
- ruff==0.16.4
35
+ ruff==0.16.6
35
36
  shellcheck-py==0.11.0.1
36
- shfmt-py==4.0.0
37
+ shfmt-py==4.1.0
37
38
  sphinx==9.1.0
38
39
  sphinx-copybutton==0.5.2
39
40
  sphinx-lint==1.0.2
@@ -41,17 +42,17 @@ sphinx-pyproject==0.3.0
41
42
  sphinx-substitution-extensions==2026.8.13.1
42
43
  sphinxcontrib-spelling==8.0.2
43
44
  sphinxcontrib-towncrier==0.5.0a0
44
- strict-kwargs==2026.8.16
45
+ strict-kwargs==2026.8.28.post2
45
46
  sybil==10.1.0
46
47
  towncrier==25.8.0
47
- ty==0.0.74
48
+ ty==0.0.78
48
49
  types-requests==2.33.0.20260712
49
- vale==3.18.0.0
50
+ vale==3.20.0.0
50
51
  vulture==2.16
51
- vws-python-mock==2026.8.26.1
52
- vws-test-fixtures==2026.8.23
52
+ vws-python-mock==2026.9.6
53
+ vws-test-fixtures==2026.8.26
53
54
  yamlfix==1.19.1
54
- zizmor==1.29.0
55
+ zizmor==1.30.0
55
56
 
56
57
  [release]
57
58
  check-wheel-contents==0.6.3
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2026.9.7",
3
+ "distance": 0,
4
+ "node": "g1184888c659ca9999e489d1e5681370060eae6ee",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-09-07"
8
+ }