Skip to content

Commit 50066b2

Browse files
committed
docs(topics): Add asyncio guide
why: Success criterion #4 - "Documentation updated with async examples" what: - Add comprehensive async topic guide with working doctests - Cover: why async, basic usage, concurrent operations, callbacks - Include comparison tables, error handling, API reference - Add to topics toctree
1 parent e9d85a5 commit 50066b2

2 files changed

Lines changed: 227 additions & 0 deletions

File tree

docs/topics/asyncio.md

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
(asyncio)=
2+
3+
# Async Support
4+
5+
libvcs provides **async equivalents** for all synchronous APIs, enabling
6+
non-blocking VCS operations ideal for managing multiple repositories
7+
concurrently.
8+
9+
## Overview
10+
11+
The async API mirrors the sync API with an `Async` prefix:
12+
13+
| Sync Class | Async Equivalent |
14+
|------------|------------------|
15+
| {class}`~libvcs.cmd.git.Git` | {class}`~libvcs.cmd._async.git.AsyncGit` |
16+
| {class}`~libvcs.cmd.hg.Hg` | {class}`~libvcs.cmd._async.hg.AsyncHg` |
17+
| {class}`~libvcs.cmd.svn.Svn` | {class}`~libvcs.cmd._async.svn.AsyncSvn` |
18+
| {class}`~libvcs.sync.git.GitSync` | {class}`~libvcs.sync._async.git.AsyncGitSync` |
19+
| {class}`~libvcs.sync.hg.HgSync` | {class}`~libvcs.sync._async.hg.AsyncHgSync` |
20+
| {class}`~libvcs.sync.svn.SvnSync` | {class}`~libvcs.sync._async.svn.AsyncSvnSync` |
21+
22+
## Why Async?
23+
24+
Async APIs excel when:
25+
26+
- **Managing multiple repositories** - Clone/update repos concurrently
27+
- **Building responsive applications** - UI remains responsive during VCS operations
28+
- **Integration with async frameworks** - FastAPI, aiohttp, etc.
29+
- **CI/CD pipelines** - Parallel repository operations
30+
31+
## Basic Usage
32+
33+
### Running Git Commands
34+
35+
```python
36+
>>> from libvcs.cmd._async.git import AsyncGit
37+
>>> async def example():
38+
... git = AsyncGit(path=tmp_path)
39+
... await git.run(['init'])
40+
... status = await git.status()
41+
... return 'On branch' in status
42+
>>> asyncio.run(example())
43+
True
44+
```
45+
46+
### Cloning a Repository
47+
48+
```python
49+
>>> from libvcs.cmd._async.git import AsyncGit
50+
>>> async def clone_example():
51+
... repo_path = tmp_path / 'myrepo'
52+
... git = AsyncGit(path=repo_path)
53+
... url = f'file://{create_git_remote_repo()}'
54+
... await git.clone(url=url)
55+
... return (repo_path / '.git').exists()
56+
>>> asyncio.run(clone_example())
57+
True
58+
```
59+
60+
### Repository Synchronization
61+
62+
For higher-level repository management, use the sync classes:
63+
64+
```python
65+
>>> from libvcs.sync._async.git import AsyncGitSync
66+
>>> async def sync_example():
67+
... url = f'file://{create_git_remote_repo()}'
68+
... repo_path = tmp_path / 'synced_repo'
69+
... repo = AsyncGitSync(url=url, path=repo_path)
70+
... await repo.obtain() # Clone
71+
... await repo.update_repo() # Pull updates
72+
... return (repo_path / '.git').exists()
73+
>>> asyncio.run(sync_example())
74+
True
75+
```
76+
77+
## Concurrent Operations
78+
79+
The primary advantage of async is running operations concurrently:
80+
81+
```python
82+
>>> from libvcs.sync._async.git import AsyncGitSync
83+
>>> async def concurrent_clone():
84+
... urls = [
85+
... f'file://{create_git_remote_repo()}',
86+
... f'file://{create_git_remote_repo()}',
87+
... ]
88+
... tasks = []
89+
... for i, url in enumerate(urls):
90+
... repo = AsyncGitSync(url=url, path=tmp_path / f'repo_{i}')
91+
... tasks.append(repo.obtain())
92+
... await asyncio.gather(*tasks) # Clone all concurrently
93+
... return all((tmp_path / f'repo_{i}' / '.git').exists() for i in range(2))
94+
>>> asyncio.run(concurrent_clone())
95+
True
96+
```
97+
98+
## Progress Callbacks
99+
100+
Async APIs support async progress callbacks for real-time output streaming:
101+
102+
```python
103+
>>> import datetime
104+
>>> from libvcs._internal.async_run import async_run
105+
>>> async def with_progress():
106+
... output_lines = []
107+
... async def progress(output: str, timestamp: datetime.datetime) -> None:
108+
... output_lines.append(output)
109+
... result = await async_run(['echo', 'hello'], callback=progress)
110+
... return result.strip()
111+
>>> asyncio.run(with_progress())
112+
'hello'
113+
```
114+
115+
For sync callbacks, use the wrapper:
116+
117+
```python
118+
>>> from libvcs._internal.async_run import wrap_sync_callback
119+
>>> def my_sync_callback(output: str, timestamp: datetime.datetime) -> None:
120+
... print(output, end='')
121+
>>> async_callback = wrap_sync_callback(my_sync_callback)
122+
```
123+
124+
## Sync vs Async Comparison
125+
126+
### Sync Pattern
127+
128+
```python
129+
from libvcs.sync.git import GitSync
130+
131+
repo = GitSync(url="https://github.com/user/repo", path="/tmp/repo")
132+
repo.obtain() # Blocks until complete
133+
repo.update_repo() # Blocks again
134+
```
135+
136+
### Async Pattern
137+
138+
```python
139+
import asyncio
140+
from libvcs.sync._async.git import AsyncGitSync
141+
142+
async def main():
143+
repo = AsyncGitSync(url="https://github.com/user/repo", path="/tmp/repo")
144+
await repo.obtain() # Non-blocking
145+
await repo.update_repo()
146+
147+
asyncio.run(main())
148+
```
149+
150+
## Error Handling
151+
152+
Async methods raise the same exceptions as sync equivalents:
153+
154+
```python
155+
>>> from libvcs import exc
156+
>>> from libvcs._internal.async_run import async_run
157+
>>> async def error_example():
158+
... try:
159+
... await async_run(['git', 'nonexistent-command'], check_returncode=True)
160+
... except exc.CommandError as e:
161+
... return 'error caught'
162+
... return 'no error'
163+
>>> asyncio.run(error_example())
164+
'error caught'
165+
```
166+
167+
### Timeout Handling
168+
169+
```python
170+
>>> from libvcs import exc
171+
>>> from libvcs._internal.async_run import async_run
172+
>>> async def timeout_example():
173+
... try:
174+
... # Very short timeout for demo
175+
... await async_run(['sleep', '10'], timeout=0.1)
176+
... except exc.CommandTimeoutError:
177+
... return 'timeout caught'
178+
... return 'completed'
179+
>>> asyncio.run(timeout_example())
180+
'timeout caught'
181+
```
182+
183+
## Testing with pytest-asyncio
184+
185+
Use the async fixtures for testing:
186+
187+
```python
188+
import pytest
189+
190+
@pytest.mark.asyncio
191+
async def test_repo_operations(async_git_repo):
192+
# async_git_repo is an AsyncGitSync instance
193+
status = await async_git_repo.cmd.status()
194+
assert 'On branch' in status
195+
```
196+
197+
See {doc}`/pytest-plugin` for available async fixtures.
198+
199+
## When to Use Async
200+
201+
| Scenario | Recommendation |
202+
|----------|----------------|
203+
| Single repository, simple script | Sync API (simpler) |
204+
| Multiple repositories concurrently | **Async API** |
205+
| Integration with async framework | **Async API** |
206+
| CI/CD with parallel operations | **Async API** |
207+
| Interactive CLI tool | Either (prefer sync for simplicity) |
208+
209+
## API Reference
210+
211+
### Command Classes
212+
213+
- {class}`~libvcs.cmd._async.git.AsyncGit` - Async git commands
214+
- {class}`~libvcs.cmd._async.hg.AsyncHg` - Async mercurial commands
215+
- {class}`~libvcs.cmd._async.svn.AsyncSvn` - Async subversion commands
216+
217+
### Sync Classes
218+
219+
- {class}`~libvcs.sync._async.git.AsyncGitSync` - Async git repository management
220+
- {class}`~libvcs.sync._async.hg.AsyncHgSync` - Async mercurial repository management
221+
- {class}`~libvcs.sync._async.svn.AsyncSvnSync` - Async subversion repository management
222+
223+
### Internal Utilities
224+
225+
- {func}`~libvcs._internal.async_run.async_run` - Low-level async command execution
226+
- {class}`~libvcs._internal.async_subprocess.AsyncSubprocessCommand` - Async subprocess wrapper

docs/topics/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ with detailed explanations and runnable examples.
99

1010
```{toctree}
1111
12+
asyncio
1213
traversing_git
1314
filtering
1415
url_parsing

0 commit comments

Comments
 (0)