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