diff --git a/.vscode/launch.json b/.vscode/launch.json index c3af759b..b0f99ce6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,6 +11,13 @@ } ], "configurations": [ + { + "name": "Python: Attach using Process Id", + "type": "python", + "request": "attach", + "processId": "${command:pickProcess}", + "justMyCode": true + }, { "type": "lldb", "request": "attach", diff --git a/veilid-python/tests/__init__.py b/veilid-python/tests/__init__.py index e69de29b..e26f2d5f 100644 --- a/veilid-python/tests/__init__.py +++ b/veilid-python/tests/__init__.py @@ -0,0 +1,22 @@ +import pytest +pytest_plugins = ('pytest_asyncio',) + +import os + +################################################################## +VEILID_SERVER = os.getenv("VEILID_SERVER") +if VEILID_SERVER is not None: + vsparts = VEILID_SERVER.split(":") + VEILID_SERVER = vsparts[0] + if len(vsparts) == 2: + VEILID_SERVER_PORT = int(vsparts[1]) + else: + VEILID_SERVER_PORT = 5959 +else: + VEILID_SERVER = "localhost" + VEILID_SERVER_PORT = 5959 + +################################################################## + +async def simple_update_callback(update): + print("VeilidUpdate: {}".format(update)) diff --git a/veilid-python/tests/test_basic.py b/veilid-python/tests/test_basic.py index 80443727..d74b25b2 100644 --- a/veilid-python/tests/test_basic.py +++ b/veilid-python/tests/test_basic.py @@ -2,42 +2,24 @@ import veilid_python import pytest -import os - -pytest_plugins = ('pytest_asyncio',) +from . import * ################################################################## -VEILID_SERVER = os.getenv("VEILID_SERVER") -if VEILID_SERVER is not None: - vsparts = VEILID_SERVER.split(":") - VEILID_SERVER = vsparts[0] - if len(vsparts) == 2: - VEILID_SERVER_PORT = int(vsparts[1]) - else: - VEILID_SERVER_PORT = 5959 -else: - VEILID_SERVER = "localhost" - VEILID_SERVER_PORT = 5959 - -################################################################## - -async def _simple_update_callback(update): - print("VeilidUpdate: {}".format(update)) @pytest.mark.asyncio async def test_connect(): - async with await veilid_python.json_api_connect(VEILID_SERVER, VEILID_SERVER_PORT, _simple_update_callback) as api: + async with await veilid_python.json_api_connect(VEILID_SERVER, VEILID_SERVER_PORT, simple_update_callback) as api: pass @pytest.mark.asyncio async def test_fail_connect(): with pytest.raises(Exception): - async with await veilid_python.json_api_connect("fuahwelifuh32luhwafluehawea", 1, _simple_update_callback) as api: + async with await veilid_python.json_api_connect("fuahwelifuh32luhwafluehawea", 1, simple_update_callback) as api: pass @pytest.mark.asyncio async def test_version(): - async with await veilid_python.json_api_connect(VEILID_SERVER, VEILID_SERVER_PORT, _simple_update_callback) as api: + async with await veilid_python.json_api_connect(VEILID_SERVER, VEILID_SERVER_PORT, simple_update_callback) as api: v = await api.veilid_version() print("veilid_version: {}".format(v.__dict__)) vstr = await api.veilid_version_string() diff --git a/veilid-python/tests/test_crypto.py b/veilid-python/tests/test_crypto.py new file mode 100644 index 00000000..6ceb993f --- /dev/null +++ b/veilid-python/tests/test_crypto.py @@ -0,0 +1,28 @@ +# Crypto veilid_python tests + +import veilid_python +import pytest +from . import * + +################################################################## + +@pytest.mark.asyncio +async def test_best_crypto_system(): + async with await veilid_python.json_api_connect(VEILID_SERVER, VEILID_SERVER_PORT, simple_update_callback) as api: + bcs = await api.best_crypto_system() + # let handle dangle for test + # del bcs + +@pytest.mark.asyncio +async def test_get_crypto_system(): + async with await veilid_python.json_api_connect(VEILID_SERVER, VEILID_SERVER_PORT, simple_update_callback) as api: + cs = await api.get_crypto_system(veilid_python.CryptoKind.CRYPTO_KIND_VLD0) + # clean up handle early + del cs + +@pytest.mark.asyncio +async def test_get_crypto_system_invalid(): + async with await veilid_python.json_api_connect(VEILID_SERVER, VEILID_SERVER_PORT, simple_update_callback) as api: + with pytest.raises(veilid_python.VeilidAPIError): + cs = await api.get_crypto_system(veilid_python.CryptoKind.CRYPTO_KIND_NONE) + diff --git a/veilid-python/veilid_python/json_api.py b/veilid-python/veilid_python/json_api.py index 4bffc670..7ec5fff8 100644 --- a/veilid-python/veilid_python/json_api.py +++ b/veilid-python/veilid_python/json_api.py @@ -59,16 +59,34 @@ class _JsonVeilidAPI(VeilidAPI): async def __aexit__(self, *excinfo): await self.close() - - async def close(self): - if self.handle_recv_messages_task is not None: - self.handle_recv_messages_task.cancel() - try: - await self.handle_recv_messages_task - except asyncio.CancelledError: - pass - + async def _cleanup_close(self): + await self.lock.acquire() + try: + self.reader = None + self.writer.close() + await self.writer.wait_closed() + self.writer = None + finally: + self.lock.release() + + async def close(self): + # Take the task + await self.lock.acquire() + try: + if self.handle_recv_messages_task is None: + return + handle_recv_messages_task = self.handle_recv_messages_task + self.handle_recv_messages_task = None + finally: + self.lock.release() + # Cancel it + handle_recv_messages_task.cancel() + try: + await handle_recv_messages_task + except asyncio.CancelledError: + pass + @staticmethod async def connect(host: str, port: int, update_callback: Callable[[VeilidUpdate], Awaitable]) -> Self: reader, writer = await asyncio.open_connection(host, port) @@ -109,11 +127,7 @@ class _JsonVeilidAPI(VeilidAPI): except: pass finally: - self.reader = None - self.writer.close() - await self.writer.wait_closed() - self.writer = None - self.handle_recv_messages_task = None + await self._cleanup_close() async def allocate_request_future(self, id: int) -> asyncio.Future: reqfuture = asyncio.get_running_loop().create_future() @@ -135,6 +149,10 @@ class _JsonVeilidAPI(VeilidAPI): self.lock.release() def send_one_way_ndjson_request(self, op: Operation, **kwargs): + + if self.writer is None: + return + # Make NDJSON string for request # Always use id 0 because no reply will be received for one-way requests req = { "id": 0, "op": op } @@ -156,6 +174,7 @@ class _JsonVeilidAPI(VeilidAPI): try: id = self.next_id self.next_id += 1 + writer = self.writer finally: self.lock.release() @@ -174,8 +193,8 @@ class _JsonVeilidAPI(VeilidAPI): # Send to socket try: - self.writer.write(reqbytes) - await self.writer.drain() + writer.write(reqbytes) + await writer.drain() except: # Send failed, release future await self.cancel_request_future(id) diff --git a/veilid-server/src/client_api.rs b/veilid-server/src/client_api.rs index 821d06ef..6b2f9d55 100644 --- a/veilid-server/src/client_api.rs +++ b/veilid-server/src/client_api.rs @@ -102,7 +102,7 @@ impl ClientApi { }; trace!("ClientApi::stop: waiting for stop"); if let Err(err) = jh.await { - error!("{}", err); + eprintln!("{}", err); } trace!("ClientApi::stop: stopped"); } @@ -225,7 +225,7 @@ impl ClientApi { // Marshal json + newline => NDJSON let response_string = serialize_json(json_api::RecvMessage::Response(response)) + "\n"; if let Err(e) = responses_tx.send_async(response_string).await { - warn!("response not sent: {}", e) + eprintln!("response not sent: {}", e) } VeilidAPIResult::Ok(None) } @@ -272,7 +272,7 @@ impl ClientApi { responses_tx: responses_tx.clone(), }; if let Err(e) = requests_tx.send_async(Some(request_line)).await { - error!("failed to enqueue request: {}", e); + eprintln!("failed to enqueue request: {}", e); break; } } @@ -291,7 +291,7 @@ impl ClientApi { ) -> VeilidAPIResult> { while let Ok(resp) = responses_rx.recv_async().await { if let Err(e) = writer.write_all(resp.as_bytes()).await { - error!("failed to write response: {}", e) + eprintln!("failed to write response: {}", e) } } VeilidAPIResult::Ok(None) @@ -302,7 +302,7 @@ impl ClientApi { let peer_addr = match stream.peer_addr() { Ok(v) => v, Err(e) => { - error!("can't get peer address: {}", e); + eprintln!("can't get peer address: {}", e); return; } }; @@ -310,7 +310,7 @@ impl ClientApi { let local_addr = match stream.local_addr() { Ok(v) => v, Err(e) => { - error!("can't get local address: {}", e); + eprintln!("can't get local address: {}", e); return; } }; @@ -387,7 +387,7 @@ impl ClientApi { } Err(e) => { // Connection processing failure, abort - error!("Connection processing failure: {}", e); + eprintln!("Connection processing failure: {}", e); break; } };