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 12, 2024
1 parent 2d9657f commit b7f6601
Showing 1 changed file with 62 additions and 22 deletions.
84 changes: 62 additions & 22 deletions src/fastcs/connections/ip_connection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
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 34 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L32-L34

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

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

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L36

Added line #L36 was not covered by tests

return cast(_AsyncFuncType, check_connected)


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

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

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L55

Added line #L55 was not covered by tests

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

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

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L59

Added line #L59 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 69 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L69

Added line #L69 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 75 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

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

Added lines #L74 - L75 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 84 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L81-L84

Added lines #L81 - L84 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 88 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

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

Added lines #L87 - L88 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 92 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L92

Added line #L92 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 94 in src/fastcs/connections/ip_connection.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/connections/ip_connection.py#L94

Added line #L94 was not covered by tests

0 comments on commit b7f6601

Please sign in to comment.