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.
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/PKG-INFO +2 -2
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/pyproject.toml +4 -1
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/__init__.py +74 -1
- phystack_hub_client-6.8.0/src/phystack/hub_client/advisory_validation.py +114 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/client.py +532 -8
- phystack_hub_client-6.8.0/src/phystack/hub_client/cloud_twins.py +173 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/instances/base_instance.py +91 -21
- phystack_hub_client-6.8.0/src/phystack/hub_client/instances/edge_instance.py +332 -0
- phystack_hub_client-6.8.0/src/phystack/hub_client/instances/peripheral_instance.py +486 -0
- phystack_hub_client-6.8.0/src/phystack/hub_client/scoped_hardware_id.py +103 -0
- phystack_hub_client-6.8.0/src/phystack/hub_client/services/cloud_app_connection.py +365 -0
- phystack_hub_client-6.8.0/src/phystack/hub_client/services/cloud_app_token.py +198 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/services/phyhub_connection.py +92 -57
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/services/signals.py +128 -17
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/services/webrtc/__init__.py +4 -0
- phystack_hub_client-6.8.0/src/phystack/hub_client/services/webrtc/data_channel.py +832 -0
- phystack_hub_client-6.8.0/src/phystack/hub_client/services/webrtc/data_responder.py +544 -0
- phystack_hub_client-6.8.0/src/phystack/hub_client/services/webrtc/media_responder.py +620 -0
- phystack_hub_client-6.8.0/src/phystack/hub_client/services/webrtc/media_source.py +120 -0
- phystack_hub_client-6.8.0/src/phystack/hub_client/services/webrtc/media_stream.py +1105 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/twin_messaging.py +55 -1
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/twin_registry.py +16 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/types/twin_types.py +43 -0
- phystack_hub_client-6.8.0/tests/test_advisory_validation.py +260 -0
- phystack_hub_client-6.8.0/tests/test_client_integration.py +114 -0
- phystack_hub_client-6.8.0/tests/test_cloud_app_token.py +594 -0
- phystack_hub_client-6.8.0/tests/test_cloud_twins.py +249 -0
- phystack_hub_client-6.8.0/tests/test_data_channel.py +354 -0
- phystack_hub_client-6.8.0/tests/test_instance_listeners.py +118 -0
- phystack_hub_client-6.8.0/tests/test_media_kinds.py +231 -0
- phystack_hub_client-6.8.0/tests/test_media_resilience.py +498 -0
- phystack_hub_client-6.8.0/tests/test_media_responder.py +318 -0
- phystack_hub_client-6.8.0/tests/test_media_source.py +86 -0
- phystack_hub_client-6.8.0/tests/test_peripheral_lifecycle.py +451 -0
- phystack_hub_client-6.8.0/tests/test_signals_rollover.py +213 -0
- phystack_hub_client-0.2.7/src/phystack/hub_client/instances/edge_instance.py +0 -216
- phystack_hub_client-0.2.7/src/phystack/hub_client/instances/peripheral_instance.py +0 -190
- phystack_hub_client-0.2.7/src/phystack/hub_client/services/webrtc/data_channel.py +0 -570
- phystack_hub_client-0.2.7/src/phystack/hub_client/services/webrtc/media_stream.py +0 -614
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/.gitignore +0 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/README.md +0 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/examples/browser_test_backend.py +0 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/scripts/python-node-test.sh +0 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/scripts/webrtc-test.sh +0 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/instances/__init__.py +0 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/services/__init__.py +0 -0
- {phystack_hub_client-0.2.7 → phystack_hub_client-6.8.0}/src/phystack/hub_client/types/__init__.py +0 -0
- {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.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: phystack-hub-client
|
|
3
|
-
Version:
|
|
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
|
|
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
|
-
|
|
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
|