Skip to content
This repository was archived by the owner on Apr 7, 2026. It is now read-only.

Commit e2f059a

Browse files
committed
wip
1 parent 2bef575 commit e2f059a

62 files changed

Lines changed: 7333 additions & 4505 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
11
name: CI
2-
on:
2+
3+
# NOTE: uv version is also pinned in s2-lite-integration-tests sdks config below.
4+
env:
5+
UV_VERSION: "0.11.3"
6+
7+
on:
38
pull_request:
4-
types: [opened, edited, synchronize, labeled, unlabeled, ready_for_review, reopened]
9+
types:
10+
[
11+
opened,
12+
edited,
13+
synchronize,
14+
labeled,
15+
unlabeled,
16+
ready_for_review,
17+
reopened,
18+
]
519
jobs:
6-
ci:
7-
name: CI
20+
local-checks:
21+
name: Local Checks (code quality, unit tests, docs build, PR title)
822
runs-on: ubuntu-latest
923
steps:
1024
- name: Checkout repository
1125
uses: actions/checkout@v4
1226
- name: Install uv
13-
uses: astral-sh/setup-uv@v6
27+
uses: astral-sh/setup-uv@v7
1428
with:
15-
version: "0.8.2"
29+
version: ${{ env.UV_VERSION }}
1630
- name: Sync dependencies
1731
run: |
1832
uv sync --all-groups
1933
- name: Static code check
2034
run: uv run poe ci_checker
35+
- name: Unit tests
36+
run: uv run pytest tests/ -v -m 'not (account or basin or stream or metrics)'
2137
- name: Check docs build
2238
working-directory: ./docs
2339
run: |
@@ -26,20 +42,60 @@ jobs:
2642
uses: actions/github-script@v7
2743
with:
2844
script: |
29-
const title = context.payload.pull_request.title;
30-
const labels = context.payload.pull_request.labels.map(l => l.name);
31-
if (labels.includes('dev')) {
32-
const regex = /^(?!feat|fix|refactor|docs|perf|style|test|chore|revert)[a-z].*$/;
33-
if (!regex.test(title)) {
34-
core.setFailed(
35-
`PR title "${title}" does not match the commit format for non-user-facing changes`
36-
);
37-
}
38-
} else {
39-
const regex = /^(feat|fix|refactor|docs|perf|style|test|chore|revert)!?:[ ][a-z].*$/;
40-
if (!regex.test(title)) {
41-
core.setFailed(
42-
`PR title "${title}" does not match the expected conventional commit format for user-facing changes`
43-
);
44-
}
45+
const title = context.payload.pull_request.title;
46+
const labels = context.payload.pull_request.labels.map(l => l.name);
47+
if (labels.includes('dev')) {
48+
const regex = /^(?!feat|fix|refactor|docs|perf|style|test|chore|revert)[a-z].*$/;
49+
if (!regex.test(title)) {
50+
core.setFailed(
51+
`PR title "${title}" does not match the commit format for non-user-facing changes`
52+
);
4553
}
54+
} else {
55+
const regex = /^(feat|fix|refactor|docs|perf|style|test|chore|revert)!?:[ ][a-z].*$/;
56+
if (!regex.test(title)) {
57+
core.setFailed(
58+
`PR title "${title}" does not match the expected conventional commit format for user-facing changes`
59+
);
60+
}
61+
}
62+
63+
s2-cloud-integration-tests:
64+
name: S2 Cloud Integration Tests
65+
runs-on: ubuntu-latest
66+
steps:
67+
- name: Checkout repository
68+
uses: actions/checkout@v4
69+
- name: Install uv
70+
uses: astral-sh/setup-uv@v7
71+
with:
72+
version: ${{ env.UV_VERSION }}
73+
- name: Sync dependencies
74+
run: uv sync --group test
75+
- name: Run integration tests
76+
env:
77+
S2_ACCESS_TOKEN: ${{ secrets.S2_ACCESS_TOKEN }}
78+
run: uv run pytest tests/ -v -s -m 'account or basin or stream or metrics'
79+
80+
build-s2-lite:
81+
uses: s2-streamstore/s2/.github/workflows/build-s2-lite.yml@main
82+
83+
s2-lite-integration-tests:
84+
name: S2-Lite Integration Tests
85+
needs: build-s2-lite
86+
uses: s2-streamstore/s2/.github/workflows/sdk-tests.yml@main
87+
with:
88+
server-binary: server
89+
server-args: "--port 8080"
90+
server-port: 8080
91+
sdks: |
92+
[
93+
{
94+
"name": "python",
95+
"repo": "${{ github.repository }}",
96+
"ref": "${{ github.ref }}",
97+
"lang": "python",
98+
"uv-version": "0.11.3",
99+
"test_cmd": "uv run pytest tests/ -v -s -m 'account or basin or stream'"
100+
}
101+
]

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Release streamstore package
1+
name: Release s2-sdk package
22
on:
33
push:
44
tags: ["[0-9]+.[0-9]+.[0-9]+*"]

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
# streamstore
1+
# s2-sdk
22
<div>
33
<p>
44
<!-- PyPI -->
5-
<a href="https://pypi.org/project/streamstore/"><img src="https://img.shields.io/pypi/v/streamstore" /></a>
5+
<a href="https://pypi.org/project/s2-sdk/"><img src="https://img.shields.io/pypi/v/s2-sdk" /></a>
66
<!-- Read the docs -->
7-
<a href="https://streamstore.readthedocs.io/"><img src="https://img.shields.io/readthedocs/streamstore/latest" /></a>
7+
<a href="https://s2-sdk.readthedocs.io/"><img src="https://img.shields.io/readthedocs/s2-sdk/latest" /></a>
88
<!-- Discord -->
99
<a href="https://discord.gg/vTCs7kMkAf"><img src="https://img.shields.io/discord/1209937852528599092?logo=discord" /></a>
1010
<!-- LICENSE -->
1111
<a href="https://github.com/s2-streamstore/s2-sdk-python/blob/main/LICENSE"><img src="https://img.shields.io/github/license/s2-streamstore/s2-sdk-python" /></a>
1212
</p>
1313
</div>
1414

15-
`streamstore` is the Python package that provides an async client for interacting with [s2.dev](https://s2.dev/).
15+
`s2_sdk` is the Python package that provides an async client for interacting with [s2.dev](https://s2.dev/).
1616

1717
## Project links
1818

19-
- [PyPI](https://pypi.org/project/streamstore/)
20-
- [Documentation](https://streamstore.readthedocs.io/)
19+
- [PyPI](https://pypi.org/project/s2-sdk/)
20+
- [Documentation](https://s2-sdk.readthedocs.io/)
2121
- [GitHub](https://github.com/s2-streamstore/s2-sdk-python)
2222

2323
## Requirements
@@ -26,10 +26,10 @@ Python >= 3.11
2626

2727
## Installation
2828

29-
You can install the package from the [Python Package Index](https://pypi.org/project/streamstore) using the package manager of your choice. E.g., with `pip`:
29+
You can install the package from the [Python Package Index](https://pypi.org/project/s2-sdk) using the package manager of your choice. E.g., with `pip`:
3030

3131
```bash
32-
pip install streamstore
32+
pip install s2-sdk
3333
```
3434

3535
## Examples

docs/source/api-reference.md

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,7 @@
11
# API Reference
22

33
```{eval-rst}
4-
.. module:: streamstore
5-
6-
.. autoclass:: S2
7-
:members:
8-
:member-order: bysource
9-
10-
.. autoclass:: Basin()
11-
:members:
12-
:member-order: bysource
13-
14-
.. autoclass:: Stream()
15-
:members:
16-
:member-order: bysource
17-
18-
.. module:: streamstore.schemas
19-
:no-index:
20-
.. autoclass:: Record(body: bytes, headers: list[tuple[bytes, bytes]] = [])
21-
:members:
22-
23-
.. automodule:: streamstore.schemas
24-
:members:
25-
:exclude-members: Record, Endpoints
26-
:member-order: bysource
27-
28-
.. module:: streamstore.schemas
29-
:no-index:
30-
.. autoclass:: Endpoints()
31-
:members:
32-
33-
.. automodule:: streamstore.utils
4+
.. automodule:: s2_sdk
345
:members:
356
:member-order: bysource
367
```

docs/source/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
from datetime import date
1010

11-
project = "streamstore"
11+
project = "s2-sdk"
1212
copyright = f"{date.today().year}, Bandar Systems Inc"
13-
release = "5.0.0"
13+
release = "0.1.0"
1414

1515
# -- General configuration ---------------------------------------------------
1616
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

examples/append_session.py

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,31 @@
11
import asyncio
22
import os
33
import random
4-
from typing import AsyncIterable
54

6-
from streamstore import S2
7-
from streamstore.schemas import AppendInput, Record
5+
from s2_sdk import S2, AppendInput, Record
86

97
ACCESS_TOKEN = os.getenv("S2_ACCESS_TOKEN")
108
MY_BASIN = os.getenv("MY_BASIN")
119
MY_STREAM = os.getenv("MY_STREAM")
1210

1311

14-
async def append_inputs_gen() -> AsyncIterable[AppendInput]:
15-
num_inputs = random.randint(1, 100)
16-
for _ in range(num_inputs):
17-
num_records = random.randint(1, 100)
18-
records = []
19-
for _ in range(num_records):
20-
body_size = random.randint(1, 1024)
21-
records.append(Record(body=os.urandom(body_size)))
22-
input = AppendInput(records)
23-
if random.random() < 0.5:
24-
await asyncio.sleep(random.random() * 2.5)
25-
yield input
26-
27-
2812
async def producer():
29-
async with S2(access_token=ACCESS_TOKEN) as s2:
13+
async with S2(ACCESS_TOKEN) as s2:
3014
stream = s2[MY_BASIN][MY_STREAM]
31-
async for output in stream.append_session(append_inputs_gen()):
32-
num_appended_records = output.end_seq_num - output.start_seq_num
33-
print(f"appended {num_appended_records} records")
15+
async with stream.append_session() as session:
16+
num_inputs = random.randint(1, 100)
17+
for _ in range(num_inputs):
18+
num_records = random.randint(1, 100)
19+
records = []
20+
for _ in range(num_records):
21+
body_size = random.randint(1, 1024)
22+
records.append(Record(body=os.urandom(body_size)))
23+
ticket = await session.submit(AppendInput(records))
24+
ack = await ticket
25+
num_appended_records = ack.end.seq_num - ack.start.seq_num
26+
print(f"appended {num_appended_records} records")
27+
if random.random() < 0.5:
28+
await asyncio.sleep(random.random() * 2.5)
3429

3530

3631
if __name__ == "__main__":

examples/append_session_with_auto_batching.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
from datetime import timedelta
55
from typing import AsyncIterable
66

7-
from streamstore import S2
8-
from streamstore.schemas import Record
9-
from streamstore.utils import append_inputs_gen
7+
from s2_sdk import S2, Batching, Record, append_inputs
108

119
ACCESS_TOKEN = os.getenv("S2_ACCESS_TOKEN")
1210
MY_BASIN = os.getenv("MY_BASIN")
@@ -23,17 +21,20 @@ async def records_gen() -> AsyncIterable[Record]:
2321

2422

2523
async def producer():
26-
async with S2(access_token=ACCESS_TOKEN) as s2:
24+
async with S2(ACCESS_TOKEN) as s2:
2725
stream = s2[MY_BASIN][MY_STREAM]
28-
async for output in stream.append_session(
29-
append_inputs_gen(
26+
async with stream.append_session() as session:
27+
async for batch in append_inputs(
3028
records=records_gen(),
31-
max_records_per_batch=10,
32-
max_linger_per_batch=timedelta(milliseconds=5),
33-
)
34-
):
35-
num_appended_records = output.end_seq_num - output.start_seq_num
36-
print(f"appended {num_appended_records} records")
29+
batching=Batching(
30+
max_records=10,
31+
linger=timedelta(milliseconds=5),
32+
),
33+
):
34+
ticket = await session.submit(batch)
35+
ack = await ticket
36+
num_appended_records = ack.end.seq_num - ack.start.seq_num
37+
print(f"appended {num_appended_records} records")
3738

3839

3940
if __name__ == "__main__":

examples/producer.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import asyncio
2+
import os
3+
import random
4+
5+
from s2_sdk import S2, Record
6+
7+
ACCESS_TOKEN = os.getenv("S2_ACCESS_TOKEN")
8+
MY_BASIN = os.getenv("MY_BASIN")
9+
MY_STREAM = os.getenv("MY_STREAM")
10+
11+
12+
async def main():
13+
async with S2(ACCESS_TOKEN) as s2:
14+
stream = s2[MY_BASIN][MY_STREAM]
15+
async with stream.producer() as producer:
16+
for i in range(100):
17+
body_size = random.randint(1, 1024)
18+
ticket = await producer.submit(Record(body=os.urandom(body_size)))
19+
ack = await ticket
20+
print(f"seq_num={ack.seq_num}")
21+
22+
23+
if __name__ == "__main__":
24+
asyncio.run(main())

examples/read_session.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import asyncio
22
import os
33

4-
from streamstore import S2
5-
from streamstore.schemas import SeqNum
4+
from s2_sdk import S2, SeqNum
65

76
ACCESS_TOKEN = os.getenv("S2_ACCESS_TOKEN")
87
MY_BASIN = os.getenv("MY_BASIN")
98
MY_STREAM = os.getenv("MY_STREAM")
109

1110

1211
async def consumer():
13-
async with S2(access_token=ACCESS_TOKEN) as s2:
12+
async with S2(ACCESS_TOKEN) as s2:
1413
stream = s2[MY_BASIN][MY_STREAM]
1514
tail = await stream.check_tail()
1615
print(f"reading from tail: {tail}")

0 commit comments

Comments
 (0)