gridfleet-testkit 0.9.4__tar.gz → 0.9.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/.gitignore +9 -2
  2. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/CHANGELOG.md +12 -0
  3. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/PKG-INFO +8 -8
  4. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/README.md +7 -6
  5. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/gridfleet_testkit/__init__.py +1 -1
  6. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/pyproject.toml +1 -2
  7. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/uv.lock +22 -24
  8. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/examples/__init__.py +0 -0
  9. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/examples/_example_helpers.py +0 -0
  10. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/examples/assets/hello-world.zip +0 -0
  11. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/examples/test_android_mobile_screenshot.py +0 -0
  12. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/examples/test_android_tv_screenshot.py +0 -0
  13. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/examples/test_firetv_screenshot.py +0 -0
  14. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/examples/test_ios_simulator_screenshot.py +0 -0
  15. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/examples/test_roku_screenshot.py +0 -0
  16. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/examples/test_roku_sideload_screenshot.py +0 -0
  17. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/examples/test_tvos_screenshot.py +0 -0
  18. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/gridfleet_testkit/allocation.py +0 -0
  19. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/gridfleet_testkit/appium.py +0 -0
  20. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/gridfleet_testkit/client.py +0 -0
  21. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/gridfleet_testkit/py.typed +0 -0
  22. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/gridfleet_testkit/pytest_plugin.py +0 -0
  23. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/gridfleet_testkit/sessions.py +0 -0
  24. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/gridfleet_testkit/types.py +0 -0
  25. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_allocation.py +0 -0
  26. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_appium.py +0 -0
  27. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_client.py +0 -0
  28. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_client_test_data.py +0 -0
  29. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_docs_contract.py +0 -0
  30. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_driver_agnostic_guard.py +0 -0
  31. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_package_metadata.py +0 -0
  32. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_pytest_plugin.py +0 -0
  33. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_pytest_plugin_grid_run_id_injection.py +0 -0
  34. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_pytest_plugin_test_data.py +0 -0
  35. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_sessions.py +0 -0
  36. {gridfleet_testkit-0.9.4 → gridfleet_testkit-0.9.5}/tests/test_sessions_resolve_device_handle.py +0 -0
@@ -42,8 +42,6 @@ dist/
42
42
 
43
43
  # Misc
44
44
  *.log
45
- .idea
46
- testing/screenshots
47
45
  frontend/test-results
48
46
 
49
47
  # Worktrees
@@ -51,3 +49,12 @@ frontend/test-results
51
49
 
52
50
  # Superpowers working docs
53
51
  .superpowers/
52
+ docs/superpowers/
53
+
54
+ # Local tooling
55
+ .claude/
56
+ .repowise/
57
+ .mcp.json
58
+
59
+ # Local-only device-state live test harness (not committed)
60
+ scripts/state-testing/
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to the GridFleet testkit (`gridfleet-testkit` on PyPI) are documented here.
4
4
 
5
+ ## [0.9.5](https://github.com/quidow/gridfleet/compare/gridfleet-testkit-v0.9.4...gridfleet-testkit-v0.9.5) (2026-06-04)
6
+
7
+
8
+ ### Dependencies
9
+
10
+ * **deps:** bump ruff in /testkit in the python-dependencies group ([#428](https://github.com/quidow/gridfleet/issues/428)) ([06ba6a7](https://github.com/quidow/gridfleet/commit/06ba6a7936769768436499d7e34f1053f3bf4710))
11
+
12
+
13
+ ### Documentation
14
+
15
+ * **docs:** align all docs with the actual implementation state ([#499](https://github.com/quidow/gridfleet/issues/499)) ([1d7a4ea](https://github.com/quidow/gridfleet/commit/1d7a4ea2afafbd5872856a01a9f73792c9ce5f7f))
16
+
5
17
  ## [0.9.4](https://github.com/quidow/gridfleet/compare/gridfleet-testkit-v0.9.3...gridfleet-testkit-v0.9.4) (2026-05-24)
6
18
 
7
19
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gridfleet-testkit
3
- Version: 0.9.4
3
+ Version: 0.9.5
4
4
  Summary: Supported pytest and run-orchestration helpers for GridFleet integrations
5
5
  Project-URL: Homepage, https://github.com/quidow/gridfleet
6
6
  Project-URL: Repository, https://github.com/quidow/gridfleet
@@ -27,7 +27,6 @@ Requires-Dist: httpx<1,>=0.27
27
27
  Requires-Dist: pytest<10,>=9.0.3
28
28
  Provides-Extra: dev
29
29
  Requires-Dist: mypy<3,>=1.20.2; extra == 'dev'
30
- Requires-Dist: pytest<10,>=9.0.3; extra == 'dev'
31
30
  Requires-Dist: ruff<1,>=0.15.12; extra == 'dev'
32
31
  Description-Content-Type: text/markdown
33
32
 
@@ -101,7 +100,7 @@ From a Git checkout or VCS URL that contains this package:
101
100
  uv pip install "git+https://github.com/<org>/<repo>.git#subdirectory=testkit"
102
101
  ```
103
102
 
104
- The package supports Python 3.10 and newer.
103
+ The package supports Python 3.10 through 3.14.
105
104
  `Appium-Python-Client` is installed as a runtime dependency because the pytest fixtures create real Appium sessions.
106
105
 
107
106
  ## Environment
@@ -150,7 +149,7 @@ If you need raw Appium control instead, omit `pack_id` and `platform_id`, then p
150
149
 
151
150
  - Creates an Appium session through `GRID_URL`
152
151
  - Injects `gridfleet:testName` with the pytest test name
153
- - Injects `gridfleet:run_id` when a `GRIDFLEET_RUN_ID` environment variable is present (for example, inside a reserved run)
152
+ - Always injects `gridfleet:run_id`, taken from the `GRIDFLEET_RUN_ID` environment variable when set (for example, inside a reserved run), otherwise defaulting to `"free"`
154
153
  - Reports final session status back to `GRIDFLEET_API_URL`
155
154
  - Exposes `device_config` for post-session config lookup using the runtime connection target
156
155
  - Exposes `device_test_data` for post-session operator-attached test data using the runtime connection target
@@ -251,7 +250,7 @@ When an operator edits device tags, GridFleet marks the device for re-verificati
251
250
 
252
251
  ### Worker Identity
253
252
 
254
- `worker_id` is an arbitrary string used for reservation telemetry and run attribution. For pytest-xdist, pass `request.config.workerinput["workerid"]` from worker processes; values are normally `gw0`, `gw1`, and so on. For controller-only flows, use `"controller"` or a stable hostname. For custom schedulers, use a UUID or job-specific worker name.
253
+ The `gridfleet_worker_id` fixture is informational only: it returns the pytest-xdist worker id (normally `gw0`, `gw1`, and so on), or `"controller"` for non-worker processes. It is never transmitted to the manager; use it client-side for local sharding or log correlation. For run attribution, pass the `created_by` argument to `GridFleetClient.reserve_devices` that is the only run-attribution field the reservation request carries.
255
254
 
256
255
  ### Reservation Flow
257
256
 
@@ -386,11 +385,12 @@ client = GridFleetClient()
386
385
  run = client.reserve_devices(
387
386
  name="my-test-run",
388
387
  requirements=[...],
389
- include=("config", "capabilities"),
388
+ include=("config",),
390
389
  )
391
390
  for device_handle in run["devices"]:
392
391
  allocated = hydrate_allocated_device(device_handle, run_id=run["id"], client=client)
393
- # zero follow-up GETs; allocated.config / allocated.live_capabilities populated inline
392
+ # no config follow-up GET; allocated.config populated inline.
393
+ # For capabilities, pass include= on this per-worker hydrate_allocated_device call.
394
394
  ```
395
395
 
396
396
  `device_config` and inline `config` payloads are returned verbatim from the manager. The testkit does not perform client-side secret masking or reveal toggles. Protect device config with manager authentication, operator access control, and your lab's secret-handling policy.
@@ -399,7 +399,7 @@ for device_handle in run["devices"]:
399
399
 
400
400
  `include=` must be a sequence of strings (tuple or list) — order is preserved in the emitted query parameter. Passing a bare string like `include="config"` raises `TypeError` to avoid silently splitting the value into characters.
401
401
 
402
- `hydrate_allocated_device` accepts device-handle payloads such as `reserve_response["devices"]` entries or rows returned by `get_device_by_connection_target`.
402
+ `hydrate_allocated_device` accepts device-handle payloads such as `reserve_response["devices"]` entries, which carry a top-level `device_id`. A `get_device_by_connection_target` row keys its primary id as `id`, so it must have that field remapped to `device_id` before being passed in.
403
403
 
404
404
  ## Examples
405
405
 
@@ -68,7 +68,7 @@ From a Git checkout or VCS URL that contains this package:
68
68
  uv pip install "git+https://github.com/<org>/<repo>.git#subdirectory=testkit"
69
69
  ```
70
70
 
71
- The package supports Python 3.10 and newer.
71
+ The package supports Python 3.10 through 3.14.
72
72
  `Appium-Python-Client` is installed as a runtime dependency because the pytest fixtures create real Appium sessions.
73
73
 
74
74
  ## Environment
@@ -117,7 +117,7 @@ If you need raw Appium control instead, omit `pack_id` and `platform_id`, then p
117
117
 
118
118
  - Creates an Appium session through `GRID_URL`
119
119
  - Injects `gridfleet:testName` with the pytest test name
120
- - Injects `gridfleet:run_id` when a `GRIDFLEET_RUN_ID` environment variable is present (for example, inside a reserved run)
120
+ - Always injects `gridfleet:run_id`, taken from the `GRIDFLEET_RUN_ID` environment variable when set (for example, inside a reserved run), otherwise defaulting to `"free"`
121
121
  - Reports final session status back to `GRIDFLEET_API_URL`
122
122
  - Exposes `device_config` for post-session config lookup using the runtime connection target
123
123
  - Exposes `device_test_data` for post-session operator-attached test data using the runtime connection target
@@ -218,7 +218,7 @@ When an operator edits device tags, GridFleet marks the device for re-verificati
218
218
 
219
219
  ### Worker Identity
220
220
 
221
- `worker_id` is an arbitrary string used for reservation telemetry and run attribution. For pytest-xdist, pass `request.config.workerinput["workerid"]` from worker processes; values are normally `gw0`, `gw1`, and so on. For controller-only flows, use `"controller"` or a stable hostname. For custom schedulers, use a UUID or job-specific worker name.
221
+ The `gridfleet_worker_id` fixture is informational only: it returns the pytest-xdist worker id (normally `gw0`, `gw1`, and so on), or `"controller"` for non-worker processes. It is never transmitted to the manager; use it client-side for local sharding or log correlation. For run attribution, pass the `created_by` argument to `GridFleetClient.reserve_devices` that is the only run-attribution field the reservation request carries.
222
222
 
223
223
  ### Reservation Flow
224
224
 
@@ -353,11 +353,12 @@ client = GridFleetClient()
353
353
  run = client.reserve_devices(
354
354
  name="my-test-run",
355
355
  requirements=[...],
356
- include=("config", "capabilities"),
356
+ include=("config",),
357
357
  )
358
358
  for device_handle in run["devices"]:
359
359
  allocated = hydrate_allocated_device(device_handle, run_id=run["id"], client=client)
360
- # zero follow-up GETs; allocated.config / allocated.live_capabilities populated inline
360
+ # no config follow-up GET; allocated.config populated inline.
361
+ # For capabilities, pass include= on this per-worker hydrate_allocated_device call.
361
362
  ```
362
363
 
363
364
  `device_config` and inline `config` payloads are returned verbatim from the manager. The testkit does not perform client-side secret masking or reveal toggles. Protect device config with manager authentication, operator access control, and your lab's secret-handling policy.
@@ -366,7 +367,7 @@ for device_handle in run["devices"]:
366
367
 
367
368
  `include=` must be a sequence of strings (tuple or list) — order is preserved in the emitted query parameter. Passing a bare string like `include="config"` raises `TypeError` to avoid silently splitting the value into characters.
368
369
 
369
- `hydrate_allocated_device` accepts device-handle payloads such as `reserve_response["devices"]` entries or rows returned by `get_device_by_connection_target`.
370
+ `hydrate_allocated_device` accepts device-handle payloads such as `reserve_response["devices"]` entries, which carry a top-level `device_id`. A `get_device_by_connection_target` row keys its primary id as `id`, so it must have that field remapped to `device_id` before being passed in.
370
371
 
371
372
  ## Examples
372
373
 
@@ -47,7 +47,7 @@ from .sessions import build_error_session_payload, resolve_device_handle_from_dr
47
47
  try:
48
48
  __version__ = version("gridfleet-testkit")
49
49
  except PackageNotFoundError:
50
- __version__ = "0.9.4"
50
+ __version__ = "0.9.5"
51
51
 
52
52
  __all__ = [
53
53
  "GRIDFLEET_API_URL",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "gridfleet-testkit"
7
- version = "0.9.4"
7
+ version = "0.9.5"
8
8
  description = "Supported pytest and run-orchestration helpers for GridFleet integrations"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -39,7 +39,6 @@ Security = "https://github.com/quidow/gridfleet/security/advisories/new"
39
39
 
40
40
  [project.optional-dependencies]
41
41
  dev = [
42
- "pytest>=9.0.3,<10",
43
42
  "mypy>=1.20.2,<3",
44
43
  "ruff>=0.15.12,<1",
45
44
  ]
@@ -136,7 +136,7 @@ wheels = [
136
136
 
137
137
  [[package]]
138
138
  name = "gridfleet-testkit"
139
- version = "0.9.4"
139
+ version = "0.9.5"
140
140
  source = { editable = "." }
141
141
  dependencies = [
142
142
  { name = "appium-python-client" },
@@ -147,7 +147,6 @@ dependencies = [
147
147
  [package.optional-dependencies]
148
148
  dev = [
149
149
  { name = "mypy" },
150
- { name = "pytest" },
151
150
  { name = "ruff" },
152
151
  ]
153
152
 
@@ -157,7 +156,6 @@ requires-dist = [
157
156
  { name = "httpx", specifier = ">=0.27,<1" },
158
157
  { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.20.2,<3" },
159
158
  { name = "pytest", specifier = ">=9.0.3,<10" },
160
- { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.3,<10" },
161
159
  { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15.12,<1" },
162
160
  ]
163
161
  provides-extras = ["dev"]
@@ -456,27 +454,27 @@ wheels = [
456
454
 
457
455
  [[package]]
458
456
  name = "ruff"
459
- version = "0.15.14"
460
- source = { registry = "https://pypi.org/simple" }
461
- sdist = { url = "https://files.pythonhosted.org/packages/dc/8a/8bce2894573e9dae6ff4d77fe34ad727d79b9e6238ad288c5638990d90f6/ruff-0.15.14.tar.gz", hash = "sha256:48e866b165be4a9bdbf310f7d3c9a07edef2fe8cd63ffeb4e00bb590506ebf9f", size = 4700910, upload-time = "2026-05-21T14:34:55.177Z" }
462
- wheels = [
463
- { url = "https://files.pythonhosted.org/packages/b9/c8/74a92c6ff9fcfb4f1f947126d3ebee8389276e161ecc85de5bda7cda51bd/ruff-0.15.14-py3-none-linux_armv6l.whl", hash = "sha256:8dd2db9416e487c8d4b01fa7056bb02c4d05969d4f8d17a08c229c2f4ff3c108", size = 10739177, upload-time = "2026-05-21T14:34:37.332Z" },
464
- { url = "https://files.pythonhosted.org/packages/45/91/254a35c20acc38a7223c9d2d594af12e794432464f2cdeb52af1dc4a892d/ruff-0.15.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:be4ff55af755bd71a00ab3dc6bd7ffc467bd76e0df6881e286c2e3d23e8fb43b", size = 11144969, upload-time = "2026-05-21T14:34:43.978Z" },
465
- { url = "https://files.pythonhosted.org/packages/56/9e/d13e40f83b8d0a94430e6778ce1d94a43b38cf2efe63278bdd2b4c65abbf/ruff-0.15.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:48d5909d7d06276ce7dde6d32bfa4b0d4cb2651145cd8ee4b440722cbc77832f", size = 10478207, upload-time = "2026-05-21T14:34:48.378Z" },
466
- { url = "https://files.pythonhosted.org/packages/8d/f1/b15a7839fa4f332f8acec78e20564f26bb2d866e3d21710b877fd0263000/ruff-0.15.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca8cbfa94c4f90984a67561978602746d4cd27103568f745fa90eee3f0d4107d", size = 10818459, upload-time = "2026-05-21T14:34:22.318Z" },
467
- { url = "https://files.pythonhosted.org/packages/45/33/53d651177f84f94b400a0e27f8824eeada3dddc9d5ee8aeb048f4352a520/ruff-0.15.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a6bbc0333f1ab053423bcbf6226477d266ca7cec7738c4c8e3f55647803f3c4", size = 10541800, upload-time = "2026-05-21T14:34:20.209Z" },
468
- { url = "https://files.pythonhosted.org/packages/b8/a6/868f87e0bf9786ed24b5d0d0ad8676b8a94fd1912f42cddf9cfc7857818a/ruff-0.15.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a24a4f7605d7003a6674d4387651effd939dead3fddd0f36561eb77a9a2e542", size = 11342149, upload-time = "2026-05-21T14:34:46.365Z" },
469
- { url = "https://files.pythonhosted.org/packages/a7/8b/38cd5c19faffdcc05a408d2b78edccc69492ab9720eadb49ea15ef80d768/ruff-0.15.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:049b5326e53ed80978f2fc041a280603f69dd6b0c95464342a2bb4572d9d9e2f", size = 12212563, upload-time = "2026-05-21T14:34:28.579Z" },
470
- { url = "https://files.pythonhosted.org/packages/3e/4d/a3c5b874a556d5731e3e657aaf04311bb76f0a5c3ec220ed43051be6b64b/ruff-0.15.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4ed42e6696c8dfa5f06728e6441993901f548eb92d73bc472cb5a38d1395fbf", size = 11493299, upload-time = "2026-05-21T14:34:41.836Z" },
471
- { url = "https://files.pythonhosted.org/packages/1e/c0/56472c251d09858a53e51efbd485b09e1995d8731668b76d52e5dd6ee0f1/ruff-0.15.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:715c543cf450c4888251f91c52f1942a800541d9bddd7ac060aa4e6b77ae7cba", size = 11455931, upload-time = "2026-05-21T14:34:57.276Z" },
472
- { url = "https://files.pythonhosted.org/packages/2c/4a/e2e7b4d8dbf233d4eace59c75bc3435fa6d8bd3bae82d351d4e4300c0fd1/ruff-0.15.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72ebab6013ec887d439d8b7593737a0a4ffb06d45d209d4e4bf2e92813082d3f", size = 11400794, upload-time = "2026-05-21T14:34:39.773Z" },
473
- { url = "https://files.pythonhosted.org/packages/97/c7/83c0539fe34c3e09136204d1e75d6052492364e0b3cb05e9465423f567d7/ruff-0.15.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:49072d36abdbe97a8dd7f480afe9c675699c0c495d4c84076e2c1203c4550581", size = 10804759, upload-time = "2026-05-21T14:34:31.045Z" },
474
- { url = "https://files.pythonhosted.org/packages/86/a6/18f2bfc095a2ab4a78745644e428205532ce6653a5d0fa8501572891534d/ruff-0.15.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:958522aee105068640c2c2ceae08f413ae44d922f52a1374ac13d6a96032fc93", size = 10539517, upload-time = "2026-05-21T14:34:53.064Z" },
475
- { url = "https://files.pythonhosted.org/packages/54/3a/5a8b3b69c654d4e4bf1d246ac5b49cbcdac6eaab6905925f8915f31e3b80/ruff-0.15.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f3707da619a143a2e8830e2abab8224478d69ace2d28cb6c20543ae97c36bf61", size = 11065169, upload-time = "2026-05-21T14:34:24.484Z" },
476
- { url = "https://files.pythonhosted.org/packages/ed/c5/8864e4e7925b836ea354b31d57641ec03830564e281a8b6f061f8c3e0ec1/ruff-0.15.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:bb01d645694e3ec0102105d07ef2d53703970407d59c04e59d3ba0b7a1d53553", size = 11560214, upload-time = "2026-05-21T14:34:50.975Z" },
477
- { url = "https://files.pythonhosted.org/packages/36/38/012bf76752e1f89ed50b77b99532d90f3a3e287bc7918e1fc0948ac866ac/ruff-0.15.14-py3-none-win32.whl", hash = "sha256:6d0c1ad2a0ab718d39b6d8fd2217981ce4d625cd96a720095f798fb47d8b13e6", size = 10805548, upload-time = "2026-05-21T14:34:33.453Z" },
478
- { url = "https://files.pythonhosted.org/packages/d1/b7/4ea2c170f10ad760fff2a5250beb18897719dc8b52b53a24cddbb9dd3f19/ruff-0.15.14-py3-none-win_amd64.whl", hash = "sha256:802342981e056db3851a7836e5b070f8f15f67d4a685ae2a6160939d364b2902", size = 11939523, upload-time = "2026-05-21T14:34:18.077Z" },
479
- { url = "https://files.pythonhosted.org/packages/62/d5/bc97ff895ec35cf3925d4bd60f3b39d822f377a446906ec9bcc87405e59b/ruff-0.15.14-py3-none-win_arm64.whl", hash = "sha256:ff47b90a9ef6a40c9e2f3b479c1fb78531adf055b94c1eba0a7ba04b31951826", size = 11208607, upload-time = "2026-05-21T14:34:26.525Z" },
457
+ version = "0.15.15"
458
+ source = { registry = "https://pypi.org/simple" }
459
+ sdist = { url = "https://files.pythonhosted.org/packages/84/6f/a76f7d96e5c962f5b69cee865e49c15c1116897c01990faa8a57edb62e7f/ruff-0.15.15.tar.gz", hash = "sha256:b8dff018130b46d8e5bf0f926ef6b60cf871d6d5ae45fc9334e09632daa741d6", size = 4706985, upload-time = "2026-05-28T14:16:57.784Z" }
460
+ wheels = [
461
+ { url = "https://files.pythonhosted.org/packages/fa/9d/3a45c05b8ab04b4705989de70a79008e27c8003296a0feaee9edc18dd7e9/ruff-0.15.15-py3-none-linux_armv6l.whl", hash = "sha256:cf93e5388f412e1b108b1f8b34a6e036b70fe8aff89393befad96fe48670311b", size = 10710652, upload-time = "2026-05-28T14:16:06.701Z" },
462
+ { url = "https://files.pythonhosted.org/packages/05/66/da974431624bf3b49f6ee1f9543c02d929ff1cba78b0d5a79c38cf21f744/ruff-0.15.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac5a646d1f6a7dadd5d50842dae2c1f9862ac887ef5d1b1375e02def791fde6e", size = 11096615, upload-time = "2026-05-28T14:16:23.313Z" },
463
+ { url = "https://files.pythonhosted.org/packages/8c/09/7443452e5d290230a712103f2fdceeef7184f3ec99a2bd01c8be78aaceb5/ruff-0.15.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:77d955a431430c66f72dd94e379ad38a16daea3d25094872ac4edf9e797be530", size = 10436683, upload-time = "2026-05-28T14:16:40.974Z" },
464
+ { url = "https://files.pythonhosted.org/packages/53/01/d330c26a57fa4f3943a14424904027428315b700fe4d14a84bb123a649e5/ruff-0.15.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7614ee79c69788cf6cedd568069ade9cecc22a1ad20494efe8d0c9ebb4b622d4", size = 10769064, upload-time = "2026-05-28T14:16:28.905Z" },
465
+ { url = "https://files.pythonhosted.org/packages/1d/85/cc8770f8bdff541b1da8392d1634141fe4a0e3f4ee596605959b7906c27f/ruff-0.15.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cdb1679e06a1f6b47bc384714ae96f6e2fb65ca441eb78c43d2ca554176ce1f", size = 10511987, upload-time = "2026-05-28T14:16:43.732Z" },
466
+ { url = "https://files.pythonhosted.org/packages/7c/29/8c190c1472b63013583ba391f3342036e02010544c1270455ed8e519bdf3/ruff-0.15.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2728b93d7b23a603ea2c0ac6eb73d760bd38ec9de35f35fb41e18f7a3fee7622", size = 11275100, upload-time = "2026-05-28T14:16:55.244Z" },
467
+ { url = "https://files.pythonhosted.org/packages/9f/6b/7e145ce2cc8e63d6834eca03d83a0e18d121def5c69f91b4cf4011ed4879/ruff-0.15.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be582fcc0db438902c7792b08d6ddf6c9b9e21addaa10092c2c741cfb09e5a45", size = 12176903, upload-time = "2026-05-28T14:16:14.368Z" },
468
+ { url = "https://files.pythonhosted.org/packages/80/a3/d5974637f68e451f7fadf015cf3101d1cd7d8ba5027cffe0b9e3826ebe6b/ruff-0.15.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7aa77465b8ecaf1a27bea098d696f7fed5e1eccbd10b321b682d6de586ae5627", size = 11404550, upload-time = "2026-05-28T14:16:20.138Z" },
469
+ { url = "https://files.pythonhosted.org/packages/fe/1c/e6e5e568f22be4fb05d6244234aba384c06b451252453b821e1a529263cf/ruff-0.15.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48decfa11d740de4889de623be1463308346312f2409a56e24aa280c86162dc4", size = 11382027, upload-time = "2026-05-28T14:16:46.615Z" },
470
+ { url = "https://files.pythonhosted.org/packages/1d/01/170921b49fcd2e8858825593f91cf7146c3e40a5c3e6df763e4bb0484dde/ruff-0.15.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a5015088452ca0081387063649ec67f06d3d1d6b8b936a1f836b5e9657ecd48c", size = 11366041, upload-time = "2026-05-28T14:16:26.247Z" },
471
+ { url = "https://files.pythonhosted.org/packages/87/54/a7bad711d7de93254e15e06a4c375b89a03d18de45d3e5dcc86a4472fb1a/ruff-0.15.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5294aab6356c81600fcdea3a62bb1b924dfd5e91767c12318d3f68f86af57cd", size = 10741795, upload-time = "2026-05-28T14:16:17.11Z" },
472
+ { url = "https://files.pythonhosted.org/packages/c9/31/38c075963668f8b41c6914ee0f6f318727fbe30ab9145cb29e6df464c5fa/ruff-0.15.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:db5bd4d802415cca656dc1616070b725952d6ae95eb5d4831e49fbd94a38f75f", size = 10511117, upload-time = "2026-05-28T14:16:31.767Z" },
473
+ { url = "https://files.pythonhosted.org/packages/9d/96/6ff689e1f7e375d1d97075eca022f74c2bab59554a432fe4d2e6f091986a/ruff-0.15.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:587a6278ed42059191c1a466e490bd7930fb50bd2e255398bc29616c895a61cb", size = 10994867, upload-time = "2026-05-28T14:16:35.149Z" },
474
+ { url = "https://files.pythonhosted.org/packages/c3/c2/5dce0ab9f92a8d534fa62b9bf9caca3eddb8c1a81b616f5e195ada4f0d6e/ruff-0.15.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:df0c1c084f5f4be9812f61518a45c440d3c30d69ce4bf6c5270e66d38338f02a", size = 11482101, upload-time = "2026-05-28T14:16:49.598Z" },
475
+ { url = "https://files.pythonhosted.org/packages/b1/c0/1003b60edd697c649faf61f1a34094b1abb38fb3d1181e3f895781250a08/ruff-0.15.15-py3-none-win32.whl", hash = "sha256:29428ea79694afbe756d45fd59b36f22b6b020dc0443cf7de0173046236964b9", size = 10716774, upload-time = "2026-05-28T14:16:52.337Z" },
476
+ { url = "https://files.pythonhosted.org/packages/02/a8/1269eddd6945a06c23f055ef7848886e37cf9d6a8bebb386a3115f01470c/ruff-0.15.15-py3-none-win_amd64.whl", hash = "sha256:8df0323902e15e24bc4bf246da830573d3cf3352bd0b9a164eab335d111ff4a4", size = 11868463, upload-time = "2026-05-28T14:16:11.333Z" },
477
+ { url = "https://files.pythonhosted.org/packages/4e/b2/920464c907b191e37469d477a1aa8bc048b8f36c4c1610dfa4ab87b39e18/ruff-0.15.15-py3-none-win_arm64.whl", hash = "sha256:3c8ceca6792f38196b8f589bc92eccd03eef286602da92e5dc05cc42ef6441b7", size = 11138498, upload-time = "2026-05-28T14:16:38.425Z" },
480
478
  ]
481
479
 
482
480
  [[package]]