<commit_before>import pickle
from collections import OrderedDict
from functools import wraps

from .storage import SQLiteStorage


MISS = object()


class Cache:

    def __init__(self, *, maxsize=1024, ttl=-1, filepath=None, typed=False, **kwargs):
        self.params = OrderedDict(
            maxsize=maxsize,
            ttl=ttl,
            filepath=filepath,
            typed=typed,
        )
        self.typed = typed
        self.params.update(kwargs)
        self.storage = SQLiteStorage(
            filepath=filepath or ':memory:',
            ttl=ttl,
            maxsize=maxsize,
        )
        self.make_key = make_key

    def __repr__(self):
        return (
            f"{self.__class__.__name__}"
            f"({', '.join(f'{k}={repr(v)}' for k,v in self.params.items())})"
        )

    def __call__(self, fn=None, **kwargs):
        def decorator(fn):

            key_prefix = self.make_key_prefix(fn)

            @wraps(fn)
            def wrapper(*args, **kwargs):
                key = self.make_key(self.typed, key_prefix, *args, **kwargs)
                res = self.get(key, MISS)
                if res is MISS:
                    res = self[key] = fn(*args, **kwargs)
                return res

            return wrapper

        if fn is None and kwargs:
            return self.copy(**kwargs)(fn)
        elif callable(fn) and not kwargs:
            return decorator(fn)
        else:
            return decorator

    def __getitem__(self, key):
        return self.decode(self.storage[self.encode(key)])

    def __setitem__(self, key, value):
        self.storage[self.encode(key)] = self.encode(value)

    def __delitem__(self, key):
        del self.storage[self.encode(key)]

    def __contains__(self, key):
        return self.get(key, MISS) is not MISS

    def get(self, key, default=None):
        res = self.storage.get(self.encode(key), default)
        if res is not default:
            res = self.decode(res)
        return res

    def clear(self):
        self.storage.clear()

    def close(self):
        self.storage.close()

    def copy(self, **kwargs):
        return self.__class__(**{**self.params, **kwargs})

    def make_key_prefix(self, fn):
        return function_name(fn)

    def encode(self, obj):
        return pickle.dumps(obj)

    def decode(self, data):
        return pickle.loads(data)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.storage.close()

    def remove(self):
        self.storage.remove()


def make_key(typed=False, *args, **kwargs):
    kwargs = sorted(kwargs.items())
    if typed:
        args = tuple((a, type_name(a)) for a in args)
        kwargs = tuple(((k, type_name(k)), (v, type_name(v)))
                       for k, v in kwargs)
    return f'{args}{kwargs}'


def type_name(obj):
    klass = type(obj)
    return f'{klass.__module__}.{klass.__qualname__}'


def function_name(fn):
    return  f'{fn.__module__}.{fn.__qualname__}'


cache = Cache()
<commit_msg>comments and minor improvements<commit_after>import pickle
from collections import OrderedDict
from functools import wraps

from .storage import SQLiteStorage


MISS = object()


class Cache:

    def __init__(self, *, maxsize=1024, ttl=-1, filepath=None, typed=False, **kwargs):
        self.params = OrderedDict(
            maxsize=maxsize,
            ttl=ttl,
            filepath=filepath,
            typed=typed,
        )
        self.typed = typed
        self.params.update(kwargs)
        self.storage = SQLiteStorage(
            filepath=filepath or ':memory:',
            ttl=ttl,
            maxsize=maxsize,
        )
        self.make_key = make_key

    def __repr__(self):
        return (
            f"{self.__class__.__name__}"
            f"({', '.join(f'{k}={repr(v)}' for k,v in self.params.items())})"
        )

    def __call__(self, fn=None, **kwargs):
        def decorator(fn):

            key_prefix = self.make_key_prefix(fn)

            @wraps(fn)
            def wrapper(*args, **kwargs):
                global MISS
                key = self.make_key(self.typed, key_prefix, *args, **kwargs)
                # Smth unique and is needed here.
                # None is not an option because fn may return None. So MISS is used.
                res = self.get(key, MISS)
                if res is MISS:
                    res = self[key] = fn(*args, **kwargs)
                return res

            return wrapper

        if fn is None and kwargs:
            return self.copy(**kwargs)(fn)
        elif callable(fn) and not kwargs:
            return decorator(fn)
        else:
            return decorator

    def __getitem__(self, key):
        return self.decode(self.storage[self.encode(key)])

    def __setitem__(self, key, value):
        self.storage[self.encode(key)] = self.encode(value)

    def __delitem__(self, key):
        del self.storage[self.encode(key)]

    def __contains__(self, key):
        return self.get(key, MISS) is not MISS

    def get(self, key, default=None):
        res = self.storage.get(self.encode(key), default)
        if res is not default:
            res = self.decode(res)
        return res

    def clear(self):
        self.storage.clear()

    def close(self):
        self.storage.close()

    def copy(self, **kwargs):
        return self.__class__(**{**self.params, **kwargs})

    def make_key_prefix(self, fn):
        return function_name(fn)

    def encode(self, obj):
        return pickle.dumps(obj)

    def decode(self, data):
        return pickle.loads(data)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.storage.close()

    def remove(self):
        self.storage.remove()


def make_key(typed=False, *args, **kwargs):
    kwargs = sorted(kwargs.items())
    if typed:
        args = tuple((a, type_name(a)) for a in args)
        kwargs = tuple(((k, type_name(k)), (v, type_name(v)))
                       for k, v in kwargs)
    return f'{args}{kwargs}'


def type_name(obj):
    klass = type(obj)
    return f'{klass.__module__}.{klass.__qualname__}'


def function_name(fn):
    return  f'{fn.__module__}.{fn.__qualname__}'


cache = Cache()

{'fraction_non_alphanumeric': 0.121546078901134, 'fraction_numerical': 0.0015971889474524837, 'mean_word_length': 2.0651003426333823, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '866682', 'n_tokens_mistral': 1983, 'n_tokens_neox': 1917, 'n_words': 471}