import hashlib
import json
import os
import pickle
import logging


def _resolve_path(key, dir='', ext='', prefix='', hash_len=16):
    key_encoded = json.dumps(
        key, sort_keys=True, separators=(',', ':')).encode('utf-8')
    key_hash = hashlib.md5(key_encoded).hexdigest()
    path = os.path.join(dir, prefix + key_hash[:hash_len] + ext)
    return path


class Cache(object):
    LOG_LEVEL = logging.DEBUG

    def __init__(self, key, dir='', ext='.pkl', prefix='', mkdir=False,
                 serializer=None, deserializer=None, logger=None):
        dir = os.path.abspath(os.path.expanduser(dir))
        if os.path.isdir(dir):
            pass
        elif mkdir:
            os.makedirs(dir)
        else:
            raise FileNotFoundError(
                "cache dir was not found: `{}`".format(dir))
        self._file = _resolve_path(key, dir, ext, prefix)
        self._serializer = serializer \
            if serializer is not None else pickle
        self._deserializer = deserializer \
            if deserializer is not None else self._serializer
        self._logger = logger \
            if logger is not None else logging.getLogger(__name__)

    def load(self):
        self._logger.log(
            self.LOG_LEVEL, "loading cache from `{}`".format(self._file))
        with open(self._file, 'rb') as f:
            return self._serializer.load(f)

    def load_or_create(self, factory, refresh=False):
        if not refresh:
            try:
                return self.load()
            except FileNotFoundError:
                self._logger.log(
                    self.LOG_LEVEL, "missing cache - creating a new object")
        else:
            self._logger.log(
                self.LOG_LEVEL, "updating cache - creating a new object")
        obj = factory()
        self.dump(obj)
        return obj

    def dump(self, obj):
        self._logger.log(
            self.LOG_LEVEL, "dumping cache to `{}`".format(self._file))
        with open(self._file, 'wb') as f:
            self._serializer.dump(obj, f)


def load(key, dir='', ext='.pkl', prefix='',
         serializer=None, deserializer=None, logger=None):
    return Cache(key, dir, ext, prefix, False,
                 serializer, deserializer, logger).load()


def load_or_create(key, factory, refresh=False,
                   dir='', ext='.pkl', prefix='', mkdir=False,
                   serializer=None, deserializer=None, logger=None):
    return Cache(key, dir, ext, prefix, mkdir,
                 serializer, deserializer, logger) \
        .load_or_create(factory, refresh)


def dump(obj, key, dir='', ext='.pkl', prefix='', mkdir=False,
         serializer=None, deserializer=None, logger=None):
    Cache(key, dir, ext, prefix, mkdir, serializer, deserializer, logger) \
        .dump(obj)

@@ -65,26 +65,83 @@
 g%0A%0A%0A
-def _resolve_path(
+class Cache(object):%0A    LOG_LEVEL = logging.DEBUG%0A%0A    def __init__(self, 
 key,
@@ -154,16 +154,20 @@
 ', ext='
+.pkl
 ', prefi
@@ -176,396 +176,369 @@
 '', 
-hash_len=16):%0A    key_encoded = json.dumps(%0A        key, sort_keys=True, separators=(',', ':')).encode('utf-8')%0A    key_hash = hashlib.md5(key_encoded).hexdigest()%0A    path = os.path.join(dir, prefix + key_hash%5B:hash_len%5D + ext)%0A    return path%0A%0A%0Aclass Cache(object):%0A    LOG_LEVEL = logging.DEBUG%0A%0A    def __init__(self, key, dir='', ext='.pkl', prefix='', mkdir=False,%0A                 
+mkdir=False,%0A                 hash_length=16, serializer=None, deserializer=None,%0A                 logger=None):%0A        self._config = %7B%0A            'key': key,%0A            'dir': dir,%0A            'ext': ext,%0A            'prefix': prefix,%0A            'mkdir': mkdir,%0A            'hash_length': hash_length,%0A            'serializer': serializer,%0A            'de
 seri
@@ -535,38 +535,34 @@
    'deserializer
-=None,
+':
  deserializer=No
@@ -558,36 +558,57 @@
 rializer
-=None, logger=None):
+,%0A            'logger': logger,%0A        %7D
 %0A       
@@ -882,50 +882,127 @@
 lf._
-file = _resolve_path(key, dir, ext, prefix
+identifier = self._encode_key(key)%5B:hash_length%5D%0A        self._file = os.path.join(dir, prefix + self._identifier + ext
 )%0A  
@@ -2174,16 +2174,391 @@
 bj, f)%0A%0A
+    def clone(self, key):%0A        return Cache(**dict(self._config, key=key))%0A%0A    @property%0A    def id(self):%0A        return self._identifier%0A%0A    @staticmethod%0A    def _encode_key(key):%0A        key_encoded = json.dumps(%0A            key, sort_keys=True, separators=(',', ':')).encode('utf-8')%0A        key_hash = hashlib.md5(key_encoded).hexdigest()%0A        return key_hash%0A%0A
 %0Adef loa
@@ -2594,16 +2594,32 @@
 efix='',
+ hash_length=16,
 %0A       
@@ -2712,24 +2712,37 @@
 efix, False,
+ hash_length,
 %0A           
@@ -2892,32 +2892,48 @@
 '', mkdir=False,
+ hash_length=16,
 %0A               
@@ -3028,24 +3028,37 @@
 efix, mkdir,
+ hash_length,
 %0A           
@@ -3197,32 +3197,48 @@
 '', mkdir=False,
+ hash_length=16,
 %0A         serial
@@ -3320,16 +3320,39 @@
 , mkdir,
+ hash_length,%0A         
  seriali
@@ -3377,27 +3377,16 @@
  logger)
- %5C%0A        
 .dump(ob

Refactor Cache.