vws-python-mock 2026.2.21__tar.gz → 2026.2.22__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.
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/CHANGELOG.rst +4 -0
- {vws_python_mock-2026.2.21/src/vws_python_mock.egg-info → vws_python_mock-2026.2.22}/PKG-INFO +28 -2
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/README.rst +24 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/ci/test_custom_linters.py +1 -0
- vws_python_mock-2026.2.22/docs/source/getting-started.rst +12 -0
- vws_python_mock-2026.2.22/docs/source/httpx-example.rst +22 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/index.rst +5 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/mock-api-reference.rst +4 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/pyproject.toml +10 -8
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/spelling_private_dict.txt +2 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/__init__.py +2 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_flask_server/target_manager.py +10 -5
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_flask_server/vwq.py +2 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_flask_server/vws.py +10 -3
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_mock_common.py +20 -1
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/content_type_validators.py +1 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/date_validators.py +1 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/image_validators.py +1 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/include_target_data_validators.py +1 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/project_state_validators.py +1 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_requests_mock_server/decorators.py +21 -4
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_requests_mock_server/mock_web_query_api.py +10 -22
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_requests_mock_server/mock_web_services_api.py +79 -91
- vws_python_mock-2026.2.22/src/mock_vws/_respx_mock_server/__init__.py +1 -0
- vws_python_mock-2026.2.22/src/mock_vws/_respx_mock_server/decorators.py +275 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/__init__.py +4 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/key_validators.py +1 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/database.py +9 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/target.py +38 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22/src/vws_python_mock.egg-info}/PKG-INFO +28 -2
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/vws_python_mock.egg-info/SOURCES.txt +4 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/vws_python_mock.egg-info/requires.txt +3 -1
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/conftest.py +4 -1
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/fixtures/prepared_requests.py +3 -1
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/fixtures/vuforia_backends.py +1 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_add_target.py +26 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_authorization_header.py +2 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_database_summary.py +9 -1
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_delete_target.py +2 -2
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_docker.py +1 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_flask_app_usage.py +178 -44
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_get_duplicates.py +5 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_get_target.py +4 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_invalid_given_id.py +4 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_query.py +41 -2
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_requests_mock_usage.py +37 -1
- vws_python_mock-2026.2.22/tests/mock_vws/test_respx_mock_usage.py +350 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_target_list.py +2 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_target_summary.py +3 -1
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_update_target.py +26 -5
- vws_python_mock-2026.2.22/tests/mock_vws/test_vumark_generation_api.py +360 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/utils/__init__.py +5 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/utils/assertions.py +1 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/utils/usage_test_helpers.py +2 -0
- vws_python_mock-2026.2.21/docs/source/getting-started.rst +0 -4
- vws_python_mock-2026.2.21/tests/mock_vws/test_vumark_generation_api.py +0 -186
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.git_archival.txt +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.gitattributes +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.github/dependabot.yml +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.github/workflows/dependabot-merge.yml +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.github/workflows/docker-build.yml +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.github/workflows/lint.yml +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.github/workflows/publish-site.yml +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.github/workflows/release.yml +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.github/workflows/test.yml +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.gitignore +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.pre-commit-config.yaml +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.prettierrc +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.vscode/extensions.json +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/.vscode/settings.json +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/CODE_OF_CONDUCT.rst +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/LICENSE +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/MANIFEST.in +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/admin/__init__.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/admin/create_secrets_files.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/admin/vumark_template.svg +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/ci/__init__.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/ci/decrypt_secret.sh +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/conftest.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docker-bake.hcl +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/__init__.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/basic-example.rst +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/changelog.rst +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/ci-setup.rst +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/conf.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/contributing.rst +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/differences-to-vws.rst +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/docker.rst +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/installation.rst +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/release-process.rst +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/docs/source/versioning-and-api-stability.rst +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/secrets.tar.gpg +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/setup.cfg +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_base64_decoding.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_constants.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_database_matchers.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_flask_server/Dockerfile +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_flask_server/__init__.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_flask_server/healthcheck.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_tools.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/__init__.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/accept_header_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/auth_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/content_length_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/exceptions.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/fields_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_query_validators/num_results_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_requests_mock_server/__init__.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/active_flag_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/auth_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/content_length_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/content_type_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/date_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/exceptions.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/image_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/json_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/metadata_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/name_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/project_state_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/target_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_services_validators/width_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/database_type.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/image_matchers.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/py.typed +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/states.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/target_manager.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/target_raters.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/vws_python_mock.egg-info/dependency_links.txt +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/vws_python_mock.egg-info/not-zip-safe +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/vws_python_mock.egg-info/top_level.txt +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/__init__.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/__init__.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/fixtures/__init__.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/fixtures/credentials.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_content_length.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_date_header.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_invalid_json.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_target_raters.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_target_validators.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/test_unexpected_json.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/utils/retries.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/tests/mock_vws/utils/too_many_requests.py +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/vuforia_secrets.env.example +0 -0
- {vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/zizmor.yml +0 -0
{vws_python_mock-2026.2.21/src/vws_python_mock.egg-info → vws_python_mock-2026.2.22}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vws-python-mock
|
|
3
|
-
Version: 2026.2.
|
|
3
|
+
Version: 2026.2.22
|
|
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
|
|
@@ -19,12 +19,14 @@ Description-Content-Type: text/x-rst
|
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: beartype>=0.22.9
|
|
21
21
|
Requires-Dist: flask>=3.0.3
|
|
22
|
+
Requires-Dist: httpx>=0.27.0
|
|
22
23
|
Requires-Dist: numpy>=1.26.4
|
|
23
24
|
Requires-Dist: pillow>=11.0.0
|
|
24
25
|
Requires-Dist: piq>=0.8.0
|
|
25
26
|
Requires-Dist: pydantic-settings>=2.6.1
|
|
26
27
|
Requires-Dist: requests>=2.32.3
|
|
27
28
|
Requires-Dist: responses>=0.25.3
|
|
29
|
+
Requires-Dist: respx>=0.21.0
|
|
28
30
|
Requires-Dist: torch>=2.5.1
|
|
29
31
|
Requires-Dist: torchmetrics>=1.5.1
|
|
30
32
|
Requires-Dist: torchvision>=0.20.1
|
|
@@ -82,7 +84,7 @@ Requires-Dist: types-pyyaml==6.0.12.20250915; extra == "dev"
|
|
|
82
84
|
Requires-Dist: types-requests==2.32.4.20260107; extra == "dev"
|
|
83
85
|
Requires-Dist: urllib3==2.6.3; extra == "dev"
|
|
84
86
|
Requires-Dist: vulture==2.14; extra == "dev"
|
|
85
|
-
Requires-Dist: vws-python==2026.2.
|
|
87
|
+
Requires-Dist: vws-python==2026.2.21; extra == "dev"
|
|
86
88
|
Requires-Dist: vws-test-fixtures==2023.3.5; extra == "dev"
|
|
87
89
|
Requires-Dist: vws-web-tools==2026.2.20; extra == "dev"
|
|
88
90
|
Requires-Dist: yamlfix==1.19.1; extra == "dev"
|
|
@@ -131,6 +133,30 @@ By default, an exception will be raised if any requests to unmocked addresses ar
|
|
|
131
133
|
|
|
132
134
|
.. _requests: https://pypi.org/project/requests/
|
|
133
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.
|
|
140
|
+
|
|
141
|
+
.. code-block:: python
|
|
142
|
+
|
|
143
|
+
"""Make a request to the Vuforia Web Services API mock."""
|
|
144
|
+
|
|
145
|
+
import httpx
|
|
146
|
+
|
|
147
|
+
from mock_vws import MockVWSForHttpx
|
|
148
|
+
from mock_vws.database import CloudDatabase
|
|
149
|
+
|
|
150
|
+
with MockVWSForHttpx() as mock:
|
|
151
|
+
database = CloudDatabase()
|
|
152
|
+
mock.add_cloud_database(cloud_database=database)
|
|
153
|
+
# This will use the Vuforia mock.
|
|
154
|
+
httpx.get(url="https://vws.vuforia.com/summary", timeout=30)
|
|
155
|
+
|
|
156
|
+
By default, an exception will be raised if any requests to unmocked addresses are made.
|
|
157
|
+
|
|
158
|
+
.. _httpx: https://pypi.org/project/httpx/
|
|
159
|
+
|
|
134
160
|
Using Docker to mock calls to Vuforia from any language
|
|
135
161
|
-------------------------------------------------------
|
|
136
162
|
|
|
@@ -38,6 +38,30 @@ By default, an exception will be raised if any requests to unmocked addresses ar
|
|
|
38
38
|
|
|
39
39
|
.. _requests: https://pypi.org/project/requests/
|
|
40
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.
|
|
45
|
+
|
|
46
|
+
.. code-block:: python
|
|
47
|
+
|
|
48
|
+
"""Make a request to the Vuforia Web Services API mock."""
|
|
49
|
+
|
|
50
|
+
import httpx
|
|
51
|
+
|
|
52
|
+
from mock_vws import MockVWSForHttpx
|
|
53
|
+
from mock_vws.database import CloudDatabase
|
|
54
|
+
|
|
55
|
+
with MockVWSForHttpx() as mock:
|
|
56
|
+
database = CloudDatabase()
|
|
57
|
+
mock.add_cloud_database(cloud_database=database)
|
|
58
|
+
# This will use the Vuforia mock.
|
|
59
|
+
httpx.get(url="https://vws.vuforia.com/summary", timeout=30)
|
|
60
|
+
|
|
61
|
+
By default, an exception will be raised if any requests to unmocked addresses are made.
|
|
62
|
+
|
|
63
|
+
.. _httpx: https://pypi.org/project/httpx/
|
|
64
|
+
|
|
41
65
|
Using Docker to mock calls to Vuforia from any language
|
|
42
66
|
-------------------------------------------------------
|
|
43
67
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Getting started
|
|
2
|
+
---------------
|
|
3
|
+
|
|
4
|
+
Mocking calls made to Vuforia with Python ``requests``
|
|
5
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
6
|
+
|
|
7
|
+
.. include:: basic-example.rst
|
|
8
|
+
|
|
9
|
+
Mocking calls made to Vuforia with Python ``httpx``
|
|
10
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
11
|
+
|
|
12
|
+
.. include:: httpx-example.rst
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Using the mock redirects requests to Vuforia made with `httpx`_ to an in-memory implementation.
|
|
2
|
+
|
|
3
|
+
.. code-block:: python
|
|
4
|
+
|
|
5
|
+
"""Make a request to the Vuforia Web Services API mock."""
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from mock_vws import MockVWSForHttpx
|
|
10
|
+
from mock_vws.database import CloudDatabase
|
|
11
|
+
|
|
12
|
+
with MockVWSForHttpx() 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
|
+
By default, an exception will be raised if any requests to unmocked addresses are made.
|
|
19
|
+
|
|
20
|
+
See :ref:`mock-api-reference` for details of what can be changed and how.
|
|
21
|
+
|
|
22
|
+
.. _httpx: https://pypi.org/project/httpx/
|
|
@@ -12,6 +12,11 @@ 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
|
+
.. include:: httpx-example.rst
|
|
19
|
+
|
|
15
20
|
Using Docker to mock calls to Vuforia from any language
|
|
16
21
|
-------------------------------------------------------
|
|
17
22
|
|
|
@@ -36,12 +36,14 @@ dynamic = [
|
|
|
36
36
|
dependencies = [
|
|
37
37
|
"beartype>=0.22.9",
|
|
38
38
|
"flask>=3.0.3",
|
|
39
|
+
"httpx>=0.27.0",
|
|
39
40
|
"numpy>=1.26.4",
|
|
40
41
|
"pillow>=11.0.0",
|
|
41
42
|
"piq>=0.8.0",
|
|
42
43
|
"pydantic-settings>=2.6.1",
|
|
43
44
|
"requests>=2.32.3",
|
|
44
45
|
"responses>=0.25.3",
|
|
46
|
+
"respx>=0.21.0",
|
|
45
47
|
"torch>=2.5.1",
|
|
46
48
|
"torchmetrics>=1.5.1",
|
|
47
49
|
"torchvision>=0.20.1",
|
|
@@ -103,7 +105,7 @@ optional-dependencies.dev = [
|
|
|
103
105
|
"types-requests==2.32.4.20260107",
|
|
104
106
|
"urllib3==2.6.3",
|
|
105
107
|
"vulture==2.14",
|
|
106
|
-
"vws-python==2026.2.
|
|
108
|
+
"vws-python==2026.2.21",
|
|
107
109
|
"vws-test-fixtures==2023.3.5",
|
|
108
110
|
"vws-web-tools==2026.2.20",
|
|
109
111
|
"yamlfix==1.19.1",
|
|
@@ -329,18 +331,18 @@ keep_full_version = true
|
|
|
329
331
|
max_supported_python = "3.13"
|
|
330
332
|
|
|
331
333
|
[tool.pytest]
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
334
|
+
xfail_strict = true
|
|
335
|
+
log_cli = true
|
|
336
|
+
addopts = [
|
|
335
337
|
"--strict-markers",
|
|
336
338
|
]
|
|
337
|
-
|
|
339
|
+
markers = [
|
|
338
340
|
"requires_docker_build",
|
|
339
341
|
]
|
|
340
342
|
# Options for pytest-retry.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
343
|
+
retries = "10"
|
|
344
|
+
retry_delay = "10"
|
|
345
|
+
cumulative_timing = false
|
|
344
346
|
|
|
345
347
|
[tool.coverage]
|
|
346
348
|
run.branch = true
|
{vws_python_mock-2026.2.21 → vws_python_mock-2026.2.22}/src/mock_vws/_flask_server/target_manager.py
RENAMED
|
@@ -91,11 +91,15 @@ def delete_vumark_database(database_name: str) -> Response:
|
|
|
91
91
|
|
|
92
92
|
:status 200: The VuMark database has been deleted.
|
|
93
93
|
"""
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
try:
|
|
95
|
+
(matching_database,) = {
|
|
96
|
+
database
|
|
97
|
+
for database in TARGET_MANAGER.vumark_databases
|
|
98
|
+
if database_name == database.database_name
|
|
99
|
+
}
|
|
100
|
+
except ValueError:
|
|
101
|
+
return Response(response="", status=HTTPStatus.NOT_FOUND)
|
|
102
|
+
|
|
99
103
|
TARGET_MANAGER.remove_vumark_database(vumark_database=matching_database)
|
|
100
104
|
return Response(response="", status=HTTPStatus.OK)
|
|
101
105
|
|
|
@@ -365,6 +369,7 @@ def delete_target(database_name: str, target_id: str) -> Response:
|
|
|
365
369
|
rule="/cloud_databases/<string:database_name>/targets/<string:target_id>",
|
|
366
370
|
methods=[HTTPMethod.PUT],
|
|
367
371
|
)
|
|
372
|
+
@beartype
|
|
368
373
|
def update_target(database_name: str, target_id: str) -> Response:
|
|
369
374
|
"""Update a target."""
|
|
370
375
|
(database,) = (
|
|
@@ -113,6 +113,7 @@ def add_response_delay(response: Response) -> Response:
|
|
|
113
113
|
|
|
114
114
|
|
|
115
115
|
@CLOUDRECO_FLASK_APP.errorhandler(code_or_exception=ValidatorError)
|
|
116
|
+
@beartype
|
|
116
117
|
def handle_exceptions(exc: ValidatorError) -> Response:
|
|
117
118
|
"""Return the error response associated with the given exception."""
|
|
118
119
|
response = Response(
|
|
@@ -127,6 +128,7 @@ def handle_exceptions(exc: ValidatorError) -> Response:
|
|
|
127
128
|
|
|
128
129
|
|
|
129
130
|
@CLOUDRECO_FLASK_APP.route(rule="/v1/query", methods=[HTTPMethod.POST])
|
|
131
|
+
@beartype
|
|
130
132
|
def query() -> Response:
|
|
131
133
|
"""Perform an image recognition query."""
|
|
132
134
|
settings = VWQSettings.model_validate(obj={})
|
|
@@ -117,6 +117,7 @@ def get_all_vumark_databases() -> set[VuMarkDatabase]:
|
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
@VWS_FLASK_APP.before_request
|
|
120
|
+
@beartype
|
|
120
121
|
def set_terminate_wsgi_input() -> None:
|
|
121
122
|
"""We set ``wsgi.input_terminated`` to ``True`` when going through
|
|
122
123
|
``requests`` in our tests, so that requests have the given ``Content-
|
|
@@ -171,6 +172,7 @@ def add_response_delay(response: Response) -> Response:
|
|
|
171
172
|
|
|
172
173
|
|
|
173
174
|
@VWS_FLASK_APP.errorhandler(code_or_exception=ValidatorError)
|
|
175
|
+
@beartype
|
|
174
176
|
def handle_exceptions(exc: ValidatorError) -> Response:
|
|
175
177
|
"""Return the error response associated with the given exception."""
|
|
176
178
|
response = Response(
|
|
@@ -319,6 +321,7 @@ def get_target(target_id: str) -> Response:
|
|
|
319
321
|
rule="/targets/<string:target_id>",
|
|
320
322
|
methods=[HTTPMethod.DELETE],
|
|
321
323
|
)
|
|
324
|
+
@beartype
|
|
322
325
|
def delete_target(target_id: str) -> Response:
|
|
323
326
|
"""Delete a target.
|
|
324
327
|
|
|
@@ -395,9 +398,6 @@ def generate_vumark_instance(target_id: str) -> Response:
|
|
|
395
398
|
databases=all_databases,
|
|
396
399
|
)
|
|
397
400
|
|
|
398
|
-
# ``target_id`` is validated by request validators.
|
|
399
|
-
del target_id
|
|
400
|
-
|
|
401
401
|
database = get_database_matching_server_keys(
|
|
402
402
|
request_headers=dict(request.headers),
|
|
403
403
|
request_body=request.data,
|
|
@@ -408,6 +408,10 @@ def generate_vumark_instance(target_id: str) -> Response:
|
|
|
408
408
|
if not isinstance(database, VuMarkDatabase):
|
|
409
409
|
raise InvalidTargetTypeError
|
|
410
410
|
|
|
411
|
+
target = database.get_vumark_target(target_id=target_id)
|
|
412
|
+
if target.status != TargetStatuses.SUCCESS.value:
|
|
413
|
+
raise TargetStatusNotSuccessError
|
|
414
|
+
|
|
411
415
|
accept = request.headers.get(key="Accept", default="")
|
|
412
416
|
valid_accept_types: dict[str, bytes] = {
|
|
413
417
|
"image/png": VUMARK_PNG,
|
|
@@ -499,6 +503,7 @@ def database_summary() -> Response:
|
|
|
499
503
|
rule="/summary/<string:target_id>",
|
|
500
504
|
methods=[HTTPMethod.GET],
|
|
501
505
|
)
|
|
506
|
+
@beartype
|
|
502
507
|
def target_summary(target_id: str) -> Response:
|
|
503
508
|
"""Get a summary report for a target.
|
|
504
509
|
|
|
@@ -616,6 +621,7 @@ def get_duplicates(target_id: str) -> Response:
|
|
|
616
621
|
|
|
617
622
|
|
|
618
623
|
@VWS_FLASK_APP.route(rule="/targets", methods=[HTTPMethod.GET])
|
|
624
|
+
@beartype
|
|
619
625
|
def target_list() -> Response:
|
|
620
626
|
"""Get a list of all targets.
|
|
621
627
|
|
|
@@ -658,6 +664,7 @@ def target_list() -> Response:
|
|
|
658
664
|
@VWS_FLASK_APP.route(
|
|
659
665
|
rule="/targets/<string:target_id>", methods=[HTTPMethod.PUT]
|
|
660
666
|
)
|
|
667
|
+
@beartype
|
|
661
668
|
def update_target(target_id: str) -> Response:
|
|
662
669
|
"""Update a target.
|
|
663
670
|
|
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
"""Common utilities for creating mock routes."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
from collections.abc import Iterable
|
|
4
|
+
from collections.abc import Iterable, Mapping
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
8
|
from beartype import beartype
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
@beartype
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class RequestData:
|
|
14
|
+
"""A library-agnostic representation of an HTTP request.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
method: The HTTP method of the request.
|
|
18
|
+
path: The path of the request.
|
|
19
|
+
headers: The headers sent with the request.
|
|
20
|
+
body: The body of the request.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
method: str
|
|
24
|
+
path: str
|
|
25
|
+
headers: Mapping[str, str]
|
|
26
|
+
body: bytes
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@beartype
|
|
11
30
|
@dataclass(frozen=True)
|
|
12
31
|
class Route:
|
|
13
32
|
"""A representation of a VWS route.
|
|
@@ -12,6 +12,7 @@ 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
16
|
from mock_vws.database import CloudDatabase, VuMarkDatabase
|
|
16
17
|
from mock_vws.image_matchers import (
|
|
17
18
|
ImageMatcher,
|
|
@@ -27,12 +28,14 @@ from .mock_web_query_api import MockVuforiaWebQueryAPI
|
|
|
27
28
|
from .mock_web_services_api import MockVuforiaWebServicesAPI
|
|
28
29
|
|
|
29
30
|
_ResponseType = tuple[int, Mapping[str, str], str | bytes]
|
|
30
|
-
|
|
31
|
+
_MockCallback = Callable[[RequestData], _ResponseType]
|
|
32
|
+
_ResponsesCallback = Callable[[PreparedRequest], _ResponseType]
|
|
31
33
|
|
|
32
34
|
_STRUCTURAL_SIMILARITY_MATCHER = StructuralSimilarityMatcher()
|
|
33
35
|
_BRISQUE_TRACKING_RATER = BrisqueTargetTrackingRater()
|
|
34
36
|
|
|
35
37
|
|
|
38
|
+
@beartype
|
|
36
39
|
class MissingSchemeError(Exception):
|
|
37
40
|
"""Raised when a URL is missing a schema."""
|
|
38
41
|
|
|
@@ -156,10 +159,10 @@ class MockVWS(ContextDecorator):
|
|
|
156
159
|
|
|
157
160
|
@staticmethod
|
|
158
161
|
def _wrap_callback(
|
|
159
|
-
callback:
|
|
162
|
+
callback: _MockCallback,
|
|
160
163
|
delay_seconds: float,
|
|
161
164
|
sleep_fn: Callable[[float], None],
|
|
162
|
-
) ->
|
|
165
|
+
) -> _ResponsesCallback:
|
|
163
166
|
"""Wrap a callback to add a response delay."""
|
|
164
167
|
|
|
165
168
|
def wrapped(
|
|
@@ -186,7 +189,21 @@ class MockVWS(ContextDecorator):
|
|
|
186
189
|
sleep_fn(effective)
|
|
187
190
|
raise requests.exceptions.Timeout
|
|
188
191
|
|
|
189
|
-
|
|
192
|
+
raw_body = request.body
|
|
193
|
+
if raw_body is None:
|
|
194
|
+
body_bytes = b""
|
|
195
|
+
elif isinstance(raw_body, str):
|
|
196
|
+
body_bytes = raw_body.encode(encoding="utf-8")
|
|
197
|
+
else:
|
|
198
|
+
body_bytes = raw_body
|
|
199
|
+
|
|
200
|
+
request_data = RequestData(
|
|
201
|
+
method=request.method or "",
|
|
202
|
+
path=request.path_url,
|
|
203
|
+
headers=dict(request.headers),
|
|
204
|
+
body=body_bytes,
|
|
205
|
+
)
|
|
206
|
+
result = callback(request_data)
|
|
190
207
|
sleep_fn(delay_seconds)
|
|
191
208
|
return result
|
|
192
209
|
|
|
@@ -10,9 +10,8 @@ from http import HTTPMethod, HTTPStatus
|
|
|
10
10
|
from typing import ParamSpec, Protocol, runtime_checkable
|
|
11
11
|
|
|
12
12
|
from beartype import beartype
|
|
13
|
-
from requests.models import PreparedRequest
|
|
14
13
|
|
|
15
|
-
from mock_vws._mock_common import Route
|
|
14
|
+
from mock_vws._mock_common import RequestData, Route
|
|
16
15
|
from mock_vws._query_tools import (
|
|
17
16
|
get_query_match_response_text,
|
|
18
17
|
)
|
|
@@ -42,6 +41,7 @@ class _RouteMethod(Protocol[_P]):
|
|
|
42
41
|
|
|
43
42
|
@beartype
|
|
44
43
|
def route(
|
|
44
|
+
*,
|
|
45
45
|
path_pattern: str,
|
|
46
46
|
http_methods: Iterable[str],
|
|
47
47
|
) -> Callable[[_RouteMethod[_P]], _RouteMethod[_P]]:
|
|
@@ -78,21 +78,9 @@ def route(
|
|
|
78
78
|
return decorator
|
|
79
79
|
|
|
80
80
|
|
|
81
|
-
@beartype
|
|
82
|
-
def _body_bytes(request: PreparedRequest) -> bytes:
|
|
83
|
-
"""Return the body of a request as bytes."""
|
|
84
|
-
if request.body is None or isinstance(request.body, str):
|
|
85
|
-
return b""
|
|
86
|
-
|
|
87
|
-
return request.body
|
|
88
|
-
|
|
89
|
-
|
|
90
81
|
@beartype
|
|
91
82
|
class MockVuforiaWebQueryAPI:
|
|
92
|
-
"""A fake implementation of the Vuforia Web Query API.
|
|
93
|
-
|
|
94
|
-
This implementation is tied to the implementation of ``responses``.
|
|
95
|
-
"""
|
|
83
|
+
"""A fake implementation of the Vuforia Web Query API."""
|
|
96
84
|
|
|
97
85
|
def __init__(
|
|
98
86
|
self,
|
|
@@ -114,14 +102,14 @@ class MockVuforiaWebQueryAPI:
|
|
|
114
102
|
self._query_match_checker = query_match_checker
|
|
115
103
|
|
|
116
104
|
@route(path_pattern="/v1/query", http_methods={HTTPMethod.POST})
|
|
117
|
-
def query(self, request:
|
|
105
|
+
def query(self, request: RequestData) -> _ResponseType:
|
|
118
106
|
"""Perform an image recognition query."""
|
|
119
107
|
try:
|
|
120
108
|
run_query_validators(
|
|
121
|
-
request_path=request.
|
|
109
|
+
request_path=request.path,
|
|
122
110
|
request_headers=request.headers,
|
|
123
|
-
request_body=
|
|
124
|
-
request_method=request.method
|
|
111
|
+
request_body=request.body,
|
|
112
|
+
request_method=request.method,
|
|
125
113
|
databases=self._target_manager.cloud_databases,
|
|
126
114
|
)
|
|
127
115
|
except ValidatorError as exc:
|
|
@@ -129,9 +117,9 @@ class MockVuforiaWebQueryAPI:
|
|
|
129
117
|
|
|
130
118
|
response_text = get_query_match_response_text(
|
|
131
119
|
request_headers=request.headers,
|
|
132
|
-
request_body=
|
|
133
|
-
request_method=request.method
|
|
134
|
-
request_path=request.
|
|
120
|
+
request_body=request.body,
|
|
121
|
+
request_method=request.method,
|
|
122
|
+
request_path=request.path,
|
|
135
123
|
databases=self._target_manager.cloud_databases,
|
|
136
124
|
query_match_checker=self._query_match_checker,
|
|
137
125
|
)
|