Skip to content

Commit

Permalink
Improvements to IPConnection
Browse files Browse the repository at this point in the history
  • Loading branch information
OCopping committed Mar 11, 2024
1 parent 2d9657f commit 189347d
Showing 1 changed file with 61 additions and 22 deletions.
83 changes: 61 additions & 22 deletions src/fastcs/connections/ip_connection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
import asyncio
import codecs
from dataclasses import dataclass
from typing import Any, Callable, Coroutine, cast

_AsyncFuncType = Callable[..., Coroutine[Any, Any, Any]]


def _with_lock(func: _AsyncFuncType) -> _AsyncFuncType:
async def with_lock(*args: Any, **kwargs: Any) -> None:
self = args[0]
async with self.lock:
await func(*args, **kwargs)

Check warning on line 13 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L11-L13

Added lines #L11 - L13 were not covered by tests

return cast(_AsyncFuncType, with_lock)

def _ensure_connected(func: _AsyncFuncType) -> _AsyncFuncType:
"""
Decorator function to check if the wrapper is connected to the device
before calling the attached function.
Args:
func: Function to call if connected to device
Returns:
The wrapped function.
"""

async def check_connected(*args: Any, **kwargs: Any) -> None:
self = args[0]
if self._reader is None or self._writer is None:
raise DisconnectedError("Need to call connect() before using IPConnection.")

Check warning on line 33 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L31-L33

Added lines #L31 - L33 were not covered by tests
else:
await func(*args, **kwargs)

Check warning on line 35 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L35

Added line #L35 was not covered by tests

return cast(_AsyncFuncType, check_connected)


class DisconnectedError(Exception):
Expand All @@ -16,39 +51,43 @@ class IPConnection:
def __init__(self):
self._reader, self._writer = (None, None)
self._lock = asyncio.Lock()
self.connected: bool = False

Check warning on line 54 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L54

Added line #L54 was not covered by tests

@property
def lock(self) -> asyncio.Lock:
return self._lock

Check warning on line 58 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L58

Added line #L58 was not covered by tests

async def connect(self, settings: IPConnectionSettings):
self._reader, self._writer = await asyncio.open_connection(
settings.ip, settings.port
)

def ensure_connected(self):
if self._reader is None or self._writer is None:
raise DisconnectedError("Need to call connect() before using IPConnection.")

async def send_command(self, message) -> None:
async with self._lock:
self.ensure_connected()
await self._send_message(message)
@_with_lock
@_ensure_connected
async def send_command(self, message: str) -> None:
await self._send_message(message)

Check warning on line 68 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L68

Added line #L68 was not covered by tests

async def send_query(self, message) -> str:
async with self._lock:
self.ensure_connected()
await self._send_message(message)
return await self._receive_response()
@_with_lock
@_ensure_connected
async def send_query(self, message: str) -> str:
await self._send_message(message)
return await self._receive_response()

Check warning on line 74 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L73-L74

Added lines #L73 - L74 were not covered by tests

# TODO: Figure out type hinting for connections. TypeGuard fails to work as expected
@_with_lock
@_ensure_connected
async def close(self):
async with self._lock:
self.ensure_connected()
self._writer.close()
await self._writer.wait_closed()
self._reader, self._writer = (None, None)

async def _send_message(self, message) -> None:
self._writer.write(message.encode("utf-8"))
assert isinstance(self._writer, asyncio.StreamWriter)
self._writer.close()
await self._writer.wait_closed()
self._reader, self._writer = (None, None)

Check warning on line 83 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L80-L83

Added lines #L80 - L83 were not covered by tests

async def _send_message(self, message: str) -> None:
assert isinstance(self._writer, asyncio.StreamWriter)
self._writer.write(codecs.encode(message, "utf-8"))

Check warning on line 87 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L86-L87

Added lines #L86 - L87 were not covered by tests
await self._writer.drain()

async def _receive_response(self) -> str:
assert isinstance(self._reader, asyncio.StreamReader)

Check warning on line 91 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L91

Added line #L91 was not covered by tests
data = await self._reader.readline()
return data.decode("utf-8")
return codecs.decode(data, "utf-8")

Check warning on line 93 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L93

Added line #L93 was not covered by tests

0 comments on commit 189347d

Please sign in to comment.