diff --git a/docs/source/ci-setup.rst b/docs/source/ci-setup.rst index d4d663010..e1b401bb7 100644 --- a/docs/source/ci-setup.rst +++ b/docs/source/ci-setup.rst @@ -24,7 +24,8 @@ Create environment variable files for secrets: $ cp vuforia_secrets.env.example ci_secrets/vuforia_secrets_1.env $ ... -Populate every variable listed in :file:`vuforia_secrets.env.example`. Each file needs distinct active Cloud database credentials so concurrent jobs do not modify the same database. +Populate every variable listed in :file:`vuforia_secrets.env.example`. +Each file needs distinct active Cloud database credentials so concurrent jobs do not modify the same database. The files can share the inactive Cloud, active and inactive VuMark, and Model Target credentials. Add at least as many consecutively numbered files, starting at zero, as there are builds in the GitHub test matrix. diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst index 175fcb6f3..dce44577d 100644 --- a/docs/source/contributing.rst +++ b/docs/source/contributing.rst @@ -56,7 +56,8 @@ Create an environment variable file for secrets: $ cp vuforia_secrets.env.example vuforia_secrets.env Some tests require Vuforia credentials. -To run these tests, add the Vuforia credentials to the file :file:`vuforia_secrets.env`. See :ref:`connecting-to-vuforia`. +To run these tests, add the Vuforia credentials to the file :file:`vuforia_secrets.env`. +See :ref:`connecting-to-vuforia`. Then run ``pytest``: @@ -72,7 +73,8 @@ Connecting to Vuforia To connect to Vuforia, Vuforia target databases must be created via the Vuforia Web UI. Then, secret keys must be set as environment variables. -The test infrastructure allows those keys to be set in the file :file:`vuforia_secrets.env`. See :file:`vuforia_secrets.env.example` for the environment variables to set. +The test infrastructure allows those keys to be set in the file :file:`vuforia_secrets.env`. +See :file:`vuforia_secrets.env.example` for the environment variables to set. Do not use a target database that you are using for other purposes. This is because the test suite adds and deletes targets. @@ -101,8 +103,7 @@ To create databases without using the browser, use :file:`admin/create_secrets_f $ python admin/create_secrets_files.py # Each generated file gets its own active Cloud database credentials. -For the complete archive and GitHub Actions setup procedure, see -:doc:`ci-setup`. +For the complete archive and GitHub Actions setup procedure, see :doc:`ci-setup`. .. _Vuforia License Manager: https://developer.vuforia.com/vui/develop/licenses .. _Vuforia Target Manager: https://developer.vuforia.com/vui/develop/databases diff --git a/docs/source/differences-to-vws.rst b/docs/source/differences-to-vws.rst index 4003332e3..b68c50031 100644 --- a/docs/source/differences-to-vws.rst +++ b/docs/source/differences-to-vws.rst @@ -35,14 +35,7 @@ The mock is accurate immediately with regards to images. The mock does not count recognitions. Real Vuforia's recognition counts lag behind its queries by far longer than a test runs, so a query in the mock does not change any count either. -Set the counts you want to see instead: -:paramref:`mock_vws.database.CloudDatabase.total_recos`, -:paramref:`mock_vws.database.CloudDatabase.current_month_recos`, -:paramref:`mock_vws.database.CloudDatabase.previous_month_recos` and -:paramref:`mock_vws.database.CloudDatabase.reco_threshold` for the database -summary report, and -:meth:`mock_vws.MockVWS.set_target_recognition_counts` for the counts of a -target, which the target summary report and the reco counts report show. +Set the counts you want to see instead: :paramref:`mock_vws.database.CloudDatabase.total_recos`, :paramref:`mock_vws.database.CloudDatabase.current_month_recos`, :paramref:`mock_vws.database.CloudDatabase.previous_month_recos` and :paramref:`mock_vws.database.CloudDatabase.reco_threshold` for the database summary report, and :meth:`mock_vws.MockVWS.set_target_recognition_counts` for the counts of a target, which the target summary report and the reco counts report show. Targets are created by API requests, so their counts are set after the target is created. The Flask and Docker mock has an equivalent target manager endpoint, described in :doc:`docker`. @@ -198,8 +191,7 @@ The mock only limits requests whose access key belongs to a database, because th The mock applies no limit by default. Applying a limit of two requests per minute to ``GET /targets`` by default would break the tests of anything which uses the mock. -Set ``request_rate_limits`` to -:data:`mock_vws.request_rate_limits.DOCUMENTED_REQUEST_RATE_LIMITS` to apply +Set ``request_rate_limits`` to :data:`mock_vws.request_rate_limits.DOCUMENTED_REQUEST_RATE_LIMITS` to apply the limits which real Vuforia applies:: from mock_vws import MockVWS @@ -229,9 +221,7 @@ Configurable Cloud Query failures --------------------------------- The Vuforia Cloud Query API documents failure responses with JSON, arbitrary content, or no body. -Use -:paramref:`mock_vws.MockVWS.cloud_query_failure_response` to make every Cloud -Query request return a particular documented failure shape through the +Use :paramref:`mock_vws.MockVWS.cloud_query_failure_response` to make every Cloud Query request return a particular documented failure shape through the in-process ``requests``, ``httpx`` and ``httpx2`` backends:: from mock_vws import CloudQueryFailureResponse, MockVWS @@ -291,11 +281,8 @@ The other three come from Vuforia's result codes table rather than from a respon :ref:`unverified-additional-result-codes` -* ``TargetQuotaReached`` is returned when adding a target to a - :class:`mock_vws.database.CloudDatabase` which already contains - ``target_quota`` targets. -* ``ProjectSuspended`` is returned with status code 403 by every VWS endpoint when a database uses the - :attr:`mock_vws.states.States.PROJECT_SUSPENDED` state. +* ``TargetQuotaReached`` is returned when adding a target to a :class:`mock_vws.database.CloudDatabase` which already contains ``target_quota`` targets. +* ``ProjectSuspended`` is returned with status code 403 by every VWS endpoint when a database uses the :attr:`mock_vws.states.States.PROJECT_SUSPENDED` state. Real Vuforia has returned this result code for a database which passed its monthly recognition threshold, but its status code, body and headers were not recorded, and reads such as ``GET /targets`` and the database summary kept working there. * ``ProjectHasNoApiAccess`` is returned by VWS endpoints when a database uses the :attr:`mock_vws.states.States.PROJECT_HAS_NO_API_ACCESS` state. This casing comes from Vuforia's result codes table, as no response from a real database in this state has been seen. @@ -322,16 +309,10 @@ The Model Target Web API mock supports OAuth2 token requests, standard and advan The generated dataset download is a small valid ``full-dataset.zip`` with the same ``MTDataset.dat`` and ``MTDataset.xml`` filenames as Vuforia. Its contents are synthetic request metadata and minimal XML, not a real Vuforia Engine Model Target dataset. Use :paramref:`mock_vws.MockVWS.model_target_generation_failure` to make in-process Model Target datasets finish with a ``failed`` status and an ``error`` object. -The failure is returned after the configured -:paramref:`~mock_vws.MockVWS.processing_time_seconds`, so callers can test -both processing and failed states. +The failure is returned after the configured :paramref:`~mock_vws.MockVWS.processing_time_seconds`, so callers can test both processing and failed states. This configuration is not supported by the Flask/Docker backend. -Use -:paramref:`mock_vws.MockVWS.model_target_training_allowance_exceeded` to make -in-process Model Target dataset creation return Vuforia's ``TRAINING_ALLOWANCE_EXCEEDED`` response. -Set the -:envvar:`MODEL_TARGET_TRAINING_ALLOWANCE_EXCEEDED` environment variable to -``true`` to configure the same response in the Flask/Docker backend. Use :paramref:`mock_vws.MockVWS.model_target_generation_warning` to make successful in-process Model Target datasets include a Vuforia-shaped ``warning`` object after processing completes. This configuration is not supported by the Flask/Docker backend. +Use :paramref:`mock_vws.MockVWS.model_target_training_allowance_exceeded` to make in-process Model Target dataset creation return Vuforia's ``TRAINING_ALLOWANCE_EXCEEDED`` response. +Set the :envvar:`MODEL_TARGET_TRAINING_ALLOWANCE_EXCEEDED` environment variable to ``true`` to configure the same response in the Flask/Docker backend. Use :paramref:`mock_vws.MockVWS.model_target_generation_warning` to make successful in-process Model Target datasets include a Vuforia-shaped ``warning`` object after processing completes. This configuration is not supported by the Flask/Docker backend. Model Target API routes require a three-part JSON Web Token with JSON object header and payload parts, a non-``none`` ``alg`` value, and a non-empty base64url-encoded signature, such as the token returned by the mock OAuth2 route. The mock does not verify token signatures, payload claims such as expiry, or token revocation. The OAuth2 route supports both the ``client_credentials`` and ``password`` grants. @@ -397,9 +378,7 @@ Setting recognition counts is mock-only, because real Vuforia's counts are delay As with real Vuforia, the report is served with a ``text/plain`` content type rather than a CSV one. Real Vuforia assigns a database an ID, which the target manager shows. -The ID of a database in the mock is -:paramref:`mock_vws.database.CloudDatabase.database_id`, which defaults to a -random string, so the path of a request to this endpoint is built by reading that attribute rather than by looking the ID up. +The ID of a database in the mock is :paramref:`mock_vws.database.CloudDatabase.database_id`, which defaults to a random string, so the path of a request to this endpoint is built by reading that attribute rather than by looking the ID up. As real Vuforia does, the mock returns a 401 response with the ``AuthenticationFailure`` result code for a request which is signed with valid server keys but which names any other database, including one named by its name rather than by its ID. Real Vuforia returns a presigned URL for cloud storage, of this form: @@ -416,8 +395,7 @@ Real Vuforia returns a presigned URL for cloud storage, of this form: &X-Amz-Signature=... The mock returns a URL with the same path and the same query parameters, served by the mock itself rather than by cloud storage. -The URL returned by the Flask and Docker mock is built from the -:envvar:`VWS_BASE_URL` environment variable. +The URL returned by the Flask and Docker mock is built from the :envvar:`VWS_BASE_URL` environment variable. The credential, the security token and the signature are placeholders of the right shape. The mock does not check the signature, so a URL whose signature or file name has been changed, which real Vuforia refuses with a ``SignatureDoesNotMatch`` error document, is served by the mock as if it were signed. diff --git a/docs/source/unreleased.rst b/docs/source/unreleased.rst index d297d1976..bcfc1279b 100644 --- a/docs/source/unreleased.rst +++ b/docs/source/unreleased.rst @@ -2,7 +2,6 @@ Unreleased changes ================== Changes that have landed on the main branch but are not yet part of a tagged release. -These entries are assembled into the -:doc:`changelog` when the next release is published. +These entries are assembled into the :doc:`changelog` when the next release is published. .. towncrier-draft-entries:: diff --git a/pyproject.toml b/pyproject.toml index 9f2c3ace8..ab06bdca4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,7 +99,7 @@ dev = [ # use it to lint shell commands in GitHub workflow files. "shellcheck-py==0.11.0.1", "shfmt-py==4.2.0", - "snapper-fmt==0.11.1", + "snapper-fmt==0.11.2", "sphinx==9.1.0", "sphinx-copybutton==0.5.2", "sphinx-lint==1.0.2", diff --git a/uv.lock b/uv.lock index be1766e8e..c3ce6007b 100644 --- a/uv.lock +++ b/uv.lock @@ -2131,15 +2131,15 @@ wheels = [ [[package]] name = "snapper-fmt" -version = "0.11.1" +version = "0.11.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b1/1e/011997013ae3a08f57deffff6c0b310ff3650116f6a712596d583c6ea65c/snapper_fmt-0.11.1.tar.gz", hash = "sha256:398e8e2197ed671d436ec416f11b33089ba230dc830456eee065a9ef39015d1a", size = 232103, upload-time = "2026-09-09T16:05:13.95Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/09/7e3b41e1af329458da2fb5ac736926f323a54c74a218e7a110e02b3d55c7/snapper_fmt-0.11.2.tar.gz", hash = "sha256:8958c536087740998760df953e95c08cf0ef13052b215d5951467ea9b6e23f47", size = 353217, upload-time = "2026-09-11T11:18:22.133Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/b6/a37ce6a064808f23bbc3e9be483dcdf447969d80d29f5be764e845cd1da3/snapper_fmt-0.11.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27933c0d11859052b7c3a9d6e6735e28d177871c78f650799e003c3ab53dc89a", size = 16364850, upload-time = "2026-09-09T16:04:59.622Z" }, - { url = "https://files.pythonhosted.org/packages/51/e0/018410becc3e2a5ef128be26ba0632d11429c3c6e8543ccf1ec41f5c8e00/snapper_fmt-0.11.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:989a28a947e62912b3582619de7678b7a610ac2431d946c226dc44eacd07da97", size = 15286551, upload-time = "2026-09-09T16:05:02.482Z" }, - { url = "https://files.pythonhosted.org/packages/68/72/917298d6e384079d653aeea6a9ec3b17025452fbd9dac304afab2c5ca36e/snapper_fmt-0.11.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02092761f9e65cf520c6502742a8abe483d39fe0e30cdbeb1614e9b732c9f70c", size = 17015645, upload-time = "2026-09-09T16:05:05.382Z" }, - { url = "https://files.pythonhosted.org/packages/dd/30/84a4fd904c1f2879a71db5b5cb998255b7e1139d611c860c00ca59ff4c65/snapper_fmt-0.11.1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:79fe41dfff8b7548fb6f314b2bde0eb98558984f4db4bfc4f83ffae89ff8ec01", size = 15621972, upload-time = "2026-09-09T16:05:08.512Z" }, - { url = "https://files.pythonhosted.org/packages/11/9d/23ac6e40e9c60b4b1c91170f46ddc36ca76f51e29dd504badf7ec2bc775d/snapper_fmt-0.11.1-py3-none-win_amd64.whl", hash = "sha256:3e18bddb2719e2ddd669c22c08b0b6a409142fc27684e6401d3fa78e70bc2c80", size = 17787762, upload-time = "2026-09-09T16:05:11.447Z" }, + { url = "https://files.pythonhosted.org/packages/dc/45/0bd4985e3f5bcd76f9b41a0ea87101fda58931fd76b255df7777fd1d47be/snapper_fmt-0.11.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:68a51e12ac5dce57da4e31be07655cac1cf509808188e6973bcbd28efafac012", size = 16465042, upload-time = "2026-09-11T11:18:04.073Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f4/6a787f8d14e720a53ecce50f61d05f46ad8d37cb593728450959cbc7035b/snapper_fmt-0.11.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ea24eb7b8f9cb7a4c3bf87025d9eb0d5d5e59b9c2b4291c910f71e310cb35733", size = 15384950, upload-time = "2026-09-11T11:18:09.287Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f7/ebc34e98f2f7dba75d82bf5a084c273d037a1c3ee1f5125723b6f03239ff/snapper_fmt-0.11.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d109f94399a860ba1a1c26d3128d0df56147103e0b353c164565b2a8b3dadab2", size = 17125676, upload-time = "2026-09-11T11:18:12.528Z" }, + { url = "https://files.pythonhosted.org/packages/ce/2d/0cee775ede18a3d0dab9405e6b638489aba188b1306706aa4052414c0553/snapper_fmt-0.11.2-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:c25c1da28ff7e25e7e66399e0159a33669f8c4cb6ce950d7007b55d91d8d2b32", size = 15726150, upload-time = "2026-09-11T11:18:15.807Z" }, + { url = "https://files.pythonhosted.org/packages/fa/51/cc081cceea0ca63c9b6e79a009d2161ee3f9bd4aaea3d85dd3dc8c7a27c4/snapper_fmt-0.11.2-py3-none-win_amd64.whl", hash = "sha256:f31f8bbec4c38a626330be8236739107be0ac81a668f84a425225e425146571f", size = 17910801, upload-time = "2026-09-11T11:18:19.433Z" }, ] [[package]] @@ -2965,7 +2965,7 @@ dev = [ { name = "ruff", specifier = "==0.16.7" }, { name = "shellcheck-py", specifier = "==0.11.0.1" }, { name = "shfmt-py", specifier = "==4.2.0" }, - { name = "snapper-fmt", specifier = "==0.11.1" }, + { name = "snapper-fmt", specifier = "==0.11.2" }, { name = "sphinx", specifier = "==9.1.0" }, { name = "sphinx-copybutton", specifier = "==0.5.2" }, { name = "sphinx-lint", specifier = "==1.0.2" },