Skip to content

Commit b38c038

Browse files
ci: faster tests
1 parent 2c06b18 commit b38c038

10 files changed

Lines changed: 237 additions & 195 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: CI
2+
on: [push, pull_request]
3+
4+
jobs:
5+
tests:
6+
runs-on: ${{ matrix.os }}
7+
strategy:
8+
matrix:
9+
python-version: ['3.11', '3.12', '3.13']
10+
os: [ubuntu-latest, macOS-latest, windows-latest]
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
- name: Set up PDM
15+
uses: pdm-project/setup-pdm@v4
16+
with:
17+
python-version: ${{ matrix.python-version }}
18+
cache: true
19+
- name: Install dependencies
20+
run: |
21+
pdm sync -G :all
22+
- name: Run tests
23+
run: |
24+
pdm run -v pytest tests

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.11.10
3+
rev: v0.12.1
44
hooks:
5-
- id: ruff
5+
- id: ruff-check
66
args:
77
- --fix
88
- --exit-non-zero-on-fix

datagraph/io.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,13 @@ def __init__(
7777
async def stream(
7878
self, cursor: StreamCursor = StreamCursor.FROM_FIRST
7979
) -> "AsyncIterator[T]":
80+
client = await Supervisor.instance().client()
81+
8082
last_entry_id: str = cursor.value
8183
last_stream_read: float = current_time()
8284

8385
while True:
84-
raw_message: RawStreamEntry | None = await (
85-
await Supervisor.instance().client()
86-
).xread(
86+
raw_message: RawStreamEntry | None = await client.xread(
8787
{self._stream_key: last_entry_id},
8888
options=StreamReadOptions(
8989
block_ms=Supervisor.instance().config.io_read_timeout, count=1
@@ -104,6 +104,7 @@ async def stream(
104104
Supervisor.instance().config.io_read_pending_timeout,
105105
)
106106

107+
await anyio.lowlevel.checkpoint()
107108
continue
108109

109110
message = StreamEntry(raw_message)
@@ -161,15 +162,13 @@ async def write(self, value: "T") -> None:
161162
elif value.name != self.name:
162163
raise MismatchedIOError("write", self.name, value.name)
163164

164-
await (await Supervisor.instance().client()).xadd(
165-
self._stream_key, [("ioval", self._serializer.dump(value))]
166-
)
165+
client = await Supervisor.instance().client()
166+
await client.xadd(self._stream_key, [("ioval", self._serializer.dump(value))])
167167

168168
async def complete(self) -> None:
169-
await (await Supervisor.instance().client()).set(self._completion_key, b"true")
169+
client = await Supervisor.instance().client()
170+
await client.set(self._completion_key, b"true")
170171

171172
async def is_complete(self) -> bool:
172-
return (
173-
await (await Supervisor.instance().client()).get(self._completion_key)
174-
== b"true"
175-
)
173+
client = await Supervisor.instance().client()
174+
return await client.get(self._completion_key) == b"true"

datagraph/serialization.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,22 @@
1616

1717
class Serializer(ABC):
1818
@abstractmethod
19-
async def serialize(self, value: "Any") -> bytes:
19+
def serialize(self, value: "Any") -> bytes:
2020
"""Serialize a value to compressed bytestream for storage."""
2121
raise NotImplementedError()
2222

2323
@abstractmethod
24-
async def deserialize(self, data: bytes) -> "Any":
24+
def deserialize(self, data: bytes) -> "Any":
2525
"""Deserialize a compressed bytestream from storage."""
2626
raise NotImplementedError()
2727

2828
@abstractmethod
29-
async def compress(self, data: bytes) -> bytes:
29+
def compress(self, data: bytes) -> bytes:
3030
"""Compress a bytestream for storage."""
3131
raise NotImplementedError()
3232

3333
@abstractmethod
34-
async def decompress(self, data: bytes) -> bytes:
34+
def decompress(self, data: bytes) -> bytes:
3535
"""Decompress a bytestream from storage."""
3636
raise NotImplementedError()
3737

@@ -58,7 +58,7 @@ def __init__(self, secret: str) -> None:
5858
self.secret_key: bytes = secret.encode()
5959

6060
def serialize(self, data: "Any") -> bytes:
61-
return pickletools.optimize(pickle.dumps(data, protocol=5))
61+
return pickle.dumps(data, protocol=5)
6262

6363
def deserialize(self, data: bytes) -> "Any":
6464
return pickle.loads(data)
@@ -78,6 +78,7 @@ def decompressor(self) -> "ZstdDecompressor":
7878
return self._thread_context.decompressor
7979

8080
def compress(self, data: bytes) -> bytes:
81+
data = pickletools.optimize(data)
8182
compressed = self.compressor.compress(data)
8283
signer = blake2b(digest_size=16, key=self.secret_key, usedforsecurity=True)
8384
signer.update(compressed)

pdm.lock

Lines changed: 182 additions & 163 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/conftest.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
pytest_plugins = ("celery.contrib.pytest",)
66

77

8-
@pytest.fixture(autouse=True)
8+
@pytest.fixture
99
async def supervisor():
1010
# Use AsyncMock for the Supervisor.instance() mock so it can be awaited
1111
with patch(
@@ -17,7 +17,6 @@ async def supervisor():
1717
@pytest.fixture(
1818
params=[
1919
pytest.param(("asyncio", {"use_uvloop": False}), id="asyncio"),
20-
pytest.param(("asyncio", {"use_uvloop": True}), id="asyncio+uvloop"),
2120
pytest.param(
2221
("trio", {"restrict_keyboard_interrupt_to_checkpoints": True}), id="trio"
2322
),

tests/integration/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,5 +180,5 @@ async def test_execute_flow_interlaced(supervisor):
180180
# increasing series of timestamps, i.e. we should see CPCPCP. if C processed
181181
# all values before P, we'd see CCCPPP, and the sorted list would not be
182182
# identical to the raw zipped one
183-
timestamps = list(zip(p_timestamps, c_timestamps))
183+
timestamps = list(zip(p_timestamps, c_timestamps, strict=False))
184184
assert timestamps == sorted(timestamps)

tests/integration/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

33

4-
@pytest.fixture
4+
@pytest.fixture(scope="session")
55
def celery_worker_parameters():
6-
return {"shutdown_timeout": 30.0}
6+
return {"shutdown_timeout": 5.0}

tests/integration/test_executors.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
from .processors import bar, consumer, first, foo, foobar, producer, second
1010

1111

12-
@pytest.fixture(params=[LocalExecutor, CeleryExecutor], ids=("local", "celery"))
13-
async def supervisor(request, monkeypatch):
14-
monkeypatch.setattr(Supervisor, "_instance", None)
12+
@pytest.fixture(
13+
params=[LocalExecutor, CeleryExecutor], ids=("local", "celery"), scope="module"
14+
)
15+
async def supervisor(request):
16+
Supervisor._instance = None
1517
config = GlideClientConfiguration(addresses=[NodeAddress()], request_timeout=10000)
1618

1719
if request.param == LocalExecutor:
@@ -35,9 +37,9 @@ async def supervisor(request, monkeypatch):
3537
shared_task(name=first.name, ignore_result=True)(_first)
3638
shared_task(name=second.name, ignore_result=True)(_second)
3739

38-
celery_app = request.getfixturevalue("celery_app")
40+
celery_app = request.getfixturevalue("celery_session_app")
3941
# this is necessary to spin up the worker thread
40-
_ = request.getfixturevalue("celery_worker")
42+
_ = request.getfixturevalue("celery_session_worker")
4143

4244
yield Supervisor.attach(
4345
glide_config=config, executor=CeleryExecutor(celery_app)

tests/test_supervisor.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,15 @@
55
from datagraph.supervisor import Supervisor
66

77

8-
@pytest.mark.anyio
9-
async def test_unattached_supervisor(monkeypatch):
8+
def test_unattached_supervisor(monkeypatch):
109
"""Test that accessing Supervisor.instance() raises an error if not attached."""
1110
monkeypatch.setattr(Supervisor, "_instance", None)
1211

1312
with pytest.raises(RuntimeError, match="Supervisor is not available"):
1413
Supervisor.instance()
1514

1615

17-
@pytest.mark.anyio
18-
async def test_attach_supervisor(monkeypatch):
16+
def test_attach_supervisor(monkeypatch):
1917
"""Test that the attach method sets the instance correctly."""
2018
monkeypatch.setattr(Supervisor, "_instance", None)
2119

0 commit comments

Comments
 (0)