1""" 2Lazily-evaluated data structures, primarily used by Salt's loader 3""" 4 5 6import logging 7from collections.abc import MutableMapping 8 9import salt.exceptions 10 11log = logging.getLogger(__name__) 12 13 14def verify_fun(lazy_obj, fun): 15 """ 16 Check that the function passed really exists 17 """ 18 if not fun: 19 raise salt.exceptions.SaltInvocationError( 20 "Must specify a function to run!\nex: manage.up" 21 ) 22 if fun not in lazy_obj: 23 # If the requested function isn't available, lets say why 24 raise salt.exceptions.CommandExecutionError(lazy_obj.missing_fun_string(fun)) 25 26 27class LazyDict(MutableMapping): 28 """ 29 A base class of dict which will lazily load keys once they are needed 30 31 TODO: negative caching? If you ask for 'foo' and it doesn't exist it will 32 look EVERY time unless someone calls load_all() 33 As of now this is left to the class which inherits from this base 34 """ 35 36 def __init__(self): 37 self.clear() 38 39 def __nonzero__(self): 40 # we are zero if dict is empty and loaded is true 41 return bool(self._dict or not self.loaded) 42 43 def __bool__(self): 44 # we are zero if dict is empty and loaded is true 45 return self.__nonzero__() 46 47 def clear(self): 48 """ 49 Clear the dict 50 """ 51 # create a dict to store loaded values in 52 self._dict = getattr(self, "mod_dict_class", dict)() 53 54 # have we already loded everything? 55 self.loaded = False 56 57 def _load(self, key): 58 """ 59 Load a single item if you have it 60 """ 61 raise NotImplementedError() 62 63 def _load_all(self): 64 """ 65 Load all of them 66 """ 67 raise NotImplementedError() 68 69 def _missing(self, key): 70 """ 71 Whether or not the key is missing (meaning we know it's not there) 72 """ 73 return False 74 75 def missing_fun_string(self, function_name): 76 """ 77 Return the error string for a missing function. 78 79 Override this to return a more meaningfull error message if possible 80 """ 81 return "'{}' is not available.".format(function_name) 82 83 def __setitem__(self, key, val): 84 self._dict[key] = val 85 86 def __delitem__(self, key): 87 del self._dict[key] 88 89 def __getitem__(self, key): 90 """ 91 Check if the key is ttld out, then do the get 92 """ 93 if self._missing(key): 94 raise KeyError(key) 95 96 if key not in self._dict and not self.loaded: 97 # load the item 98 if self._load(key): 99 log.debug("LazyLoaded %s", key) 100 return self._dict[key] 101 else: 102 log.debug( 103 "Could not LazyLoad %s: %s", key, self.missing_fun_string(key) 104 ) 105 raise KeyError(key) 106 else: 107 return self._dict[key] 108 109 def __len__(self): 110 # if not loaded, 111 if not self.loaded: 112 self._load_all() 113 return len(self._dict) 114 115 def __iter__(self): 116 if not self.loaded: 117 self._load_all() 118 return iter(self._dict) 119