vws-python-mock 2026.4.26__tar.gz → 2026.8.4__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 (153) hide show
  1. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.github/workflows/docker-build.yml +3 -3
  2. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.github/workflows/lint.yml +5 -2
  3. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.github/workflows/release.yml +26 -34
  4. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.github/workflows/test.yml +34 -13
  5. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.pre-commit-config.yaml +14 -2
  6. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/CHANGELOG.rst +22 -2
  7. {vws_python_mock-2026.4.26/src/vws_python_mock.egg-info → vws_python_mock-2026.8.4}/PKG-INFO +30 -26
  8. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/README.rst +1 -1
  9. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/admin/create_secrets_files.py +35 -1
  10. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/conftest.py +2 -2
  11. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/conf.py +9 -0
  12. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/differences-to-vws.rst +38 -4
  13. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/index.rst +1 -0
  14. vws_python_mock-2026.8.4/docs/source/unreleased.rst +8 -0
  15. vws_python_mock-2026.8.4/docs/towncrier_template.rst.jinja +14 -0
  16. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/pyproject.toml +224 -171
  17. vws_python_mock-2026.8.4/secrets.tar.gpg +0 -0
  18. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/spelling_private_dict.txt +1 -0
  19. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_constants.py +6 -2
  20. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_flask_server/target_manager.py +32 -9
  21. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_flask_server/vwq.py +6 -5
  22. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_flask_server/vws.py +200 -6
  23. vws_python_mock-2026.8.4/src/mock_vws/_model_target_web_api.py +486 -0
  24. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_requests_mock_server/decorators.py +9 -2
  25. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_requests_mock_server/mock_web_services_api.py +162 -0
  26. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_respx_mock_server/decorators.py +4 -1
  27. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/__init__.py +16 -0
  28. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/exceptions.py +134 -0
  29. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/project_state_validators.py +13 -1
  30. vws_python_mock-2026.8.4/src/mock_vws/_services_validators/request_quota_validators.py +42 -0
  31. vws_python_mock-2026.8.4/src/mock_vws/_services_validators/target_quota_validators.py +41 -0
  32. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/database.py +11 -1
  33. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/image_matchers.py +2 -12
  34. vws_python_mock-2026.8.4/src/mock_vws/model_target.py +79 -0
  35. vws_python_mock-2026.8.4/src/mock_vws/py.typed +0 -0
  36. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/states.py +4 -0
  37. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/target_manager.py +20 -0
  38. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/target_raters.py +1 -6
  39. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4/src/vws_python_mock.egg-info}/PKG-INFO +30 -26
  40. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/vws_python_mock.egg-info/SOURCES.txt +10 -0
  41. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/vws_python_mock.egg-info/requires.txt +28 -24
  42. vws_python_mock-2026.8.4/src/vws_python_mock.egg-info/scm_file_list.json +146 -0
  43. vws_python_mock-2026.8.4/src/vws_python_mock.egg-info/scm_version.json +8 -0
  44. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/fixtures/credentials.py +35 -0
  45. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/fixtures/prepared_requests.py +2 -2
  46. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/fixtures/vuforia_backends.py +79 -2
  47. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_authorization_header.py +1 -1
  48. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_date_header.py +1 -1
  49. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_docker.py +36 -11
  50. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_flask_app_usage.py +129 -1
  51. vws_python_mock-2026.8.4/tests/mock_vws/test_model_target_web_api.py +657 -0
  52. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_requests_mock_usage.py +299 -1
  53. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_respx_mock_usage.py +48 -0
  54. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/utils/retries.py +7 -3
  55. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/vuforia_secrets.env.example +4 -0
  56. vws_python_mock-2026.4.26/secrets.tar.gpg +0 -0
  57. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.git_archival.txt +0 -0
  58. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.gitattributes +0 -0
  59. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.github/dependabot.yml +0 -0
  60. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.github/workflows/dependabot-merge.yml +0 -0
  61. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.github/workflows/publish-site.yml +0 -0
  62. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.gitignore +0 -0
  63. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.prettierrc +0 -0
  64. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.vscode/extensions.json +0 -0
  65. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/.vscode/settings.json +0 -0
  66. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/CODE_OF_CONDUCT.rst +0 -0
  67. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/LICENSE +0 -0
  68. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/MANIFEST.in +0 -0
  69. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/admin/__init__.py +0 -0
  70. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/admin/vumark_template.svg +0 -0
  71. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/ci/__init__.py +0 -0
  72. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/ci/decrypt_secret.sh +0 -0
  73. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/ci/test_custom_linters.py +0 -0
  74. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docker-bake.hcl +0 -0
  75. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/__init__.py +0 -0
  76. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/basic-example.rst +0 -0
  77. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/changelog.rst +0 -0
  78. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/ci-setup.rst +0 -0
  79. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/contributing.rst +0 -0
  80. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/docker.rst +0 -0
  81. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/getting-started.rst +0 -0
  82. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/httpx-example.rst +0 -0
  83. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/installation.rst +0 -0
  84. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/mock-api-reference.rst +0 -0
  85. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/release-process.rst +0 -0
  86. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/docs/source/versioning-and-api-stability.rst +0 -0
  87. /vws_python_mock-2026.4.26/src/mock_vws/py.typed → /vws_python_mock-2026.8.4/newsfragments/.gitkeep +0 -0
  88. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/setup.cfg +0 -0
  89. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/__init__.py +0 -0
  90. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_base64_decoding.py +0 -0
  91. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_database_matchers.py +0 -0
  92. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_flask_server/Dockerfile +0 -0
  93. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_flask_server/__init__.py +0 -0
  94. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_flask_server/healthcheck.py +0 -0
  95. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_mock_common.py +0 -0
  96. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_tools.py +0 -0
  97. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/__init__.py +0 -0
  98. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/accept_header_validators.py +0 -0
  99. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/auth_validators.py +0 -0
  100. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/content_length_validators.py +0 -0
  101. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/content_type_validators.py +0 -0
  102. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/date_validators.py +0 -0
  103. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/exceptions.py +0 -0
  104. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/fields_validators.py +0 -0
  105. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/image_validators.py +0 -0
  106. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/include_target_data_validators.py +0 -0
  107. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/num_results_validators.py +0 -0
  108. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_query_validators/project_state_validators.py +0 -0
  109. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_requests_mock_server/__init__.py +0 -0
  110. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_requests_mock_server/mock_web_query_api.py +0 -0
  111. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_respx_mock_server/__init__.py +0 -0
  112. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/active_flag_validators.py +0 -0
  113. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/auth_validators.py +0 -0
  114. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/content_length_validators.py +0 -0
  115. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/content_type_validators.py +0 -0
  116. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/date_validators.py +0 -0
  117. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/image_validators.py +0 -0
  118. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/json_validators.py +0 -0
  119. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/key_validators.py +0 -0
  120. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/metadata_validators.py +0 -0
  121. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/name_validators.py +0 -0
  122. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/target_validators.py +0 -0
  123. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/_services_validators/width_validators.py +0 -0
  124. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/database_type.py +0 -0
  125. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/mock_vws/target.py +0 -0
  126. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/vws_python_mock.egg-info/dependency_links.txt +0 -0
  127. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/vws_python_mock.egg-info/not-zip-safe +0 -0
  128. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/src/vws_python_mock.egg-info/top_level.txt +0 -0
  129. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/__init__.py +0 -0
  130. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/conftest.py +0 -0
  131. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/__init__.py +0 -0
  132. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/fixtures/__init__.py +0 -0
  133. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_add_target.py +0 -0
  134. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_content_length.py +0 -0
  135. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_database_summary.py +0 -0
  136. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_delete_target.py +0 -0
  137. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_get_duplicates.py +0 -0
  138. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_get_target.py +0 -0
  139. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_invalid_given_id.py +0 -0
  140. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_invalid_json.py +0 -0
  141. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_query.py +0 -0
  142. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_target_list.py +0 -0
  143. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_target_raters.py +0 -0
  144. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_target_summary.py +0 -0
  145. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_target_validators.py +0 -0
  146. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_unexpected_json.py +0 -0
  147. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_update_target.py +0 -0
  148. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/test_vumark_generation_api.py +0 -0
  149. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/utils/__init__.py +0 -0
  150. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/utils/assertions.py +0 -0
  151. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/utils/too_many_requests.py +0 -0
  152. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/tests/mock_vws/utils/usage_test_helpers.py +0 -0
  153. {vws_python_mock-2026.4.26 → vws_python_mock-2026.8.4}/zizmor.yml +0 -0
@@ -24,7 +24,7 @@ jobs:
24
24
  runs-on: ubuntu-latest
25
25
 
26
26
  steps:
27
- - uses: actions/checkout@v6
27
+ - uses: actions/checkout@v7
28
28
  with:
29
29
  persist-credentials: false
30
30
 
@@ -35,11 +35,11 @@ jobs:
35
35
  uses: docker/setup-buildx-action@v4
36
36
 
37
37
  - name: Check Docker bake definition
38
- uses: docker/bake-action@v7.1.0
38
+ uses: docker/bake-action@v7.3.0
39
39
  with:
40
40
  call: check
41
41
 
42
42
  - name: Build Docker images
43
- uses: docker/bake-action@v7.1.0
43
+ uses: docker/bake-action@v7.3.0
44
44
  with:
45
45
  push: false
@@ -25,12 +25,12 @@ jobs:
25
25
  runs-on: ${{ matrix.platform }}
26
26
 
27
27
  steps:
28
- - uses: actions/checkout@v6
28
+ - uses: actions/checkout@v7
29
29
  with:
30
30
  persist-credentials: false
31
31
 
32
32
  - name: Install uv
33
- uses: astral-sh/setup-uv@v7
33
+ uses: astral-sh/setup-uv@v9.0.0
34
34
  with:
35
35
  enable-cache: true
36
36
  cache-dependency-glob: '**/pyproject.toml'
@@ -42,6 +42,9 @@ jobs:
42
42
  run: uv run --extra=dev prek run --all-files --hook-stage ${{ matrix.hook-stage }}
43
43
  --verbose
44
44
  env:
45
+ # Avoid intermittent uv distribution cache rename failures while
46
+ # prek installs hook environments on Windows.
47
+ UV_NO_CACHE: '1'
45
48
  UV_PYTHON: ${{ matrix.python-version }}
46
49
 
47
50
  - uses: pre-commit-ci/lite-action@v1.1.0
@@ -18,7 +18,7 @@ jobs:
18
18
  tag: ${{ steps.tag_version.outputs.new_tag }}
19
19
 
20
20
  steps:
21
- - uses: actions/checkout@v6
21
+ - uses: actions/checkout@v7
22
22
  with:
23
23
  # Fetch all history including tags.
24
24
  # Needed to find the latest tag.
@@ -34,6 +34,12 @@ jobs:
34
34
  # The default GITHUB_TOKEN cannot bypass rulesets.
35
35
  token: ${{ secrets.RELEASE_PAT }}
36
36
 
37
+ - name: Install uv
38
+ uses: astral-sh/setup-uv@v9.0.0
39
+ with:
40
+ enable-cache: true
41
+ cache-dependency-glob: '**/pyproject.toml'
42
+
37
43
  - name: Calver calculate version
38
44
  uses: StephaneBour/actions-calver@master
39
45
  id: calver
@@ -43,41 +49,27 @@ jobs:
43
49
  env:
44
50
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45
51
 
46
- - name: Get the changelog underline
47
- id: changelog_underline
52
+ # towncrier writes the rendered notes to stdout (informational
53
+ # chatter goes to stderr), so this is the curated release body for
54
+ # this version, not github-tag-action's commit-derived changelog.
55
+ - name: Generate the GitHub release notes
48
56
  env:
49
57
  RELEASE: ${{ steps.calver.outputs.release }}
50
- run: |
51
- underline="$(echo "$RELEASE" | tr -c '\n' '-')"
52
- echo "underline=${underline}" >> "$GITHUB_OUTPUT"
58
+ run: uv run --extra=release towncrier build --draft --version "$RELEASE" >
59
+ release-notes.md
53
60
 
54
- - name: Update changelog
55
- id: update_changelog
56
- uses: jacobtomlinson/gha-find-replace@v3
57
- with:
58
- find: "Next\n----"
59
- replace: |
60
- Next
61
- ----
62
-
63
- ${{ steps.calver.outputs.release }}
64
- ${{ steps.changelog_underline.outputs.underline }}
65
- include: CHANGELOG.rst
66
- regex: false
67
-
68
- - name: Check Update changelog was modified
61
+ # Assemble the same fragments into CHANGELOG.rst under a new
62
+ # ``$RELEASE`` section and delete the consumed fragment files.
63
+ - name: Update the changelog
69
64
  env:
70
- MODIFIED_FILES: ${{ steps.update_changelog.outputs.modifiedFiles }}
71
- run: |
72
- if [ "$MODIFIED_FILES" = "0" ]; then
73
- echo "Error: No files were modified when updating changelog"
74
- exit 1
75
- fi
65
+ RELEASE: ${{ steps.calver.outputs.release }}
66
+ run: uv run --extra=release towncrier build --yes --version "$RELEASE"
67
+
76
68
  - uses: stefanzweifel/git-auto-commit-action@v7
77
69
  id: commit
78
70
  with:
79
71
  commit_message: Bump CHANGELOG
80
- file_pattern: CHANGELOG.rst
72
+ file_pattern: CHANGELOG.rst newsfragments
81
73
  # Error if there are no changes.
82
74
  skip_dirty_check: true
83
75
 
@@ -96,7 +88,7 @@ jobs:
96
88
  tag: ${{ steps.tag_version.outputs.new_tag }}
97
89
  makeLatest: true
98
90
  name: Release ${{ steps.tag_version.outputs.new_tag }}
99
- body: ${{ steps.tag_version.outputs.changelog }}
91
+ bodyFile: release-notes.md
100
92
 
101
93
  pypi:
102
94
  name: Publish to PyPI
@@ -113,7 +105,7 @@ jobs:
113
105
  id-token: write
114
106
 
115
107
  steps:
116
- - uses: actions/checkout@v6
108
+ - uses: actions/checkout@v7
117
109
  with:
118
110
  ref: ${{ needs.release.outputs.tag }}
119
111
  # Fetch all history including tags.
@@ -122,7 +114,7 @@ jobs:
122
114
  persist-credentials: false
123
115
 
124
116
  - name: Install uv
125
- uses: astral-sh/setup-uv@v7
117
+ uses: astral-sh/setup-uv@v9.0.0
126
118
  with:
127
119
  enable-cache: true
128
120
  cache-dependency-glob: '**/pyproject.toml'
@@ -149,13 +141,13 @@ jobs:
149
141
  packages: write
150
142
 
151
143
  steps:
152
- - uses: actions/checkout@v6
144
+ - uses: actions/checkout@v7
153
145
  with:
154
146
  ref: ${{ needs.release.outputs.tag }}
155
147
  persist-credentials: false
156
148
 
157
149
  - name: Login to GHCR
158
- uses: docker/login-action@v4
150
+ uses: docker/login-action@v4.6.0
159
151
  with:
160
152
  registry: ghcr.io
161
153
  username: ${{ github.actor }}
@@ -168,7 +160,7 @@ jobs:
168
160
  uses: docker/setup-qemu-action@v4
169
161
 
170
162
  - name: Build and push Docker images
171
- uses: docker/bake-action@v7.1.0
163
+ uses: docker/bake-action@v7.3.0
172
164
  with:
173
165
  push: true
174
166
  env:
@@ -115,6 +115,7 @@ jobs:
115
115
  - tests/mock_vws/test_requests_mock_usage.py
116
116
  - tests/mock_vws/test_respx_mock_usage.py
117
117
  - tests/mock_vws/test_flask_app_usage.py
118
+ - tests/mock_vws/test_model_target_web_api.py
118
119
  - tests/mock_vws/test_vumark_generation_api.py
119
120
  - tests/mock_vws/test_target_validators.py
120
121
  - tests/mock_vws/test_docker.py
@@ -123,12 +124,12 @@ jobs:
123
124
  - docs/
124
125
 
125
126
  steps:
126
- - uses: actions/checkout@v6
127
+ - uses: actions/checkout@v7
127
128
  with:
128
129
  persist-credentials: false
129
130
 
130
131
  - name: Install uv
131
- uses: astral-sh/setup-uv@v7
132
+ uses: astral-sh/setup-uv@v9.0.0
132
133
  with:
133
134
  enable-cache: true
134
135
  cache-dependency-glob: '**/pyproject.toml'
@@ -195,12 +196,12 @@ jobs:
195
196
  platform: [ubuntu-latest]
196
197
 
197
198
  steps:
198
- - uses: actions/checkout@v6
199
+ - uses: actions/checkout@v7
199
200
  with:
200
201
  persist-credentials: false
201
202
 
202
203
  - name: Install uv
203
- uses: astral-sh/setup-uv@v7
204
+ uses: astral-sh/setup-uv@v9.0.0
204
205
  with:
205
206
  enable-cache: true
206
207
  cache-dependency-glob: '**/pyproject.toml'
@@ -240,12 +241,12 @@ jobs:
240
241
  python-version: ['3.14']
241
242
 
242
243
  steps:
243
- - uses: actions/checkout@v6
244
+ - uses: actions/checkout@v7
244
245
  with:
245
246
  persist-credentials: false
246
247
 
247
248
  - name: Install uv
248
- uses: astral-sh/setup-uv@v7
249
+ uses: astral-sh/setup-uv@v9.0.0
249
250
  with:
250
251
  enable-cache: true
251
252
  cache-dependency-glob: '**/pyproject.toml'
@@ -254,17 +255,29 @@ jobs:
254
255
  run: |
255
256
  cp ./vuforia_secrets.env.example ./vuforia_secrets.env
256
257
 
258
+ - name: Start Docker
259
+ shell: pwsh
260
+ run: |
261
+ $service = Get-Service docker -ErrorAction Stop
262
+ if ($service.Status -ne "Running") {
263
+ Start-Service docker
264
+ }
265
+ $deadline = (Get-Date).AddMinutes(2)
266
+ do {
267
+ docker info
268
+ if ($LASTEXITCODE -eq 0) {
269
+ exit 0
270
+ }
271
+ Start-Sleep -Seconds 5
272
+ } while ((Get-Date) -lt $deadline)
273
+ docker info
274
+
257
275
  - name: Run tests
258
276
  shell: bash
259
277
  run: |
260
278
  # We use pytest-xdist to make this run much faster.
261
279
  # The downside is that we cannot use -s / --capture=no.
262
280
  #
263
- # We use coverage to collect coverage data but we currently
264
- # do not upload / use it because combining Windows and Linux
265
- # coverage is challenging.
266
- #
267
- # We therefore have a few ``# pragma: no cover`` statements.
268
281
  uv run --extra=dev \
269
282
  coverage run -m pytest \
270
283
  --skip-real \
@@ -275,6 +288,14 @@ jobs:
275
288
  env:
276
289
  UV_PYTHON: ${{ matrix.python-version }}
277
290
 
291
+ - name: Upload coverage data
292
+ uses: actions/upload-artifact@v7
293
+ with:
294
+ name: coverage-data-windows-${{ matrix.python-version }}
295
+ path: .coverage.*
296
+ include-hidden-files: true
297
+ if-no-files-found: error
298
+
278
299
  coverage:
279
300
  name: Combine & check coverage
280
301
  needs: [ci-tests, skip-tests, windows-tests]
@@ -282,12 +303,12 @@ jobs:
282
303
  runs-on: ubuntu-latest
283
304
 
284
305
  steps:
285
- - uses: actions/checkout@v6
306
+ - uses: actions/checkout@v7
286
307
  with:
287
308
  persist-credentials: false
288
309
 
289
310
  - name: Install uv
290
- uses: astral-sh/setup-uv@v7
311
+ uses: astral-sh/setup-uv@v9.0.0
291
312
  with:
292
313
  enable-cache: true
293
314
  cache-dependency-glob: '**/pyproject.toml'
@@ -9,6 +9,7 @@ ci:
9
9
  skip:
10
10
  - actionlint
11
11
  - sphinx-lint
12
+ - strict-kwargs-fix
12
13
  - check-manifest
13
14
  - custom-linters
14
15
  - deptry
@@ -174,7 +175,7 @@ repos:
174
175
  - id: mypy
175
176
  name: mypy
176
177
  stages: [pre-push]
177
- entry: uv run --extra=dev -m mypy
178
+ entry: uv run --extra=dev -m mypy --num-workers=4
178
179
  language: python
179
180
  types_or: [python, toml]
180
181
  pass_filenames: false
@@ -185,7 +186,8 @@ repos:
185
186
  - id: mypy-docs
186
187
  name: mypy-docs
187
188
  stages: [pre-push]
188
- entry: uv run --extra=dev doccmd --no-write-to-file --language=python --command="mypy"
189
+ entry: uv run --extra=dev doccmd --no-write-to-file --language=python --command="mypy
190
+ --num-workers=4"
189
191
  language: python
190
192
  types_or: [markdown, rst]
191
193
 
@@ -337,6 +339,16 @@ repos:
337
339
  - *uv_version
338
340
  stages: [pre-commit]
339
341
 
342
+ - id: strict-kwargs-fix
343
+ name: strict-kwargs
344
+ entry: uv run --extra=dev strict-kwargs check --fix
345
+ language: python
346
+ types_or: [python]
347
+ additional_dependencies:
348
+ - *uv_version
349
+ stages: [pre-commit]
350
+ require_serial: true
351
+
340
352
  - id: doc8
341
353
  name: doc8
342
354
  entry: uv run --extra=dev -m doc8
@@ -1,8 +1,28 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- Next
5
- ----
4
+ .. towncrier release notes start
5
+
6
+ 2026.08.04
7
+ ----------
8
+
9
+ - Cloud databases with ``request_quota=0`` now return a
10
+ ``RequestQuotaReached`` response from VWS endpoints.
11
+
12
+ - Add a mock implementation of the Model Target Web API, including OAuth2 token creation, standard and advanced dataset creation, status polling, dataset download, and deletion.
13
+
14
+ - Improve Model Target Web API mock authentication failure responses.
15
+
16
+ - Match real Vuforia Model Target dataset creation validation error shape, including per-request UUID, details list, and status codes (415 for unsupported media type, 400 with ``BAD_REQUEST`` validation details).
17
+
18
+ - Match real Vuforia Model Target unknown-dataset response shape (``NOT_FOUND`` code, ``Could not find a model-view database with uuid <uuid>`` message, ``userId:`` target).
19
+
20
+ - Make synthetic Model Target dataset zip downloads byte-for-byte reproducible.
21
+
22
+ - Match real Vuforia Model Target Web API error responses for invalid request bodies, invalid dataset creation payloads, unknown datasets, and downloads of still-processing datasets.
23
+
24
+ - Add configurable ``TargetQuotaReached``, ``ProjectSuspended``, and
25
+ ``ProjectHasNoAPIAccess`` responses from VWS endpoints.
6
26
 
7
27
  2026.04.26
8
28
  ----------
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vws-python-mock
3
- Version: 2026.4.26
3
+ Version: 2026.8.4
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
@@ -37,59 +37,63 @@ Provides-Extra: dev
37
37
  Requires-Dist: actionlint-py==1.7.12.24; extra == "dev"
38
38
  Requires-Dist: check-manifest==0.51; extra == "dev"
39
39
  Requires-Dist: check-wheel-contents==0.6.3; extra == "dev"
40
- Requires-Dist: coverage==7.13.5; extra == "dev"
40
+ Requires-Dist: coverage==7.15.2; extra == "dev"
41
41
  Requires-Dist: deptry==0.25.1; extra == "dev"
42
42
  Requires-Dist: dirty-equals==0.11; extra == "dev"
43
43
  Requires-Dist: doc8==2.0.0; extra == "dev"
44
- Requires-Dist: doccmd==2026.3.26.2; extra == "dev"
45
- Requires-Dist: docker==7.1.0; extra == "dev"
44
+ Requires-Dist: doccmd==2026.7.19; extra == "dev"
45
+ Requires-Dist: docker==7.2.0; extra == "dev"
46
46
  Requires-Dist: freezegun==1.5.5; extra == "dev"
47
47
  Requires-Dist: furo==2025.12.19; extra == "dev"
48
48
  Requires-Dist: interrogate==1.7.0; extra == "dev"
49
- Requires-Dist: mypy[faster-cache]==1.20.2; extra == "dev"
50
- Requires-Dist: mypy-strict-kwargs==2026.1.12; extra == "dev"
51
- Requires-Dist: prek==0.3.10; extra == "dev"
52
- Requires-Dist: pydocstringformatter==0.7.5; extra == "dev"
49
+ Requires-Dist: mypy[faster-cache]==2.3.0; extra == "dev"
50
+ Requires-Dist: mypy-strict-kwargs==2026.7.19.1; extra == "dev"
51
+ Requires-Dist: prek==0.4.11; extra == "dev"
52
+ Requires-Dist: pydocstringformatter==1.0.0; extra == "dev"
53
53
  Requires-Dist: pydocstyle==6.3; extra == "dev"
54
- Requires-Dist: pylint[spelling]==4.0.5; extra == "dev"
54
+ Requires-Dist: pylint[spelling]==4.0.6; extra == "dev"
55
55
  Requires-Dist: pylint-per-file-ignores==3.2.1; extra == "dev"
56
- Requires-Dist: pyproject-fmt==2.21.1; extra == "dev"
57
- Requires-Dist: pyrefly==0.62.0; extra == "dev"
58
- Requires-Dist: pyright==1.1.409; extra == "dev"
56
+ Requires-Dist: pyproject-fmt==2.26.0; extra == "dev"
57
+ Requires-Dist: pyrefly==1.1.1; extra == "dev"
58
+ Requires-Dist: pyright==1.1.411; extra == "dev"
59
59
  Requires-Dist: pyroma==5.0.1; extra == "dev"
60
- Requires-Dist: pytest==9.0.3; extra == "dev"
60
+ Requires-Dist: pytest==9.1.1; extra == "dev"
61
61
  Requires-Dist: pytest-beartype-tests==2026.4.26; 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
64
  Requires-Dist: pyyaml==6.0.3; extra == "dev"
65
65
  Requires-Dist: requests-mock-flask==2026.4.2; extra == "dev"
66
- Requires-Dist: ruff==0.15.11; extra == "dev"
66
+ Requires-Dist: ruff==0.16.1; extra == "dev"
67
67
  Requires-Dist: shellcheck-py==0.11.0.1; extra == "dev"
68
- Requires-Dist: shfmt-py==3.12.0.2; extra == "dev"
68
+ Requires-Dist: shfmt-py==4.0.0; extra == "dev"
69
69
  Requires-Dist: sphinx==9.1.0; extra == "dev"
70
70
  Requires-Dist: sphinx-copybutton==0.5.2; extra == "dev"
71
71
  Requires-Dist: sphinx-lint==1.0.2; extra == "dev"
72
72
  Requires-Dist: sphinx-paramlinks==0.6; extra == "dev"
73
73
  Requires-Dist: sphinx-pyproject==0.3.0; extra == "dev"
74
- Requires-Dist: sphinx-substitution-extensions==2026.1.12; extra == "dev"
75
- Requires-Dist: sphinx-toolbox==4.2.0rc1; extra == "dev"
74
+ Requires-Dist: sphinx-substitution-extensions==2026.6.17; extra == "dev"
75
+ Requires-Dist: sphinx-toolbox==4.3.0; extra == "dev"
76
76
  Requires-Dist: sphinxcontrib-httpdomain==2.0.0; extra == "dev"
77
77
  Requires-Dist: sphinxcontrib-spelling==8.0.2; extra == "dev"
78
- Requires-Dist: sybil==10.0.1; extra == "dev"
78
+ Requires-Dist: sphinxcontrib-towncrier==0.5.0a0; extra == "dev"
79
+ Requires-Dist: strict-kwargs==2026.7.24; extra == "dev"
80
+ Requires-Dist: sybil==10.1.0; extra == "dev"
79
81
  Requires-Dist: tenacity==9.1.4; extra == "dev"
80
- Requires-Dist: ty==0.0.32; extra == "dev"
81
- Requires-Dist: types-docker==7.1.0.20260409; extra == "dev"
82
- Requires-Dist: types-pyyaml==6.0.12.20260408; extra == "dev"
83
- Requires-Dist: types-requests==2.33.0.20260408; extra == "dev"
84
- Requires-Dist: urllib3==2.6.3; extra == "dev"
82
+ Requires-Dist: towncrier==25.8.0; extra == "dev"
83
+ Requires-Dist: ty==0.0.65; extra == "dev"
84
+ Requires-Dist: types-docker==7.2.0.20260728; extra == "dev"
85
+ Requires-Dist: types-pyyaml==6.0.12.20260724; extra == "dev"
86
+ Requires-Dist: types-requests==2.33.0.20260712; extra == "dev"
87
+ Requires-Dist: urllib3==2.7.0; extra == "dev"
85
88
  Requires-Dist: vulture==2.16; extra == "dev"
86
89
  Requires-Dist: vws-python==2026.2.25.1; extra == "dev"
87
90
  Requires-Dist: vws-test-fixtures==2023.3.5; extra == "dev"
88
- Requires-Dist: vws-web-tools==2026.2.22.1; extra == "dev"
91
+ Requires-Dist: vws-web-tools==2026.5.21; extra == "dev"
89
92
  Requires-Dist: yamlfix==1.19.1; extra == "dev"
90
- Requires-Dist: zizmor==1.24.1; extra == "dev"
93
+ Requires-Dist: zizmor==1.28.0; extra == "dev"
91
94
  Provides-Extra: release
92
95
  Requires-Dist: check-wheel-contents==0.6.3; extra == "release"
96
+ Requires-Dist: towncrier==25.8.0; extra == "release"
93
97
  Dynamic: license-file
94
98
 
95
99
  |Build Status| |PyPI|
@@ -100,7 +104,7 @@ VWS Mock
100
104
  .. contents::
101
105
  :local:
102
106
 
103
- Mock for the Vuforia Web Services (VWS) API and the Vuforia Web Query API.
107
+ Mock for the Vuforia Web Services (VWS) API, the Vuforia Web Query API, and the Model Target Web API.
104
108
 
105
109
  Mocking calls made to Vuforia
106
110
  ------------------------------
@@ -6,7 +6,7 @@ VWS Mock
6
6
  .. contents::
7
7
  :local:
8
8
 
9
- Mock for the Vuforia Web Services (VWS) API and the Vuforia Web Query API.
9
+ Mock for the Vuforia Web Services (VWS) API, the Vuforia Web Query API, and the Model Target Web API.
10
10
 
11
11
  Mocking calls made to Vuforia
12
12
  ------------------------------
@@ -12,7 +12,11 @@ from pathlib import Path
12
12
  import vws_web_tools
13
13
  from selenium.common.exceptions import TimeoutException
14
14
  from selenium.webdriver.remote.webdriver import WebDriver
15
- from vws_web_tools import DatabaseDict, VuMarkDatabaseDict
15
+ from vws_web_tools import (
16
+ DatabaseDict,
17
+ ModelTargetWebAPIDict,
18
+ VuMarkDatabaseDict,
19
+ )
16
20
 
17
21
  VUMARK_TEMPLATE_SVG_FILE_PATH = Path(__file__).with_name(
18
22
  name="vumark_template.svg",
@@ -72,11 +76,13 @@ def _create_and_get_vumark_details(
72
76
 
73
77
 
74
78
  def _generate_secrets_file_content(
79
+ *,
75
80
  cloud_database_details: DatabaseDict,
76
81
  vumark_details: VuMarkDatabaseDict,
77
82
  inactive_database_details: DatabaseDict,
78
83
  inactive_vumark_details: VuMarkDatabaseDict,
79
84
  vumark_target_id: str,
85
+ model_target_web_api_details: ModelTargetWebAPIDict,
80
86
  ) -> str:
81
87
  """Generate the content of a secrets file."""
82
88
  return textwrap.dedent(
@@ -101,6 +107,10 @@ def _generate_secrets_file_content(
101
107
  INACTIVE_VUMARK_VUFORIA_TARGET_MANAGER_DATABASE_NAME={inactive_vumark_details["database_name"]}
102
108
  INACTIVE_VUMARK_VUFORIA_SERVER_ACCESS_KEY={inactive_vumark_details["server_access_key"]}
103
109
  INACTIVE_VUMARK_VUFORIA_SERVER_SECRET_KEY={inactive_vumark_details["server_secret_key"]}
110
+
111
+ MODEL_TARGET_VUFORIA_CLIENT_ID={model_target_web_api_details["client_id"]}
112
+ MODEL_TARGET_VUFORIA_CLIENT_SECRET={model_target_web_api_details["client_secret"]}
113
+ MODEL_TARGET_VUFORIA_CAD_DATA_URL={model_target_web_api_details["cad_data_url"]}
104
114
  """,
105
115
  )
106
116
 
@@ -193,6 +203,21 @@ def _create_and_get_inactive_vumark_details(
193
203
  return vumark_database_details
194
204
 
195
205
 
206
+ def _get_model_target_web_api_details(
207
+ driver: WebDriver,
208
+ email_address: str,
209
+ password: str,
210
+ ) -> ModelTargetWebAPIDict:
211
+ """Get credentials and input data for the Model Target Web API."""
212
+ vws_web_tools.log_in(
213
+ driver=driver,
214
+ email_address=email_address,
215
+ password=password,
216
+ )
217
+ vws_web_tools.wait_for_logged_in(driver=driver)
218
+ return vws_web_tools.get_model_target_web_api_details(driver=driver)
219
+
220
+
196
221
  def _create_vuforia_resource_names() -> tuple[str, str, str, str]:
197
222
  """Create names for Vuforia resources."""
198
223
  time = datetime.datetime.now(tz=datetime.UTC).strftime(
@@ -236,6 +261,14 @@ def main() -> None:
236
261
  )
237
262
  inactive_vumark_driver.quit()
238
263
 
264
+ model_target_driver = vws_web_tools.create_chrome_driver()
265
+ model_target_web_api_details = _get_model_target_web_api_details(
266
+ driver=model_target_driver,
267
+ email_address=email_address,
268
+ password=password,
269
+ )
270
+ model_target_driver.quit()
271
+
239
272
  num_databases = 100
240
273
  required_files = [
241
274
  (new_secrets_dir / f"vuforia_secrets_{i}.env")
@@ -291,6 +324,7 @@ def main() -> None:
291
324
  inactive_database_details=inactive_database_details,
292
325
  inactive_vumark_details=inactive_vumark_details,
293
326
  vumark_target_id=vumark_target_id,
327
+ model_target_web_api_details=model_target_web_api_details,
294
328
  )
295
329
  file.write_text(data=file_contents)
296
330
  sys.stdout.write(f"Created database {file.name}\n")
@@ -10,7 +10,7 @@ from sybil.parsers.rest import (
10
10
  PythonCodeBlockParser,
11
11
  )
12
12
 
13
- from tests.mock_vws.utils.retries import RETRY_EXCEPTIONS
13
+ from tests.mock_vws.utils.retries import TRANSIENT_VWS_EXCEPTIONS
14
14
 
15
15
  pytest_collect_file = Sybil(
16
16
  parsers=[
@@ -29,4 +29,4 @@ def pytest_set_filtered_exceptions() -> tuple[type[Exception], ...]:
29
29
  This is for ``pytest-retry``.
30
30
  The configuration for retries is in ``pyproject.toml``.
31
31
  """
32
- return RETRY_EXCEPTIONS
32
+ return TRANSIENT_VWS_EXCEPTIONS
@@ -24,10 +24,19 @@ extensions = [
24
24
  "sphinx_paramlinks",
25
25
  "sphinx_substitution_extensions",
26
26
  "sphinxcontrib.spelling",
27
+ "sphinxcontrib.towncrier.ext",
27
28
  "sphinxcontrib.autohttp.flask",
28
29
  "sphinx_toolbox.more_autodoc.autoprotocol",
29
30
  ]
30
31
 
32
+ # Render the unreleased ``newsfragments/`` entries into
33
+ # ``docs/source/unreleased.rst`` so the Sphinx spelling, doc-build and
34
+ # link-checking gates cover the prose before it is assembled into
35
+ # CHANGELOG.rst at release time.
36
+ towncrier_draft_autoversion_mode = "draft"
37
+ towncrier_draft_include_empty = True
38
+ towncrier_draft_working_directory = f"{_pyproject_file.parent}"
39
+
31
40
  # Required by sphinx-toolbox 4.2.0rc1 for compatibility with Sphinx 9.
32
41
  # See https://github.com/sphinx-toolbox/sphinx-toolbox/issues/201#issuecomment-4313483053.
33
42
  autodoc_use_legacy_class_based = True
@@ -92,12 +92,31 @@ There are some result codes which the mock cannot return.
92
92
  These are:
93
93
 
94
94
  * ``DateRangeError``
95
- * ``ProjectHasNoAPIAccess``
96
- * ``ProjectSuspended``
97
- * ``RequestQuotaReached``
98
- * ``TargetQuotaReached``
99
95
  * ``TooManyRequests``
100
96
 
97
+ Request quota exhaustion
98
+ ------------------------
99
+
100
+ The mock returns ``RequestQuotaReached`` when a
101
+ :class:`mock_vws.database.CloudDatabase` is created with
102
+ ``request_quota=0``. This behavior follows the public Vuforia documentation,
103
+ but the response has not been verified against a real database with an
104
+ exhausted quota.
105
+
106
+ Other configurable result codes
107
+ -------------------------------
108
+
109
+ The mock also supports three other result codes which have not been verified
110
+ against real databases in the corresponding states:
111
+
112
+ * ``TargetQuotaReached`` is returned when adding a target to a
113
+ :class:`mock_vws.database.CloudDatabase` which already contains
114
+ ``target_quota`` targets.
115
+ * ``ProjectSuspended`` is returned by VWS endpoints when a database uses the
116
+ :attr:`mock_vws.states.States.PROJECT_SUSPENDED` state.
117
+ * ``ProjectHasNoAPIAccess`` is returned by VWS endpoints when a database uses
118
+ the :attr:`mock_vws.states.States.PROJECT_HAS_NO_API_ACCESS` state.
119
+
101
120
  ``Content-Length`` headers
102
121
  --------------------------
103
122
 
@@ -110,6 +129,21 @@ The mock returns a fixed minimal image in the requested format.
110
129
  The ``instance_id`` value is not encoded into the response image.
111
130
  Real Vuforia encodes the instance ID into the VuMark pattern.
112
131
 
132
+ Model Target datasets
133
+ ---------------------
134
+
135
+ The Model Target Web API mock supports OAuth2 token requests, standard and advanced dataset creation, status polling, dataset downloads, and deletion.
136
+ The generated dataset download is a small valid zip file containing request metadata, not a real Vuforia Engine Model Target dataset.
137
+ Model Target API routes require a syntactically JSON Web Token-shaped bearer token, such as the token returned by the mock OAuth2 route.
138
+ The mock does not verify token signatures, claims, expiry, or revocation.
139
+
140
+ For unknown Model Target datasets, the mock returns an error whose ``target`` is ``userId:mock``.
141
+ Real Vuforia uses ``userId:<numeric-user-id>`` where the numeric portion is per-account.
142
+
143
+ Two Model Target Web API error paths remain mock-only in ``tests/mock_vws/test_model_target_web_api.py::TestMockOnlyErrors``.
144
+ Downloads of still-processing datasets are mock-only because exercising the path against real Vuforia would require creating a dataset on every test run; the mock drives the processing window deterministically.
145
+ Advanced-dataset creation with more than 20 models is mock-only because the available test account lacks the advanced-dataset scope and real Vuforia rejects the request with a 403 before validating model counts.
146
+
113
147
  Header cases
114
148
  ------------
115
149
 
@@ -40,6 +40,7 @@ Reference
40
40
  .. toctree::
41
41
  :hidden:
42
42
 
43
+ unreleased
43
44
  changelog
44
45
  release-process
45
46
  ci-setup