""" 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