phystack-hub-client 6.18.1__tar.gz → 6.18.2__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 (48) hide show
  1. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/PKG-INFO +1 -1
  2. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/pyproject.toml +1 -1
  3. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/client.py +12 -2
  4. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/cloud_twins.py +11 -3
  5. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_client_integration.py +31 -1
  6. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/.gitignore +0 -0
  7. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/CHANGELOG.md +0 -0
  8. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/README.md +0 -0
  9. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/examples/browser_test_backend.py +0 -0
  10. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/scripts/python-node-test.sh +0 -0
  11. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/scripts/webrtc-test.sh +0 -0
  12. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/__init__.py +0 -0
  13. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/advisory_validation.py +0 -0
  14. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/instances/__init__.py +0 -0
  15. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/instances/base_instance.py +0 -0
  16. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/instances/edge_instance.py +0 -0
  17. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/instances/peripheral_instance.py +0 -0
  18. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/scoped_hardware_id.py +0 -0
  19. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/__init__.py +0 -0
  20. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/cloud_app_connection.py +0 -0
  21. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/cloud_app_token.py +0 -0
  22. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/phyhub_connection.py +0 -0
  23. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/signals.py +0 -0
  24. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/web_session_code.py +0 -0
  25. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/webrtc/__init__.py +0 -0
  26. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/webrtc/data_channel.py +0 -0
  27. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/webrtc/data_responder.py +0 -0
  28. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/webrtc/media_responder.py +0 -0
  29. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/webrtc/media_source.py +0 -0
  30. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/services/webrtc/media_stream.py +0 -0
  31. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/twin_messaging.py +0 -0
  32. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/twin_registry.py +0 -0
  33. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/types/__init__.py +0 -0
  34. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/src/phystack/hub_client/types/twin_types.py +0 -0
  35. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_advisory_validation.py +0 -0
  36. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_cloud_app_token.py +0 -0
  37. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_cloud_twins.py +0 -0
  38. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_communication.py +0 -0
  39. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_data_channel.py +0 -0
  40. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_instance_listeners.py +0 -0
  41. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_media_kinds.py +0 -0
  42. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_media_resilience.py +0 -0
  43. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_media_responder.py +0 -0
  44. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_media_source.py +0 -0
  45. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_no_google_stun.py +0 -0
  46. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_peripheral_lifecycle.py +0 -0
  47. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_signals_rollover.py +0 -0
  48. {phystack_hub_client-6.18.1 → phystack_hub_client-6.18.2}/tests/test_web_session_code.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: phystack-hub-client
3
- Version: 6.18.1
3
+ Version: 6.18.2
4
4
  Summary: Python client for PhyHub - events, actions, DataChannel, and MediaStream
5
5
  Project-URL: Homepage, https://github.com/phystack/phystack-pypi
6
6
  Project-URL: Documentation, https://docs.phystack.com
@@ -7,7 +7,7 @@ name = "phystack-hub-client"
7
7
  # Fixed version group with the Node hub-client (@phystack/hub-client in
8
8
  # phystack/ps-platform): both clients implement the same twin/WebRTC contract
9
9
  # and are released together under the same version.
10
- version = "6.18.1"
10
+ version = "6.18.2"
11
11
  description = "Python client for PhyHub - events, actions, DataChannel, and MediaStream"
12
12
  readme = "README.md"
13
13
  license = "MIT"
@@ -365,8 +365,18 @@ class PhyHubClient:
365
365
 
366
366
  self._connection.on(self.EVENTS.TWIN_MESSAGE, on_twin_message)
367
367
 
368
- def on_twin_updated(payload: dict[str, Any]) -> None:
369
- """Handle twin property update events."""
368
+ def on_twin_updated(payload: dict[str, Any], *_extra: Any) -> None:
369
+ """Handle twin property update events.
370
+
371
+ ``*_extra`` absorbs any trailing positional argument the server
372
+ appends to the ``twinUpdated`` emit. Some phyhub builds send twin
373
+ lifecycle events with a second positional arg (event metadata);
374
+ JS clients silently ignore it, but python-socketio passes every
375
+ positional arg through, so a strict ``(payload)`` signature raises
376
+ "takes 1 positional argument but 2 were given" and DROPS EVERY twin
377
+ update — the device stops seeing settings/desired-property changes
378
+ until restarted. Matches the tolerant ``on_twin_message`` sibling.
379
+ """
370
380
  twin_data = payload.get("data") or payload
371
381
  twin_id = payload.get("twinId") or (twin_data.get("id") if isinstance(twin_data, dict) else None)
372
382
 
@@ -83,15 +83,23 @@ class CloudTwinStore:
83
83
 
84
84
  # --- lifecycle events ---
85
85
 
86
- def apply_created(self, payload: CloudTwinLifecycleEvent) -> None:
86
+ # ``*_extra`` on these three absorbs any trailing positional arg the
87
+ # server appends to a twin lifecycle emit. They are registered directly
88
+ # as socket.io handlers (PhyHubClient cloud-app mode), so a strict
89
+ # ``(payload)`` signature would raise "takes 1 positional argument but 2
90
+ # were given" and silently drop every cloud-twin update — the same defect
91
+ # fixed in PhyHubClient.on_twin_updated. Direct internal callers still
92
+ # pass a single argument.
93
+
94
+ def apply_created(self, payload: CloudTwinLifecycleEvent, *_extra: Any) -> None:
87
95
  """Apply a ``twinCreated`` event and notify ``on_created`` listeners."""
88
96
  self._apply(_KIND_CREATED, payload)
89
97
 
90
- def apply_updated(self, payload: CloudTwinLifecycleEvent) -> None:
98
+ def apply_updated(self, payload: CloudTwinLifecycleEvent, *_extra: Any) -> None:
91
99
  """Apply a ``twinUpdated`` event and notify ``on_updated`` listeners."""
92
100
  self._apply(_KIND_UPDATED, payload)
93
101
 
94
- def apply_deleted(self, payload: CloudTwinLifecycleEvent) -> None:
102
+ def apply_deleted(self, payload: CloudTwinLifecycleEvent, *_extra: Any) -> None:
95
103
  """Apply a ``twinDeleted`` event and notify ``on_deleted`` listeners."""
96
104
  self._apply(_KIND_DELETED, payload)
97
105
 
@@ -12,6 +12,7 @@ class FakeConnection:
12
12
  self.emitted: list[tuple[str, dict[str, Any]]] = []
13
13
  self.device_id = "device-1"
14
14
  self.ack_response: Optional[dict[str, Any]] = {"status": "success"}
15
+ self.handlers: dict[str, Any] = {}
15
16
 
16
17
  @property
17
18
  def is_connected(self) -> bool:
@@ -22,7 +23,7 @@ class FakeConnection:
22
23
  return True
23
24
 
24
25
  def on(self, event: str, handler: Any) -> None:
25
- return None
26
+ self.handlers[event] = handler
26
27
 
27
28
  async def emit(
28
29
  self, method: str, payload: Any = None, callback: bool = False
@@ -95,6 +96,35 @@ async def test_device_status_cached_for_ten_seconds() -> None:
95
96
  assert status_calls.count("getDeviceStatus") == 1
96
97
 
97
98
 
99
+ def test_twin_updated_handler_tolerates_trailing_positional_arg() -> None:
100
+ """Regression: some phyhub builds emit `twinUpdated` with a second
101
+ positional arg. python-socketio forwards every positional arg, so a
102
+ strict `(payload)` handler raised "takes 1 positional argument but 2 were
103
+ given" and dropped every twin update fleet-wide (edge apps stopped seeing
104
+ settings changes until restarted). The handler must accept and ignore the
105
+ extra arg the way JS clients do."""
106
+ client, connection = make_client()
107
+ client._setup_socket_listeners()
108
+
109
+ on_twin_updated = connection.handlers["twinUpdated"]
110
+ received: list[dict[str, Any]] = []
111
+ # Register the listener directly to keep the test on the handler's arity,
112
+ # not the subscribe machinery (`on_twin_update` spawns an async task).
113
+ client._twin_update_listeners["twin-x"] = {lambda data: received.append(data)}
114
+
115
+ payload = {
116
+ "twinId": "twin-x",
117
+ "data": {"id": "twin-x", "properties": {"desired": {"volume": 5}}},
118
+ }
119
+
120
+ # One positional arg (phyhub master) — the baseline that already worked.
121
+ on_twin_updated(payload)
122
+ # Two positional args (the QA build that triggered the crash) — must not raise.
123
+ on_twin_updated(payload, {"eventId": "evt-1"})
124
+
125
+ assert received == [payload["data"], payload["data"]]
126
+
127
+
98
128
  def test_public_exports_importable() -> None:
99
129
  import phystack.hub_client as hub_client
100
130