import trafaret as t class DecisionLog: def __init__(self, reason_key=''): self.init() self._reason_key = reason_key self._len_reason_key = len(reason_key) def init(self): self.logs = [] def log(self, text): self.logs.append(text) def _read_reason_from_value(self, value): reason = None if( isinstance(value, tuple) and len(value) == 2 and isinstance(value[1], str) and len(value[1]) > self._len_reason_key and value[1][:self._len_reason_key] == self._reason_key ): reason = value[1][self._len_reason_key:] value = value[0] return value, reason def _generate_setattr_text(self, name, value, reason): text = f'Setting {name} to {value}' if reason: text += f' because {reason}' return text def log_setattr_with_reason(self, name, value): value, reason = self._read_reason_from_value(value) text = self._generate_setattr_text(name, value, reason) self.log(text) return value def generate_reason(self, value): return self._reason_key + value def to_string(self): for log in self.logs: print(log) return '\n'.join(self.logs) class BaseDataWithDecisionLog: _converter = t.Dict() _logger = DecisionLog(reason_key='r:') def __init__(self, **kwargs): self._logger.init() kwargs = self._converter.check_and_return(kwargs) self._set_init_kwargs(kwargs) def _set_init_kwargs(self, kwargs): for key, value in kwargs.items(): if key in self._keys: reason = 'initial user input' if value == self._keys[key].default: reason = 'default value' reason = self._logger.generate_reason(reason) value = value, reason setattr(self, key, value) def __setattr__(self, name, value): value = self._logger.log_setattr_with_reason(name, value) super().__setattr__(name, value) @property def _keys(self): return {k.name:k for k in self._converter.keys} @classmethod def from_dict(cls, data): return cls(**data) def to_dict(self): return {k:getattr(self, k) for k in self._keys} def get_log(self): return self._logger.to_string() class MyDataClass(BaseDataWithDecisionLog): _converter = t.Dict({ t.Key('var1', optional=True, default=None): t.Or(t.Int, t.Null), t.Key('var2', optional=True, default=None): t.Or(t.Int, t.Null), t.Key('var3', optional=True, default=None): t.Or(t.Int, t.Null), t.Key('var4', optional=True, default=None): t.Or(t.Int, t.Null), })