Skip to content

Commit aa039e1

Browse files
roznawskclaude
andcommitted
ci: skip examples on 3.10 lane and harden test cleanup
Address Copilot review on #77: - Lower examples/*/pyproject.toml requires-python to >=3.10 so the workspace lockfile resolves on 3.10 (uv unifies requires-python across workspace members; any member at >=3.11 forces the lock to refuse 3.10). Example sources still target 3.11+ at runtime; they are not installed or type-checked on the 3.10 CI lane. - ci.yml: on the 3.10 matrix entry, sync without --all-packages and run pyright scoped to fishjam/ so the 3.11+ examples never reach the 3.10 venv. 3.11+ lanes are unchanged. - tests/test_notifier.py: wrap each async test in try/finally with asyncio.gather(*, return_exceptions=True) so background tasks are always cancelled and awaited, restoring the auto-cleanup semantics asyncio.TaskGroup provided. Replace the inconsistent try/except CancelledError pattern in two tests. - uv.lock regenerated against the new workspace constraint. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 21c165b commit aa039e1

7 files changed

Lines changed: 433 additions & 61 deletions

File tree

.github/workflows/ci.yml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,20 @@ jobs:
3333
enable-cache: true
3434

3535
- name: Install project dependencies
36-
run: uv sync --locked --all-extras --all-packages
36+
run: |
37+
if [ "${{ matrix.python-version }}" = "3.10" ]; then
38+
uv sync --locked --all-extras
39+
else
40+
uv sync --locked --all-extras --all-packages
41+
fi
3742
3843
- name: Type check
39-
run: uv run pyright
44+
run: |
45+
if [ "${{ matrix.python-version }}" = "3.10" ]; then
46+
uv run pyright fishjam
47+
else
48+
uv run pyright
49+
fi
4050
4151
test:
4252
runs-on: ubuntu-latest
@@ -50,7 +60,12 @@ jobs:
5060
enable-cache: true
5161

5262
- name: Install project dependencies
53-
run: uv sync --locked --all-extras --all-packages
63+
run: |
64+
if [ "${{ matrix.python-version }}" = "3.10" ]; then
65+
uv sync --locked --all-extras
66+
else
67+
uv sync --locked --all-extras --all-packages
68+
fi
5469
5570
- name: Initialize Localtunnel
5671
id: tunnel

examples/multimodal/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "multimodal"
33
version = "0.1.0"
44
description = "Fishjam multimodal demo with Gemini Live API"
55
readme = "README.md"
6-
requires-python = ">=3.11"
6+
requires-python = ">=3.10"
77
dependencies = [
88
"fastapi[standard]==0.116.0",
99
"fishjam-server-sdk",

examples/poet_chat/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "poet_chat"
33
version = "0.1.0"
44
description = "Fishjam voice agent example"
55
readme = "README.md"
6-
requires-python = ">=3.11"
6+
requires-python = ">=3.10"
77
dependencies = ["fishjam-server-sdk", "openai-agents[voice]>=0.2.11"]
88

99
[tool.uv.sources]

examples/selective_subscription/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "selective-subscription-demo"
33
version = "0.1.0"
44
description = "Selective subscription demo using Fishjam Python SDK"
55
readme = "README.md"
6-
requires-python = ">=3.11"
6+
requires-python = ">=3.10"
77
dependencies = [
88
"starlette>=0.35.0",
99
"uvicorn>=0.25.0",

examples/transcription/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "transcription"
33
version = "0.1.0"
44
description = "Fishjam transcription demo"
55
readme = "README.md"
6-
requires-python = ">=3.11"
6+
requires-python = ">=3.10"
77
dependencies = [
88
"fastapi[standard]==0.116.0",
99
"fishjam-server-sdk",

tests/test_notifier.py

Lines changed: 61 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,16 @@ def handle_notitifcation(_notification):
8181
pass
8282

8383
notifier_task = asyncio.ensure_future(notifier.connect())
84-
await notifier.wait_ready()
85-
86-
assert (
87-
notifier._websocket
88-
and notifier._websocket.state == websockets.State.OPEN
89-
)
90-
91-
notifier_task.cancel()
9284
try:
93-
await notifier_task
94-
except asyncio.CancelledError:
95-
pass
85+
await notifier.wait_ready()
86+
87+
assert (
88+
notifier._websocket
89+
and notifier._websocket.state == websockets.State.OPEN
90+
)
91+
finally:
92+
notifier_task.cancel()
93+
await asyncio.gather(notifier_task, return_exceptions=True)
9694

9795

9896
@pytest.fixture
@@ -117,22 +115,23 @@ async def test_room_created_deleted(
117115
):
118116
event_checks = [ServerMessageRoomCreated, ServerMessageRoomDeleted]
119117

120-
assert_task = asyncio.ensure_future(assert_events(notifier, event_checks.copy()))
118+
assert_task = asyncio.ensure_future(
119+
assert_events(notifier, event_checks.copy())
120+
)
121121
notifier_task = asyncio.ensure_future(notifier.connect())
122-
await notifier.wait_ready()
122+
try:
123+
await notifier.wait_ready()
123124

124-
options = RoomOptions(webhook_url=WEBHOOK_URL)
125-
room = room_api.create_room(options=options)
125+
options = RoomOptions(webhook_url=WEBHOOK_URL)
126+
room = room_api.create_room(options=options)
126127

127-
room_api.delete_room(room.id)
128+
room_api.delete_room(room.id)
128129

129-
await assert_task
130-
131-
notifier_task.cancel()
132-
try:
133-
await notifier_task
134-
except asyncio.CancelledError:
135-
pass
130+
await assert_task
131+
finally:
132+
assert_task.cancel()
133+
notifier_task.cancel()
134+
await asyncio.gather(assert_task, notifier_task, return_exceptions=True)
136135

137136
self.assert_webhook_events(event_checks, event_queue, room.id)
138137

@@ -149,27 +148,32 @@ async def test_peer_connected_disconnected(
149148
ServerMessageRoomDeleted,
150149
]
151150

152-
assert_task = asyncio.ensure_future(assert_events(notifier, event_checks.copy()))
151+
assert_task = asyncio.ensure_future(
152+
assert_events(notifier, event_checks.copy())
153+
)
153154
notifier_task = asyncio.ensure_future(notifier.connect())
154-
await notifier.wait_ready()
155-
156-
options = RoomOptions(webhook_url=WEBHOOK_URL)
157-
room = room_api.create_room(options=options)
155+
tasks = [assert_task, notifier_task]
156+
try:
157+
await notifier.wait_ready()
158158

159-
peer, token = room_api.create_peer(room.id)
160-
peer_socket = PeerSocket(fishjam_url=FISHJAM_ID)
161-
peer_socket_task = asyncio.ensure_future(peer_socket.connect(token))
159+
options = RoomOptions(webhook_url=WEBHOOK_URL)
160+
room = room_api.create_room(options=options)
162161

163-
await peer_socket.wait_ready()
162+
peer, token = room_api.create_peer(room.id)
163+
peer_socket = PeerSocket(fishjam_url=FISHJAM_ID)
164+
peer_socket_task = asyncio.ensure_future(peer_socket.connect(token))
165+
tasks.append(peer_socket_task)
164166

165-
room_api.delete_peer(room.id, peer.id)
166-
room_api.delete_room(room.id)
167+
await peer_socket.wait_ready()
167168

168-
await assert_task
169+
room_api.delete_peer(room.id, peer.id)
170+
room_api.delete_room(room.id)
169171

170-
notifier_task.cancel()
171-
peer_socket_task.cancel()
172-
await asyncio.gather(notifier_task, peer_socket_task, return_exceptions=True)
172+
await assert_task
173+
finally:
174+
for task in tasks:
175+
task.cancel()
176+
await asyncio.gather(*tasks, return_exceptions=True)
173177

174178
self.assert_webhook_events(event_checks, event_queue, room.id)
175179

@@ -185,26 +189,31 @@ async def test_peer_connected_room_deleted(
185189
ServerMessageRoomDeleted,
186190
]
187191

188-
assert_task = asyncio.ensure_future(assert_events(notifier, event_checks.copy()))
192+
assert_task = asyncio.ensure_future(
193+
assert_events(notifier, event_checks.copy())
194+
)
189195
notifier_task = asyncio.ensure_future(notifier.connect())
190-
await notifier.wait_ready()
191-
192-
options = RoomOptions(webhook_url=WEBHOOK_URL)
193-
room = room_api.create_room(options=options)
194-
_peer, token = room_api.create_peer(room.id)
196+
tasks = [assert_task, notifier_task]
197+
try:
198+
await notifier.wait_ready()
195199

196-
peer_socket = PeerSocket(fishjam_url=FISHJAM_ID)
197-
peer_socket_task = asyncio.ensure_future(peer_socket.connect(token))
200+
options = RoomOptions(webhook_url=WEBHOOK_URL)
201+
room = room_api.create_room(options=options)
202+
_peer, token = room_api.create_peer(room.id)
198203

199-
await peer_socket.wait_ready()
204+
peer_socket = PeerSocket(fishjam_url=FISHJAM_ID)
205+
peer_socket_task = asyncio.ensure_future(peer_socket.connect(token))
206+
tasks.append(peer_socket_task)
200207

201-
room_api.delete_room(room.id)
208+
await peer_socket.wait_ready()
202209

203-
await assert_task
210+
room_api.delete_room(room.id)
204211

205-
notifier_task.cancel()
206-
peer_socket_task.cancel()
207-
await asyncio.gather(notifier_task, peer_socket_task, return_exceptions=True)
212+
await assert_task
213+
finally:
214+
for task in tasks:
215+
task.cancel()
216+
await asyncio.gather(*tasks, return_exceptions=True)
208217

209218
self.assert_webhook_events(event_checks, event_queue, room.id)
210219

0 commit comments

Comments
 (0)