diff options
| author | Scott Gasch <[email protected]> | 2021-09-21 18:25:50 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2021-09-21 18:25:50 -0700 |
| commit | c46018e2b1ddc78f9df557c3fb24d2c2c849f054 (patch) | |
| tree | b44229a07af57dff717e579b789d9e5f4d00f408 /persistent.py | |
| parent | 946279b8b220d9dffe3b47b3bda8ba9e48ccb25a (diff) | |
Persistent state.
Diffstat (limited to 'persistent.py')
| -rw-r--r-- | persistent.py | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/persistent.py b/persistent.py new file mode 100644 index 0000000..30e4ccb --- /dev/null +++ b/persistent.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +from abc import ABC, abstractmethod +import atexit +import functools +import logging + +import dill + +import file_utils + + +logger = logging.getLogger(__name__) + + +class Persistent(ABC): + @abstractmethod + def save(self): + pass + + @abstractmethod + def load(self): + pass + + +class persistent_autoload_singleton(Persistent): + def __init__(self, filename: str, *, max_age_sec: int = 0): + self.filename = filename + self.max_age_sec = max_age_sec + self.instance = None + + def __call__(self, cls): + @functools.wraps(cls) + def _load(*args, **kwargs): + + # If class has already been loaded, act like a singleton + # and return a reference to the one and only instance in + # memory. + if self.instance is not None: + logger.debug( + f'Returning already instantiated singleton instance of {cls.__name__}.' + ) + return self.instance + + if not self.load(): + assert self.instance is None + logger.debug( + f'Instantiating {cls.__name__} directly.' + ) + self.instance = cls(*args, **kwargs) + + # On program exit, save state to disk. + atexit.register(self.save) + assert self.instance is not None + return self.instance + return _load + + def load(self) -> bool: + if ( + file_utils.does_file_exist(self.filename) + and ( + self.max_age_sec == 0 or + file_utils.get_file_mtime_age_seconds(self.filename) <= self.max_age_sec + ) + ): + logger.debug( + f'Attempting to load from file {self.filename}' + ) + try: + with open(self.filename, 'rb') as f: + self.instance = dill.load(f) + return True + except Exception: + self.instance = None + return False + return False + + def save(self) -> bool: + if self.instance is not None: + logger.debug( + f'Attempting to save {type(self.instance).__name__} to file {self.filename}' + ) + try: + with open(self.filename, 'wb') as f: + dill.dump(self.instance, f, dill.HIGHEST_PROTOCOL) + return True + except Exception: + return False + return False |
