vws-python-mock 2026.2.22.1__tar.gz → 2026.2.22.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 (142) hide show
  1. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/CHANGELOG.rst +4 -0
  2. {vws_python_mock-2026.2.22.1/src/vws_python_mock.egg-info → vws_python_mock-2026.2.22.2}/PKG-INFO +1 -1
  3. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_requests_mock_server/decorators.py +8 -1
  4. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_respx_mock_server/decorators.py +18 -3
  5. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2/src/vws_python_mock.egg-info}/PKG-INFO +1 -1
  6. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_requests_mock_usage.py +38 -1
  7. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_respx_mock_usage.py +36 -0
  8. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.git_archival.txt +0 -0
  9. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.gitattributes +0 -0
  10. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.github/dependabot.yml +0 -0
  11. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.github/workflows/dependabot-merge.yml +0 -0
  12. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.github/workflows/docker-build.yml +0 -0
  13. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.github/workflows/lint.yml +0 -0
  14. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.github/workflows/publish-site.yml +0 -0
  15. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.github/workflows/release.yml +0 -0
  16. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.github/workflows/test.yml +0 -0
  17. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.gitignore +0 -0
  18. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.pre-commit-config.yaml +0 -0
  19. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.prettierrc +0 -0
  20. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.vscode/extensions.json +0 -0
  21. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/.vscode/settings.json +0 -0
  22. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/CODE_OF_CONDUCT.rst +0 -0
  23. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/LICENSE +0 -0
  24. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/MANIFEST.in +0 -0
  25. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/README.rst +0 -0
  26. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/admin/__init__.py +0 -0
  27. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/admin/create_secrets_files.py +0 -0
  28. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/admin/vumark_template.svg +0 -0
  29. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/ci/__init__.py +0 -0
  30. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/ci/decrypt_secret.sh +0 -0
  31. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/ci/test_custom_linters.py +0 -0
  32. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/conftest.py +0 -0
  33. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docker-bake.hcl +0 -0
  34. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/__init__.py +0 -0
  35. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/basic-example.rst +0 -0
  36. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/changelog.rst +0 -0
  37. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/ci-setup.rst +0 -0
  38. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/conf.py +0 -0
  39. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/contributing.rst +0 -0
  40. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/differences-to-vws.rst +0 -0
  41. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/docker.rst +0 -0
  42. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/getting-started.rst +0 -0
  43. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/httpx-example.rst +0 -0
  44. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/index.rst +0 -0
  45. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/installation.rst +0 -0
  46. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/mock-api-reference.rst +0 -0
  47. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/release-process.rst +0 -0
  48. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/docs/source/versioning-and-api-stability.rst +0 -0
  49. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/pyproject.toml +0 -0
  50. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/secrets.tar.gpg +0 -0
  51. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/setup.cfg +0 -0
  52. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/spelling_private_dict.txt +0 -0
  53. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/__init__.py +0 -0
  54. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_base64_decoding.py +0 -0
  55. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_constants.py +0 -0
  56. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_database_matchers.py +0 -0
  57. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_flask_server/Dockerfile +0 -0
  58. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_flask_server/__init__.py +0 -0
  59. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_flask_server/healthcheck.py +0 -0
  60. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_flask_server/target_manager.py +0 -0
  61. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_flask_server/vwq.py +0 -0
  62. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_flask_server/vws.py +0 -0
  63. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_mock_common.py +0 -0
  64. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_tools.py +0 -0
  65. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/__init__.py +0 -0
  66. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/accept_header_validators.py +0 -0
  67. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/auth_validators.py +0 -0
  68. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/content_length_validators.py +0 -0
  69. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/content_type_validators.py +0 -0
  70. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/date_validators.py +0 -0
  71. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/exceptions.py +0 -0
  72. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/fields_validators.py +0 -0
  73. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/image_validators.py +0 -0
  74. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/include_target_data_validators.py +0 -0
  75. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/num_results_validators.py +0 -0
  76. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_query_validators/project_state_validators.py +0 -0
  77. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_requests_mock_server/__init__.py +0 -0
  78. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_requests_mock_server/mock_web_query_api.py +0 -0
  79. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_requests_mock_server/mock_web_services_api.py +0 -0
  80. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_respx_mock_server/__init__.py +0 -0
  81. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/__init__.py +0 -0
  82. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/active_flag_validators.py +0 -0
  83. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/auth_validators.py +0 -0
  84. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/content_length_validators.py +0 -0
  85. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/content_type_validators.py +0 -0
  86. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/date_validators.py +0 -0
  87. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/exceptions.py +0 -0
  88. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/image_validators.py +0 -0
  89. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/json_validators.py +0 -0
  90. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/key_validators.py +0 -0
  91. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/metadata_validators.py +0 -0
  92. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/name_validators.py +0 -0
  93. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/project_state_validators.py +0 -0
  94. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/target_validators.py +0 -0
  95. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/_services_validators/width_validators.py +0 -0
  96. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/database.py +0 -0
  97. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/database_type.py +0 -0
  98. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/image_matchers.py +0 -0
  99. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/py.typed +0 -0
  100. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/states.py +0 -0
  101. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/target.py +0 -0
  102. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/target_manager.py +0 -0
  103. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/mock_vws/target_raters.py +0 -0
  104. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/vws_python_mock.egg-info/SOURCES.txt +0 -0
  105. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/vws_python_mock.egg-info/dependency_links.txt +0 -0
  106. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/vws_python_mock.egg-info/not-zip-safe +0 -0
  107. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/vws_python_mock.egg-info/requires.txt +0 -0
  108. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/src/vws_python_mock.egg-info/top_level.txt +0 -0
  109. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/__init__.py +0 -0
  110. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/conftest.py +0 -0
  111. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/__init__.py +0 -0
  112. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/fixtures/__init__.py +0 -0
  113. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/fixtures/credentials.py +0 -0
  114. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/fixtures/prepared_requests.py +0 -0
  115. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/fixtures/vuforia_backends.py +0 -0
  116. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_add_target.py +0 -0
  117. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_authorization_header.py +0 -0
  118. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_content_length.py +0 -0
  119. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_database_summary.py +0 -0
  120. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_date_header.py +0 -0
  121. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_delete_target.py +0 -0
  122. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_docker.py +0 -0
  123. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_flask_app_usage.py +0 -0
  124. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_get_duplicates.py +0 -0
  125. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_get_target.py +0 -0
  126. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_invalid_given_id.py +0 -0
  127. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_invalid_json.py +0 -0
  128. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_query.py +0 -0
  129. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_target_list.py +0 -0
  130. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_target_raters.py +0 -0
  131. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_target_summary.py +0 -0
  132. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_target_validators.py +0 -0
  133. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_unexpected_json.py +0 -0
  134. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_update_target.py +0 -0
  135. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/test_vumark_generation_api.py +0 -0
  136. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/utils/__init__.py +0 -0
  137. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/utils/assertions.py +0 -0
  138. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/utils/retries.py +0 -0
  139. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/utils/too_many_requests.py +0 -0
  140. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/tests/mock_vws/utils/usage_test_helpers.py +0 -0
  141. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/vuforia_secrets.env.example +0 -0
  142. {vws_python_mock-2026.2.22.1 → vws_python_mock-2026.2.22.2}/zizmor.yml +0 -0
@@ -4,6 +4,10 @@ Changelog
4
4
  Next
5
5
  ----
6
6
 
7
+ 2026.02.22.2
8
+ ------------
9
+
10
+
7
11
  2026.02.22.1
8
12
  ------------
9
13
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vws-python-mock
3
- Version: 2026.2.22.1
3
+ Version: 2026.2.22.2
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
@@ -162,6 +162,7 @@ class MockVWS(ContextDecorator):
162
162
  callback: _MockCallback,
163
163
  delay_seconds: float,
164
164
  sleep_fn: Callable[[float], None],
165
+ base_path: str,
165
166
  ) -> _ResponsesCallback:
166
167
  """Wrap a callback to add a response delay."""
167
168
 
@@ -197,9 +198,13 @@ class MockVWS(ContextDecorator):
197
198
  else:
198
199
  body_bytes = raw_body
199
200
 
201
+ path = request.path_url
202
+ if base_path and path.startswith(base_path):
203
+ path = path[len(base_path) :]
204
+
200
205
  request_data = RequestData(
201
206
  method=request.method or "",
202
- path=request.path_url,
207
+ path=path,
203
208
  headers=dict(request.headers),
204
209
  body=body_bytes,
205
210
  )
@@ -221,6 +226,7 @@ class MockVWS(ContextDecorator):
221
226
  (self._mock_vws_api, self._base_vws_url),
222
227
  (self._mock_vwq_api, self._base_vwq_url),
223
228
  ):
229
+ base_path = urlparse(url=base_url).path.rstrip("/")
224
230
  for route in api.routes:
225
231
  url_pattern = base_url.rstrip("/") + route.path_pattern + "$"
226
232
  compiled_url_pattern = re.compile(pattern=url_pattern)
@@ -234,6 +240,7 @@ class MockVWS(ContextDecorator):
234
240
  callback=original_callback,
235
241
  delay_seconds=self._response_delay_seconds,
236
242
  sleep_fn=self._sleep_fn,
243
+ base_path=base_path,
237
244
  ),
238
245
  content_type=None,
239
246
  )
@@ -36,18 +36,26 @@ _STRUCTURAL_SIMILARITY_MATCHER = StructuralSimilarityMatcher()
36
36
  _BRISQUE_TRACKING_RATER = BrisqueTargetTrackingRater()
37
37
 
38
38
 
39
- def _to_request_data(request: httpx.Request) -> RequestData:
39
+ def _to_request_data(
40
+ request: httpx.Request,
41
+ *,
42
+ base_path: str,
43
+ ) -> RequestData:
40
44
  """Convert an httpx.Request to a RequestData.
41
45
 
42
46
  Args:
43
47
  request: The httpx request to convert.
48
+ base_path: The base path prefix to strip from the request path.
44
49
 
45
50
  Returns:
46
51
  A RequestData with method, path, headers, and body set.
47
52
  """
53
+ path = request.url.raw_path.decode(encoding="ascii")
54
+ if base_path and path.startswith(base_path):
55
+ path = path[len(base_path) :]
48
56
  return RequestData(
49
57
  method=request.method,
50
- path=request.url.raw_path.decode(encoding="ascii"),
58
+ path=path,
51
59
  headers={k.title(): v for k, v in request.headers.items()},
52
60
  body=request.content,
53
61
  )
@@ -155,12 +163,14 @@ class MockVWSForHttpx(ContextDecorator):
155
163
  def _make_callback(
156
164
  self,
157
165
  handler: Callable[[RequestData], _ResponseType],
166
+ base_path: str,
158
167
  ) -> Callable[[httpx.Request], httpx.Response]:
159
168
  """Create a respx-compatible callback from a handler.
160
169
 
161
170
  Args:
162
171
  handler: A handler that takes a RequestData and returns a
163
172
  response tuple.
173
+ base_path: The base path prefix to strip from the request path.
164
174
 
165
175
  Returns:
166
176
  A callback that takes an httpx.Request and returns an
@@ -183,7 +193,10 @@ class MockVWSForHttpx(ContextDecorator):
183
193
  Exception: A timeout error is raised when the response
184
194
  delay exceeds the read timeout.
185
195
  """
186
- request_data = _to_request_data(request=request)
196
+ request_data = _to_request_data(
197
+ request=request,
198
+ base_path=base_path,
199
+ )
187
200
  timeout_info: dict[str, float | None] = request.extensions.get(
188
201
  "timeout", {}
189
202
  )
@@ -237,6 +250,7 @@ class MockVWSForHttpx(ContextDecorator):
237
250
  (self._mock_vws_api, self._base_vws_url),
238
251
  (self._mock_vwq_api, self._base_vwq_url),
239
252
  ):
253
+ base_path = urlparse(url=base_url).path.rstrip("/")
240
254
  for route in api.routes:
241
255
  url_pattern = base_url.rstrip("/") + route.path_pattern + "$"
242
256
  compiled_url_pattern = re.compile(pattern=url_pattern)
@@ -249,6 +263,7 @@ class MockVWSForHttpx(ContextDecorator):
249
263
  ).mock(
250
264
  side_effect=self._make_callback(
251
265
  handler=original_callback,
266
+ base_path=base_path,
252
267
  ),
253
268
  )
254
269
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vws-python-mock
3
- Version: 2026.2.22.1
3
+ Version: 2026.2.22.2
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
@@ -5,6 +5,7 @@ import email.utils
5
5
  import io
6
6
  import json
7
7
  import socket
8
+ from http import HTTPStatus
8
9
  from urllib.parse import urlparse
9
10
 
10
11
  import pytest
@@ -13,7 +14,7 @@ from beartype import beartype
13
14
  from freezegun import freeze_time
14
15
  from PIL import Image
15
16
  from vws import VWS, CloudRecoService
16
- from vws_auth_tools import rfc_1123_date
17
+ from vws_auth_tools import authorization_header, rfc_1123_date
17
18
 
18
19
  from mock_vws import MissingSchemeError, MockVWS
19
20
  from mock_vws.database import CloudDatabase, VuMarkDatabase
@@ -391,6 +392,42 @@ class TestCustomBaseURLs:
391
392
  timeout=30,
392
393
  )
393
394
 
395
+ @staticmethod
396
+ def test_vws_operations_work_with_path_prefix() -> None:
397
+ """VWS API operations work correctly with a base URL path
398
+ prefix.
399
+ """
400
+ database = CloudDatabase()
401
+ base_vws_url = "https://vuforia.vws.example.com/prefix"
402
+
403
+ with MockVWS(base_vws_url=base_vws_url) as mock:
404
+ mock.add_cloud_database(cloud_database=database)
405
+
406
+ request_path = "/targets"
407
+ date = rfc_1123_date()
408
+ auth = authorization_header(
409
+ access_key=database.server_access_key,
410
+ secret_key=database.server_secret_key,
411
+ method="GET",
412
+ content=b"",
413
+ content_type="",
414
+ date=date,
415
+ request_path=request_path,
416
+ )
417
+ response = requests.get(
418
+ url=base_vws_url + request_path,
419
+ headers={
420
+ "Authorization": auth,
421
+ "Date": date,
422
+ },
423
+ timeout=30,
424
+ )
425
+
426
+ assert response.status_code == HTTPStatus.OK
427
+ response_json = response.json()
428
+ assert response_json["result_code"] == "Success"
429
+ assert response_json["results"] == []
430
+
394
431
  @staticmethod
395
432
  def test_no_scheme() -> None:
396
433
  """An error if raised if a URL is given with no scheme."""
@@ -252,6 +252,42 @@ class TestCustomBaseURLs:
252
252
  timeout=30,
253
253
  )
254
254
 
255
+ @staticmethod
256
+ def test_vws_operations_work_with_path_prefix() -> None:
257
+ """VWS API operations work correctly with a base URL path
258
+ prefix.
259
+ """
260
+ database = CloudDatabase()
261
+ base_vws_url = "https://vuforia.vws.example.com/prefix"
262
+
263
+ with MockVWSForHttpx(base_vws_url=base_vws_url) as mock:
264
+ mock.add_cloud_database(cloud_database=database)
265
+
266
+ request_path = "/targets"
267
+ date = rfc_1123_date()
268
+ auth = authorization_header(
269
+ access_key=database.server_access_key,
270
+ secret_key=database.server_secret_key,
271
+ method="GET",
272
+ content=b"",
273
+ content_type="",
274
+ date=date,
275
+ request_path=request_path,
276
+ )
277
+ response = httpx.get(
278
+ url=base_vws_url + request_path,
279
+ headers={
280
+ "Authorization": auth,
281
+ "Date": date,
282
+ },
283
+ timeout=30,
284
+ )
285
+
286
+ assert response.status_code == HTTPStatus.OK
287
+ response_json = response.json()
288
+ assert response_json["result_code"] == "Success"
289
+ assert response_json["results"] == []
290
+
255
291
  @staticmethod
256
292
  def test_no_scheme() -> None:
257
293
  """An error is raised if a URL is given with no scheme."""