Skip to content

Commit 1425db3

Browse files
committed
docs: Convert quickstart_async.md to executable doctests
Removed all 7 # doctest: +SKIP markers and converted to executable doctests using CPython asyncio.run() pattern. All examples now use server fixture for TestServer isolation and assert behavior (True/False) rather than exact values. Changes: - Pattern A demo: Uses server.acmd() with cleanup - Pattern B demo: Uses tmux_cmd_async with socket isolation - Quick example: Creates windows concurrently, verifies count - Objects demo: Tests session/window/pane creation with ID format checks - Error handling: Verifies stderr and returncode behavior - Performance comparison: Tests async is at least as fast as sync - Hybrid example: Demonstrates both patterns working together All examples use server.socket_name for isolation, include cleanup, and return boolean assertions for deterministic test results. Verified with: pytest docs/quickstart_async.md --doctest-modules -v Result: 7 passed in 0.75s
1 parent bae7107 commit 1425db3

File tree

3 files changed

+74
-83
lines changed

3 files changed

+74
-83
lines changed

IMPLEMENTATION_COMPLETE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ The implementation is **complete, tested, and ready for production use**.
300300

301301
---
302302

303-
**Created**: 2025-11-03
303+
**Created**: 2025-11-08
304304
**Worktree**: `libtmux-asyncio-psycopg`
305305
**Branch**: `libtmux-asyncio-psycopg`
306306
**Status**: ✅ Production Ready

IMPLEMENTATION_STATUS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# libtmux Async Implementation Status
22

33
**Status**: ✅ **Complete and Validated**
4-
**Date**: 2025-11-03
4+
**Date**: 2025-11-08
55
**Branch**: `libtmux-asyncio-psycopg`
66

77
## Executive Summary

docs/quickstart_async.md

Lines changed: 72 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,24 @@ Use `.acmd()` on existing Server/Session/Window/Pane objects. Perfect for gradua
2323

2424
```python
2525
>>> import asyncio
26-
>>> import libtmux
27-
>>>
28-
>>> async def main():
29-
... server = libtmux.Server()
30-
...
31-
... # Execute async command
26+
>>> async def pattern_a_demo():
27+
... # Uses 'server' fixture from conftest for isolation
3228
... result = await server.acmd('new-session', '-d', '-P', '-F#{session_id}')
33-
... print(f"Created session: {result.stdout[0]}")
29+
... session_id = result.stdout[0]
3430
...
3531
... # Execute concurrent commands
3632
... results = await asyncio.gather(
37-
... server.acmd('new-window', '-n', 'window1'),
38-
... server.acmd('new-window', '-n', 'window2'),
39-
... server.acmd('new-window', '-n', 'window3'),
33+
... server.acmd('new-window', '-t', session_id, '-n', 'window1'),
34+
... server.acmd('new-window', '-t', session_id, '-n', 'window2'),
35+
... server.acmd('new-window', '-t', session_id, '-n', 'window3'),
4036
... )
41-
... print(f"Created {len(results)} windows concurrently")
42-
>>>
43-
>>> asyncio.run(main()) # doctest: +SKIP
44-
Created session: $...
45-
Created 3 windows concurrently
37+
...
38+
... # Cleanup
39+
... await server.acmd('kill-session', '-t', session_id)
40+
...
41+
... return len(results) == 3
42+
>>> asyncio.run(pattern_a_demo())
43+
True
4644
```
4745

4846
### Pattern B: `common_async` Module
@@ -52,23 +50,21 @@ Use `tmux_cmd_async()` for direct async command execution. Perfect for new async
5250
```python
5351
>>> import asyncio
5452
>>> from libtmux.common_async import tmux_cmd_async, get_version
55-
>>>
56-
>>> async def main():
53+
>>> async def pattern_b_demo():
5754
... # Get tmux version
5855
... version = await get_version()
59-
... print(f"tmux version: {version}")
56+
... sock = server.socket_name
6057
...
61-
... # Execute concurrent commands
58+
... # Execute concurrent commands with isolated socket
6259
... sessions, windows, panes = await asyncio.gather(
63-
... tmux_cmd_async('list-sessions'),
64-
... tmux_cmd_async('list-windows'),
65-
... tmux_cmd_async('list-panes'),
60+
... tmux_cmd_async('-L', sock, 'list-sessions'),
61+
... tmux_cmd_async('-L', sock, 'list-windows', '-a'),
62+
... tmux_cmd_async('-L', sock, 'list-panes', '-a'),
6663
... )
67-
... print(f"Sessions: {len(sessions.stdout)}, Windows: {len(windows.stdout)}, Panes: {len(panes.stdout)}")
68-
>>>
69-
>>> asyncio.run(main()) # doctest: +SKIP
70-
tmux version: ...
71-
Sessions: ..., Windows: ..., Panes: ...
64+
...
65+
... return len(str(version)) > 0 and all(isinstance(r.stdout, list) for r in [sessions, windows, panes])
66+
>>> asyncio.run(pattern_b_demo())
67+
True
7268
```
7369

7470
## Installation
@@ -91,13 +87,9 @@ Here's a practical example showing the performance benefit of async:
9187

9288
```python
9389
>>> import asyncio
94-
>>> import libtmux
95-
>>>
9690
>>> async def create_windows_async():
9791
... """Create multiple windows concurrently (fast)."""
98-
... server = libtmux.Server()
99-
...
100-
... # Get or create session
92+
... # Uses 'server' fixture from conftest
10193
... result = await server.acmd('new-session', '-d', '-P', '-F#{session_id}')
10294
... session_id = result.stdout[0]
10395
...
@@ -109,13 +101,12 @@ Here's a practical example showing the performance benefit of async:
109101
... server.acmd('new-window', '-t', session_id, '-n', 'logs'),
110102
... )
111103
...
112-
... print(f"Created {len(results)} windows concurrently")
113-
...
114104
... # Cleanup
115105
... await server.acmd('kill-session', '-t', session_id)
116-
>>>
117-
>>> asyncio.run(create_windows_async()) # doctest: +SKIP
118-
Created 4 windows concurrently
106+
...
107+
... return len(results) == 4
108+
>>> asyncio.run(create_windows_async())
109+
True
119110
```
120111

121112
Compare this to sequential sync operations, which would be 2-3x slower.
@@ -136,11 +127,8 @@ Pattern A makes it easy to work with tmux objects asynchronously:
136127

137128
```python
138129
>>> import asyncio
139-
>>> import libtmux
140-
>>>
141130
>>> async def demo_objects():
142-
... server = libtmux.Server()
143-
...
131+
... # Uses 'server' fixture from conftest
144132
... # Create session
145133
... result = await server.acmd('new-session', '-d', '-P', '-F#{session_id}')
146134
... session_id = result.stdout[0]
@@ -153,16 +141,20 @@ Pattern A makes it easy to work with tmux objects asynchronously:
153141
... result = await server.acmd('split-window', '-t', window_id, '-P', '-F#{pane_id}')
154142
... pane_id = result.stdout[0]
155143
...
156-
... print(f"Created session {session_id}, window {window_id}, pane {pane_id}")
157-
...
158144
... # Send keys to pane
159145
... await server.acmd('send-keys', '-t', pane_id, 'echo "Hello from async!"', 'Enter')
160146
...
161147
... # Cleanup
162148
... await server.acmd('kill-session', '-t', session_id)
163-
>>>
164-
>>> asyncio.run(demo_objects()) # doctest: +SKIP
165-
Created session $..., window @..., pane %...
149+
...
150+
... # Verify all IDs were created
151+
... return all([
152+
... session_id.startswith('$'),
153+
... window_id.startswith('@'),
154+
... pane_id.startswith('%')
155+
... ])
156+
>>> asyncio.run(demo_objects())
157+
True
166158
```
167159

168160
## Error Handling
@@ -172,20 +164,20 @@ Error handling works the same in async as in sync:
172164
```python
173165
>>> import asyncio
174166
>>> from libtmux.common_async import tmux_cmd_async
175-
>>>
176167
>>> async def demo_errors():
168+
... sock = server.socket_name
169+
...
177170
... # Invalid command
178-
... result = await tmux_cmd_async('invalid-command')
179-
... if result.stderr:
180-
... print(f"Error: {result.stderr[0]}")
171+
... result = await tmux_cmd_async('-L', sock, 'invalid-command')
172+
... has_error = len(result.stderr) > 0 and 'unknown command' in result.stderr[0]
181173
...
182174
... # Non-existent session
183-
... result = await tmux_cmd_async('has-session', '-t', 'non_existent')
184-
... print(f"Return code: {result.returncode}")
185-
>>>
186-
>>> asyncio.run(demo_errors()) # doctest: +SKIP
187-
Error: unknown command: invalid-command
188-
Return code: 1
175+
... result = await tmux_cmd_async('-L', sock, 'has-session', '-t', 'non_existent_99999')
176+
... has_nonzero_return = result.returncode != 0
177+
...
178+
... return has_error and has_nonzero_return
179+
>>> asyncio.run(demo_errors())
180+
True
189181
```
190182

191183
## Performance: Sequential vs Concurrent
@@ -197,31 +189,31 @@ Here's a real-world example showing the performance difference:
197189
>>> import time
198190
>>> from libtmux.common_async import tmux_cmd_async
199191
>>> from libtmux.common import tmux_cmd
200-
>>>
201192
>>> async def compare_performance():
193+
... sock = server.socket_name
194+
...
202195
... # Async (concurrent)
203196
... start = time.time()
204197
... await asyncio.gather(
205-
... tmux_cmd_async('list-sessions'),
206-
... tmux_cmd_async('list-windows'),
207-
... tmux_cmd_async('list-panes'),
208-
... tmux_cmd_async('show-options', '-g'),
198+
... tmux_cmd_async('-L', sock, 'list-sessions'),
199+
... tmux_cmd_async('-L', sock, 'list-windows', '-a'),
200+
... tmux_cmd_async('-L', sock, 'list-panes', '-a'),
201+
... tmux_cmd_async('-L', sock, 'show-options', '-g'),
209202
... )
210203
... async_time = time.time() - start
211204
...
212205
... # Sync (sequential)
213206
... start = time.time()
214-
... tmux_cmd('list-sessions')
215-
... tmux_cmd('list-windows')
216-
... tmux_cmd('list-panes')
217-
... tmux_cmd('show-options', '-g')
207+
... tmux_cmd('-L', sock, 'list-sessions')
208+
... tmux_cmd('-L', sock, 'list-windows', '-a')
209+
... tmux_cmd('-L', sock, 'list-panes', '-a')
210+
... tmux_cmd('-L', sock, 'show-options', '-g')
218211
... sync_time = time.time() - start
219212
...
220-
... speedup = sync_time / async_time
221-
... print(f"Async: {async_time:.4f}s, Sync: {sync_time:.4f}s, Speedup: {speedup:.2f}x")
222-
>>>
223-
>>> asyncio.run(compare_performance()) # doctest: +SKIP
224-
Async: 0.0...s, Sync: 0.0...s, Speedup: ...x
213+
... # Concurrent should be at least as fast (allow 10% variance)
214+
... return async_time <= sync_time * 1.1
215+
>>> asyncio.run(compare_performance())
216+
True
225217
```
226218

227219
Typical speedup: **2-3x faster** for 4 concurrent commands.
@@ -232,30 +224,29 @@ You can mix both patterns in the same application:
232224

233225
```python
234226
>>> import asyncio
235-
>>> import libtmux
236227
>>> from libtmux.common_async import tmux_cmd_async
237-
>>>
238228
>>> async def hybrid_example():
239-
... server = libtmux.Server()
229+
... # Uses 'server' fixture from conftest
230+
... sock = server.socket_name
240231
...
241232
... # Pattern A: Use .acmd() on server
242233
... result_a = await server.acmd('new-session', '-d', '-P', '-F#{session_id}')
243234
... session_a = result_a.stdout[0]
244235
...
245-
... # Pattern B: Use tmux_cmd_async directly
246-
... result_b = await tmux_cmd_async('new-session', '-d', '-P', '-F#{session_id}')
236+
... # Pattern B: Use tmux_cmd_async directly with socket
237+
... result_b = await tmux_cmd_async('-L', sock, 'new-session', '-d', '-P', '-F#{session_id}')
247238
... session_b = result_b.stdout[0]
248239
...
249-
... print(f"Pattern A: {session_a}, Pattern B: {session_b}")
250-
...
251240
... # Both patterns work with asyncio.gather
252241
... await asyncio.gather(
253242
... server.acmd('kill-session', '-t', session_a),
254-
... tmux_cmd_async('kill-session', '-t', session_b),
243+
... tmux_cmd_async('-L', sock, 'kill-session', '-t', session_b),
255244
... )
256-
>>>
257-
>>> asyncio.run(hybrid_example()) # doctest: +SKIP
258-
Pattern A: $..., Pattern B: $...
245+
...
246+
... # Verify both sessions were created and start with $
247+
... return session_a.startswith('$') and session_b.startswith('$')
248+
>>> asyncio.run(hybrid_example())
249+
True
259250
```
260251

261252
## Next Steps

0 commit comments

Comments
 (0)