sora-sdk 2026.2.0.dev2__tar.gz → 2026.2.0.dev3__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 (58) hide show
  1. {sora_sdk-2026.2.0.dev2/src/sora_sdk.egg-info → sora_sdk-2026.2.0.dev3}/PKG-INFO +1 -1
  2. sora_sdk-2026.2.0.dev3/VERSION +1 -0
  3. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/src/sora_sdk/sora_sdk_ext.cpython-312-darwin.so +0 -0
  4. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/src/sora_sdk/sora_sdk_ext.pyi +2 -0
  5. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3/src/sora_sdk.egg-info}/PKG-INFO +1 -1
  6. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/src/sora_sdk.egg-info/SOURCES.txt +1 -0
  7. sora_sdk-2026.2.0.dev3/tests/test_rpc.py +202 -0
  8. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_type_switched.py +5 -1
  9. sora_sdk-2026.2.0.dev2/VERSION +0 -1
  10. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/LICENSE +0 -0
  11. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/MANIFEST.in +0 -0
  12. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/README.md +0 -0
  13. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/buildbase.py +0 -0
  14. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/pypath.py +0 -0
  15. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/pyproject.toml +0 -0
  16. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/run.py +0 -0
  17. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/setup.cfg +0 -0
  18. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/setup.py +0 -0
  19. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/src/sora_sdk/__init__.py +0 -0
  20. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/src/sora_sdk/py.typed +0 -0
  21. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/src/sora_sdk.egg-info/dependency_links.txt +0 -0
  22. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/src/sora_sdk.egg-info/top_level.txt +0 -0
  23. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_amd_amf.py +0 -0
  24. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_apple_video_toolbox.py +0 -0
  25. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_audio_sink_callbacks.py +0 -0
  26. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_audio_sink_read_gil.py +0 -0
  27. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_audio_source_invalid_params.py +0 -0
  28. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_authz.py +0 -0
  29. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_authz_simulcast.py +0 -0
  30. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_ca_cert.py +0 -0
  31. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_capability.py +0 -0
  32. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_convert_json_value_int64.py +0 -0
  33. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_degradation_preference.py +0 -0
  34. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_disconnect_timeout.py +0 -0
  35. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_encoded_transform.py +0 -0
  36. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_field_trials.py +0 -0
  37. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_intel_vpl.py +0 -0
  38. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_key_frame_request.py +0 -0
  39. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_messaging.py +0 -0
  40. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_messaging_header.py +0 -0
  41. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_ndarray_parent_reference.py +0 -0
  42. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_nvidia_video_codec.py +0 -0
  43. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_openh264.py +0 -0
  44. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_openh264_simulcast.py +0 -0
  45. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_opus.py +0 -0
  46. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_raspberry_pi.py +0 -0
  47. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_re_offer_re_answer_sdp.py +0 -0
  48. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_rtc_log_refcount.py +0 -0
  49. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_send_data_channel_get_stats_after_disconnect.py +0 -0
  50. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_sendonly_recvonly.py +0 -0
  51. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_signaling.py +0 -0
  52. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_signaling_message.py +0 -0
  53. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_signaling_notify.py +0 -0
  54. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_simulcast.py +0 -0
  55. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_sora_disconnect.py +0 -0
  56. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_type_disconnect.py +0 -0
  57. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_vad.py +0 -0
  58. {sora_sdk-2026.2.0.dev2 → sora_sdk-2026.2.0.dev3}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sora_sdk
3
- Version: 2026.2.0.dev2
3
+ Version: 2026.2.0.dev3
4
4
  Summary: WebRTC SFU Sora Python SDK
5
5
  Home-page: https://github.com/shiguredo/sora-python-sdk
6
6
  Author-email: "Shiguredo Inc." <contact+pypi@shiguredo.jp>
@@ -0,0 +1 @@
1
+ 2026.2.0.dev3
@@ -185,6 +185,8 @@ class SoraConnection:
185
185
 
186
186
  def send_data_channel(self, label: str, data: bytes) -> bool: ...
187
187
 
188
+ def send_rpc(self, id: int | None, method: str, params: object | None = None) -> bool: ...
189
+
188
190
  def get_stats(self) -> str: ...
189
191
 
190
192
  @property
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sora_sdk
3
- Version: 2026.2.0.dev2
3
+ Version: 2026.2.0.dev3
4
4
  Summary: WebRTC SFU Sora Python SDK
5
5
  Home-page: https://github.com/shiguredo/sora-python-sdk
6
6
  Author-email: "Shiguredo Inc." <contact+pypi@shiguredo.jp>
@@ -40,6 +40,7 @@ tests/test_openh264_simulcast.py
40
40
  tests/test_opus.py
41
41
  tests/test_raspberry_pi.py
42
42
  tests/test_re_offer_re_answer_sdp.py
43
+ tests/test_rpc.py
43
44
  tests/test_rtc_log_refcount.py
44
45
  tests/test_send_data_channel_get_stats_after_disconnect.py
45
46
  tests/test_sendonly_recvonly.py
@@ -0,0 +1,202 @@
1
+ """
2
+ Sora の RPC 機能 (JSON-RPC 2.0 over DataChannel) の E2E テスト。
3
+
4
+ SoraConnection.send_rpc() が rpc ラベルへ JSON-RPC 2.0 のリクエストを送信し、
5
+ 応答が on_rpc に届くことを確認する。rpc ラベルは Sora の RPC 機能が有効な場合に
6
+ だけ offer に含まれるため、rpc ラベルを必要とするテストは含まれない環境では
7
+ 送信できないことを確認して skip する。
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from queue import Empty
13
+ from typing import Any
14
+
15
+ import pytest
16
+ from client import SoraClient, SoraRole
17
+
18
+ NOT_EXIST_METHOD = "9999.0.0/not_exist"
19
+
20
+
21
+ def _create_rpc_client(settings) -> SoraClient:
22
+ """
23
+ RPC のテストで使うクライアントを生成する (Sora への接続は with 文で行う)。
24
+
25
+ rpc ラベルは DataChannel 経由のシグナリングでのみ使うため data_channel_signaling を
26
+ 有効にする。RPC は音声・映像とは独立して使えるため両方無効にするが、この構成では
27
+ ユーザー定義の DataChannel が 1 つも無いと Sora が接続メッセージを 4490
28
+ INVALID-MESSAGE で拒否するため、ダミーの #test ラベルを 1 つ用意する。
29
+ """
30
+ return SoraClient(
31
+ settings,
32
+ SoraRole.SENDONLY,
33
+ data_channel_signaling=True,
34
+ audio=False,
35
+ video=False,
36
+ data_channels=[{"label": "#test", "direction": "sendonly"}],
37
+ )
38
+
39
+
40
+ def test_send_rpc_request_and_response(settings):
41
+ """
42
+ send_rpc() で送信した JSON-RPC 2.0 のリクエストに応答が返ることを確認する。
43
+
44
+ 前提:
45
+ - RPC が有効な Sora に data_channel_signaling を有効にして接続する
46
+ - 払い出される rpc_methods に依存しないよう、存在しないメソッドを指定する
47
+
48
+ 期待:
49
+ - send_rpc() が True を返す
50
+ - 応答が on_rpc に届き、送信した id と一致する
51
+ - 存在しないメソッドなので error を含む応答になる
52
+ - params に None / Array を指定した場合と、params を省略した場合も送信できる
53
+ """
54
+ with _create_rpc_client(settings) as client:
55
+ if not client.wait_rpc_ready(timeout=5):
56
+ # rpc ラベルが使えない環境では送信されず False が返る
57
+ assert client.send_rpc(1, NOT_EXIST_METHOD, {"key": "value"}) is False
58
+ pytest.skip("rpc ラベルが使えない環境のため送信できない")
59
+
60
+ # 送信した id がそのまま応答に含まれることを確認する (0 と 2^64-1 は境界値)
61
+ for request_id in [0, 1, 2**64 - 1]:
62
+ assert client.send_rpc(request_id, NOT_EXIST_METHOD, {"key": "value"})
63
+
64
+ response = client.recv_rpc(timeout=5)
65
+ assert response["id"] == request_id
66
+ assert "error" in response
67
+
68
+ # params に None を指定した場合は params を含めずに送信する
69
+ assert client.send_rpc(2, NOT_EXIST_METHOD, None)
70
+
71
+ response = client.recv_rpc(timeout=5)
72
+ assert response["id"] == 2
73
+
74
+ # params には Object だけでなく Array も指定できる
75
+ assert client.send_rpc(3, NOT_EXIST_METHOD, [])
76
+
77
+ response = client.recv_rpc(timeout=5)
78
+ assert response["id"] == 3
79
+
80
+ # params を省略した 2 引数呼び出しではバインディングの既定値 (None) が使われる。
81
+ # ヘルパは常に params を渡すため、ここでは SDK を直接呼ぶ
82
+ assert client._connection.send_rpc(4, NOT_EXIST_METHOD)
83
+
84
+ response = client.recv_rpc(timeout=5)
85
+ assert response["id"] == 4
86
+
87
+
88
+ def test_send_rpc_notification_has_no_response(settings):
89
+ """
90
+ id に None を指定した Notification には Sora が応答を返さないことを確認する。
91
+
92
+ 前提:
93
+ - RPC が有効な Sora に接続する
94
+ - id に None を指定してリクエストを送る
95
+
96
+ 期待:
97
+ - send_rpc() が True を返す
98
+ - 応答が返らないため recv_rpc() が Empty でタイムアウトする
99
+ """
100
+ with _create_rpc_client(settings) as client:
101
+ if not client.wait_rpc_ready(timeout=5):
102
+ pytest.skip("rpc ラベルが使えない環境のため送信できない")
103
+
104
+ assert client.send_rpc(None, NOT_EXIST_METHOD, {"key": "value"})
105
+
106
+ # Notification には Sora が応答を返さない
107
+ with pytest.raises(Empty):
108
+ client.recv_rpc(timeout=3)
109
+
110
+
111
+ def test_send_rpc_invalid_params(settings):
112
+ """
113
+ params に送信できない値を指定したときの挙動を確認する。
114
+
115
+ JSON-RPC 2.0 の params は Object か Array でなければならないため、それ以外の値に
116
+ 変換される場合は送信せず False が返る。JSON の値として扱えない型の場合は既存の
117
+ Sora::ConvertJsonValue() と同じく TypeError、int64 の範囲を超える整数とキーが
118
+ 文字列でない dict は同じく RuntimeError になる。TypeError と RuntimeError は引数の
119
+ 変換時に発生するため rpc ラベルの有無に依存しない。False はラベルが無い場合も
120
+ Object / Array 以外の場合も返る。
121
+ """
122
+ with _create_rpc_client(settings) as client:
123
+ # 文字列・整数・浮動小数点数・真偽値は JSON の値には変換できるが params の要件を満たさない
124
+ for params in ["not-structured", 1, 1.5, True]:
125
+ assert client.send_rpc(1, NOT_EXIST_METHOD, params) is False
126
+
127
+ # JSON の値として扱えない型は TypeError になる
128
+ # 0.1 は float32 で厳密に表現できないため (Sora::ConvertJsonValue() の既知の制限)
129
+ for invalid_params in [
130
+ {"key": set()},
131
+ {"key": (1, 2)},
132
+ {"key": b"bytes"},
133
+ {"key": 0.1},
134
+ ]:
135
+ with pytest.raises(TypeError, match="Invalid JSON value in params"):
136
+ client.send_rpc(1, NOT_EXIST_METHOD, invalid_params)
137
+
138
+ # int64 の範囲を超える整数は nb::cast_error 由来の RuntimeError になる
139
+ with pytest.raises(RuntimeError):
140
+ client.send_rpc(1, NOT_EXIST_METHOD, {"key": 2**70})
141
+
142
+ # キーが文字列でない dict も nb::cast_error 由来の RuntimeError になる
143
+ with pytest.raises(RuntimeError):
144
+ client.send_rpc(1, NOT_EXIST_METHOD, {1: "value"})
145
+
146
+
147
+ def test_send_rpc_invalid_id(settings):
148
+ """
149
+ id に None でも 0 以上 2^64-1 以下の整数でもない値を指定すると TypeError になることを確認する。
150
+
151
+ C++ SDK の SendRpc() の id は std::optional<uint64_t> のため、文字列・負の整数・
152
+ 2^64 以上の整数は指定できない。
153
+ """
154
+ with _create_rpc_client(settings) as client:
155
+ # 型注釈 (int | None) では通せない値も実行時に渡すため、Any のリストを経由する
156
+ invalid_ids: list[Any] = ["id", -1, 2**64]
157
+ for invalid_id in invalid_ids:
158
+ with pytest.raises(TypeError):
159
+ client.send_rpc(invalid_id, NOT_EXIST_METHOD, {"key": "value"})
160
+
161
+
162
+ def test_send_rpc_without_rpc_label_returns_false(settings):
163
+ """
164
+ rpc ラベルが使えない接続では send_rpc() が送信せず False を返すことを確認する。
165
+
166
+ 前提:
167
+ - DataChannel 経由のシグナリングを使わずに接続する (offer の data_channels に
168
+ rpc ラベルが含まれない)
169
+
170
+ 期待:
171
+ - send_rpc() が送信せず False を返す
172
+ - Sora の RPC 機能の有効無効に依存せず検証できる
173
+ """
174
+ with SoraClient(
175
+ settings,
176
+ SoraRole.RECVONLY,
177
+ audio=True,
178
+ video=True,
179
+ data_channel_signaling=False,
180
+ ignore_disconnect_websocket=False,
181
+ ) as client:
182
+ assert client.send_rpc(1, NOT_EXIST_METHOD, {"key": "value"}) is False
183
+
184
+
185
+ def test_send_rpc_after_disconnect_raises_runtime_error(settings):
186
+ """
187
+ disconnect() 後の send_rpc() が SEGV ではなく RuntimeError になることを確認する。
188
+
189
+ 前提:
190
+ - Sora に接続してから disconnect() する
191
+ - disconnect() 後の SoraConnection インスタンスをそのまま使う
192
+
193
+ 期待:
194
+ - RuntimeError が発生する (メッセージに Already disconnected を含む)
195
+ - プロセスが SEGV で落ちない
196
+ """
197
+ with _create_rpc_client(settings) as client:
198
+ connection = client._connection
199
+ client.disconnect()
200
+
201
+ with pytest.raises(RuntimeError, match="Already disconnected"):
202
+ connection.send_rpc(1, NOT_EXIST_METHOD, {"key": "value"})
@@ -8,6 +8,8 @@ def test_websocket_signaling_only_type_switched(settings):
8
8
  """
9
9
  - WebSocket シグナリングのみ
10
10
  - type: switched 送られてこない
11
+ - signaling の redirect で張り直した WebSocket のクローズ (SELF-CLOSED) は
12
+ 接続中の WebSocket のクローズではないため ws_close には記録されない
11
13
  """
12
14
 
13
15
  with SoraClient(
@@ -29,6 +31,8 @@ def test_hybrid_signaling_type_switched(settings):
29
31
  """
30
32
  - WebSocket シグナリング + DataChannel シグナリング
31
33
  - type: switched 送られてくる
34
+ - signaling の redirect で張り直した WebSocket のクローズ (SELF-CLOSED) は
35
+ 接続中の WebSocket のクローズではないため ws_close には記録されない
32
36
  """
33
37
  with SoraClient(
34
38
  settings,
@@ -49,7 +53,7 @@ def test_datachannel_signaling_only_type_switched(settings):
49
53
  """
50
54
  - DataChannel シグナリングのみ
51
55
  - type: switched 送られてくる
52
- - Python SDK は WebSocket を自分で切断する
56
+ - Python SDK は WebSocket を自分で切断する (switched によるクローズは記録される)
53
57
  """
54
58
  with SoraClient(
55
59
  settings,
@@ -1 +0,0 @@
1
- 2026.2.0.dev2