diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst index 09faeb7b3..24ab6e067 100644 --- a/docs/source/contributing.rst +++ b/docs/source/contributing.rst @@ -124,6 +124,24 @@ Use the following custom ``pytest`` options to skip some tests: --skip-docker_build_tests Skip tests for building Docker images +Verifying signed Model Target requests +-------------------------------------- + +Creating an advanced Model Target dataset with a state-based configuration is a "signed" request: the real Vuforia signs the trained dataset, and each signing consumes the account's Model Target training allowance. +The allowance is small (roughly 20 signings), it is shared by every CI job and every concurrent run, and it cannot be raised or reset. +Verifying signed requests on every run exhausted the allowance within hours and then made every CI run fail with ``TRAINING_ALLOWANCE_EXCEEDED``. + +The signed test cases therefore run against the mock backends on every run, but are skipped against the real Vuforia by default. +To verify them against the real Vuforia, for example after the allowance has recovered, opt in with: + +.. code-block:: text + + --verify-model-target-signing + Run signed Model Target dataset tests against + the real Vuforia + +The equivalent unsigned requests (a standard dataset, or an advanced dataset without a state-based configuration) consume no allowance and are verified against the real Vuforia on every run. + Documentation ------------- diff --git a/pyproject.toml b/pyproject.toml index 51f38d769..a704a8d93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ optional-dependencies.dev = [ "pydocstyle==6.3", "pylint[spelling]==4.0.7", "pylint-per-file-ignores==3.2.1", - "pyproject-fmt==2.28.1", + "pyproject-fmt==2.28.2", "pyrefly==1.2.0", "pyright==1.1.411", "pyroma==5.0.1", diff --git a/tests/mock_vws/fixtures/vuforia_backends.py b/tests/mock_vws/fixtures/vuforia_backends.py index 11dae25f5..533e9dea0 100644 --- a/tests/mock_vws/fixtures/vuforia_backends.py +++ b/tests/mock_vws/fixtures/vuforia_backends.py @@ -371,6 +371,14 @@ class VuforiaBackend(Enum): } +# Signed Model Target requests (advanced datasets with a state-based +# configuration) consume the Vuforia account's Model Target training +# allowance. The allowance is small, shared across all CI jobs, and +# cannot be raised or reset, so signed requests run against the real +# Vuforia only when this option is given. +VERIFY_MODEL_TARGET_SIGNING_OPTION = "--verify-model-target-signing" + + @beartype def pytest_addoption(parser: pytest.Parser) -> None: """ @@ -387,6 +395,18 @@ def pytest_addoption(parser: pytest.Parser) -> None: help="Skip tests for building Docker images", ) + parser.addoption( + VERIFY_MODEL_TARGET_SIGNING_OPTION, + action="store_true", + default=False, + help=( + "Run signed Model Target dataset tests against the real " + "Vuforia. These consume the account's small, shared, " + "non-resettable Model Target training allowance, so they " + "run against the mock backends only by default." + ), + ) + @beartype def pytest_collection_modifyitems( diff --git a/tests/mock_vws/test_model_target_web_api.py b/tests/mock_vws/test_model_target_web_api.py index 138340abf..1e930040e 100644 --- a/tests/mock_vws/test_model_target_web_api.py +++ b/tests/mock_vws/test_model_target_web_api.py @@ -31,7 +31,10 @@ credentials_for_backend, get_access_token, ) -from tests.mock_vws.fixtures.vuforia_backends import VuforiaBackend +from tests.mock_vws.fixtures.vuforia_backends import ( + VERIFY_MODEL_TARGET_SIGNING_OPTION, + VuforiaBackend, +) from tests.mock_vws.utils import ModelTargetEndpoint from tests.mock_vws.utils.assertions import ( assert_model_target_status, @@ -1595,8 +1598,34 @@ def test_unknown_dataset( assert error["target"].startswith("userId:") +# Creating an advanced dataset with a state-based configuration is a +# "signed" request: the real Vuforia signs the trained dataset, and each +# signing consumes the account's Model Target training allowance. The +# allowance is tiny (roughly 20 signings, under ten CI runs' worth), it +# is shared by every CI job and every concurrent run, and it cannot be +# raised or reset by us. Verifying this behavior on every run therefore +# burns the whole allowance within hours and then turns every CI run red +# with ``TRAINING_ALLOWANCE_EXCEEDED`` - which is exactly what happened +# when it ran unconditionally. The equivalent unsigned requests (a +# standard dataset, or an advanced dataset without a state-based +# configuration) consume nothing and stay verified on every run. +_SIGNED_REQUEST_SKIP_REASON = ( + "Signed Model Target requests consume the real Vuforia account's " + "small, shared, non-resettable training allowance, so they are not " + "verified against the real Vuforia by default. Pass " + f"{VERIFY_MODEL_TARGET_SIGNING_OPTION} to verify them, for example " + "after the allowance has recovered. The mock backends always run " + "this test." +) + + class TestStateBasedDatasets: - """Verified fake tests for State-Based Model Targets.""" + """Verified fake tests for State-Based Model Targets. + + The advanced (signed) cases are verified against the real Vuforia + only when ``--verify-model-target-signing`` is given: see + ``_SIGNED_REQUEST_SKIP_REASON``. + """ @staticmethod @pytest.mark.parametrize( @@ -1621,6 +1650,7 @@ class TestStateBasedDatasets: ) def test_state_based_dataset( *, + request: pytest.FixtureRequest, verify_model_target_mock_vuforia: VuforiaBackend, dataset_path: str, view_updates: dict[str, object], @@ -1628,6 +1658,14 @@ def test_state_based_dataset( """State-Based Model Target fields survive a dataset round trip. """ + if ( + verify_model_target_mock_vuforia is VuforiaBackend.REAL + and dataset_path == "/modeltargets/advancedDatasets" + and not request.config.getoption( + name=VERIFY_MODEL_TARGET_SIGNING_OPTION, + ) + ): + pytest.skip(reason=_SIGNED_REQUEST_SKIP_REASON) body = { **_UNAUTHENTICATED_DATASET_REQUEST, "models": [ diff --git a/tests/mock_vws/utils/__init__.py b/tests/mock_vws/utils/__init__.py index 3f405ca85..368c26f8c 100644 --- a/tests/mock_vws/utils/__init__.py +++ b/tests/mock_vws/utils/__init__.py @@ -15,6 +15,8 @@ from mock_vws._constants import ResultCodes +_REQUEST_TIMEOUT_SECONDS = 30 + @beartype def _send_request( @@ -34,7 +36,10 @@ def _send_request( prepared_request = request.prepare() prepared_request.headers = CaseInsensitiveDict(data=headers) session = requests.Session() - requests_response = session.send(request=prepared_request) + requests_response = session.send( + request=prepared_request, + timeout=_REQUEST_TIMEOUT_SECONDS, + ) return Response( text=requests_response.text, url=requests_response.url, diff --git a/uv.lock b/uv.lock index 15fa38766..722044b2d 100644 --- a/uv.lock +++ b/uv.lock @@ -1552,33 +1552,33 @@ wheels = [ [[package]] name = "pyproject-fmt" -version = "2.28.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/85/2f79ce9e7f97550be6261239efc172a55fe9aa49c70952db501807d8c8e8/pyproject_fmt-2.28.1.tar.gz", hash = "sha256:f1d50e4da97e4018755ca9b33bdb4cf7a12bd10484c909db14b6a0dc0f658d69", size = 302216, upload-time = "2026-08-24T15:15:51.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/97/ead39411dc92113700a8a30eef5b2ca59f0ab53fe7ada8352340b4d06cfe/pyproject_fmt-2.28.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0332e5d27f654fccebef42ef31e88ebd60461b49387020a51f0dc072b49bf577", size = 5385847, upload-time = "2026-08-24T15:14:51.936Z" }, - { url = "https://files.pythonhosted.org/packages/f0/90/cf12b3ecccd7cca288079f3c6d609b4847f659e06b1d4b7da498f6837a3f/pyproject_fmt-2.28.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:73c8fe2f921ee63a171907d3ee539f5b3b6394a3407ad1dbfea1c41cae030570", size = 5142650, upload-time = "2026-08-24T15:14:54.484Z" }, - { url = "https://files.pythonhosted.org/packages/47/19/fcc295e75ed75f3efede319e44bdbc3f4b4212271feb863983d196336b69/pyproject_fmt-2.28.1-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3e6589a358d2111305e68fab047b8b8c1f6fd888d83af7b3520d48b110440f3d", size = 5325396, upload-time = "2026-08-24T15:14:56.977Z" }, - { url = "https://files.pythonhosted.org/packages/49/b7/e905306d40c3016fa7830b641428a194106f29d53232340107ae606cf214/pyproject_fmt-2.28.1-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f954785a53dbb4b673dfaea9dae8d8173e6a59d59cbddf55c6dc845a444f0745", size = 5724936, upload-time = "2026-08-24T15:14:59.166Z" }, - { url = "https://files.pythonhosted.org/packages/32/1b/06e3d5fb5f0b2cb80ad6a86783d40c91c8d5a9bc474a3bde509d4a0723da/pyproject_fmt-2.28.1-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:5bb8e96cf753ca77ca2d31e936e2b5caddf4c52c270aa92c696c66265ec9f27c", size = 5430330, upload-time = "2026-08-24T15:15:01.129Z" }, - { url = "https://files.pythonhosted.org/packages/21/b8/9e2ed7ecf9c9ceeafea24e9868e0f4fd934c342c4360a46dfc95bf0b0878/pyproject_fmt-2.28.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8de853759fa97ee7f4aa43f7808dd5976c24a6dcb570f55b7e19cb5724762bc2", size = 5322272, upload-time = "2026-08-24T15:15:03.572Z" }, - { url = "https://files.pythonhosted.org/packages/4f/03/cd34eb6b19b5eceb562d392d1c0206e5f925f12424924c953bfef2371835/pyproject_fmt-2.28.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a224ea42b8852ff2154862983f6e40fdd76fd40cbc35d2c60bb02c3546bc58f", size = 5907185, upload-time = "2026-08-24T15:15:05.613Z" }, - { url = "https://files.pythonhosted.org/packages/ee/eb/36f0ebf0b711f68d65b731e5775014afef6132de9a18b7e20d9ca98b3684/pyproject_fmt-2.28.1-cp310-abi3-win_amd64.whl", hash = "sha256:e03195060d5b5a11bda70f33d8a62b545eff6c73882dc2391ebd846098a215eb", size = 5595591, upload-time = "2026-08-24T15:15:07.762Z" }, - { url = "https://files.pythonhosted.org/packages/ef/ce/1e3cfc266530ff5ce10d4815fe9932df26765e1f0306821654e9e0d83c8c/pyproject_fmt-2.28.1-cp310-abi3-win_arm64.whl", hash = "sha256:ce268a6152e46d9b3aaf34b51db7fc6305db5690c3140ee30a66c2a47fa96dda", size = 5136186, upload-time = "2026-08-24T15:15:10.211Z" }, - { url = "https://files.pythonhosted.org/packages/2a/c3/59ee2207a6a23a910e90a47bb3dd5dc6447619a27c62cbd5f45b159e2e73/pyproject_fmt-2.28.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7d2a5cfe78aa168e6a48b2a01ed6863cca54f2ede459d016aee3ee2862ad223d", size = 5387139, upload-time = "2026-08-24T15:15:12.427Z" }, - { url = "https://files.pythonhosted.org/packages/ab/76/d240ff9b18f4632e5977009f761c0b33799afcc4fb39ba16f23736cda169/pyproject_fmt-2.28.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:daa3382d70516d744e90e4a72d107e8b927eed820aea8170970fc3aa9744d0e8", size = 5139563, upload-time = "2026-08-24T15:15:14.901Z" }, - { url = "https://files.pythonhosted.org/packages/29/66/9eb5fe2b2d992aec01b42e85ef5c32b5210064e99db0cd6417ccdc7752e6/pyproject_fmt-2.28.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:5c121a0e9ca33f9bf0ada36677ae994c90b185a265e1bea849817242de379ae6", size = 5323920, upload-time = "2026-08-24T15:15:16.969Z" }, - { url = "https://files.pythonhosted.org/packages/10/61/9e9a3ce6b20b2d2a283b595a54b4a81da674c43616253a5bf901119469a0/pyproject_fmt-2.28.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a6d89550185474a115b96430d6ef8176e40f8c82b892076988cdf9b2baff0594", size = 5720576, upload-time = "2026-08-24T15:15:19.262Z" }, - { url = "https://files.pythonhosted.org/packages/b4/1b/e03addaec91831159e179c5c065d5fba7d6f6bbb52513962b6daeffc851d/pyproject_fmt-2.28.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:147da7f72c9b6655272f7010dbd5670e9e19a722e27b63cf0673b02272574e90", size = 5320336, upload-time = "2026-08-24T15:15:21.384Z" }, - { url = "https://files.pythonhosted.org/packages/55/a5/d13077498baec3f0cd5e68c1990819808a474277f9e5855ed482a98e1042/pyproject_fmt-2.28.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0326b6825d784233b001e116cf900e3902690adf379ccc8f39ea63466f87a722", size = 5904008, upload-time = "2026-08-24T15:15:23.381Z" }, - { url = "https://files.pythonhosted.org/packages/34/e7/7bdedbfc75337a9e008bcd4e5975351275e7f186bf6d7428515866e2987f/pyproject_fmt-2.28.1-cp314-cp314t-win_amd64.whl", hash = "sha256:bc65346cb914c066427ac6fa1908b97005544438af47775a54865596b51fc927", size = 5590227, upload-time = "2026-08-24T15:15:25.899Z" }, - { url = "https://files.pythonhosted.org/packages/1d/1d/590398b164af4ec74ec3c1da4575aff66ee85210b0f9bd21c9e11fe5805a/pyproject_fmt-2.28.1-cp314-cp314t-win_arm64.whl", hash = "sha256:01202c2b28c6861ee60b17f4f2cc239c66e32f382ed8585f234280e1c9cdf540", size = 5130131, upload-time = "2026-08-24T15:15:27.808Z" }, - { url = "https://files.pythonhosted.org/packages/be/4d/13b7e7aea436ee1dc65a11039b35d542641d9ea66fc13b44357968ab6602/pyproject_fmt-2.28.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ff21f60738d124fe3b698f91e3fb60bb94363aadb75d30f0a7b398312815299f", size = 5387716, upload-time = "2026-08-24T15:15:30.357Z" }, - { url = "https://files.pythonhosted.org/packages/8d/82/4e8960e1f47a2dc9bad1880a1575ac3c147478cc1c83f7a9ff429fad7436/pyproject_fmt-2.28.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:3056a41f2f5b728f99077ed66e08d5f6ddc05d87991c8b45a75b6bba0fbf7e7d", size = 5139993, upload-time = "2026-08-24T15:15:33.467Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/ad55f2db7c7fe07def11475e58e5f6af452311d85ff2c5a85917aa051f05/pyproject_fmt-2.28.1-cp315-cp315t-manylinux_2_28_aarch64.whl", hash = "sha256:990c24adcb7907417afcc13f750434bc3583dbf16686227c6cfbf534be96c069", size = 5323040, upload-time = "2026-08-24T15:15:35.934Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d3/75ce6a095d07144062f10d386d31fcc037b4f2bfc0b3bf73df236c3b39c8/pyproject_fmt-2.28.1-cp315-cp315t-manylinux_2_28_x86_64.whl", hash = "sha256:4ee55207f1daee00a5584bf67421ef1ef1479df07f37de2a34f9f4b6c160f363", size = 5720072, upload-time = "2026-08-24T15:15:38.233Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ed/44d00c87cd6c3fd3fb49aadb71fd52ef924868d88815a1fd675edc65a01a/pyproject_fmt-2.28.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:a03c6a0aeb54970e1f90310788315e42e0b5c22f107f851d4e20aac1a6eaa0d7", size = 5318969, upload-time = "2026-08-24T15:15:40.15Z" }, - { url = "https://files.pythonhosted.org/packages/a7/7a/0c7ac22e7f1ff84dd2879807c9f90c4e1d8592cce7a5de9b77e238b95ed4/pyproject_fmt-2.28.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:9a5e1c26ec293845eced9894be23ae3339d144c4de1b0aec0a93571f3a97306b", size = 5904447, upload-time = "2026-08-24T15:15:42.106Z" }, +version = "2.28.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/92/522a10aa6cba271db9aebe75b4c3e756dc231db263b0e92e66d5ff9138cc/pyproject_fmt-2.28.2.tar.gz", hash = "sha256:46a6b2dde5eca5ec26824a8a329cbdc045081fef76ebe2303996cccb9b203965", size = 303257, upload-time = "2026-08-28T22:20:28.677Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/99/a674ee900e602a223123a37389d520b8842f201a587ae516d0c2af35fa98/pyproject_fmt-2.28.2-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b72f53977c013f3d2498f286ce46db7647da671ad9aaa4a1f717ec6cc1671b8b", size = 5387440, upload-time = "2026-08-28T22:19:34.673Z" }, + { url = "https://files.pythonhosted.org/packages/79/64/dfa633d246be4f48c8f5ba93b89ead8b3a8a3db9b356ccd0b60a79ddc1f3/pyproject_fmt-2.28.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:a46f21e1fac9d5f32adfe0eb23b05c147763c8aebd3aab98b9de2697bd67b10a", size = 5144236, upload-time = "2026-08-28T22:19:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/47/2a/4e94c3dabb26e9dd6a5685ef67b2364151ec0d5ce8059361b98247006791/pyproject_fmt-2.28.2-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de7ee6b8dadd7fb3061e1a027451a08eaf67808ea29c7e2373751b33bc95493a", size = 5325008, upload-time = "2026-08-28T22:19:38.884Z" }, + { url = "https://files.pythonhosted.org/packages/8a/a2/ebd35c88d1d25cf99caee52d4b6fb5486923f69554edaaf3b72b54f7b14c/pyproject_fmt-2.28.2-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:83498c22d3110e49f933f73b8eef9182ff2c921af1e578244a65eb06f72cddaf", size = 5722792, upload-time = "2026-08-28T22:19:41.196Z" }, + { url = "https://files.pythonhosted.org/packages/ec/af/6d38176aef12b09268cb9b20efc24504490eb2219f5ff273258c7147a489/pyproject_fmt-2.28.2-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:c7bbeed7020cef28d44295f8e49f4e4fcd7eec819eef9be9141e6ecc23b202a8", size = 5430526, upload-time = "2026-08-28T22:19:43.206Z" }, + { url = "https://files.pythonhosted.org/packages/d7/40/cc3e4aedb9bdf5b9caccb0eacb9adf070092066364213badb9a64c1a3eb2/pyproject_fmt-2.28.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6479a82982d9cd0daad76cb2819624cf7cfd464da11b2798561dc54e019bc765", size = 5322550, upload-time = "2026-08-28T22:19:45.215Z" }, + { url = "https://files.pythonhosted.org/packages/cd/10/475d6683fd8d9a4a95049dc5605f6809d93cfb2d0f13f88b62eca7e69269/pyproject_fmt-2.28.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91e084bcfb713a24b2a1744bc2818658d6002136fcaa729a28d4a8e2172f66bf", size = 5906178, upload-time = "2026-08-28T22:19:47.537Z" }, + { url = "https://files.pythonhosted.org/packages/39/56/07502cc36c73aa2ef25bc1fc6edd4b30a56872641223eb45a64ace157442/pyproject_fmt-2.28.2-cp310-abi3-win_amd64.whl", hash = "sha256:e812fba58da1d0ca15ab8d7c6c7cfca9a4a344b6d5ca47372dafe05d0d568798", size = 5595424, upload-time = "2026-08-28T22:19:49.427Z" }, + { url = "https://files.pythonhosted.org/packages/85/33/e4c4dea65be4221e955e27df046165e7aceecf28a570f3148218c47a46f6/pyproject_fmt-2.28.2-cp310-abi3-win_arm64.whl", hash = "sha256:e4c83891a770a6d8dfc727928d296d9b4e0032b563ad3aa97b51eef1ad0bde15", size = 5132425, upload-time = "2026-08-28T22:19:51.421Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ca/f82dfe2d774c05066c6dcf78416aee96c13b32fa1a71d7fea824dc9612b2/pyproject_fmt-2.28.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:0af5b8965489b513145d2d0e5bcb5b7162796d63565e509624c1baf3fb6c37ff", size = 5388523, upload-time = "2026-08-28T22:19:53.183Z" }, + { url = "https://files.pythonhosted.org/packages/36/7b/c2a0f24011d4cc60fb02d5520676be77d9bcb843a31f6dd037ec5d5ffd14/pyproject_fmt-2.28.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9a0b61ea088b17722625f32ef5d5f0d14e2b40ec09051c0d28c1eb4cf29cd612", size = 5141159, upload-time = "2026-08-28T22:19:55.27Z" }, + { url = "https://files.pythonhosted.org/packages/05/3f/45b8e70257ebf959566e138f4d1944925ccfedaf3ef6e137be273939233b/pyproject_fmt-2.28.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:07bf4bca68d93ddb3001a2cdb089246fe543965a9262427dbd33a5d851d85fa6", size = 5323005, upload-time = "2026-08-28T22:19:57.363Z" }, + { url = "https://files.pythonhosted.org/packages/46/c9/a1bdb706e3969407a66b49b81f2391f4de6ca09390aa7f5aecd7ce25934e/pyproject_fmt-2.28.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a1d3148bfaebf811047a9738dfab80d419e6f2d99bef5344098698e664e265bf", size = 5718508, upload-time = "2026-08-28T22:19:59.411Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f4/fb576d93f04c213a8e5fec86a3fc244a142db0aa6ac3c7e6d2a01aa1ea36/pyproject_fmt-2.28.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1678986193465d04616da4f31f8a0464eba2029fa4f20e4ea051bce806a10adc", size = 5319985, upload-time = "2026-08-28T22:20:01.509Z" }, + { url = "https://files.pythonhosted.org/packages/d8/0d/941f69e9355ac0ab03c75628ffad253346f016fec68799bb43a2a014d53a/pyproject_fmt-2.28.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ddc5bd7bbb7cc8b74c4477fc9c2916e267303b3a24e3eb062b7f4961e129629a", size = 5903601, upload-time = "2026-08-28T22:20:04.342Z" }, + { url = "https://files.pythonhosted.org/packages/3f/27/38ba607c4003d5005f92907432c44e7484e5c7f3b94c25004ce1eb601438/pyproject_fmt-2.28.2-cp314-cp314t-win_amd64.whl", hash = "sha256:e01bb95fe0771ba604162e2c19592e65879845c6daaebaa77884d3235489016a", size = 5590202, upload-time = "2026-08-28T22:20:06.206Z" }, + { url = "https://files.pythonhosted.org/packages/51/61/51754d4c517be66b1a0c47cb4b2f20549abe6fff673ac2068f9095547040/pyproject_fmt-2.28.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d0b449805d01b1652bb6e89f40dfb0855fed32529138ce88897cc4430ec5bbd9", size = 5125832, upload-time = "2026-08-28T22:20:08.067Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5d/30b0eee5a64fd98497e2006a79deddda552c808c43fd98232acd6e6a1197/pyproject_fmt-2.28.2-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:e2f2311c007d15afdd590eb5ef94997d4d116d3c3de5f2bb8d7578c1ebbb6dde", size = 5387865, upload-time = "2026-08-28T22:20:09.954Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b1/b737ec591f67bd81624b57d91b951140b3eab6f4874e814fdea05c863859/pyproject_fmt-2.28.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:e290a076818cc33363875900c518a3363a5b2817688ab1335ae0961f6c87366b", size = 5140388, upload-time = "2026-08-28T22:20:11.803Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e2/cb3508aa7dc47b2290c5d95155d4650748b86791b96d5b99250692dc416e/pyproject_fmt-2.28.2-cp315-cp315t-manylinux_2_28_aarch64.whl", hash = "sha256:2c64aa62ddbfd053a4e529e40818514930e3624c330035922a2da63b5a97075f", size = 5322380, upload-time = "2026-08-28T22:20:13.783Z" }, + { url = "https://files.pythonhosted.org/packages/88/fa/1dd7adb6fd588a7e3ed7d5dbd5039da0fbea6d791ac364f3cd5ee2d8de8b/pyproject_fmt-2.28.2-cp315-cp315t-manylinux_2_28_x86_64.whl", hash = "sha256:47f2777233b729843fb9b297ada42754c23cac4bea30d26d1d6f305ffdce80e6", size = 5720085, upload-time = "2026-08-28T22:20:16.857Z" }, + { url = "https://files.pythonhosted.org/packages/a7/21/543ad9937d5c6d87d288e78ab1bd782d15f2bfba1af88e0b83a11d6d842a/pyproject_fmt-2.28.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:8884c3864106af23081ab4f827acf20219be34898e18ea16ad06cffb526ddc51", size = 5319273, upload-time = "2026-08-28T22:20:18.738Z" }, + { url = "https://files.pythonhosted.org/packages/31/38/589ce16adcd6416f03057b73222d58b5a299537b183c46e87ffe0884b8e4/pyproject_fmt-2.28.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:df3e6c5237417ef7d4fbc84ca312b886db563b107352a7accce364b358c9f39e", size = 5905255, upload-time = "2026-08-28T22:20:20.97Z" }, ] [[package]] @@ -2849,7 +2849,7 @@ requires-dist = [ { name = "pydocstyle", marker = "extra == 'dev'", specifier = "==6.3" }, { name = "pylint", extras = ["spelling"], marker = "extra == 'dev'", specifier = "==4.0.7" }, { name = "pylint-per-file-ignores", marker = "extra == 'dev'", specifier = "==3.2.1" }, - { name = "pyproject-fmt", marker = "extra == 'dev'", specifier = "==2.28.1" }, + { name = "pyproject-fmt", marker = "extra == 'dev'", specifier = "==2.28.2" }, { name = "pyrefly", marker = "extra == 'dev'", specifier = "==1.2.0" }, { name = "pyright", marker = "extra == 'dev'", specifier = "==1.1.411" }, { name = "pyroma", marker = "extra == 'dev'", specifier = "==5.0.1" },