Skip to content

Commit 4ca3ee8

Browse files
committed
fix: update model version in README and improve context manager tests for CopilotClient and CopilotSession
1 parent f982ac2 commit 4ca3ee8

File tree

3 files changed

+56
-28
lines changed

3 files changed

+56
-28
lines changed

python/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ async def main():
2626
# Client automatically starts on enter and cleans up on exit
2727
async with CopilotClient() as client:
2828
# Create a session with automatic cleanup
29-
async with await client.create_session({"model": "gpt-5"}) as session:
29+
async with await client.create_session({"model": "gpt-4"}) as session:
3030
# Wait for response using session.idle event
3131
done = asyncio.Event()
3232

python/copilot/session.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -536,14 +536,17 @@ async def destroy(self) -> None:
536536
>>> # Clean up when done
537537
>>> await session.destroy()
538538
"""
539-
if self._destroyed:
540-
return
539+
# Ensure that the check and update of _destroyed are atomic so that
540+
# only the first caller proceeds to send the destroy RPC.
541+
with self._event_handlers_lock:
542+
if self._destroyed:
543+
return
544+
self._destroyed = True
541545

542546
try:
543547
await self._client.request("session.destroy", {"sessionId": self.session_id})
544548
finally:
545-
# Mark as destroyed and clear handlers even if the request fails
546-
self._destroyed = True
549+
# Clear handlers even if the request fails
547550
with self._event_handlers_lock:
548551
self._event_handlers.clear()
549552
with self._tool_handlers_lock:

python/e2e/test_context_managers.py

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""E2E Context Manager Tests"""
22

3+
import os
4+
35
import pytest
46

57
from copilot import CopilotClient
@@ -9,10 +11,24 @@
911
pytestmark = pytest.mark.asyncio(loop_scope="module")
1012

1113

14+
def create_test_client(ctx) -> CopilotClient:
15+
"""Create a fresh CopilotClient configured with the test harness proxy."""
16+
github_token = "fake-token-for-e2e-tests" if os.environ.get("CI") == "true" else None
17+
return CopilotClient(
18+
{
19+
"cli_path": ctx.cli_path,
20+
"cwd": ctx.work_dir,
21+
"env": ctx.get_env(),
22+
"github_token": github_token,
23+
}
24+
)
25+
26+
1227
class TestCopilotClientContextManager:
1328
async def test_should_auto_start_and_cleanup_with_context_manager(self, ctx):
1429
"""Test that CopilotClient context manager auto-starts and cleans up."""
15-
async with ctx.client as client:
30+
client = create_test_client(ctx)
31+
async with client:
1632
assert client.get_state() == "connected"
1733
# Verify we can use the client
1834
pong = await client.ping("test")
@@ -23,7 +39,8 @@ async def test_should_auto_start_and_cleanup_with_context_manager(self, ctx):
2339

2440
async def test_should_create_session_in_context(self, ctx):
2541
"""Test creating and using a session within client context."""
26-
async with ctx.client as client:
42+
client = create_test_client(ctx)
43+
async with client:
2744
session = await client.create_session({"model": "fake-test-model"})
2845
assert session.session_id
2946

@@ -37,7 +54,8 @@ async def test_should_create_session_in_context(self, ctx):
3754

3855
async def test_should_cleanup_multiple_sessions(self, ctx):
3956
"""Test that all sessions are cleaned up when client context exits."""
40-
async with ctx.client as client:
57+
client = create_test_client(ctx)
58+
async with client:
4159
session1 = await client.create_session()
4260
session2 = await client.create_session()
4361
session3 = await client.create_session()
@@ -51,8 +69,9 @@ async def test_should_cleanup_multiple_sessions(self, ctx):
5169

5270
async def test_should_propagate_exceptions(self, ctx):
5371
"""Test that exceptions within context are propagated."""
72+
client = create_test_client(ctx)
5473
with pytest.raises(ValueError, match="test error"):
55-
async with ctx.client as client:
74+
async with client:
5675
assert client.get_state() == "connected"
5776
raise ValueError("test error")
5877

@@ -61,7 +80,8 @@ async def test_should_propagate_exceptions(self, ctx):
6180

6281
async def test_should_handle_cleanup_errors_gracefully(self, ctx):
6382
"""Test that cleanup errors don't prevent context from exiting."""
64-
async with ctx.client as client:
83+
client = create_test_client(ctx)
84+
async with client:
6585
await client.create_session()
6686

6787
# Kill the process to force cleanup to fail
@@ -75,29 +95,34 @@ async def test_should_handle_cleanup_errors_gracefully(self, ctx):
7595
class TestCopilotSessionContextManager:
7696
async def test_should_cleanup_session_with_context_manager(self, ctx):
7797
"""Test that CopilotSession context manager cleans up session."""
78-
async with await ctx.client.create_session() as session:
79-
assert session.session_id
80-
# Send a message to verify session is working
81-
await session.send({"prompt": "Hello!"})
98+
client = create_test_client(ctx)
99+
async with client:
100+
async with await client.create_session() as session:
101+
assert session.session_id
102+
# Send a message to verify session is working
103+
await session.send({"prompt": "Hello!"})
82104

83-
# After exiting context, session should be destroyed
84-
with pytest.raises(Exception, match="Session not found"):
85-
await session.get_messages()
105+
# After exiting context, session should be destroyed
106+
with pytest.raises(Exception, match="Session not found"):
107+
await session.get_messages()
86108

87109
async def test_should_propagate_exceptions_in_session_context(self, ctx):
88110
"""Test that exceptions within session context are propagated."""
89-
with pytest.raises(ValueError, match="test session error"):
90-
async with await ctx.client.create_session() as session:
91-
assert session.session_id
92-
raise ValueError("test session error")
93-
94-
# Session should still be cleaned up after exception
95-
with pytest.raises(Exception, match="Session not found"):
96-
await session.get_messages()
111+
client = create_test_client(ctx)
112+
async with client:
113+
with pytest.raises(ValueError, match="test session error"):
114+
async with await client.create_session() as session:
115+
assert session.session_id
116+
raise ValueError("test session error")
117+
118+
# Session should still be cleaned up after exception
119+
with pytest.raises(Exception, match="Session not found"):
120+
await session.get_messages()
97121

98122
async def test_nested_context_managers(self, ctx):
99123
"""Test using nested context managers for client and session."""
100-
async with ctx.client as client:
124+
client = create_test_client(ctx)
125+
async with client:
101126
async with await client.create_session() as session:
102127
assert session.session_id
103128
await session.send({"prompt": "Test message"})
@@ -111,7 +136,8 @@ async def test_nested_context_managers(self, ctx):
111136

112137
async def test_multiple_sequential_session_contexts(self, ctx):
113138
"""Test creating multiple sessions sequentially with context managers."""
114-
async with ctx.client as client:
139+
client = create_test_client(ctx)
140+
async with client:
115141
# First session
116142
async with await client.create_session() as session1:
117143
session1_id = session1.session_id
@@ -128,4 +154,3 @@ async def test_multiple_sequential_session_contexts(self, ctx):
128154
# First session should be destroyed
129155
with pytest.raises(Exception, match="Session not found"):
130156
await session1.get_messages()
131-

0 commit comments

Comments
 (0)