Advertisement
Uno-Dan

Version 1

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