span-panel-api 2.5.2__tar.gz → 2.5.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.
Files changed (80) hide show
  1. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/CHANGELOG.md +38 -27
  2. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/PKG-INFO +1 -1
  3. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/pyproject.toml +1 -1
  4. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/accumulator.py +17 -11
  5. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_accumulator.py +37 -31
  6. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_mqtt_homie.py +9 -4
  7. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/uv.lock +53 -35
  8. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.codefactor +0 -0
  9. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.codefactor.yml +0 -0
  10. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.deps-installed +0 -0
  11. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  12. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  13. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.github/dependabot.yml +0 -0
  14. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.github/workflows/ci.yml +0 -0
  15. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.github/workflows/dependabot-auto-approve.yml +0 -0
  16. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.github/workflows/dependabot-auto-merge.yml +0 -0
  17. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.github/workflows/release.yml +0 -0
  18. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.gitignore +0 -0
  19. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.markdownlint-cli2.jsonc +0 -0
  20. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.markdownlint.json +0 -0
  21. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.pre-commit-config.yaml +0 -0
  22. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.prettierrc.json +0 -0
  23. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.vscode/extensions.json +0 -0
  24. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/.vscode/tasks.json +0 -0
  25. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/DEVELOPMENT.md +0 -0
  26. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/LICENSE +0 -0
  27. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/README.md +0 -0
  28. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/SECURITY.md +0 -0
  29. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/conftest.py +0 -0
  30. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/developer_attribute_readme.md +0 -0
  31. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/openapi.json +0 -0
  32. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/pytest.ini +0 -0
  33. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/pytest_output.log +0 -0
  34. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/scripts/__init__.py +0 -0
  35. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/scripts/coverage.py +0 -0
  36. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/scripts/format.sh +0 -0
  37. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/scripts/format_markdown.py +0 -0
  38. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/scripts/test_live_auth.py +0 -0
  39. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/setup-hooks.sh +0 -0
  40. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/__init__.py +0 -0
  41. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/_http.py +0 -0
  42. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/auth.py +0 -0
  43. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/const.py +0 -0
  44. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/detection.py +0 -0
  45. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/exceptions.py +0 -0
  46. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/factory.py +0 -0
  47. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/models.py +0 -0
  48. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/__init__.py +0 -0
  49. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/async_client.py +0 -0
  50. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/client.py +0 -0
  51. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/connection.py +0 -0
  52. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/const.py +0 -0
  53. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/field_metadata.py +0 -0
  54. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/homie.py +0 -0
  55. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/mqtt/models.py +0 -0
  56. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/phase_validation.py +0 -0
  57. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/protocol.py +0 -0
  58. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/src/span_panel_api/py.typed +0 -0
  59. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/conftest.py +0 -0
  60. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/fixtures/configs/simulation_config_32_circuit.yaml +0 -0
  61. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/fixtures/configs/simulation_config_40_circuit_with_battery.yaml +0 -0
  62. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/fixtures/configs/simulation_config_8_tab_workshop.yaml +0 -0
  63. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/fixtures/v2/README.md +0 -0
  64. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/fixtures/v2/homie_schema.json +0 -0
  65. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/fixtures/v2/status.json +0 -0
  66. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/simulation_fixtures/circuits.response.txt +0 -0
  67. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/simulation_fixtures/panel.response.txt +0 -0
  68. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/simulation_fixtures/soe.response.txt +0 -0
  69. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/simulation_fixtures/status.response.txt +0 -0
  70. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_async_mqtt_client.py +0 -0
  71. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_auth_and_homie_helpers.py +0 -0
  72. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_detection_auth.py +0 -0
  73. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_field_metadata.py +0 -0
  74. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_mqtt_bridge.py +0 -0
  75. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_mqtt_connect_flow.py +0 -0
  76. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_mqtt_debounce.py +0 -0
  77. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_phase_validation_configs.py +0 -0
  78. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_phase_validation_errors.py +0 -0
  79. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_protocol_conformance.py +0 -0
  80. {span_panel_api-2.5.2 → span_panel_api-2.5.3}/tests/test_protocol_models.py +0 -0
@@ -4,7 +4,17 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [2.5.2] - 04/2026
7
+ ## [2.5.3] - 04/2026
8
+
9
+ ### Fixed
10
+
11
+ - **Preserve property values on lifecycle reset** — the 2.5.2 property clear caused `_parse_float('')` to return `0.0` for energy counters during panel reboots or network interruptions, triggering false dip-compensation offsets in the integration that
12
+ permanently inflated energy sensor values. Removed the property/timestamp/target clearing from `_handle_description()`. Pre-reboot values now serve as safe placeholders until the panel re-publishes fresh data.
13
+ - **Snapshot cache invalidated on reboot** — the generation counter still increments on lifecycle resets, forcing consumers to discard cached snapshots and rebuild from current accumulator state.
14
+
15
+ ## [2.5.2] - 04/2026 (retired)
16
+
17
+ > **Retired:** The property-clearing behavior introduced in this release caused false energy dip spikes. Superseded by 2.5.3.
8
18
 
9
19
  ### Fixed
10
20
 
@@ -334,29 +344,30 @@ Package versions prior to 2.0.0 depend on the SPAN v1 REST API. SPAN will sunset
334
344
 
335
345
  ## Version History Summary
336
346
 
337
- | Version | Date | Transport | Summary |
338
- | ---------- | ------- | ---------- | ------------------------------------------------------------------------------------------------- |
339
- | **2.5.2** | 04/2026 | MQTT/Homie | Clear stale property values on panel reboot; fast reboot detection; cache generation invalidation |
340
- | **2.5.1** | 04/2026 | MQTT/Homie | Replace assert with RuntimeError; fix bandit pre-commit hook |
341
- | **2.5.0** | 03/2026 | MQTT/Homie | Homie accumulator layer, $target support, dirty-node snapshot caching |
342
- | **2.4.2** | 03/2026 | MQTT/Homie | SSL context creation moved to executor |
343
- | **2.4.1** | 03/2026 | MQTT/Homie | License metadata, loosened httpx constraint |
344
- | **2.4.0** | 03/2026 | MQTT/Homie | proximityProven, injected HTTP client, executor file I/O, type alias, test cleanup |
345
- | **2.3.2** | 03/2026 | MQTT/Homie | FQDN management endpoints |
346
- | **2.3.1** | 03/2026 | MQTT/Homie | MQTT connection errors wrapped as SpanPanelConnectionError |
347
- | **2.3.0** | 03/2026 | MQTT/Homie | Simulation engine removed |
348
- | **2.2.4** | 03/2026 | MQTT/Homie | Negative zero fix on idle circuits |
349
- | **2.2.3** | 03/2026 | MQTT/Homie | Panel size from Homie schema; `panel_size` always populated on snapshot |
350
- | **2.0.2** | 03/2026 | MQTT/Homie | EVSE (EV charger) snapshot model, Homie parsing, simulation support |
351
- | **2.0.1** | 03/2026 | MQTT/Homie | Full BESS metadata parsing, README documentation |
352
- | **2.0.0** | 02/2026 | MQTT/Homie | Ground-up rewrite: MQTT-only, protocol-based API, real-time push, PV/BESS metadata |
353
- | **1.1.14** | 12/2025 | REST | Keep-Alive and RemoteProtocolError handling |
354
- | **1.1.9** | 9/2025 | REST | Simulation sign corrections |
355
- | **1.1.8** | 2024 | REST | Simulation power sign fix |
356
- | **1.1.6** | 2024 | REST | YAML simulation API, battery simulation |
357
- | **1.1.5** | 2024 | REST | Simulation edge cases |
358
- | **1.1.4** | 2024 | REST | Formatting and linting |
359
- | **1.1.3** | 2024 | REST | Test and lint fixes |
360
- | **1.1.2** | 2024 | REST | Simulation mode added |
361
- | **1.1.1** | 2024 | REST | Dependency updates |
362
- | **1.1.0** | 2024 | REST | Initial release |
347
+ | Version | Date | Transport | Summary |
348
+ | ---------- | ------- | ---------- | ----------------------------------------------------------------------------------------- |
349
+ | **2.5.3** | 04/2026 | MQTT/Homie | Preserve property values on lifecycle reset; fix false energy dip spikes from 2.5.2 clear |
350
+ | **2.5.2** | 04/2026 | MQTT/Homie | _(retired)_ Clear stale property values on panel reboot; caused false energy dip spikes |
351
+ | **2.5.1** | 04/2026 | MQTT/Homie | Replace assert with RuntimeError; fix bandit pre-commit hook |
352
+ | **2.5.0** | 03/2026 | MQTT/Homie | Homie accumulator layer, $target support, dirty-node snapshot caching |
353
+ | **2.4.2** | 03/2026 | MQTT/Homie | SSL context creation moved to executor |
354
+ | **2.4.1** | 03/2026 | MQTT/Homie | License metadata, loosened httpx constraint |
355
+ | **2.4.0** | 03/2026 | MQTT/Homie | proximityProven, injected HTTP client, executor file I/O, type alias, test cleanup |
356
+ | **2.3.2** | 03/2026 | MQTT/Homie | FQDN management endpoints |
357
+ | **2.3.1** | 03/2026 | MQTT/Homie | MQTT connection errors wrapped as SpanPanelConnectionError |
358
+ | **2.3.0** | 03/2026 | MQTT/Homie | Simulation engine removed |
359
+ | **2.2.4** | 03/2026 | MQTT/Homie | Negative zero fix on idle circuits |
360
+ | **2.2.3** | 03/2026 | MQTT/Homie | Panel size from Homie schema; `panel_size` always populated on snapshot |
361
+ | **2.0.2** | 03/2026 | MQTT/Homie | EVSE (EV charger) snapshot model, Homie parsing, simulation support |
362
+ | **2.0.1** | 03/2026 | MQTT/Homie | Full BESS metadata parsing, README documentation |
363
+ | **2.0.0** | 02/2026 | MQTT/Homie | Ground-up rewrite: MQTT-only, protocol-based API, real-time push, PV/BESS metadata |
364
+ | **1.1.14** | 12/2025 | REST | Keep-Alive and RemoteProtocolError handling |
365
+ | **1.1.9** | 9/2025 | REST | Simulation sign corrections |
366
+ | **1.1.8** | 2024 | REST | Simulation power sign fix |
367
+ | **1.1.6** | 2024 | REST | YAML simulation API, battery simulation |
368
+ | **1.1.5** | 2024 | REST | Simulation edge cases |
369
+ | **1.1.4** | 2024 | REST | Formatting and linting |
370
+ | **1.1.3** | 2024 | REST | Test and lint fixes |
371
+ | **1.1.2** | 2024 | REST | Simulation mode added |
372
+ | **1.1.1** | 2024 | REST | Dependency updates |
373
+ | **1.1.0** | 2024 | REST | Initial release |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: span-panel-api
3
- Version: 2.5.2
3
+ Version: 2.5.3
4
4
  Summary: A client library for SPAN Panel API
5
5
  Project-URL: Homepage, https://github.com/SpanPanel/span-panel-api
6
6
  Project-URL: Issues, https://github.com/SpanPanel/span-panel-api/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "span-panel-api"
3
- version = "2.5.2"
3
+ version = "2.5.3"
4
4
  description = "A client library for SPAN Panel API"
5
5
  authors = [
6
6
  {name = "SpanPanel"}
@@ -56,8 +56,9 @@ class HomiePropertyAccumulator:
56
56
  # Node type mapping from $description
57
57
  self._node_types: dict[str, str] = {}
58
58
 
59
- # Generation counter — incremented when $description clears property
60
- # values so consumers can invalidate caches built from stale data.
59
+ # Generation counter — incremented whenever a new lifecycle's
60
+ # $description is accepted (including initial boot) so consumers
61
+ # can invalidate snapshot caches built from prior-lifecycle data.
61
62
  self._generation: int = 0
62
63
 
63
64
  # Dirty tracking
@@ -85,7 +86,7 @@ class HomiePropertyAccumulator:
85
86
 
86
87
  @property
87
88
  def generation(self) -> int:
88
- """Counter incremented on the initial $description and after lifecycle resets."""
89
+ """Counter incremented on each new lifecycle's ``$description`` to invalidate consumer caches."""
89
90
  return self._generation
90
91
 
91
92
  def is_ready(self) -> bool:
@@ -225,16 +226,21 @@ class HomiePropertyAccumulator:
225
226
  # _handle_state() already reset _received_description to False due to
226
227
  # a state change that starts a new panel lifecycle, including
227
228
  # $state=disconnected/lost and other non-ready states such as init.
228
- # This means the panel rebooted while we were connected. On a pure
229
- # MQTT reconnect (no panel reboot), _received_description is still
230
- # True from the previous session so we skip the clear the retained
231
- # property messages will carry the correct (unchanged) values.
229
+ # Increment the generation counter to invalidate consumer snapshot
230
+ # caches, but preserve property values — pre-reboot readings serve
231
+ # as safe placeholders until the panel re-publishes. Clearing values
232
+ # would emit 0.0 for energy counters via _parse_float(""), triggering
233
+ # false dip-compensation offsets in the integration.
234
+ #
235
+ # On a pure MQTT reconnect (no panel reboot), _received_description
236
+ # is still True from the previous session so we skip this block —
237
+ # the retained property messages carry the correct (unchanged) values.
232
238
  if not self._received_description:
233
- self._property_values.clear()
234
- self._property_timestamps.clear()
235
- self._target_values.clear()
236
239
  self._generation += 1
237
- _LOGGER.debug("Cleared stale property values (generation %d)", self._generation)
240
+ _LOGGER.debug(
241
+ "Panel reboot detected (generation %d); preserving property values as placeholders",
242
+ self._generation,
243
+ )
238
244
 
239
245
  self._received_description = True
240
246
  self._node_types.clear()
@@ -164,17 +164,21 @@ class TestLifecycleDisconnection:
164
164
 
165
165
 
166
166
  # ---------------------------------------------------------------------------
167
- # Panel reboot: $description clears stale property values
167
+ # Panel reboot: $description preserves property values as placeholders
168
168
  # ---------------------------------------------------------------------------
169
169
 
170
170
 
171
- class TestDescriptionClearsProperties:
172
- """Verify that a panel reboot (disconnected -> $description) clears stale data.
171
+ class TestDescriptionOnReboot:
172
+ """Verify that a panel reboot preserves property values as placeholders.
173
173
 
174
- The clear only happens when _received_description is False, which requires
175
- a $state=disconnected (or lost) to have reset the lifecycle first. A
176
- re-delivered retained $description on a pure network reconnect does NOT
177
- clear, because _received_description is still True from the previous session.
174
+ On lifecycle reset ($state=disconnected/lost or non-ready states like init),
175
+ _received_description is set to False. The subsequent $description increments
176
+ the generation counter (invalidating consumer snapshot caches) but does NOT
177
+ clear property values pre-reboot values serve as safe placeholders until
178
+ the panel re-publishes fresh data.
179
+
180
+ A re-delivered retained $description on a pure network reconnect does NOT
181
+ increment generation, because _received_description is still True.
178
182
  """
179
183
 
180
184
  def _simulate_reboot(self, acc: HomiePropertyAccumulator) -> None:
@@ -183,36 +187,37 @@ class TestDescriptionClearsProperties:
183
187
  acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
184
188
  acc.handle_message(f"{PREFIX}/$state", "ready")
185
189
 
186
- def test_description_clears_property_values_on_reboot(self):
187
- """A panel reboot must clear property values from the previous lifecycle."""
190
+ def test_reboot_preserves_property_values(self):
191
+ """A panel reboot must preserve property values as placeholders."""
188
192
  acc = HomiePropertyAccumulator(SERIAL)
189
193
  _make_ready(acc)
190
194
  acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "1000")
191
195
  assert acc.get_prop("circuit-1", "exported-energy") == "1000"
192
196
 
193
- # Panel reboots — $state=disconnected resets lifecycle, then $description clears
197
+ # Panel reboots — $state=disconnected resets lifecycle, then $description preserves
194
198
  self._simulate_reboot(acc)
195
- assert acc.get_prop("circuit-1", "exported-energy") == ""
199
+ assert acc.get_prop("circuit-1", "exported-energy") == "1000"
196
200
 
197
- def test_description_clears_timestamps_on_reboot(self):
198
- """Timestamps must also be cleared so stale timing data doesn't persist."""
201
+ def test_reboot_preserves_timestamps(self):
202
+ """Timestamps must be preserved on reboot so callers can detect stale data if needed."""
199
203
  acc = HomiePropertyAccumulator(SERIAL)
200
204
  _make_ready(acc)
201
205
  acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "1000")
202
- assert acc.get_timestamp("circuit-1", "exported-energy") > 0
206
+ ts_before = acc.get_timestamp("circuit-1", "exported-energy")
207
+ assert ts_before > 0
203
208
 
204
209
  self._simulate_reboot(acc)
205
- assert acc.get_timestamp("circuit-1", "exported-energy") == 0
210
+ assert acc.get_timestamp("circuit-1", "exported-energy") == ts_before
206
211
 
207
- def test_description_clears_target_values_on_reboot(self):
208
- """Target values must also be cleared on panel reboot."""
212
+ def test_reboot_preserves_target_values(self):
213
+ """Target values must also be preserved on panel reboot."""
209
214
  acc = HomiePropertyAccumulator(SERIAL)
210
215
  _make_ready(acc)
211
216
  acc.handle_message(f"{PREFIX}/circuit-1/relay/$target", "OPEN")
212
217
  assert acc.get_target("circuit-1", "relay") == "OPEN"
213
218
 
214
219
  self._simulate_reboot(acc)
215
- assert acc.get_target("circuit-1", "relay") is None
220
+ assert acc.get_target("circuit-1", "relay") == "OPEN"
216
221
 
217
222
  def test_reboot_increments_generation(self):
218
223
  """Each panel reboot must advance the generation counter."""
@@ -228,8 +233,8 @@ class TestDescriptionClearsProperties:
228
233
  acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
229
234
  assert acc.generation == 2
230
235
 
231
- def test_retained_redescription_does_not_clear(self):
232
- """A re-delivered retained $description without a disconnect must NOT clear."""
236
+ def test_retained_redescription_preserves_values_without_generation_bump(self):
237
+ """A re-delivered retained $description without a disconnect must NOT bump generation."""
233
238
  acc = HomiePropertyAccumulator(SERIAL)
234
239
  _make_ready(acc)
235
240
  acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "1000")
@@ -237,24 +242,24 @@ class TestDescriptionClearsProperties:
237
242
  # Simulate network reconnect — $description re-delivered without $state=disconnected
238
243
  acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
239
244
 
240
- # Property values should be preserved
245
+ # Property values should be preserved and generation must not increment
241
246
  assert acc.get_prop("circuit-1", "exported-energy") == "1000"
242
247
  assert acc.generation == 1 # still 1 from initial boot, no increment on re-delivery
243
248
 
244
- def test_fresh_properties_available_after_reboot(self):
245
- """Post-reboot properties should be stored normally after clear."""
249
+ def test_fresh_properties_overwrite_preserved_after_reboot(self):
250
+ """Post-reboot properties should overwrite the preserved placeholder values."""
246
251
  acc = HomiePropertyAccumulator(SERIAL)
247
252
  _make_ready(acc)
248
253
  acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "1000")
249
254
 
250
255
  self._simulate_reboot(acc)
251
256
 
252
- # Fresh post-reboot value
257
+ # Fresh post-reboot value overwrites preserved placeholder
253
258
  acc.handle_message(f"{PREFIX}/circuit-1/exported-energy", "50")
254
259
  assert acc.get_prop("circuit-1", "exported-energy") == "50"
255
260
 
256
- def test_fresh_target_values_available_after_reboot(self):
257
- """Post-reboot target values should be stored normally after clear."""
261
+ def test_fresh_target_values_overwrite_preserved_after_reboot(self):
262
+ """Post-reboot target values should overwrite the preserved placeholder values."""
258
263
  acc = HomiePropertyAccumulator(SERIAL)
259
264
  _make_ready(acc)
260
265
  acc.handle_message(f"{PREFIX}/circuit-1/relay/$target", "OPEN")
@@ -262,16 +267,17 @@ class TestDescriptionClearsProperties:
262
267
 
263
268
  self._simulate_reboot(acc)
264
269
 
265
- # Fresh post-reboot target value
270
+ # Fresh post-reboot target value overwrites preserved placeholder
266
271
  acc.handle_message(f"{PREFIX}/circuit-1/relay/$target", "CLOSED")
267
272
  assert acc.get_target("circuit-1", "relay") == "CLOSED"
268
273
 
269
- def test_fast_reboot_without_lwt_still_clears(self):
274
+ def test_fast_reboot_without_lwt_preserves_values(self):
270
275
  """Panel reboots so fast that $state=disconnected (LWT) is skipped.
271
276
 
272
277
  The panel goes directly from ready -> init -> description -> ready.
273
278
  $state=init must reset _received_description so the subsequent
274
- $description triggers the property clear.
279
+ $description increments the generation counter but does NOT clear
280
+ property values — pre-reboot values serve as safe placeholders.
275
281
  """
276
282
  acc = HomiePropertyAccumulator(SERIAL)
277
283
  _make_ready(acc)
@@ -283,8 +289,8 @@ class TestDescriptionClearsProperties:
283
289
  acc.handle_message(f"{PREFIX}/$description", SIMPLE_DESC)
284
290
  acc.handle_message(f"{PREFIX}/$state", "ready")
285
291
 
286
- # Property values should be cleared
287
- assert acc.get_prop("circuit-1", "exported-energy") == ""
292
+ # Property values should be preserved
293
+ assert acc.get_prop("circuit-1", "exported-energy") == "1000"
288
294
  assert acc.generation == gen_before + 1
289
295
 
290
296
 
@@ -906,7 +906,7 @@ class TestSnapshotCaching:
906
906
  assert snap2 is not snap1
907
907
 
908
908
  def test_description_invalidates_cache_via_generation(self):
909
- """Panel reboot ($description) must clear cached snapshot so stale data is not reused."""
909
+ """Panel reboot ($description) must invalidate cached snapshot but preserve values."""
910
910
  acc, consumer = _build_ready_consumer()
911
911
  node = "aabbccdd-1122-3344-5566-778899001122"
912
912
 
@@ -916,16 +916,21 @@ class TestSnapshotCaching:
916
916
  circuit_id = "aabbccdd112233445566778899001122"
917
917
  assert snap_pre.circuits[circuit_id].consumed_energy_wh == 1000.0
918
918
 
919
- # Panel reboots — $state=disconnected resets lifecycle, $description clears values
919
+ # Panel reboots — $state=disconnected resets lifecycle, $description preserves values
920
920
  acc.handle_message(f"{PREFIX}/$state", "disconnected")
921
921
  acc.handle_message(f"{PREFIX}/$description", _make_description(_full_description()))
922
922
  acc.handle_message(f"{PREFIX}/$state", "ready")
923
923
 
924
- # Post-reboot: circuit publishes reset energy = 50
924
+ # Before new property messages: snapshot uses preserved pre-reboot value
925
+ snap_preserved = consumer.build_snapshot()
926
+ assert snap_preserved.circuits[circuit_id].consumed_energy_wh == 1000.0
927
+ assert snap_preserved is not snap_pre # cache was invalidated
928
+
929
+ # Post-reboot: circuit publishes new value
925
930
  acc.handle_message(f"{PREFIX}/{node}/exported-energy", "50")
926
931
  snap_post = consumer.build_snapshot()
927
932
 
928
- # Must reflect post-reboot value, not stale pre-reboot cache
933
+ # Must reflect post-reboot value
929
934
  assert snap_post.circuits[circuit_id].consumed_energy_wh == 50.0
930
935
 
931
936
 
@@ -130,31 +130,43 @@ sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8
130
130
  wheels = [
131
131
  { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" },
132
132
  { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" },
133
+ { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" },
134
+ { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" },
133
135
  { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" },
134
136
  { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" },
135
137
  { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" },
136
138
  { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" },
137
139
  { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
138
140
  { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
141
+ { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
142
+ { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
139
143
  { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
140
144
  { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
141
145
  { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
142
146
  { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
143
147
  { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
144
148
  { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
149
+ { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
150
+ { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
145
151
  { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
146
152
  { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
147
153
  { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
148
154
  { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
149
155
  { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
156
+ { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
157
+ { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
150
158
  { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
151
159
  { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
152
160
  { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
153
161
  { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
162
+ { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
163
+ { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
154
164
  { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
155
165
  { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
156
166
  { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
157
167
  { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
168
+ { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
169
+ { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
158
170
  { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
159
171
  { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
160
172
  { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
@@ -415,45 +427,51 @@ toml = [
415
427
 
416
428
  [[package]]
417
429
  name = "cryptography"
418
- version = "46.0.6"
430
+ version = "46.0.7"
419
431
  source = { registry = "https://pypi.org/simple" }
420
432
  dependencies = [
421
433
  { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
422
434
  { name = "typing-extensions", marker = "python_full_version < '3.11'" },
423
435
  ]
424
- sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" }
425
- wheels = [
426
- { url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" },
427
- { url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" },
428
- { url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" },
429
- { url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" },
430
- { url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" },
431
- { url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" },
432
- { url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" },
433
- { url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" },
434
- { url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" },
435
- { url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" },
436
- { url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" },
437
- { url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" },
438
- { url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" },
439
- { url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" },
440
- { url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" },
441
- { url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" },
442
- { url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" },
443
- { url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" },
444
- { url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" },
445
- { url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" },
446
- { url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" },
447
- { url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" },
448
- { url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" },
449
- { url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" },
450
- { url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" },
451
- { url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" },
452
- { url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" },
453
- { url = "https://files.pythonhosted.org/packages/bc/1f/4c926f50df7749f000f20eede0c896769509895e2648db5da0ed55db711d/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8", size = 4218227, upload-time = "2026-03-25T23:34:40.871Z" },
454
- { url = "https://files.pythonhosted.org/packages/c6/65/707be3ffbd5f786028665c3223e86e11c4cda86023adbc56bd72b1b6bab5/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0", size = 4381399, upload-time = "2026-03-25T23:34:42.609Z" },
455
- { url = "https://files.pythonhosted.org/packages/f3/6d/73557ed0ef7d73d04d9aba745d2c8e95218213687ee5e76b7d236a5030fc/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b", size = 4217595, upload-time = "2026-03-25T23:34:44.205Z" },
456
- { url = "https://files.pythonhosted.org/packages/9e/c5/e1594c4eec66a567c3ac4400008108a415808be2ce13dcb9a9045c92f1a0/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a", size = 4380912, upload-time = "2026-03-25T23:34:46.328Z" },
436
+ sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" }
437
+ wheels = [
438
+ { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" },
439
+ { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" },
440
+ { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" },
441
+ { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" },
442
+ { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" },
443
+ { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" },
444
+ { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" },
445
+ { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" },
446
+ { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" },
447
+ { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" },
448
+ { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" },
449
+ { url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" },
450
+ { url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" },
451
+ { url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" },
452
+ { url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" },
453
+ { url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" },
454
+ { url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" },
455
+ { url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" },
456
+ { url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" },
457
+ { url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" },
458
+ { url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" },
459
+ { url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" },
460
+ { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" },
461
+ { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" },
462
+ { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" },
463
+ { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" },
464
+ { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" },
465
+ { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" },
466
+ { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" },
467
+ { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" },
468
+ { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" },
469
+ { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" },
470
+ { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" },
471
+ { url = "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", size = 4219700, upload-time = "2026-04-08T01:57:40.625Z" },
472
+ { url = "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", size = 4385982, upload-time = "2026-04-08T01:57:42.725Z" },
473
+ { url = "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", size = 4219115, upload-time = "2026-04-08T01:57:44.939Z" },
474
+ { url = "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", size = 4385479, upload-time = "2026-04-08T01:57:46.86Z" },
457
475
  ]
458
476
 
459
477
  [[package]]
@@ -1292,7 +1310,7 @@ wheels = [
1292
1310
 
1293
1311
  [[package]]
1294
1312
  name = "span-panel-api"
1295
- version = "2.5.2"
1313
+ version = "2.5.3"
1296
1314
  source = { editable = "." }
1297
1315
  dependencies = [
1298
1316
  { name = "httpx" },
File without changes
File without changes