From 2f4aadde102f6c6adc5d4686bb56187be6c9479b Mon Sep 17 00:00:00 2001 From: hongyu Date: Fri, 19 Jun 2026 19:07:58 +0100 Subject: [PATCH 1/6] CHORE(typing): annot Self for __enter__ / __aenter__ to propagate subclass types --- src/py/client/__init__.py | 4 ++-- src/py/client/_async.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/py/client/__init__.py b/src/py/client/__init__.py index d743c566..d37680f9 100644 --- a/src/py/client/__init__.py +++ b/src/py/client/__init__.py @@ -1,7 +1,7 @@ # pyright: strict, reportUnknownMemberType=false, reportUnknownVariableType=false from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any, Optional, Self import requests @@ -123,7 +123,7 @@ def __init__( else: raise - def __enter__(self, *_: Any) -> Client: + def __enter__(self, *_: Any) -> Self: return self def __exit__(self, *_: Any) -> None: diff --git a/src/py/client/_async.py b/src/py/client/_async.py index 898d3616..95382eb2 100644 --- a/src/py/client/_async.py +++ b/src/py/client/_async.py @@ -1,7 +1,7 @@ # pyright: strict, reportUnknownMemberType=false, reportUnknownVariableType=false from __future__ import annotations -from typing import Any, Optional +from typing import Any, Optional, Self import aiohttp # type: ignore[import-not-found] @@ -75,7 +75,7 @@ def __init__( ) self._timeout = timeout - async def __aenter__(self, *_: Any) -> AsyncClient: + async def __aenter__(self, *_: Any) -> Self: await self._session.__aenter__() if self._session_id is None: try: From 26af8a6e5f502f7e045449dd8c795f0e6c307461 Mon Sep 17 00:00:00 2001 From: hongyu Date: Fri, 19 Jun 2026 19:09:04 +0100 Subject: [PATCH 2/6] add `test` endpoints to async py client --- src/py/client/_async.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/py/client/_async.py b/src/py/client/_async.py index 95382eb2..49d7e90a 100644 --- a/src/py/client/_async.py +++ b/src/py/client/_async.py @@ -217,6 +217,20 @@ async def instance_src( timeout=timeout, ) + async def test_src( + self, + src: str, + seed: Optional[int] = None, + timeout: Optional[float] = None, + ) -> simple_api_pb2.TestRes: + seed = seed or 0 + timeout = timeout or self._timeout + return await self._client.test_src( + ctx=self.mk_context(), + request=simple_api_pb2.TestSrcReq(src=src, session=self._sesh, seed=seed), + timeout=timeout, + ) + async def qcheck_src( self, src: str, @@ -231,6 +245,22 @@ async def qcheck_src( timeout=timeout, ) + async def test_name( + self, + name: str, + seed: Optional[int] = None, + timeout: Optional[float] = None, + ) -> simple_api_pb2.TestRes: + seed = seed or 0 + timeout = timeout or self._timeout + return await self._client.test_name( + ctx=self.mk_context(), + request=simple_api_pb2.TestNameReq( + name=name, session=self._sesh, seed=seed + ), + timeout=timeout, + ) + async def qcheck_name( self, name: str, From 9e822ce901ee99be78cca7ad9868c2b4e04c586e Mon Sep 17 00:00:00 2001 From: hongyu Date: Mon, 22 Jun 2026 14:10:42 +0100 Subject: [PATCH 3/6] `test`: do not set default seed when it's not provided --- src/py/client/__init__.py | 16 ++++++++++------ src/py/client/_async.py | 16 ++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/py/client/__init__.py b/src/py/client/__init__.py index d37680f9..75ced954 100644 --- a/src/py/client/__init__.py +++ b/src/py/client/__init__.py @@ -244,11 +244,14 @@ def test_src( seed: Optional[int] = None, timeout: Optional[float] = None, ) -> simple_api_pb2.TestRes: - seed = seed or 0 + req = simple_api_pb2.TestSrcReq(src=src, session=self._sesh) + if seed is not None: + req.seed = seed + timeout = timeout or self._timeout return self._client.test_src( ctx=self.mk_context(), - request=simple_api_pb2.TestSrcReq(src=src, session=self._sesh, seed=seed), + request=req, timeout=timeout, ) @@ -266,13 +269,14 @@ def test_name( seed: Optional[int] = None, timeout: Optional[float] = None, ) -> simple_api_pb2.TestRes: - seed = seed or 0 + req = simple_api_pb2.TestNameReq(name=name, session=self._sesh) + if seed is not None: + req.seed = seed + timeout = timeout or self._timeout return self._client.test_name( ctx=self.mk_context(), - request=simple_api_pb2.TestNameReq( - name=name, session=self._sesh, seed=seed - ), + request=req, timeout=timeout, ) diff --git a/src/py/client/_async.py b/src/py/client/_async.py index 49d7e90a..e35dd6f5 100644 --- a/src/py/client/_async.py +++ b/src/py/client/_async.py @@ -223,11 +223,14 @@ async def test_src( seed: Optional[int] = None, timeout: Optional[float] = None, ) -> simple_api_pb2.TestRes: - seed = seed or 0 + req = simple_api_pb2.TestSrcReq(src=src, session=self._sesh) + if seed is not None: + req.seed = seed + timeout = timeout or self._timeout return await self._client.test_src( ctx=self.mk_context(), - request=simple_api_pb2.TestSrcReq(src=src, session=self._sesh, seed=seed), + request=req, timeout=timeout, ) @@ -251,13 +254,14 @@ async def test_name( seed: Optional[int] = None, timeout: Optional[float] = None, ) -> simple_api_pb2.TestRes: - seed = seed or 0 + req = simple_api_pb2.TestNameReq(name=name, session=self._sesh) + if seed is not None: + req.seed = seed + timeout = timeout or self._timeout return await self._client.test_name( ctx=self.mk_context(), - request=simple_api_pb2.TestNameReq( - name=name, session=self._sesh, seed=seed - ), + request=req, timeout=timeout, ) From 4c5544c33857b8467617af357a60d7ca6b5801cb Mon Sep 17 00:00:00 2001 From: hongyu Date: Mon, 22 Jun 2026 14:11:42 +0100 Subject: [PATCH 4/6] delegate qcheck calls to `test` endpoints --- src/py/client/_async.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/py/client/_async.py b/src/py/client/_async.py index e35dd6f5..f986a948 100644 --- a/src/py/client/_async.py +++ b/src/py/client/_async.py @@ -240,13 +240,7 @@ async def qcheck_src( seed: Optional[int] = None, timeout: Optional[float] = None, ) -> simple_api_pb2.TestRes: - seed = seed or 0 - timeout = timeout or self._timeout - return await self._client.qcheck_src( - ctx=self.mk_context(), - request=simple_api_pb2.QCheckSrcReq(src=src, session=self._sesh, seed=seed), - timeout=timeout, - ) + return await self.test_src(src=src, seed=seed, timeout=timeout) async def test_name( self, @@ -271,15 +265,7 @@ async def qcheck_name( seed: Optional[int] = None, timeout: Optional[float] = None, ) -> simple_api_pb2.TestRes: - seed = seed or 0 - timeout = timeout or self._timeout - return await self._client.qcheck_name( - ctx=self.mk_context(), - request=simple_api_pb2.QCheckNameReq( - name=name, session=self._sesh, seed=seed - ), - timeout=timeout, - ) + return await self.test_name(name=name, seed=seed, timeout=timeout) async def list_artifacts( self, task: task_pb2.Task, timeout: Optional[float] = None From d479fcbc76fa7189d0046079b243c9f480ab7693 Mon Sep 17 00:00:00 2001 From: hongyu Date: Mon, 22 Jun 2026 14:30:26 +0100 Subject: [PATCH 5/6] FIX: catch invalid session error --- src/py/client/_common.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/py/client/_common.py b/src/py/client/_common.py index 453a7c91..d53b2c32 100644 --- a/src/py/client/_common.py +++ b/src/py/client/_common.py @@ -21,4 +21,8 @@ def is_session_not_found(ex: TwirpServerException) -> bool: # Replace with a typed code check once the server is updated. body = (getattr(ex, "meta", None) or {}).get("body") or {} # type: ignore msg = body.get("msg") or "" # type: ignore - return "Session not found" in msg or "Unknown session" in msg + return ( + "Session not found" in msg + or "Unknown session" in msg + or "InvalidSession" in msg + ) From 311ee7901bbecf5ba12af291dade876e853ebd82 Mon Sep 17 00:00:00 2001 From: hongyu Date: Mon, 22 Jun 2026 19:48:16 +0100 Subject: [PATCH 6/6] bump ver --- src/py/pyproject.toml | 2 +- src/py/setup.py | 2 +- src/py/uv.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/py/pyproject.toml b/src/py/pyproject.toml index 563bc4d6..26d51276 100644 --- a/src/py/pyproject.toml +++ b/src/py/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "imandrax_api" -version = "0.20.1" +version = "0.20.2" description = "Imandrax API client library" requires-python = ">=3.12" dependencies = [ diff --git a/src/py/setup.py b/src/py/setup.py index a05f6ae6..6b70cb86 100644 --- a/src/py/setup.py +++ b/src/py/setup.py @@ -1,6 +1,6 @@ from setuptools import setup -VERSION = "0.20.1" +VERSION = "0.20.2" setup( name="imandrax_api", version=VERSION, diff --git a/src/py/uv.lock b/src/py/uv.lock index 73f63b44..35e10436 100644 --- a/src/py/uv.lock +++ b/src/py/uv.lock @@ -284,7 +284,7 @@ wheels = [ [[package]] name = "imandrax-api" -version = "0.20.1" +version = "0.20.2" source = { editable = "." } dependencies = [ { name = "protobuf" },