Skip to content

Commit

Permalink
Simplify API to allow granular reader operation
Browse files Browse the repository at this point in the history
In response to [^1].

Previously it was hard to operate the reader classes from calling
code that separates resource management ("open" / "close") and the
actual polling of data ("read_one"). This exposes new APIs to make
it easier to use reader classes in such a granular way.

Implementation notes:

- Reinstating a type hint for __enter__ of "-> Self" will become
possible in Python 3.11.

- "start" / "stop" were discussed in relation to the Sensor being
operated by a SensorReader, but these concepts don't make much sense
for the MessageReader class, so I've used "open" / "close" instead.

[^1]: #32 (comment)
  • Loading branch information
benthorner authored and avaldebe committed Dec 6, 2022
1 parent 401863e commit 820d267
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 10 deletions.
28 changes: 20 additions & 8 deletions src/pms/core/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def hexdump(self, line: Optional[int] = None) -> str:
return f"{offset:08x}: {hex} {dump}"


class Reader(AbstractContextManager):
class Reader:
@abstractmethod
def __call__(self, *, raw: Optional[bool]) -> Iterator[Union[RawData, ObsData]]:
"""
Expand All @@ -60,6 +60,21 @@ def __call__(self, *, raw: Optional[bool]) -> Iterator[Union[RawData, ObsData]]:
"""
...

@abstractmethod
def open(self) -> None:
...

@abstractmethod
def close(self) -> None:
...

def __enter__(self):
self.open()
return self

def __exit__(self, exception_type, exception_value, traceback) -> None:
self.close()


class SensorReader(Reader):
"""Read sensor messages from serial port
Expand Down Expand Up @@ -119,7 +134,7 @@ def _pre_heat(self):
# only pre-heat the firs time
self.pre_heat = 0

def __enter__(self) -> "SensorReader":
def open(self) -> None:
"""Open serial port and sensor setup"""
if not self.serial.is_open:
logger.debug(f"open {self.serial.port}")
Expand All @@ -143,9 +158,7 @@ def __enter__(self) -> "SensorReader":
logger.error(f"Sensor is not {self.sensor.name}")
raise UnableToRead("Sensor failed validation")

return self

def __exit__(self, exception_type, exception_value, traceback) -> None:
def close(self) -> None:
"""Put sensor to sleep and close serial port"""
logger.debug(f"sleep {self.sensor}")
buffer = self._cmd("sleep")
Expand Down Expand Up @@ -188,14 +201,13 @@ def __init__(self, path: Path, sensor: Sensor, samples: Optional[int] = None) ->
self.sensor = sensor
self.samples = samples

def __enter__(self) -> "MessageReader":
def open(self) -> None:
logger.debug(f"open {self.path}")
self.csv = self.path.open()
reader = DictReader(self.csv)
self.data = (row for row in reader if row["sensor"] == self.sensor.name)
return self

def __exit__(self, exception_type, exception_value, traceback) -> None:
def close(self) -> None:
logger.debug(f"close {self.path}")
self.csv.close()

Expand Down
4 changes: 2 additions & 2 deletions tests/core/test_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ def __init__(self, raise_on_enter=False):
def __call__(self):
raise NotImplemented

def __enter__(self):
def open(self):
if self.raise_on_enter:
raise reader.UnableToRead()
self.entered = True

def __exit__(self, *_args):
def close(self):
self.exited = True


Expand Down

0 comments on commit 820d267

Please sign in to comment.