vws-python-mock 2026.2.22.2__tar.gz → 2026.2.22.3__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 (146) hide show
  1. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/CHANGELOG.rst +7 -0
  2. {vws_python_mock-2026.2.22.2/src/vws_python_mock.egg-info → vws_python_mock-2026.2.22.3}/PKG-INFO +10 -17
  3. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/README.rst +8 -14
  4. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/admin/create_secrets_files.py +30 -17
  5. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/basic-example.rst +2 -1
  6. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/contributing.rst +1 -1
  7. vws_python_mock-2026.2.22.3/docs/source/getting-started.rst +9 -0
  8. vws_python_mock-2026.2.22.3/docs/source/httpx-example.rst +18 -0
  9. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/index.rst +2 -5
  10. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/mock-api-reference.rst +0 -4
  11. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/pyproject.toml +2 -2
  12. vws_python_mock-2026.2.22.3/src/mock_vws/__init__.py +9 -0
  13. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_mock_common.py +23 -0
  14. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_requests_mock_server/decorators.py +24 -26
  15. vws_python_mock-2026.2.22.3/src/mock_vws/_respx_mock_server/decorators.py +183 -0
  16. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3/src/vws_python_mock.egg-info}/PKG-INFO +10 -17
  17. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/vws_python_mock.egg-info/requires.txt +1 -2
  18. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_requests_mock_usage.py +48 -0
  19. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_respx_mock_usage.py +17 -138
  20. vws_python_mock-2026.2.22.2/docs/source/getting-started.rst +0 -12
  21. vws_python_mock-2026.2.22.2/docs/source/httpx-example.rst +0 -22
  22. vws_python_mock-2026.2.22.2/src/mock_vws/__init__.py +0 -13
  23. vws_python_mock-2026.2.22.2/src/mock_vws/_respx_mock_server/decorators.py +0 -287
  24. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.git_archival.txt +0 -0
  25. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.gitattributes +0 -0
  26. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.github/dependabot.yml +0 -0
  27. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.github/workflows/dependabot-merge.yml +0 -0
  28. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.github/workflows/docker-build.yml +0 -0
  29. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.github/workflows/lint.yml +0 -0
  30. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.github/workflows/publish-site.yml +0 -0
  31. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.github/workflows/release.yml +0 -0
  32. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.github/workflows/test.yml +0 -0
  33. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.gitignore +0 -0
  34. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.pre-commit-config.yaml +0 -0
  35. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.prettierrc +0 -0
  36. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.vscode/extensions.json +0 -0
  37. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/.vscode/settings.json +0 -0
  38. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/CODE_OF_CONDUCT.rst +0 -0
  39. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/LICENSE +0 -0
  40. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/MANIFEST.in +0 -0
  41. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/admin/__init__.py +0 -0
  42. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/admin/vumark_template.svg +0 -0
  43. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/ci/__init__.py +0 -0
  44. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/ci/decrypt_secret.sh +0 -0
  45. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/ci/test_custom_linters.py +0 -0
  46. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/conftest.py +0 -0
  47. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docker-bake.hcl +0 -0
  48. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/__init__.py +0 -0
  49. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/changelog.rst +0 -0
  50. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/ci-setup.rst +0 -0
  51. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/conf.py +0 -0
  52. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/differences-to-vws.rst +0 -0
  53. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/docker.rst +0 -0
  54. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/installation.rst +0 -0
  55. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/release-process.rst +0 -0
  56. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/docs/source/versioning-and-api-stability.rst +0 -0
  57. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/secrets.tar.gpg +0 -0
  58. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/setup.cfg +0 -0
  59. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/spelling_private_dict.txt +0 -0
  60. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_base64_decoding.py +0 -0
  61. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_constants.py +0 -0
  62. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_database_matchers.py +0 -0
  63. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_flask_server/Dockerfile +0 -0
  64. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_flask_server/__init__.py +0 -0
  65. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_flask_server/healthcheck.py +0 -0
  66. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_flask_server/target_manager.py +0 -0
  67. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_flask_server/vwq.py +0 -0
  68. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_flask_server/vws.py +0 -0
  69. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_tools.py +0 -0
  70. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/__init__.py +0 -0
  71. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/accept_header_validators.py +0 -0
  72. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/auth_validators.py +0 -0
  73. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/content_length_validators.py +0 -0
  74. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/content_type_validators.py +0 -0
  75. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/date_validators.py +0 -0
  76. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/exceptions.py +0 -0
  77. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/fields_validators.py +0 -0
  78. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/image_validators.py +0 -0
  79. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/include_target_data_validators.py +0 -0
  80. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/num_results_validators.py +0 -0
  81. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_query_validators/project_state_validators.py +0 -0
  82. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_requests_mock_server/__init__.py +0 -0
  83. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_requests_mock_server/mock_web_query_api.py +0 -0
  84. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_requests_mock_server/mock_web_services_api.py +0 -0
  85. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_respx_mock_server/__init__.py +0 -0
  86. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/__init__.py +0 -0
  87. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/active_flag_validators.py +0 -0
  88. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/auth_validators.py +0 -0
  89. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/content_length_validators.py +0 -0
  90. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/content_type_validators.py +0 -0
  91. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/date_validators.py +0 -0
  92. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/exceptions.py +0 -0
  93. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/image_validators.py +0 -0
  94. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/json_validators.py +0 -0
  95. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/key_validators.py +0 -0
  96. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/metadata_validators.py +0 -0
  97. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/name_validators.py +0 -0
  98. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/project_state_validators.py +0 -0
  99. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/target_validators.py +0 -0
  100. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/_services_validators/width_validators.py +0 -0
  101. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/database.py +0 -0
  102. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/database_type.py +0 -0
  103. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/image_matchers.py +0 -0
  104. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/py.typed +0 -0
  105. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/states.py +0 -0
  106. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/target.py +0 -0
  107. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/target_manager.py +0 -0
  108. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/mock_vws/target_raters.py +0 -0
  109. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/vws_python_mock.egg-info/SOURCES.txt +0 -0
  110. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/vws_python_mock.egg-info/dependency_links.txt +0 -0
  111. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/vws_python_mock.egg-info/not-zip-safe +0 -0
  112. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/src/vws_python_mock.egg-info/top_level.txt +0 -0
  113. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/__init__.py +0 -0
  114. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/conftest.py +0 -0
  115. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/__init__.py +0 -0
  116. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/fixtures/__init__.py +0 -0
  117. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/fixtures/credentials.py +0 -0
  118. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/fixtures/prepared_requests.py +0 -0
  119. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/fixtures/vuforia_backends.py +0 -0
  120. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_add_target.py +0 -0
  121. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_authorization_header.py +0 -0
  122. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_content_length.py +0 -0
  123. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_database_summary.py +0 -0
  124. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_date_header.py +0 -0
  125. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_delete_target.py +0 -0
  126. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_docker.py +0 -0
  127. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_flask_app_usage.py +0 -0
  128. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_get_duplicates.py +0 -0
  129. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_get_target.py +0 -0
  130. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_invalid_given_id.py +0 -0
  131. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_invalid_json.py +0 -0
  132. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_query.py +0 -0
  133. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_target_list.py +0 -0
  134. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_target_raters.py +0 -0
  135. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_target_summary.py +0 -0
  136. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_target_validators.py +0 -0
  137. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_unexpected_json.py +0 -0
  138. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_update_target.py +0 -0
  139. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/test_vumark_generation_api.py +0 -0
  140. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/utils/__init__.py +0 -0
  141. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/utils/assertions.py +0 -0
  142. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/utils/retries.py +0 -0
  143. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/utils/too_many_requests.py +0 -0
  144. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/tests/mock_vws/utils/usage_test_helpers.py +0 -0
  145. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/vuforia_secrets.env.example +0 -0
  146. {vws_python_mock-2026.2.22.2 → vws_python_mock-2026.2.22.3}/zizmor.yml +0 -0
@@ -4,6 +4,13 @@ Changelog
4
4
  Next
5
5
  ----
6
6
 
7
+ 2026.02.22.3
8
+ ------------
9
+
10
+
11
+ - ``MockVWS`` now intercepts both ``requests`` (via ``responses``) and ``httpx`` (via ``respx``) simultaneously.
12
+ ``MockVWSForHttpx`` has been removed — ``MockVWS`` handles both HTTP libraries.
13
+
7
14
  2026.02.22.2
8
15
  ------------
9
16
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vws-python-mock
3
- Version: 2026.2.22.2
3
+ Version: 2026.2.22.3
4
4
  Summary: A mock for the Vuforia Web Services (VWS) API.
5
5
  Author-email: Adam Dangoor <adamdangoor@gmail.com>
6
6
  License-Expression: MIT
@@ -61,7 +61,6 @@ Requires-Dist: pyroma==5.0.1; extra == "dev"
61
61
  Requires-Dist: pytest==9.0.2; extra == "dev"
62
62
  Requires-Dist: pytest-retry==1.7.0; extra == "dev"
63
63
  Requires-Dist: pytest-xdist==3.8.0; extra == "dev"
64
- Requires-Dist: python-dotenv==1.2.1; extra == "dev"
65
64
  Requires-Dist: pyyaml==6.0.3; extra == "dev"
66
65
  Requires-Dist: requests-mock-flask==2026.2.16; extra == "dev"
67
66
  Requires-Dist: ruff==0.15.1; extra == "dev"
@@ -86,7 +85,7 @@ Requires-Dist: urllib3==2.6.3; extra == "dev"
86
85
  Requires-Dist: vulture==2.14; extra == "dev"
87
86
  Requires-Dist: vws-python==2026.2.21; extra == "dev"
88
87
  Requires-Dist: vws-test-fixtures==2023.3.5; extra == "dev"
89
- Requires-Dist: vws-web-tools==2026.2.20; extra == "dev"
88
+ Requires-Dist: vws-web-tools==2026.2.22; extra == "dev"
90
89
  Requires-Dist: yamlfix==1.19.1; extra == "dev"
91
90
  Requires-Dist: zizmor==1.22.0; extra == "dev"
92
91
  Provides-Extra: release
@@ -103,10 +102,10 @@ VWS Mock
103
102
 
104
103
  Mock for the Vuforia Web Services (VWS) API and the Vuforia Web Query API.
105
104
 
106
- Mocking calls made to Vuforia with Python ``requests``
107
- ------------------------------------------------------
105
+ Mocking calls made to Vuforia
106
+ ------------------------------
108
107
 
109
- Using the mock redirects requests to Vuforia made with `requests`_ to an in-memory implementation.
108
+ ``MockVWS`` intercepts requests made with `requests`_ or `httpx`_.
110
109
 
111
110
  .. code-block:: shell
112
111
 
@@ -129,25 +128,18 @@ This requires Python |minimum-python-version|\+.
129
128
  # This will use the Vuforia mock.
130
129
  requests.get(url="https://vws.vuforia.com/summary", timeout=30)
131
130
 
132
- By default, an exception will be raised if any requests to unmocked addresses are made.
133
-
134
- .. _requests: https://pypi.org/project/requests/
135
-
136
- Mocking calls made to Vuforia with Python ``httpx``
137
- ----------------------------------------------------
138
-
139
- Using the mock redirects requests to Vuforia made with `httpx`_ to an in-memory implementation.
131
+ ``MockVWS`` also intercepts `httpx`_ requests:
140
132
 
141
133
  .. code-block:: python
142
134
 
143
- """Make a request to the Vuforia Web Services API mock."""
135
+ """Make a request to the Vuforia Web Services API mock using httpx."""
144
136
 
145
137
  import httpx
146
138
 
147
- from mock_vws import MockVWSForHttpx
139
+ from mock_vws import MockVWS
148
140
  from mock_vws.database import CloudDatabase
149
141
 
150
- with MockVWSForHttpx() as mock:
142
+ with MockVWS() as mock:
151
143
  database = CloudDatabase()
152
144
  mock.add_cloud_database(cloud_database=database)
153
145
  # This will use the Vuforia mock.
@@ -155,6 +147,7 @@ Using the mock redirects requests to Vuforia made with `httpx`_ to an in-memory
155
147
 
156
148
  By default, an exception will be raised if any requests to unmocked addresses are made.
157
149
 
150
+ .. _requests: https://pypi.org/project/requests/
158
151
  .. _httpx: https://pypi.org/project/httpx/
159
152
 
160
153
  Using Docker to mock calls to Vuforia from any language
@@ -8,10 +8,10 @@ VWS Mock
8
8
 
9
9
  Mock for the Vuforia Web Services (VWS) API and the Vuforia Web Query API.
10
10
 
11
- Mocking calls made to Vuforia with Python ``requests``
12
- ------------------------------------------------------
11
+ Mocking calls made to Vuforia
12
+ ------------------------------
13
13
 
14
- Using the mock redirects requests to Vuforia made with `requests`_ to an in-memory implementation.
14
+ ``MockVWS`` intercepts requests made with `requests`_ or `httpx`_.
15
15
 
16
16
  .. code-block:: shell
17
17
 
@@ -34,25 +34,18 @@ This requires Python |minimum-python-version|\+.
34
34
  # This will use the Vuforia mock.
35
35
  requests.get(url="https://vws.vuforia.com/summary", timeout=30)
36
36
 
37
- By default, an exception will be raised if any requests to unmocked addresses are made.
38
-
39
- .. _requests: https://pypi.org/project/requests/
40
-
41
- Mocking calls made to Vuforia with Python ``httpx``
42
- ----------------------------------------------------
43
-
44
- Using the mock redirects requests to Vuforia made with `httpx`_ to an in-memory implementation.
37
+ ``MockVWS`` also intercepts `httpx`_ requests:
45
38
 
46
39
  .. code-block:: python
47
40
 
48
- """Make a request to the Vuforia Web Services API mock."""
41
+ """Make a request to the Vuforia Web Services API mock using httpx."""
49
42
 
50
43
  import httpx
51
44
 
52
- from mock_vws import MockVWSForHttpx
45
+ from mock_vws import MockVWS
53
46
  from mock_vws.database import CloudDatabase
54
47
 
55
- with MockVWSForHttpx() as mock:
48
+ with MockVWS() as mock:
56
49
  database = CloudDatabase()
57
50
  mock.add_cloud_database(cloud_database=database)
58
51
  # This will use the Vuforia mock.
@@ -60,6 +53,7 @@ Using the mock redirects requests to Vuforia made with `httpx`_ to an in-memory
60
53
 
61
54
  By default, an exception will be raised if any requests to unmocked addresses are made.
62
55
 
56
+ .. _requests: https://pypi.org/project/requests/
63
57
  .. _httpx: https://pypi.org/project/httpx/
64
58
 
65
59
  Using Docker to mock calls to Vuforia from any language
@@ -11,7 +11,6 @@ from pathlib import Path
11
11
  from typing import TYPE_CHECKING
12
12
 
13
13
  import vws_web_tools
14
- from dotenv import load_dotenv
15
14
  from selenium.common.exceptions import TimeoutException
16
15
 
17
16
  if TYPE_CHECKING:
@@ -123,6 +122,25 @@ def _create_and_get_vumark_target_id(
123
122
  )
124
123
 
125
124
 
125
+ def _fetch_inactive_database_details(
126
+ driver: "WebDriver",
127
+ email_address: str,
128
+ password: str,
129
+ database_name: str,
130
+ ) -> "DatabaseDict":
131
+ """Fetch details for an existing inactive database."""
132
+ vws_web_tools.log_in(
133
+ driver=driver,
134
+ email_address=email_address,
135
+ password=password,
136
+ )
137
+ vws_web_tools.wait_for_logged_in(driver=driver)
138
+ return vws_web_tools.get_database_details(
139
+ driver=driver,
140
+ database_name=database_name,
141
+ )
142
+
143
+
126
144
  def _create_vuforia_resource_names() -> tuple[str, str, str, str]:
127
145
  """Create names for Vuforia resources."""
128
146
  time = datetime.datetime.now(tz=datetime.UTC).strftime(
@@ -141,23 +159,18 @@ def main() -> None:
141
159
  email_address = os.environ["VWS_EMAIL_ADDRESS"]
142
160
  password = os.environ["VWS_PASSWORD"]
143
161
  new_secrets_dir = Path(os.environ["NEW_SECRETS_DIR"]).expanduser()
144
- existing_secrets_file = Path(
145
- os.environ["EXISTING_SECRETS_FILE"]
146
- ).expanduser()
147
- if not existing_secrets_file.exists():
148
- msg = f"Existing secrets file does not exist: {existing_secrets_file}"
149
- raise FileNotFoundError(msg)
150
- load_dotenv(dotenv_path=existing_secrets_file)
151
- inactive_database_details: DatabaseDict = {
152
- "database_name": os.environ[
153
- "INACTIVE_VUFORIA_TARGET_MANAGER_DATABASE_NAME"
154
- ],
155
- "server_access_key": os.environ["INACTIVE_VUFORIA_SERVER_ACCESS_KEY"],
156
- "server_secret_key": os.environ["INACTIVE_VUFORIA_SERVER_SECRET_KEY"],
157
- "client_access_key": os.environ["INACTIVE_VUFORIA_CLIENT_ACCESS_KEY"],
158
- "client_secret_key": os.environ["INACTIVE_VUFORIA_CLIENT_SECRET_KEY"],
159
- }
162
+ inactive_database_name = os.environ[
163
+ "INACTIVE_VUFORIA_TARGET_MANAGER_DATABASE_NAME"
164
+ ]
160
165
  new_secrets_dir.mkdir(exist_ok=True)
166
+ inactive_driver = vws_web_tools.create_chrome_driver()
167
+ inactive_database_details = _fetch_inactive_database_details(
168
+ driver=inactive_driver,
169
+ email_address=email_address,
170
+ password=password,
171
+ database_name=inactive_database_name,
172
+ )
173
+ inactive_driver.quit()
161
174
 
162
175
  num_databases = 100
163
176
  required_files = [
@@ -1,4 +1,4 @@
1
- Using the mock redirects requests to Vuforia made with `requests`_ to an in-memory implementation.
1
+ ``MockVWS`` intercepts requests to Vuforia made with `requests`_ or `httpx`_.
2
2
 
3
3
  .. code-block:: python
4
4
 
@@ -20,3 +20,4 @@ By default, an exception will be raised if any requests to unmocked addresses ar
20
20
  See :ref:`mock-api-reference` for details of what can be changed and how.
21
21
 
22
22
  .. _requests: https://pypi.org/project/requests/
23
+ .. _httpx: https://pypi.org/project/httpx/
@@ -102,7 +102,7 @@ To create databases without using the browser, use :file:`admin/create_secrets_f
102
102
  $ export VWS_EMAIL_ADDRESS=...
103
103
  $ export VWS_PASSWORD=...
104
104
  $ export NEW_SECRETS_DIR=...
105
- $ export EXISTING_SECRETS_FILE=/existing/file/with/inactive/db/creds
105
+ $ export INACTIVE_VUFORIA_TARGET_MANAGER_DATABASE_NAME=...
106
106
  # You may have to run this a few times, but it is idempotent.
107
107
  $ python admin/create_secrets_files.py
108
108
  # Each generated file gets its own Cloud database credentials and shares
@@ -0,0 +1,9 @@
1
+ Getting started
2
+ ---------------
3
+
4
+ Mocking calls made to Vuforia
5
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6
+
7
+ .. include:: basic-example.rst
8
+
9
+ .. include:: httpx-example.rst
@@ -0,0 +1,18 @@
1
+ ``MockVWS`` also intercepts requests made with `httpx`_.
2
+
3
+ .. code-block:: python
4
+
5
+ """Make a request to the Vuforia Web Services API mock using httpx."""
6
+
7
+ import httpx
8
+
9
+ from mock_vws import MockVWS
10
+ from mock_vws.database import CloudDatabase
11
+
12
+ with MockVWS() as mock:
13
+ database = CloudDatabase()
14
+ mock.add_cloud_database(cloud_database=database)
15
+ # This will use the Vuforia mock.
16
+ httpx.get(url="https://vws.vuforia.com/summary", timeout=30)
17
+
18
+ .. _httpx: https://pypi.org/project/httpx/
@@ -1,8 +1,8 @@
1
1
  |project|
2
2
  =========
3
3
 
4
- Mocking calls made to Vuforia with Python ``requests``
5
- ------------------------------------------------------
4
+ Mocking calls made to Vuforia
5
+ ------------------------------
6
6
 
7
7
  .. code-block:: console
8
8
 
@@ -12,9 +12,6 @@ This requires Python |minimum-python-version|\+.
12
12
 
13
13
  .. include:: basic-example.rst
14
14
 
15
- Mocking calls made to Vuforia with Python ``httpx``
16
- ----------------------------------------------------
17
-
18
15
  .. include:: httpx-example.rst
19
16
 
20
17
  Using Docker to mock calls to Vuforia from any language
@@ -7,10 +7,6 @@ API Reference
7
7
  :members:
8
8
  :undoc-members:
9
9
 
10
- .. autoclass:: mock_vws.MockVWSForHttpx
11
- :members:
12
- :undoc-members:
13
-
14
10
  .. autoclass:: mock_vws.MissingSchemeError
15
11
  :members:
16
12
  :undoc-members:
@@ -79,7 +79,6 @@ optional-dependencies.dev = [
79
79
  "pytest==9.0.2",
80
80
  "pytest-retry==1.7.0",
81
81
  "pytest-xdist==3.8.0",
82
- "python-dotenv==1.2.1",
83
82
  "pyyaml==6.0.3",
84
83
  "requests-mock-flask==2026.2.16",
85
84
  "ruff==0.15.1",
@@ -107,7 +106,7 @@ optional-dependencies.dev = [
107
106
  "vulture==2.14",
108
107
  "vws-python==2026.2.21",
109
108
  "vws-test-fixtures==2023.3.5",
110
- "vws-web-tools==2026.2.20",
109
+ "vws-web-tools==2026.2.22",
111
110
  "yamlfix==1.19.1",
112
111
  "zizmor==1.22.0",
113
112
  ]
@@ -453,6 +452,7 @@ ignore_names = [
453
452
  "model_config",
454
453
  # Used in TYPE_CHECKING for type hints
455
454
  "CloudDatabaseDict",
455
+ "DatabaseDict",
456
456
  "VuMarkDatabaseDict",
457
457
  "VuMarkTargetDict",
458
458
  ]
@@ -0,0 +1,9 @@
1
+ """Tools for using a fake implementation of Vuforia."""
2
+
3
+ from mock_vws._mock_common import MissingSchemeError
4
+ from mock_vws._requests_mock_server.decorators import MockVWS
5
+
6
+ __all__ = [
7
+ "MissingSchemeError",
8
+ "MockVWS",
9
+ ]
@@ -8,6 +8,29 @@ from typing import Any
8
8
  from beartype import beartype
9
9
 
10
10
 
11
+ @beartype
12
+ class MissingSchemeError(Exception):
13
+ """Raised when a URL is missing a schema."""
14
+
15
+ def __init__(self, url: str) -> None:
16
+ """
17
+ Args:
18
+ url: The URL which is missing a scheme.
19
+ """
20
+ super().__init__()
21
+ self.url = url
22
+
23
+ def __str__(self) -> str:
24
+ """
25
+ Give a string representation of this error with a
26
+ suggestion.
27
+ """
28
+ return (
29
+ f'Invalid URL "{self.url}": No scheme supplied. '
30
+ f'Perhaps you meant "https://{self.url}".'
31
+ )
32
+
33
+
11
34
  @beartype
12
35
  @dataclass(frozen=True)
13
36
  class RequestData:
@@ -4,7 +4,7 @@ import re
4
4
  import time
5
5
  from collections.abc import Callable, Mapping
6
6
  from contextlib import ContextDecorator
7
- from typing import Any, Literal, Self
7
+ from typing import TYPE_CHECKING, Any, Literal, Self
8
8
  from urllib.parse import urlparse
9
9
 
10
10
  import requests
@@ -12,7 +12,8 @@ from beartype import BeartypeConf, beartype
12
12
  from requests import PreparedRequest
13
13
  from responses import RequestsMock
14
14
 
15
- from mock_vws._mock_common import RequestData
15
+ from mock_vws._mock_common import MissingSchemeError, RequestData
16
+ from mock_vws._respx_mock_server.decorators import start_respx_router
16
17
  from mock_vws.database import CloudDatabase, VuMarkDatabase
17
18
  from mock_vws.image_matchers import (
18
19
  ImageMatcher,
@@ -27,6 +28,9 @@ from mock_vws.target_raters import (
27
28
  from .mock_web_query_api import MockVuforiaWebQueryAPI
28
29
  from .mock_web_services_api import MockVuforiaWebServicesAPI
29
30
 
31
+ if TYPE_CHECKING:
32
+ import respx
33
+
30
34
  _ResponseType = tuple[int, Mapping[str, str], str | bytes]
31
35
  _MockCallback = Callable[[RequestData], _ResponseType]
32
36
  _ResponsesCallback = Callable[[PreparedRequest], _ResponseType]
@@ -35,32 +39,12 @@ _STRUCTURAL_SIMILARITY_MATCHER = StructuralSimilarityMatcher()
35
39
  _BRISQUE_TRACKING_RATER = BrisqueTargetTrackingRater()
36
40
 
37
41
 
38
- @beartype
39
- class MissingSchemeError(Exception):
40
- """Raised when a URL is missing a schema."""
41
-
42
- def __init__(self, url: str) -> None:
43
- """
44
- Args:
45
- url: The URL which is missing a scheme.
46
- """
47
- super().__init__()
48
- self.url = url
49
-
50
- def __str__(self) -> str:
51
- """
52
- Give a string representation of this error with a
53
- suggestion.
54
- """
55
- return (
56
- f'Invalid URL "{self.url}": No scheme supplied. '
57
- f'Perhaps you meant "https://{self.url}".'
58
- )
59
-
60
-
61
42
  @beartype(conf=BeartypeConf(is_pep484_tower=True))
62
43
  class MockVWS(ContextDecorator):
63
- """Route requests to Vuforia's Web Service APIs to fakes of those APIs."""
44
+ """Route requests to Vuforia's Web Service APIs to fakes of those APIs.
45
+
46
+ Works with both ``requests`` and ``httpx``.
47
+ """
64
48
 
65
49
  def __init__(
66
50
  self,
@@ -78,6 +62,8 @@ class MockVWS(ContextDecorator):
78
62
  """Route requests to Vuforia's Web Service APIs to fakes of those
79
63
  APIs.
80
64
 
65
+ Works with both ``requests`` and ``httpx``.
66
+
81
67
  Args:
82
68
  real_http: Whether or not to forward requests to the real
83
69
  server if they are not handled by the mock.
@@ -108,6 +94,7 @@ class MockVWS(ContextDecorator):
108
94
  self._response_delay_seconds = response_delay_seconds
109
95
  self._sleep_fn = sleep_fn
110
96
  self._mock: RequestsMock
97
+ self._router: respx.MockRouter
111
98
  self._target_manager = TargetManager()
112
99
 
113
100
  self._base_vws_url = base_vws_url
@@ -252,6 +239,16 @@ class MockVWS(ContextDecorator):
252
239
  self._mock = mock
253
240
  self._mock.start()
254
241
 
242
+ self._router = start_respx_router(
243
+ mock_vws_api=self._mock_vws_api,
244
+ mock_vwq_api=self._mock_vwq_api,
245
+ base_vws_url=self._base_vws_url,
246
+ base_vwq_url=self._base_vwq_url,
247
+ response_delay_seconds=self._response_delay_seconds,
248
+ sleep_fn=self._sleep_fn,
249
+ real_http=self._real_http,
250
+ )
251
+
255
252
  return self
256
253
 
257
254
  def __exit__(self, *exc: object) -> Literal[False]:
@@ -265,4 +262,5 @@ class MockVWS(ContextDecorator):
265
262
  del exc
266
263
 
267
264
  self._mock.stop()
265
+ self._router.stop()
268
266
  return False
@@ -0,0 +1,183 @@
1
+ """Helpers for mocking Vuforia with httpx via respx."""
2
+
3
+ import re
4
+ from collections.abc import Callable, Mapping
5
+ from typing import Protocol
6
+ from urllib.parse import urlparse
7
+
8
+ import httpx
9
+ import respx
10
+
11
+ from mock_vws._mock_common import RequestData, Route
12
+
13
+ _ResponseType = tuple[int, Mapping[str, str], str | bytes]
14
+
15
+
16
+ class _APIHandler(Protocol):
17
+ """An API handler with mock routes."""
18
+
19
+ routes: set[Route]
20
+
21
+
22
+ def _to_request_data(
23
+ request: httpx.Request,
24
+ *,
25
+ base_path: str,
26
+ ) -> RequestData:
27
+ """Convert an httpx.Request to a RequestData.
28
+
29
+ Args:
30
+ request: The httpx request to convert.
31
+ base_path: The base path prefix to strip from the request path.
32
+
33
+ Returns:
34
+ A RequestData with method, path, headers, and body set.
35
+ """
36
+ path = request.url.raw_path.decode(encoding="ascii")
37
+ if base_path and path.startswith(base_path):
38
+ path = path[len(base_path) :]
39
+ return RequestData(
40
+ method=request.method,
41
+ path=path,
42
+ headers={k.title(): v for k, v in request.headers.items()},
43
+ body=request.content,
44
+ )
45
+
46
+
47
+ def _block_unmatched(request: httpx.Request) -> httpx.Response:
48
+ """Raise ConnectError for unmatched requests when real_http=False.
49
+
50
+ Args:
51
+ request: The unmatched httpx request.
52
+
53
+ Raises:
54
+ Exception: A connection error is always raised to block
55
+ unmatched requests.
56
+ """
57
+ raise httpx.ConnectError(
58
+ message="Connection refused by mock",
59
+ request=request,
60
+ )
61
+
62
+
63
+ def _make_respx_callback(
64
+ *,
65
+ handler: Callable[[RequestData], _ResponseType],
66
+ base_path: str,
67
+ delay_seconds: float,
68
+ sleep_fn: Callable[[float], None],
69
+ ) -> Callable[[httpx.Request], httpx.Response]:
70
+ """Create a respx-compatible callback from a handler.
71
+
72
+ Args:
73
+ handler: A handler that takes a RequestData and returns a
74
+ response tuple.
75
+ base_path: The base path prefix to strip from the request path.
76
+ delay_seconds: The number of seconds to delay the response by.
77
+ sleep_fn: The function to use for sleeping during delays.
78
+
79
+ Returns:
80
+ A callback that takes an httpx.Request and returns an
81
+ httpx.Response.
82
+ """
83
+
84
+ def callback(request: httpx.Request) -> httpx.Response:
85
+ """Handle an httpx request by converting it and calling the
86
+ handler.
87
+
88
+ Args:
89
+ request: The httpx request to handle.
90
+
91
+ Returns:
92
+ An httpx.Response built from the handler's return value.
93
+
94
+ Raises:
95
+ Exception: A timeout error is raised when the response
96
+ delay exceeds the read timeout.
97
+ """
98
+ request_data = _to_request_data(
99
+ request=request,
100
+ base_path=base_path,
101
+ )
102
+ timeout_info: dict[str, float | None] = request.extensions.get(
103
+ "timeout", {}
104
+ )
105
+ read_timeout = timeout_info.get("read")
106
+ if read_timeout is not None and delay_seconds > read_timeout:
107
+ sleep_fn(read_timeout)
108
+ raise httpx.ReadTimeout(
109
+ message="Response delay exceeded read timeout",
110
+ request=request,
111
+ )
112
+ status_code, headers, body = handler(request_data)
113
+ sleep_fn(delay_seconds)
114
+ if isinstance(body, str):
115
+ body = body.encode()
116
+ return httpx.Response(
117
+ status_code=status_code,
118
+ headers=headers,
119
+ content=body,
120
+ )
121
+
122
+ return callback
123
+
124
+
125
+ def start_respx_router(
126
+ *,
127
+ mock_vws_api: _APIHandler,
128
+ mock_vwq_api: _APIHandler,
129
+ base_vws_url: str,
130
+ base_vwq_url: str,
131
+ response_delay_seconds: float,
132
+ sleep_fn: Callable[[float], None],
133
+ real_http: bool,
134
+ ) -> respx.MockRouter:
135
+ """Configure and start a respx router with Vuforia routes.
136
+
137
+ Args:
138
+ mock_vws_api: The VWS API handler.
139
+ mock_vwq_api: The VWQ API handler.
140
+ base_vws_url: The base URL for the VWS API.
141
+ base_vwq_url: The base URL for the VWQ API.
142
+ response_delay_seconds: The number of seconds to delay responses.
143
+ sleep_fn: The function to use for sleeping during delays.
144
+ real_http: Whether to pass through unmatched requests.
145
+
146
+ Returns:
147
+ A started respx router.
148
+ """
149
+ router = respx.MockRouter(
150
+ assert_all_called=False,
151
+ assert_all_mocked=False,
152
+ )
153
+
154
+ for api, base_url in (
155
+ (mock_vws_api, base_vws_url),
156
+ (mock_vwq_api, base_vwq_url),
157
+ ):
158
+ base_path = urlparse(url=base_url).path.rstrip("/")
159
+ for route in api.routes:
160
+ url_pattern = base_url.rstrip("/") + route.path_pattern + "$"
161
+ compiled_url_pattern = re.compile(pattern=url_pattern)
162
+
163
+ for http_method in route.http_methods:
164
+ original_callback = getattr(api, route.route_name)
165
+ router.route(
166
+ method=http_method,
167
+ url=compiled_url_pattern,
168
+ ).mock(
169
+ side_effect=_make_respx_callback(
170
+ handler=original_callback,
171
+ base_path=base_path,
172
+ delay_seconds=response_delay_seconds,
173
+ sleep_fn=sleep_fn,
174
+ ),
175
+ )
176
+
177
+ if real_http:
178
+ router.route().pass_through()
179
+ else:
180
+ router.route().mock(side_effect=_block_unmatched)
181
+
182
+ router.start()
183
+ return router