Advertisement
Uno-Dan

Cache

Dec 1st, 2019
868
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.40 KB | None | 0 0
  1. ########################################################################################################################
  2. #    File: cache.py
  3. #  Author: Dan Huckson, https://github.com/unodan
  4. #    Date: 2019-09-07
  5. # Version: 1.1.0
  6. ########################################################################################################################
  7.  
  8. from os import path, makedirs
  9. from json import load, dump
  10. from copy import deepcopy
  11. from importlib.util import module_from_spec, spec_from_file_location
  12. from collections import defaultdict
  13.  
  14.  
  15. def uri2dict(uri, *args, **kwargs):
  16.     _args = args[0] if args else kwargs
  17.  
  18.     uri = uri.split('/')
  19.     uri.reverse()
  20.     items = {}
  21.     for idx, item in enumerate(uri):
  22.         items = {item: items} if idx else {item: _args}
  23.  
  24.     return items
  25.  
  26.  
  27. class Cache:
  28.     def __init__(self, *args, **kwargs):
  29.         self.nodes = {}
  30.         _args = args[0] if args else kwargs
  31.  
  32.         if _args and isinstance(_args, dict):
  33.             key = next(iter(_args.keys()))
  34.             if '/' in key:
  35.                 self.set(key, next(iter(_args.values())))
  36.             else:
  37.                 self.nodes = _args
  38.  
  39.         elif isinstance(_args, str):
  40.             kwargs = args[1] if len(args) > 1 else kwargs
  41.             if isinstance(kwargs, dict):
  42.                 self.nodes = deepcopy(uri2dict(_args, **kwargs))
  43.             else:
  44.                 self.nodes = deepcopy(uri2dict(args[0], args[1]))
  45.  
  46.         self.indent = '.'
  47.  
  48.     def __eq__(self, other):
  49.         if self.nodes == other.nodes:
  50.             return True
  51.         return False
  52.  
  53.     def __ne__(self, other):
  54.         if self.nodes != other.nodes:
  55.             return True
  56.         return False
  57.  
  58.     def __str__(self):
  59.         return str(self.nodes.items())
  60.  
  61.     def __repr__(self):
  62.         data = "**{'key': 'value'}"
  63.         return f"{self.__class__.__name__}({data})"
  64.  
  65.     def keys(self):
  66.         return self.nodes.keys()
  67.  
  68.     def get(self, uri=None, default=None):
  69.         if not uri:
  70.             return self.nodes
  71.  
  72.         def walk(_uri, nodes):
  73.             parts = _uri.split('/', 1)
  74.             key = parts.pop(0)
  75.  
  76.             if key in nodes:
  77.                 node = nodes[key]
  78.  
  79.                 if not parts:
  80.                     return node
  81.                 else:
  82.                     return walk(parts[0], node)
  83.             elif default:
  84.                 return default
  85.             else:
  86.                 return None
  87.  
  88.         return walk(uri, self.nodes)
  89.  
  90.     def set(self, *args, **kwargs):
  91.         data = args[0] if args else kwargs
  92.  
  93.         if isinstance(data, dict):
  94.             uri = next(iter(data.keys()))
  95.             data = next((iter(data.values())))
  96.         elif not isinstance(data, str):
  97.             return
  98.         else:
  99.             uri = args[0]
  100.             data = {}
  101.             if len(args) > 1:
  102.                 data = args[1]
  103.             elif kwargs:
  104.                 data = kwargs
  105.  
  106.         nodes = self.nodes
  107.         parts = uri.strip("/").split("/")
  108.  
  109.         while parts:
  110.             item = parts.pop(0)
  111.  
  112.             if item in nodes and not isinstance(nodes[item], dict):
  113.                 if isinstance(data, dict):
  114.                     nodes[item] = uri2dict(uri.split(item).pop().strip("/"), data)
  115.                 else:
  116.                     nodes[item] = data
  117.                 return
  118.  
  119.             if item in nodes:
  120.                 nodes = nodes[item]
  121.             else:
  122.                 if isinstance(nodes, dict):
  123.                     _parts = uri.split(item)
  124.                     _uri = _parts.pop()
  125.                     if _uri:
  126.                         data = uri2dict(_uri.strip("/"), data)
  127.                     nodes[item] = data
  128.                 return
  129.  
  130.         if isinstance(nodes, dict) and isinstance(data, dict):
  131.             nodes.update(**data)
  132.         else:
  133.             parts = uri.split("/")
  134.             nodes = self.nodes
  135.             while parts:
  136.                 item = parts.pop(0)
  137.                 if parts:
  138.                     nodes = nodes[item]
  139.                 else:
  140.                     nodes[item] = data
  141.  
  142.     def copy(self):
  143.         return Cache(**deepcopy(self.nodes))
  144.  
  145.     def dump(self, indent=None):
  146.         """ Dumps the contents of the cache to the screen.
  147.        The output from dump goes stdout and is used to view the cache contents.
  148.        Default indentation is a dot for each level.
  149.        :param indent:
  150.            indent (str): String to be use for indenting levels.
  151.        :return:
  152.            Nothing.
  153.        """
  154.         indent = indent if indent else '.'
  155.  
  156.         print('-------------------------------------------------------------------------------------------------------')
  157.         print('id =', id(self), '\nnodes =', self)
  158.         if self.nodes:
  159.             def walk(_cfg, count):
  160.                 count += 1
  161.                 for key, value in _cfg.items():
  162.                     if isinstance(value, dict):
  163.                         item = '' if value else '{}'
  164.                         print(indent * count, key, item)
  165.                         walk(value, count)
  166.                     else:
  167.                         if isinstance(value, str):
  168.                             value = f'"{value}"'
  169.                         print(indent * count, key, f'value={value}')
  170.             walk(self.nodes, 0)
  171.         else:
  172.             print(' (No Data)')
  173.  
  174.         print('-------------------------------------------------------------------------------------------------------')
  175.  
  176.     def save(self, file=None):
  177.         if file:
  178.             dirname = path.dirname(file)
  179.  
  180.             if dirname and not path.exists(dirname):
  181.                 makedirs(dirname)
  182.  
  183.             with open(file, 'w') as f:
  184.                 dump(self.nodes, f, indent=3)
  185.  
  186.     def load(self, file=None):
  187.         file_type = path.splitext(file)[1].lstrip('.').lower()
  188.  
  189.         if file_type == 'py' and path.exists(file):
  190.             spec = spec_from_file_location("module.name", file)
  191.             module = module_from_spec(spec)
  192.             spec.loader.exec_module(module)
  193.             self.nodes = module.config
  194.  
  195.         if file_type == 'json' and path.exists(file):
  196.             with open(file) as f:
  197.                 self.nodes = load(f)
  198.  
  199.         return self
  200.  
  201.     def merge(self, src):
  202.  
  203.         def recursive_update(d1, d2):
  204.             """Updates recursively the dictionary values of d1"""
  205.  
  206.             for key, value in d2.items():
  207.                 if key in d1 and isinstance(d1[key], dict) and isinstance(value, dict):
  208.                     recursive_update(d1[key], value)
  209.                 else:
  210.                     d1[key] = value
  211.  
  212.         recursive_update(self.get(), src.get())
  213.  
  214.     def remove(self, uri):
  215.         """ Remove entree from cache.
  216.        Removes an entree from the cache if it exists.
  217.        :param uri:
  218.            uri (str): URI that points to the entree to remove.
  219.        :return:
  220.            Nothing.
  221.        """
  222.  
  223.         uri = uri.strip('/')
  224.         if self.exists(uri):
  225.             parts = uri.rsplit("/", 1)
  226.             if len(parts) == 1:
  227.                 self.nodes.pop(parts[0])
  228.             else:
  229.                 node = self.get(parts[0])
  230.                 node.pop(parts[1], None)
  231.  
  232.     def exists(self, uri):
  233.         """ Test if URI exists in the cache.
  234.  
  235.        :param uri:
  236.        :return:
  237.        """
  238.         nodes = self.nodes
  239.         parts = uri.strip("/").split("/")
  240.         while parts:
  241.             item = parts.pop(0)
  242.             if item in nodes:
  243.                 nodes = nodes[item]
  244.             else:
  245.                 return False
  246.         return True
  247.  
  248.     def destroy(self):
  249.         """ Destroy cache.
  250.        Deletes all entries in the cache.
  251.        :return:
  252.            Nothing.
  253.        """
  254.         del self.nodes
  255.         self.nodes = {}
  256.  
  257.     def has_nodes(self, uri=''):
  258.         if self.get_nodes(uri):
  259.             return True
  260.         return False
  261.  
  262.     def get_nodes(self, uri):
  263.         node = self.get(uri)
  264.  
  265.         _nodes = {}
  266.         for k, v in node.items():
  267.             if isinstance(v, dict):
  268.                 _nodes[k] = v
  269.  
  270.         return _nodes
  271.  
  272. # If uncommented and the class name changed to _Cache the cache will ba a singleton.
  273. # class Cache:
  274. #     instance = None
  275. #
  276. #     def __new__(cls, **kwargs):
  277. #         if not cls.instance:
  278. #             cls.instance = super(Cache, cls).__new__(cls)
  279. #             cls.instance.cache = _Cache(**kwargs)
  280. #
  281. #         return cls.instance.cache
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement