Files @ 5a73d3d8f8d4
Branch filter:

Location: NPO-Accounting/oxrlib/oxrlib/cache.py

Brett Smith
historical: Move normalize_rate and _pretty_rate to base Formatter.

This reduces the number of method overrides to help readability,
and gets rid of the annoying format_rate/pretty_rate distinction.
import functools
import json

from . import errors

class CacheFileBase:
    def __init__(self, path, *args, **kwargs):
        self.path = path
        try:
            self.open_file = path.open(*args, **kwargs)
        except OSError as error:
            self._translate_error(error, 'init')

    def _translate_error(self, error, when):
        for orig_type, mapped_type in self.ERRORS_MAP:
            if isinstance(error, orig_type):
                raise mapped_type(self.path) from error
        raise error

    def __enter__(self):
        return self.open_file

    def __exit__(self, exc_type, exc_value, exc_tb):
        try:
            self.open_file.close()
        except OSError as error:
            self._translate_error(error, 'exit')


class CacheBase:
    ConfigurationError = errors.CacheConfigurationError

    def __init__(self, dir_path, **kwargs):
        self.dir_path = dir_path
        try:
            self.CacheFile = kwargs.pop('file_class')
        except KeyError:
            pass
        self.fn_patterns = {}
        self.setup(**kwargs)

    def setup(self, **kwargs):
        for method_name, pattern in kwargs.items():
            try:
                is_api_method = getattr(self, method_name).is_api_method
            except AttributeError:
                is_api_method = False
            if not is_api_method:
                raise errors.CacheConfigurationError(method_name)
            self.fn_patterns[method_name] = pattern

    def _wrap_api_method(orig_func):
        @functools.wraps(orig_func)
        def api_method_wrapper(self, *args, **kwargs):
            try:
                fn_pattern = self.fn_patterns[orig_func.__name__]
            except KeyError:
                raise self.ConfigurationError(orig_func.__name__) from None
            pattern_kwargs = orig_func(self, *args, **kwargs)
            path = self.dir_path / fn_pattern.format(**pattern_kwargs)
            return self.open(path)
        api_method_wrapper.is_api_method = True
        return api_method_wrapper

    @_wrap_api_method
    def historical(self, date, base):
        return {'date': date.isoformat(), 'base': base}


class WriteCacheFile(CacheFileBase):
    ERRORS_MAP = []


class CacheWriter(CacheBase):
    CacheFile = WriteCacheFile

    def open(self, path):
        return self.CacheFile(path, 'w')

    def write_json(self, cache_file, thing):
        json.dump(thing, cache_file, indent=2)

    def save_rate(self, rate):
        with self.historical(rate.timestamp.date(), rate.base) as cache_file:
            self.write_json(cache_file, rate.serialize())