You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
3.3 KiB
117 lines
3.3 KiB
""" data logger """ |
|
|
|
from pathlib import Path |
|
|
|
|
|
CR = chr(13) |
|
LF = chr(10) |
|
|
|
|
|
def is_container(obj): |
|
"""' checks if the object is iterable but not string or bytes """ |
|
return hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes)) |
|
|
|
|
|
class DataLogger: |
|
def __init__(self, log_path, none_rep="", sep="\t", end=CR + LF): |
|
""" initializes the data logger class |
|
|
|
:params log_path: file path of the log file |
|
:params none_rep: string representation for None values |
|
:params sep: character used to separate values in a list |
|
:params end: character used for line ending |
|
""" |
|
|
|
self.path = Path(log_path) |
|
self.end = end |
|
self.sep = sep |
|
self.none_rep = none_rep |
|
|
|
self.handle = None |
|
|
|
def open(self): |
|
""" opens the log file for writing """ |
|
if self.handle is None: |
|
self.path.parent.mkdir(parents=True, exist_ok=True) |
|
self.handle = self.path.open("a") |
|
|
|
def close(self): |
|
""" closes the log file for writing """ |
|
if self.handle: |
|
self.handle.close() |
|
self.handle = None |
|
|
|
def __call__(self, data): |
|
""" writes data to the log |
|
|
|
:params data: either a string or a container, like a list |
|
""" |
|
if is_container(data): |
|
self.add_list(data) |
|
else: |
|
self.add(data) |
|
|
|
def add(self, text): |
|
""" adds one line of text to the logfile """ |
|
if self.handle is None: |
|
raise IOError("Log file not opened for writing") |
|
text = self._universal_new_lines(text) |
|
self.handle.write(text) |
|
|
|
def add_list(self, data): |
|
""" adds the values in a list to one line in the log FileCache |
|
|
|
The string set in the `sep` property is used to separate the values |
|
in the output, None values are represented by the string in `none_rep` |
|
property. |
|
""" |
|
conveted_nones = ((self.none_rep if v is None else v) for v in data) |
|
value_strings = (str(v) for v in conveted_nones) |
|
line = self.sep.join(value_strings) |
|
self.add(line) |
|
|
|
def add_section(self, title, list_of_data, append_empty_line=True): |
|
""" adds a section to the log file """ |
|
title = title.strip() |
|
self.add(f"[{title}]") |
|
for data in list_of_data: |
|
self.add_list(data) |
|
if append_empty_line: |
|
self.add("") |
|
|
|
def __enter__(self): |
|
""" Context manager: opens the log file for writing """ |
|
self.open() |
|
return self |
|
|
|
def __exit__(self, exc_type, exc_value, exc_traceback): |
|
""" Context manager: closes the log file """ |
|
self.close() |
|
|
|
def _universal_new_lines(self, text): |
|
""" unifies new line characters of a text |
|
|
|
The `end` property is used as a newline character and ensures one is |
|
present at the end of the text. |
|
""" |
|
lines = text.splitlines() |
|
return self.end.join(lines) + self.end |
|
|
|
|
|
class NullLogger: |
|
""" A stub logger with the DataLogger interface that does nothing """ |
|
|
|
def __init__(self, *args, **kargs): |
|
pass |
|
|
|
def __call__(self, *args, **kargs): |
|
pass |
|
|
|
def __getattr__(self, key): |
|
return self |
|
|
|
def __enter__(self): |
|
return self |
|
|
|
def __exit__(self, *args, **kargs): |
|
pass
|
|
|