phystack-hub-client 0.2.7__tar.gz → 6.8.0__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-0.2.7 → phystack_hub_client-6.8.0}/PKG-INFO +2 -2
  2. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/pyproject.toml +4 -1
  3. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/__init__.py +74 -1
  4. phystack_hub_client-6.8.0/src/phystack/hub_client/advisory_validation.py +114 -0
  5. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/client.py +532 -8
  6. phystack_hub_client-6.8.0/src/phystack/hub_client/cloud_twins.py +173 -0
  7. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/instances/base_instance.py +91 -21
  8. phystack_hub_client-6.8.0/src/phystack/hub_client/instances/edge_instance.py +332 -0
  9. phystack_hub_client-6.8.0/src/phystack/hub_client/instances/peripheral_instance.py +486 -0
  10. phystack_hub_client-6.8.0/src/phystack/hub_client/scoped_hardware_id.py +103 -0
  11. phystack_hub_client-6.8.0/src/phystack/hub_client/services/cloud_app_connection.py +365 -0
  12. phystack_hub_client-6.8.0/src/phystack/hub_client/services/cloud_app_token.py +198 -0
  13. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/services/phyhub_connection.py +92 -57
  14. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/services/signals.py +128 -17
  15. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/services/webrtc/__init__.py +4 -0
  16. phystack_hub_client-6.8.0/src/phystack/hub_client/services/webrtc/data_channel.py +832 -0
  17. phystack_hub_client-6.8.0/src/phystack/hub_client/services/webrtc/data_responder.py +544 -0
  18. phystack_hub_client-6.8.0/src/phystack/hub_client/services/webrtc/media_responder.py +620 -0
  19. phystack_hub_client-6.8.0/src/phystack/hub_client/services/webrtc/media_source.py +120 -0
  20. phystack_hub_client-6.8.0/src/phystack/hub_client/services/webrtc/media_stream.py +1105 -0
  21. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/twin_messaging.py +55 -1
  22. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/twin_registry.py +16 -0
  23. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/types/twin_types.py +43 -0
  24. phystack_hub_client-6.8.0/tests/test_advisory_validation.py +260 -0
  25. phystack_hub_client-6.8.0/tests/test_client_integration.py +114 -0
  26. phystack_hub_client-6.8.0/tests/test_cloud_app_token.py +594 -0
  27. phystack_hub_client-6.8.0/tests/test_cloud_twins.py +249 -0
  28. phystack_hub_client-6.8.0/tests/test_data_channel.py +354 -0
  29. phystack_hub_client-6.8.0/tests/test_instance_listeners.py +118 -0
  30. phystack_hub_client-6.8.0/tests/test_media_kinds.py +231 -0
  31. phystack_hub_client-6.8.0/tests/test_media_resilience.py +498 -0
  32. phystack_hub_client-6.8.0/tests/test_media_responder.py +318 -0
  33. phystack_hub_client-6.8.0/tests/test_media_source.py +86 -0
  34. phystack_hub_client-6.8.0/tests/test_peripheral_lifecycle.py +451 -0
  35. phystack_hub_client-6.8.0/tests/test_signals_rollover.py +213 -0
  36. phystack_hub_client-0.2.7/src/phystack/hub_client/instances/edge_instance.py +0 -216
  37. phystack_hub_client-0.2.7/src/phystack/hub_client/instances/peripheral_instance.py +0 -190
  38. phystack_hub_client-0.2.7/src/phystack/hub_client/services/webrtc/data_channel.py +0 -570
  39. phystack_hub_client-0.2.7/src/phystack/hub_client/services/webrtc/media_stream.py +0 -614
  40. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/.gitignore +0 -0
  41. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/README.md +0 -0
  42. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/examples/browser_test_backend.py +0 -0
  43. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/scripts/python-node-test.sh +0 -0
  44. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/scripts/webrtc-test.sh +0 -0
  45. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/instances/__init__.py +0 -0
  46. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/services/__init__.py +0 -0
  47. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/types/__init__.py +0 -0
  48. {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/tests/test_communication.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: phystack-hub-client
3
- Version: 0.2.7
3
+ Version: 6.8.0
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
@@ -4,7 +4,10 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "phystack-hub-client"
7
- version = "0.2.7"
7
+ # Fixed version group with the Node hub-client (@phystack/hub-client in
8
+ # phystack/ps-platform): both clients implement the same twin/WebRTC contract
9
+ # and are released together under the same version.
10
+ version = "6.8.0"
8
11
  description = "Python client for PhyHub - events, actions, DataChannel, and MediaStream"
9
12
  readme = "README.md"
10
13
  license = "MIT"
@@ -46,12 +46,20 @@ Or with context manager:
46
46
 
47
47
  from phystack.hub_client.client import PhyHubClient, connect_phy_client
48
48
  from phystack.hub_client.types.twin_types import (
49
+ PeripheralCreateResult,
50
+ PeripheralHandover,
51
+ PeripheralOwnershipLoss,
49
52
  TwinMessageResult,
50
53
  TwinMessageResultStatus,
51
54
  TwinProperties,
52
55
  TwinTypeEnum,
53
56
  TwinStatusEnum,
54
57
  )
58
+ from phystack.hub_client.scoped_hardware_id import (
59
+ HARDWARE_ID_SCOPES,
60
+ HARDWARE_ID_USER_PART_MAX_LENGTH,
61
+ format_scoped_hardware_id,
62
+ )
55
63
  from phystack.hub_client.instances import (
56
64
  BaseInstance,
57
65
  EdgeInstance,
@@ -66,9 +74,42 @@ from phystack.hub_client.services.webrtc.data_channel import (
66
74
  from phystack.hub_client.services.webrtc.media_stream import (
67
75
  MediaStreamHandler,
68
76
  MediaStreamOptions,
77
+ MediaTrackKind,
78
+ MissingMediaTrackError,
69
79
  PhygridMediaStream,
70
80
  WebcamVideoTrack,
71
81
  )
82
+ from phystack.hub_client.services.webrtc.media_source import (
83
+ FrameSourceTrack,
84
+ VideoFrameSource,
85
+ )
86
+ from phystack.hub_client.services.webrtc.media_responder import (
87
+ MediaStreamResponder,
88
+ MediaStreamResponderOptions,
89
+ PeerCapReachedError,
90
+ ResponderPeer,
91
+ )
92
+ from phystack.hub_client.services.webrtc.data_responder import (
93
+ DataChannelResponder,
94
+ DataChannelResponderOptions,
95
+ ResponderChannel,
96
+ )
97
+ from phystack.hub_client.advisory_validation import (
98
+ CompiledValidator,
99
+ DescriptorValidator,
100
+ validate_emitted_facet,
101
+ )
102
+ from phystack.hub_client.services.cloud_app_token import (
103
+ CloudAppToken,
104
+ CloudAppTokenExchangeError,
105
+ exchange_cloud_app_token,
106
+ )
107
+ from phystack.hub_client.services.cloud_app_connection import (
108
+ CloudAppConnection,
109
+ CloudAppConnectionParams,
110
+ CloudAppConnectResult,
111
+ )
112
+ from phystack.hub_client.cloud_twins import CloudTwinStore
72
113
  from phystack.hub_client.services.signals import (
73
114
  SignalsService,
74
115
  InitSignalPayload,
@@ -80,7 +121,9 @@ from phystack.hub_client.services.signals import (
80
121
  PurchaseProps,
81
122
  )
82
123
 
83
- __version__ = "0.1.0"
124
+ # Kept in a fixed version group with @phystack/hub-client (npm) — same
125
+ # contract, same release version.
126
+ __version__ = "6.8.0"
84
127
  __all__ = [
85
128
  # Client
86
129
  "PhyHubClient",
@@ -96,14 +139,44 @@ __all__ = [
96
139
  "TwinProperties",
97
140
  "TwinTypeEnum",
98
141
  "TwinStatusEnum",
142
+ # Peripheral identity + ownership (TECH-1394)
143
+ "PeripheralCreateResult",
144
+ "PeripheralHandover",
145
+ "PeripheralOwnershipLoss",
146
+ "HARDWARE_ID_SCOPES",
147
+ "HARDWARE_ID_USER_PART_MAX_LENGTH",
148
+ "format_scoped_hardware_id",
99
149
  # WebRTC
100
150
  "DataChannelHandler",
101
151
  "DataChannelOptions",
102
152
  "PhygridDataChannel",
103
153
  "MediaStreamHandler",
104
154
  "MediaStreamOptions",
155
+ "MediaTrackKind",
156
+ "MissingMediaTrackError",
105
157
  "PhygridMediaStream",
106
158
  "WebcamVideoTrack",
159
+ "FrameSourceTrack",
160
+ "VideoFrameSource",
161
+ "MediaStreamResponder",
162
+ "MediaStreamResponderOptions",
163
+ "PeerCapReachedError",
164
+ "ResponderPeer",
165
+ "DataChannelResponder",
166
+ "DataChannelResponderOptions",
167
+ "ResponderChannel",
168
+ # Advisory validation
169
+ "CompiledValidator",
170
+ "DescriptorValidator",
171
+ "validate_emitted_facet",
172
+ # Cloud-app mode
173
+ "CloudAppToken",
174
+ "CloudAppTokenExchangeError",
175
+ "exchange_cloud_app_token",
176
+ "CloudAppConnection",
177
+ "CloudAppConnectionParams",
178
+ "CloudAppConnectResult",
179
+ "CloudTwinStore",
107
180
  # Signals
108
181
  "SignalsService",
109
182
  "InitSignalPayload",
@@ -0,0 +1,114 @@
1
+ """Advisory descriptor validation (opt-in, on-device).
2
+
3
+ Python port of the npm hub-client ``advisory-validation.ts`` module. Validates
4
+ the facets an edge app EMITS (reported properties, event payloads, action
5
+ returns) against precompiled validators the app supplies from its own compiled
6
+ descriptor schema (the build-time-embed approach).
7
+
8
+ Hard rules this module enforces (npm parity):
9
+
10
+ - **Opt-in.** No validator supplied -> no validation; behavior is identical to
11
+ a non-validating client.
12
+ - **Advisory + routing-independent.** On a mismatch a warning is logged and the
13
+ caller STILL emits/sends/stores. The validation outcome never affects whether
14
+ or how a message goes on the wire.
15
+ - **Fail-open.** Missing validator for a facet -> skip; any error raised inside
16
+ the validation path -> caught, logged, and skipped. This module never raises.
17
+
18
+ Like the npm package (which deliberately does not depend on Ajv), this module
19
+ carries no JSON-schema runtime. The opt-in contract is a set of precompiled
20
+ validate callables: ``Callable[[Any], bool]`` that may expose the last run's
21
+ errors on an ``errors`` attribute (duck-typed, mirroring Ajv's compiled shape).
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+ from dataclasses import dataclass, field
28
+ from typing import Any, Callable, Optional
29
+
30
+ # A precompiled validate function: returns True/False for a payload and may
31
+ # expose the last run's errors on an `errors` attribute (duck-typed).
32
+ CompiledValidator = Callable[[Any], bool]
33
+
34
+ # Facet names mirror the npm module: 'reported' | 'event' | 'actionReturns'.
35
+ FACET_REPORTED = "reported"
36
+ FACET_EVENT = "event"
37
+ FACET_ACTION_RETURNS = "actionReturns"
38
+
39
+ _default_logger = logging.getLogger("phystack.hub_client.advisory_validation")
40
+
41
+
42
+ @dataclass
43
+ class DescriptorValidator:
44
+ """Per-facet precompiled validators supplied by the edge app.
45
+
46
+ Every field is optional: a facet with no validator is simply not checked
47
+ (fail-open). Event validators are keyed by event type; action-return
48
+ validators by action name.
49
+ """
50
+
51
+ reported: Optional[CompiledValidator] = None
52
+ events: dict[str, CompiledValidator] = field(default_factory=dict)
53
+ action_returns: dict[str, CompiledValidator] = field(default_factory=dict)
54
+
55
+
56
+ def _describe(descriptor_label: str, facet: str, facet_key: Optional[str]) -> str:
57
+ facet_part = f"{facet} '{facet_key}'" if facet_key else facet
58
+ return f"{facet_part} on '{descriptor_label}'"
59
+
60
+
61
+ def validate_emitted_facet(
62
+ validator: Optional[CompiledValidator],
63
+ data: Any,
64
+ descriptor_label: str,
65
+ facet: str,
66
+ facet_key: Optional[str] = None,
67
+ logger: Optional[logging.Logger] = None,
68
+ ) -> bool:
69
+ """Run a single precompiled validator over ``data``, advisory + fail-open.
70
+
71
+ NEVER raises and NEVER signals "block this message" — the return value is
72
+ purely informational (True = conformed/skipped, False = a mismatch was
73
+ logged) and callers MUST emit regardless of it. A ``validator`` of ``None``
74
+ means the app didn't supply one for this facet -> skip (fail-open). Any
75
+ error raised by the validator itself is caught, logged, and treated as a
76
+ skip (fail-open) so a broken schema can never block telemetry.
77
+
78
+ Args:
79
+ validator: Precompiled validate callable, or None to skip.
80
+ data: The emitted payload to check.
81
+ descriptor_label: Identifies the descriptor/peripheral in the warning.
82
+ facet: Which emitted facet this is ('reported'/'event'/'actionReturns').
83
+ facet_key: Event type or action name, when applicable.
84
+ logger: Logger for warnings; defaults to the module logger.
85
+
86
+ Returns:
87
+ True if the data conformed or validation was skipped, False on mismatch.
88
+ """
89
+ if validator is None:
90
+ # Opt-out / no schema for this facet -> skip, behave like a
91
+ # non-validating client.
92
+ return True
93
+
94
+ active_logger = logger if logger is not None else _default_logger
95
+
96
+ try:
97
+ conforms = bool(validator(data))
98
+ except Exception as error: # noqa: BLE001 - fail-open by contract
99
+ # Fail-open: a broken validator must never block an emit.
100
+ active_logger.warning(
101
+ "Failed to run advisory descriptor validation for %s — skipping (fail-open): %r",
102
+ _describe(descriptor_label, facet, facet_key),
103
+ error,
104
+ )
105
+ return True
106
+
107
+ if not conforms:
108
+ active_logger.warning(
109
+ "Advisory descriptor validation mismatch for %s — sending anyway (advisory): %r",
110
+ _describe(descriptor_label, facet, facet_key),
111
+ getattr(validator, "errors", None),
112
+ )
113
+
114
+ return conforms