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.
118 lines
3.3 KiB
118 lines
3.3 KiB
5 years ago
|
""" 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
|