Advertisement
Uno-Dan

Latest Version

Sep 12th, 2019
309
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.85 KB | None | 0 0
  1. ########################################################################################################################
  2. #    File: cache.py
  3. #  Author: Dan Huckson, https://github.com/unodan
  4. #    Date: 2019-09-07
  5. ########################################################################################################################
  6.  
  7. from os import path, makedirs
  8. from json import load, dump
  9. from copy import deepcopy
  10. from importlib.util import module_from_spec, spec_from_file_location
  11.  
  12.  
  13. def uri2dict(uri, *args, **kwargs):
  14.     node = output = {}
  15.     parts = uri.split('/')
  16.  
  17.     while parts:
  18.         node[parts.pop(0)] = node = {}
  19.  
  20.     _args = args[0] if args else kwargs
  21.     if not isinstance(_args, dict) and uri:
  22.         return Cache(**output).set(uri, _args).get()
  23.     return Cache(**output).set(uri, **_args).get()
  24.  
  25.  
  26. class Cache:
  27.     def __init__(self, *args, **kwargs):
  28.         self.nodes = {}
  29.         _args = args[0] if args else kwargs
  30.  
  31.         if _args and isinstance(_args, dict):
  32.             key = next(iter(_args.keys()))
  33.             if '/' in key:
  34.                 self.set(key, next(iter(_args.values())))
  35.             else:
  36.                 self.nodes = deepcopy(_args)
  37.  
  38.         elif isinstance(_args, str):
  39.             kwargs = args[1] if len(args) > 1 else kwargs
  40.             if isinstance(kwargs, dict):
  41.                 self.nodes = deepcopy(uri2dict(_args, **kwargs))
  42.             else:
  43.                 self.nodes = deepcopy(uri2dict(args[0], args[1]))
  44.  
  45.         self.indent = '.'
  46.  
  47.     def __eq__(self, other):
  48.         if self.nodes == other.nodes:
  49.             return True
  50.         return False
  51.  
  52.     def __str__(self):
  53.         return str(self.nodes.items())
  54.  
  55.     def __repr__(self):
  56.         data = "**{'key': 'value'}"
  57.         return f"{self.__class__.__name__}({data})"
  58.  
  59.     def get(self, uri=None):
  60.         if not uri:
  61.             return self.nodes
  62.  
  63.         def walk(_uri, nodes):
  64.             parts = _uri.split('/', 1)
  65.             key = parts.pop(0)
  66.  
  67.             if key in nodes:
  68.                 node = nodes[key]
  69.  
  70.                 if not parts:
  71.                     return node
  72.                 else:
  73.                     return walk(parts[0], node)
  74.  
  75.         return walk(uri, self.nodes)
  76.  
  77.     def set(self, uri, *args, **kwargs):
  78.         data = args[0] if args else kwargs
  79.         data = deepcopy(data)
  80.  
  81.         def walk(_uri, nodes):
  82.             parts = _uri.split('/', 1)
  83.             key = parts.pop(0)
  84.  
  85.             if key in nodes and parts:
  86.                 walk(parts[0], nodes[key])
  87.             elif len(_uri.split('/')) == 1:
  88.                 nodes[key] = data
  89.             else:
  90.                 self.nodes.update(**uri2dict(_uri, data))
  91.  
  92.         walk(uri, self.nodes)
  93.  
  94.         return self
  95.  
  96.     def copy(self):
  97.         return Cache(**deepcopy(self.nodes))
  98.  
  99.     def dump(self, indent=None):
  100.         """ Dumps the contents of the cache to the screen.
  101.        The output from dump goes stdout and is used to view the cache contents.
  102.        Default indentation is a dot for each level.
  103.        :param indent:
  104.            indent (str): String to be use for indenting levels.
  105.        :return:
  106.            Nothing.
  107.        """
  108.         indent = indent if indent else '.'
  109.  
  110.         print('-------------------------------------------------------------------------------------------------------')
  111.         print('id =', id(self), '\nnodes =', self)
  112.         if self.nodes:
  113.             def walk(_cfg, count):
  114.                 count += 1
  115.                 for key, value in _cfg.items():
  116.                     if isinstance(value, dict):
  117.                         print(indent * count, key)
  118.                         walk(value, count)
  119.                     else:
  120.                         if isinstance(value, str):
  121.                             value = f'"{value}"'
  122.                         print(indent * count, key, f'value={value}')
  123.             walk(self.nodes, 0)
  124.         else:
  125.             print(' (No Data)')
  126.  
  127.         print('-------------------------------------------------------------------------------------------------------')
  128.  
  129.     def save(self, file=None):
  130.         if file:
  131.             dirname = path.dirname(file)
  132.  
  133.             if dirname and not path.exists(dirname):
  134.                 makedirs(dirname)
  135.  
  136.             with open(file, 'w') as f:
  137.                 dump(self.nodes, f, indent=3)
  138.  
  139.     def load(self, file=None):
  140.         file_type = path.splitext(file)[1].lstrip('.').lower()
  141.  
  142.         if file_type == 'py' and path.exists(file):
  143.             spec = spec_from_file_location("module.name", file)
  144.             module = module_from_spec(spec)
  145.             spec.loader.exec_module(module)
  146.             self.nodes = module.config
  147.  
  148.         if file_type == 'json' and path.exists(file):
  149.             with open(file) as f:
  150.                 self.nodes = load(f)
  151.  
  152.     def merge(self, src):
  153.         def walk(data, parent=''):
  154.             if isinstance(data, dict):
  155.                 for key, value in data.items():
  156.                     _uri = f'{parent}/{key}'.lstrip('/')
  157.                     if self.exists(_uri):
  158.                         walk(src.get(_uri), key)
  159.                     else:
  160.                         parts = _uri.rsplit("/", 1)
  161.                         key = parts[1] if len(parts) > 1 else _uri
  162.                         self.set(f'{parent}/{key}'.lstrip('/'), data[key])
  163.             else:
  164.                 print(12345, parent, data)
  165.  
  166.         walk(src.get())
  167.  
  168.     def remove(self, uri):
  169.         """ Remove entree from cache.
  170.        Removes an entree from the cache if it exists.
  171.        :param uri:
  172.            uri (str): URI that points to the entree to remove.
  173.        :return:
  174.            Nothing.
  175.        """
  176.  
  177.         uri = uri.lstrip('/')
  178.         if self.exists(uri):
  179.             node = self.get('/'.join(uri.split('/')[:-1]))
  180.             del node[uri.split('/')[-1]]
  181.  
  182.     def exists(self, uri):
  183.         """ Test if URI exists in the cache.
  184.  
  185.        :param uri:
  186.        :return:
  187.        """
  188.         return True if self.get(uri) else False
  189.  
  190.     def destroy(self):
  191.         """ Destroy cache.
  192.        Deletes all entries in the cache.
  193.        :return:
  194.            Nothing.
  195.        """
  196.         del self.nodes
  197.         self.nodes = {}
  198.  
  199.     def has_nodes(self):
  200.         if len(self.nodes):
  201.             return True
  202.         return False
  203.  
  204. # If uncommented the cache will ba a singleton.
  205. # class Cache:
  206. #     instance = None
  207. #
  208. #     def __new__(cls, **kwargs):
  209. #         if not cls.instance:
  210. #             cls.instance = super(Cache, cls).__new__(cls)
  211. #             cls.instance.cache = _Cache(**kwargs)
  212. #
  213. #         return cls.instance.cache
  214.  
  215.  
  216. cache = Cache()
  217. cache.load('data.py')
  218. cache.dump()
  219.  
  220. print('\nlevel_1/level_2/item2 = ', cache.get('level_1/level_2/item2'))
  221. cache.set('level_1/level_2/item2', 200)
  222. print('level_1/level_2/item2 = ', cache.get('level_1/level_2/item2'))
  223.  
  224. level_4 = {
  225.     'level_4': {
  226.         'item3': 10,
  227.         'item4': 20,
  228.     }
  229. }
  230.  
  231. cache.set('level_1/level_2/level_3', 100)
  232. print(cache.get('level_1/level_2/level_3'))
  233. cache.dump()
  234.  
  235. cache.set('level_1/level_2/level_3', level_4)
  236. cache.dump()
  237.  
  238. cache.get('level_1/level_2/level_3').update({'level_5': {'name': 'Dan'}})
  239.  
  240. if cache.exists('level_1/level_2/level_3'):
  241.     print('level_1/level_2/level_3 =', cache.get('level_1/level_2/level_3'), '\n')
  242.  
  243. cache.save('data.json')
  244. cache.dump()
  245.  
  246. my_cache = cache.copy()
  247. my_cache.dump()
  248.  
  249. cache.destroy()
  250. cache.set('p1', {'first_name': 'Dan', 'last_name': 'Huckson'})
  251. cache.set('p2', {'first_name': 'Geoff', 'last_name': 'Skerlan'})
  252. cache.dump()
  253.  
  254. my_cache.remove('level_1/level_2/item1')
  255. my_cache.remove('level_1/level_2/level_3')
  256. my_cache.dump()
  257.  
  258. your_cache = Cache()
  259. your_cache.load('data.json')
  260. your_cache.dump()
  261.  
  262. # Test equality code below.
  263. my_cache2 = my_cache.copy()
  264.  
  265. # uncomment the line below to test inequality.
  266. # my_cache.set('level_1/level_2/item2', 201)
  267. if my_cache == my_cache2:
  268.     print('\nThe contents of both caches are the same!')
  269.  
  270.     if my_cache is my_cache2:
  271.         print('my_cache2 is the same instance as my_cache.')
  272.     else:
  273.         print('my_cache2 is not the same instance as my_cache, but the contents are the same.')
  274.  
  275. else:
  276.     print('\nThe contents of both caches are not exactly same!')
  277.  
  278. my_cache.dump()
  279. my_cache2.dump()
  280.  
  281. print()
  282. my_cache3 = my_cache2
  283. if my_cache3 is my_cache2:
  284.     print('my_cache3 is the same instance as my_cache2, notice ids.')
  285.     my_cache3.dump()
  286.  
  287. print()
  288. print('your_cache =', str(your_cache))
  289. print('your_cache =', repr(your_cache))
  290. print()
  291.  
  292. x = Cache(abc={'item': 123}, xyx={'item': 456}, **{'123': 'abc'})
  293. x.dump()
  294.  
  295. # New features added 2019/09/12
  296. c = Cache()
  297. c.set('aa/bb', **{'ddd': 'test'})
  298. c.dump()
  299.  
  300. c = Cache({'abc/xyz/aaa': {'xxx': 1000}})
  301. c.dump()
  302.  
  303. x = Cache('aa1/bb1', **{'ddd1': 'test 1'})
  304. x.dump()
  305.  
  306. y = Cache('aa1/bb2', 123456)
  307. y.dump()
  308.  
  309. x.merge(y)
  310. x.dump()
  311.  
  312. y.merge(x)
  313. y.dump()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement