Skip to content
This repository was archived by the owner on Nov 23, 2025. It is now read-only.

Commit 03879c0

Browse files
committed
Redesigned the logging to be more suitable for a library rather than a top level application.
1 parent 2753464 commit 03879c0

4 files changed

Lines changed: 32 additions & 44 deletions

File tree

CHANGES.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ Version 0.5.0
55

66
* BREAKING! Remaps address for redesign of UOS protocol.
77
* PEP 484 implicit optional typing fixes.
8+
* Redesigned the logging to be more suitable for a library.
9+
A global logger is configured at the top level and used throughout
10+
the project.
11+
The logger has a NullHandler so is suppressed unless the client enables.
12+
Removed configure_logs function as this was no longer functional.
813

914
Version 0.4.0
1015
-------------

uoshardware/__init__.py

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""The high level interface for communicating with UOS devices."""
2+
import logging
23
from enum import Enum
3-
from logging import FileHandler, Formatter, getLogger
4-
from pathlib import Path
54

65
__author__ = "Steve Richardson (Creating Null)"
76
__copyright__ = f"2023, {__author__}"
@@ -27,23 +26,6 @@ class Loading(Enum):
2726
EAGER = 1
2827

2928

30-
# Dead code false positive as interface intended to be used by client.
31-
def configure_logs(name: str, level: int, base_path: Path): # dead: disable
32-
"""Per-package logs must be manually configured to prefix correctly."""
33-
logger = getLogger(name)
34-
logger.setLevel(level)
35-
# Don't capture to console as custom messages only, root logger captures stderr
36-
logger.propagate = False
37-
log_dir = Path(base_path.joinpath(Path("logs/")))
38-
if not log_dir.exists():
39-
log_dir.mkdir()
40-
file_handler = FileHandler(log_dir.joinpath(Path(name + ".log")))
41-
file_handler.setFormatter(
42-
Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")
43-
)
44-
logger.addHandler(file_handler)
45-
46-
4729
class UOSError(Exception):
4830
"""Base class exception for all UOS Interface Errors."""
4931

@@ -54,3 +36,9 @@ class UOSUnsupportedError(UOSError):
5436

5537
class UOSCommunicationError(UOSError):
5638
"""Exception while communicating with a UOS Device."""
39+
40+
41+
# Configures the global logger for the library
42+
# Note: Clients need to initialize logging otherwise no output will be visible.
43+
logger = logging.getLogger(__name__)
44+
logger.addHandler(logging.NullHandler())

uoshardware/api.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"""Provides the HAL layer for communicating with the hardware."""
2-
from logging import getLogger as Log
3-
4-
from uoshardware import Loading, Persistence, UOSUnsupportedError
2+
from uoshardware import Loading, Persistence, UOSUnsupportedError, logger
53
from uoshardware.abstractions import (
64
ComResult,
75
Device,
@@ -114,7 +112,7 @@ def __init__(
114112
self.loading == Loading.EAGER
115113
): # eager connections open when they are created
116114
self.open()
117-
Log(__name__).debug("Created device %s", self.__device_interface.__repr__())
115+
logger.debug("Created device %s", self.__device_interface.__repr__())
118116

119117
def __enter__(self):
120118
"""Dunder function for opening the interface as a context manager."""
@@ -264,7 +262,7 @@ def __execute_instruction(
264262
or instruction_data.volatility
265263
not in self.device.functions_enabled[function.name]
266264
):
267-
Log(__name__).debug(
265+
logger.debug(
268266
"Known functions %s", str(self.device.functions_enabled.keys())
269267
)
270268
raise UOSUnsupportedError(
@@ -299,7 +297,7 @@ def __execute_instruction(
299297
current_packet[1:-2]
300298
)
301299
)
302-
Log(__name__).debug(
300+
logger.debug(
303301
"Calculated checksum %s must match rx %s",
304302
computed_checksum,
305303
current_packet[-2],

uoshardware/interface/serial.py

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
"""Module defining the low level UOSImplementation for serial port devices."""
22
import platform
3-
from logging import getLogger as Log
43
from time import sleep, time_ns
54

65
import serial
76
from serial.serialutil import SerialException
87
from serial.tools import list_ports
98

10-
from uoshardware import UOSCommunicationError
9+
from uoshardware import UOSCommunicationError, logger
1110
from uoshardware.abstractions import ComResult, UOSInterface
1211

1312
if platform.system() == "Linux":
@@ -38,44 +37,42 @@ def __init__(self, connection: str, **kwargs):
3837
self._port = self.check_port_exists(connection)
3938
self._kwargs = kwargs
4039
if self._port is None:
41-
Log(__name__).error("%s port does not exist", connection)
40+
logger.error("%s port does not exist", connection)
4241
else:
43-
Log(__name__).debug("%s located", self._port)
42+
logger.debug("%s located", self._port)
4443

4544
def open(self):
4645
"""Open a connection to the port and creates the device object."""
4746
try:
4847
self._port = self.check_port_exists(self._connection)
4948
if self._port is None:
50-
Log(__name__).error(
51-
"%s device was not present to open", self._connection
52-
)
49+
logger.error("%s device was not present to open", self._connection)
5350
raise UOSCommunicationError("Device could not be found on system.")
5451
self._device = serial.Serial()
5552
self._device.port = self._connection
5653
if "baudrate" in self._kwargs:
5754
self._device.baudrate = self._kwargs["baudrate"]
5855
if platform.system() == "Linux": # DTR transient workaround for Unix
59-
Log(__name__).debug("Linux platform found so using DTR workaround")
56+
logger.debug("Linux platform found so using DTR workaround")
6057
with open(self._connection, mode="rb") as port:
6158
attrs = termios.tcgetattr(port)
6259
attrs[2] = attrs[2] & ~termios.HUPCL
6360
termios.tcsetattr(port, termios.TCSAFLUSH, attrs)
6461
else: # DTR transient workaround for Windows
6562
self._device.dtr = False
6663
self._device.open()
67-
Log(__name__).debug("%s opened successfully", self._port.device)
64+
logger.debug("%s opened successfully", self._port.device)
6865
return
6966
except (SerialException, FileNotFoundError) as exception:
70-
Log(__name__).error(
67+
logger.error(
7168
"Opening %s threw error %s",
7269
self._port.device if self._port is not None else "None",
7370
str(exception),
7471
)
7572
if (
7673
exception.errno == 13
7774
): # permission denied another connection open to this device.
78-
Log(__name__).error(
75+
logger.error(
7976
"Cannot open connection, account has insufficient permissions."
8077
)
8178
raise UOSCommunicationError(
@@ -92,11 +89,11 @@ def close(self):
9289
try:
9390
self._device.close()
9491
except SerialException as exception:
95-
Log(__name__).debug("Closing the connection threw error %s", str(exception))
92+
logger.debug("Closing the connection threw error %s", str(exception))
9693
raise UOSCommunicationError(
9794
f"Closing connection threw error '{exception}'."
9895
) from exception
99-
Log(__name__).debug("Connection closed successfully")
96+
logger.debug("Connection closed successfully")
10097
self._device = None
10198

10299
# Kwargs is defined in the abstractmethod definition, false positive.
@@ -112,11 +109,11 @@ def execute_instruction(self, address, payload, **kwargs): # dead: disable
112109
"Connection must be open to execute instructions."
113110
)
114111
packet = self.get_npc_packet(to_addr=address, from_addr=0, payload=payload)
115-
Log(__name__).debug("packet formed %s", packet)
112+
logger.debug("packet formed %s", packet)
116113
try: # Send the packet.
117114
num_bytes = self._device.write(packet)
118115
self._device.flush()
119-
Log(__name__).debug("Sent %s bytes of data", num_bytes)
116+
logger.debug("Sent %s bytes of data", num_bytes)
120117
except serial.SerialException as exception:
121118
raise UOSCommunicationError(
122119
f"Executing instruction threw error '{exception}'"
@@ -163,7 +160,7 @@ def read_response(self, expect_packets: int, timeout_s: float):
163160
packet = []
164161
byte_index += 1
165162
sleep(0.05) # Don't churn CPU cycles waiting for data
166-
Log(__name__).debug("Packet received %s", packet)
163+
logger.debug("Packet received %s", packet)
167164
if expect_packets != packet_index or len(packet) < 6 or byte_index != -2:
168165
response_object.rx_packets.append(packet)
169166
response_object.exception = "did not receive all the expected data"
@@ -182,7 +179,7 @@ def hard_reset(self):
182179
"""
183180
if self._device is None:
184181
raise UOSCommunicationError("Connection must be open to hard reset device.")
185-
Log(__name__).debug("Resetting the device using the DTR line")
182+
logger.debug("Resetting the device using the DTR line")
186183
self._device.dtr = not self._device.dtr
187184
sleep(0.2)
188185
self._device.dtr = not self._device.dtr
@@ -217,7 +214,7 @@ def decode_and_capture(
217214
if byte_in == b">":
218215
byte_index += 1
219216
if byte_index >= 0:
220-
Log(__name__).debug(
217+
logger.debug(
221218
"read %s byte index = %s",
222219
byte_in,
223220
byte_index,
@@ -226,7 +223,7 @@ def decode_and_capture(
226223
if byte_index == 3 + 2 + payload_len: # End packet symbol
227224
if byte_in == b"<":
228225
byte_index = -2 # packet complete
229-
Log(__name__).debug("Found end packet symbol")
226+
logger.debug("Found end packet symbol")
230227
else: # Errored data
231228
byte_index = -1
232229
packet = []

0 commit comments

Comments
 (0)