From 2ccf39800db78daea053ab8f4c53072dd6595d6f Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 4 Dec 2022 00:02:32 +0000 Subject: [PATCH] Remove start_rpc_server() and make Rpc a context manager Rpc now has a start() method. This way it is possible to use Rpc from IPython without calling __aenter__() --- deltachat-rpc-client/README.md | 15 +++--- deltachat-rpc-client/examples/echobot.py | 2 +- .../src/deltachat_rpc_client/__init__.py | 2 +- .../src/deltachat_rpc_client/pytestplugin.py | 5 +- .../src/deltachat_rpc_client/rpc.py | 50 +++++++++---------- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/deltachat-rpc-client/README.md b/deltachat-rpc-client/README.md index 559806391..647dec7c2 100644 --- a/deltachat-rpc-client/README.md +++ b/deltachat-rpc-client/README.md @@ -30,11 +30,12 @@ from the REPL. $ pip install ipython $ PATH="../target/debug:$PATH" ipython ... -In [1]: from deltachat_rpc_client import * -In [2]: rpc_generator = start_rpc_server() -In [3]: rpc = await rpc_generator.__aenter__() -In [4]: dc = Deltachat(rpc) -In [5]: system_info = await dc.get_system_info() -In [6]: system_info["level"] -Out [6]: 'awesome' +In [1]: from deltachat_rpc_client import * +In [2]: rpc = Rpc() +In [3]: await rpc.start() +In [4]: dc = Deltachat(rpc) +In [5]: system_info = await dc.get_system_info() +In [6]: system_info["level"] +Out[6]: 'awesome' +In [7]: await rpc.close() ``` diff --git a/deltachat-rpc-client/examples/echobot.py b/deltachat-rpc-client/examples/echobot.py index 18ec47d8a..136b65795 100755 --- a/deltachat-rpc-client/examples/echobot.py +++ b/deltachat-rpc-client/examples/echobot.py @@ -7,7 +7,7 @@ import deltachat_rpc_client as dc async def main(): - async with dc.start_rpc_server() as rpc: + async with dc.Rpc() as rpc: deltachat = dc.Deltachat(rpc) system_info = await deltachat.get_system_info() logging.info("Running deltachat core %s", system_info["deltachat_core_version"]) diff --git a/deltachat-rpc-client/src/deltachat_rpc_client/__init__.py b/deltachat-rpc-client/src/deltachat_rpc_client/__init__.py index 11a168a07..4b25df47d 100644 --- a/deltachat-rpc-client/src/deltachat_rpc_client/__init__.py +++ b/deltachat-rpc-client/src/deltachat_rpc_client/__init__.py @@ -2,4 +2,4 @@ from .account import Account from .contact import Contact from .deltachat import Deltachat from .message import Message -from .rpc import Rpc, start_rpc_server +from .rpc import Rpc diff --git a/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py b/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py index bda51cdd5..a3eb4341c 100644 --- a/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py +++ b/deltachat-rpc-client/src/deltachat_rpc_client/pytestplugin.py @@ -8,7 +8,7 @@ import pytest_asyncio from .account import Account from .deltachat import Deltachat -from .rpc import start_rpc_server +from .rpc import Rpc async def get_temp_credentials() -> dict: @@ -42,7 +42,8 @@ class ACFactory: @pytest_asyncio.fixture async def rpc(tmp_path) -> AsyncGenerator: env = {**os.environ, "DC_ACCOUNTS_PATH": str(tmp_path / "accounts")} - async with start_rpc_server(env=env) as rpc: + rpc_server = Rpc(env=env) + async with rpc_server as rpc: yield rpc diff --git a/deltachat-rpc-client/src/deltachat_rpc_client/rpc.py b/deltachat-rpc-client/src/deltachat_rpc_client/rpc.py index bd42044a5..d679770bc 100644 --- a/deltachat-rpc-client/src/deltachat_rpc_client/rpc.py +++ b/deltachat-rpc-client/src/deltachat_rpc_client/rpc.py @@ -1,6 +1,5 @@ import asyncio import json -from contextlib import asynccontextmanager from typing import Any, AsyncGenerator, Dict, Optional @@ -9,8 +8,19 @@ class JsonRpcError(Exception): class Rpc: - def __init__(self, process: asyncio.subprocess.Process) -> None: - self.process = process + def __init__(self, *args, **kwargs): + """The given arguments will be passed to asyncio.create_subprocess_exec()""" + self.args = args + self.kwargs = kwargs + + async def start(self) -> None: + self.process = await asyncio.create_subprocess_exec( + "deltachat-rpc-server", + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE, + *self.args, + **self.kwargs + ) self.event_queues: Dict[int, asyncio.Queue] = {} self.id = 0 self.reader_task = asyncio.create_task(self.reader_loop()) @@ -18,6 +28,18 @@ class Rpc: # Map from request ID to `asyncio.Future` returning the response. self.request_events: Dict[int, asyncio.Future] = {} + async def close(self) -> None: + """Terminate RPC server process and wait until the reader loop finishes.""" + self.process.terminate() + await self.reader_task + + async def __aenter__(self): + await self.start() + return self + + async def __aexit__(self, exc_type, exc, tb): + await self.close() + async def reader_loop(self) -> None: while True: line = await self.process.stdout.readline() @@ -37,11 +59,6 @@ class Rpc: else: print(response) - async def close(self) -> None: - """Terminate RPC server process and wait until the reader loop finishes.""" - self.process.terminate() - await self.reader_task - async def wait_for_event(self, account_id: int) -> Optional[dict]: """Waits for the next event from the given account and returns it.""" if account_id in self.event_queues: @@ -73,20 +90,3 @@ class Rpc: return response["result"] return method - - -@asynccontextmanager -async def start_rpc_server(*args, **kwargs) -> AsyncGenerator: - """The given arguments will be passed to asyncio.create_subprocess_exec()""" - proc = await asyncio.create_subprocess_exec( - "deltachat-rpc-server", - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - *args, - **kwargs - ) - rpc = Rpc(proc) - try: - yield rpc - finally: - await rpc.close()