Skip to content

Commit 7b6364e

Browse files
committed
add ci for the docs
1 parent ed51c14 commit 7b6364e

5 files changed

Lines changed: 341 additions & 0 deletions

File tree

.github/workflows/mkdocs.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Build and Deploy MkDocs
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
paths:
8+
- docs/**
9+
- mkdocs.yml
10+
- .github/workflows/mkdocs.yml
11+
pull_request:
12+
paths:
13+
- docs/**
14+
- mkdocs.yml
15+
- .github/workflows/mkdocs.yml
16+
workflow_dispatch:
17+
18+
permissions:
19+
contents: read
20+
pages: write
21+
id-token: write
22+
23+
concurrency:
24+
group: pages
25+
cancel-in-progress: false
26+
27+
jobs:
28+
build:
29+
runs-on: ubuntu-latest
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v4
33+
34+
- name: Setup Python
35+
uses: actions/setup-python@v5
36+
with:
37+
python-version: "3.12"
38+
39+
- name: Install MkDocs
40+
run: python -m pip install mkdocs
41+
42+
- name: Build docs
43+
run: mkdocs build --strict
44+
45+
- name: Upload Pages artifact
46+
if: github.event_name != 'pull_request'
47+
uses: actions/upload-pages-artifact@v3
48+
with:
49+
path: site
50+
51+
deploy:
52+
if: github.event_name != 'pull_request'
53+
needs: build
54+
environment:
55+
name: github-pages
56+
url: ${{ steps.deployment.outputs.page_url }}
57+
runs-on: ubuntu-latest
58+
steps:
59+
- name: Setup Pages
60+
uses: actions/configure-pages@v5
61+
62+
- name: Deploy to GitHub Pages
63+
id: deployment
64+
uses: actions/deploy-pages@v4

docs/examples.md

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
# Examples
2+
3+
This page shows practical ways to use `rsloop`.
4+
5+
All examples use normal Python `asyncio` patterns. The difference is that `rsloop` provides the event loop implementation.
6+
7+
## Run one coroutine
8+
9+
This is the simplest starting point:
10+
11+
```python
12+
import rsloop
13+
14+
15+
async def main() -> None:
16+
print("hello from rsloop")
17+
18+
19+
rsloop.run(main())
20+
```
21+
22+
Use this style when your program has one main async entry point.
23+
24+
## Create tasks inside the loop
25+
26+
This looks almost the same as standard `asyncio`:
27+
28+
```python
29+
import asyncio
30+
import rsloop
31+
32+
33+
async def worker(name: str) -> str:
34+
await asyncio.sleep(0.1)
35+
return f"{name} done"
36+
37+
38+
async def main() -> None:
39+
task1 = asyncio.create_task(worker("first"))
40+
task2 = asyncio.create_task(worker("second"))
41+
results = await asyncio.gather(task1, task2)
42+
print(results)
43+
44+
45+
rsloop.run(main())
46+
```
47+
48+
If your app already uses `asyncio.create_task(...)`, `await`, and `gather(...)`, moving to `rsloop` is usually straightforward.
49+
50+
## Create a loop manually
51+
52+
Manual loop creation is useful when you want explicit setup and cleanup:
53+
54+
```python
55+
import asyncio
56+
import rsloop
57+
58+
59+
async def main() -> None:
60+
print("running on", type(asyncio.get_running_loop()).__name__)
61+
62+
63+
loop = rsloop.new_event_loop()
64+
asyncio.set_event_loop(loop)
65+
try:
66+
loop.run_until_complete(main())
67+
finally:
68+
asyncio.set_event_loop(None)
69+
loop.close()
70+
```
71+
72+
Use this style if your program manages the loop lifecycle itself.
73+
74+
## Schedule callbacks
75+
76+
`rsloop.Loop` supports the familiar callback APIs:
77+
78+
```python
79+
import rsloop
80+
81+
82+
loop = rsloop.new_event_loop()
83+
84+
85+
def say(message: str) -> None:
86+
print(message)
87+
loop.stop()
88+
89+
90+
loop.call_later(0.5, say, "timer fired")
91+
loop.run_forever()
92+
loop.close()
93+
```
94+
95+
This is useful when reading older `asyncio` code that still uses callbacks instead of only coroutines.
96+
97+
## Work with raw sockets
98+
99+
You can use socket helpers directly on the running loop:
100+
101+
```python
102+
import asyncio
103+
import socket
104+
import rsloop
105+
106+
107+
async def main() -> None:
108+
loop = asyncio.get_running_loop()
109+
left, right = socket.socketpair()
110+
left.setblocking(False)
111+
right.setblocking(False)
112+
113+
try:
114+
await loop.sock_sendall(left, b"ping")
115+
data = await loop.sock_recv(right, 4)
116+
print(data.decode())
117+
finally:
118+
left.close()
119+
right.close()
120+
121+
122+
rsloop.run(main())
123+
```
124+
125+
Use this style when you need lower-level control than streams provide.
126+
127+
## Start a TCP server
128+
129+
`rsloop` supports the normal protocol and transport style:
130+
131+
```python
132+
import asyncio
133+
import rsloop
134+
135+
136+
class EchoProtocol(asyncio.Protocol):
137+
def connection_made(self, transport: asyncio.BaseTransport) -> None:
138+
self.transport = transport
139+
140+
def data_received(self, data: bytes) -> None:
141+
self.transport.write(data.upper())
142+
143+
144+
async def main() -> None:
145+
loop = asyncio.get_running_loop()
146+
server = await loop.create_server(EchoProtocol, "127.0.0.1", 9000)
147+
try:
148+
print("serving on 127.0.0.1:9000")
149+
await asyncio.sleep(60)
150+
finally:
151+
server.close()
152+
await server.wait_closed()
153+
154+
155+
rsloop.run(main())
156+
```
157+
158+
This is a good fit when your project already uses `asyncio.Protocol`.
159+
160+
## Use streams
161+
162+
Because `rsloop` can patch stream helpers, high-level stream code can stay familiar:
163+
164+
```python
165+
import asyncio
166+
import rsloop
167+
168+
169+
async def handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
170+
data = await reader.read(100)
171+
writer.write(data.upper())
172+
await writer.drain()
173+
writer.close()
174+
await writer.wait_closed()
175+
176+
177+
async def main() -> None:
178+
server = await asyncio.start_server(handle_client, "127.0.0.1", 9001)
179+
try:
180+
reader, writer = await asyncio.open_connection("127.0.0.1", 9001)
181+
writer.write(b"hello")
182+
await writer.drain()
183+
print((await reader.read(100)).decode())
184+
writer.close()
185+
await writer.wait_closed()
186+
finally:
187+
server.close()
188+
await server.wait_closed()
189+
190+
191+
rsloop.run(main())
192+
```
193+
194+
This is often the easiest path for developers who already use high-level `asyncio` streams.
195+
196+
## Run blocking work in an executor
197+
198+
Not everything has to be async:
199+
200+
```python
201+
import asyncio
202+
import rsloop
203+
204+
205+
def blocking_sum() -> int:
206+
return sum(range(100000))
207+
208+
209+
async def main() -> None:
210+
loop = asyncio.get_running_loop()
211+
result = await loop.run_in_executor(None, blocking_sum)
212+
print(result)
213+
214+
215+
rsloop.run(main())
216+
```
217+
218+
Use this when you must call blocking Python code without freezing the event loop.
219+
220+
## Run a subprocess
221+
222+
`rsloop` also supports subprocess workflows:
223+
224+
```python
225+
import asyncio
226+
import rsloop
227+
import sys
228+
229+
230+
async def main() -> None:
231+
proc = await asyncio.create_subprocess_exec(
232+
sys.executable,
233+
"-c",
234+
"print('hello from child')",
235+
stdout=asyncio.subprocess.PIPE,
236+
)
237+
stdout, _ = await proc.communicate()
238+
print(stdout.decode().strip())
239+
240+
241+
rsloop.run(main())
242+
```
243+
244+
This is useful for scripts, tooling, and service code that needs to call external programs.
245+
246+
## Where to find bigger examples
247+
248+
For fuller examples, read the repository files in `examples/`:
249+
250+
- `01_basics.py`
251+
- `02_fd_and_sockets.py`
252+
- `03_streams.py`
253+
- `04_unix_and_accepted_socket.py`
254+
- `05_pipes_signals_subprocesses.py`

docs/getting-started.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,5 @@ The `examples/` directory is the best hands-on tour of the project:
105105
- `examples/05_pipes_signals_subprocesses.py`: pipes, signals, and subprocesses
106106

107107
If you are new to lower-level `asyncio` features, start with `01_basics.py` and `03_streams.py`.
108+
109+
There is also a shorter docs page with copy-paste snippets in [Examples](examples.md).

docs/index.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,25 @@ That means:
2727
- `rsloop.Loop` is the main event loop class.
2828
- Importing `rsloop` also installs a few compatibility patches so it fits better into normal `asyncio` code.
2929

30+
## For Intermediate Python Developers
31+
32+
If you already use `asyncio` comfortably, these are the main things worth knowing early:
33+
34+
- `rsloop` tries to keep the standard `asyncio` mental model, not invent a new one.
35+
- The Python package is mostly a thin wrapper around a Rust extension built with PyO3.
36+
- `rsloop.run(...)` behaves like a focused `asyncio.run(...)` helper that ensures an `rsloop` loop is actually being used.
37+
- Importing `rsloop` can monkeypatch parts of `asyncio`, especially `set_event_loop(...)` and optionally stream helpers such as `open_connection(...)` and `start_server(...)`.
38+
- The runtime model is hybrid: Python coroutines still run as Python code, while lower-level loop coordination and parts of the I/O stack are handled in Rust.
39+
- Compatibility is a project goal, but not every `asyncio` edge case is identical yet, especially around TLS and some transport internals.
40+
41+
If you want to read code instead of only using the package, a good path is:
42+
43+
1. `python/rsloop/_run.py`
44+
2. `python/rsloop/_loop_compat.py`
45+
3. `src/lib.rs`
46+
4. `src/python_api.rs`
47+
5. `src/loop_core.rs`
48+
3049
## What is inside this repository?
3150

3251
At a high level, the repository has five parts:
@@ -55,6 +74,7 @@ rsloop.run(main())
5574
## Recommended reading order
5675

5776
- Start with [Getting Started](getting-started.md) if you want to use the package.
77+
- Read [Examples](examples.md) if you want copy-paste usage patterns.
5878
- Read [How It Works](how-it-works.md) if you want the big picture.
5979
- Read [Project Structure](project-structure.md) if you want to explore the codebase.
6080
- Read [Development](development.md) if you want to build or test the project locally.

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ theme:
66
nav:
77
- Home: index.md
88
- Getting Started: getting-started.md
9+
- Examples: examples.md
910
- How It Works: how-it-works.md
1011
- Project Structure: project-structure.md
1112
- Development: development.md

0 commit comments

Comments
 (0)