class CaseInsensitiveDict(MutableMapping): """ Inspired by requests' case-insensitive dict implementation, but works with non-string keys as well. """ def __init__(self, init=None, **kwargs): """ Force internal dict to be ordered to ensure a consistent iteration order, irrespective of case. """ self._data = OrderedDict() self.update(init or {}, **kwargs) def __len__(self): return len(self._data) def __setitem__(self, key, value): # Store the case-sensitive key so it is available for dict iteration self._data[to_lowercase(key)] = (key, value) def __delitem__(self, key): del self._data[to_lowercase(key)] def __getitem__(self, key): return self._data[to_lowercase(key)][1] def __iter__(self): return (item[0] for item in self._data.values()) def __eq__(self, rval): if not isinstance(rval, Mapping): # Comparing to non-mapping type (e.g. int) is always False return False return dict(self.items_lower()) == dict(CaseInsensitiveDict(rval).items_lower()) def __repr__(self): return repr(dict(self.items())) def items_lower(self): """ Returns a generator iterating over keys and values, with the keys all being lowercase. """ return ((key, val[1]) for key, val in self._data.items()) def copy(self): """ Returns a copy of the object """ return CaseInsensitiveDict(self._data.items())
class PathInfoDict(fileinfo.FileInfo): def __init__(self, match_each=True, **patterns): ''' match_each: If True, each file path is matched which prevents uses less memory but sacrifices performance a little bit. If False, the complete list is matched after all the file information has been added to pathinfo. patterns: Contains the patterns to match. Example: { 'saltenv': 'base', 'relpath': ['*.sls'] } ''' super(PathInfoDict, self).__init__( fields=PATHINFO_FIELDS, match_each=match_each, **patterns ) self._elements = OrderedDict() @property def as_sequence(self): if self.pattern and not self.match_each: return list( matcher.ifilter( self._elements.values(), _pattern=self.pattern ) ) return self._elements.values() def element(self, root=None, abspath=None, **kwargs): ''' kwargs contain extra information for custom methods. This method must return a valid empty object if no vars are passed to allow introspection to create patterns. :param root: :param abspath: ''' if root is None and abspath is None: root = os.path.abspath('.') abspath = os.path.abspath('.') relpath = os.path.relpath(abspath, root) try: element = self._elements.get(relpath, OrderedDict()) except AttributeError: element = OrderedDict() if not element: for field in PATHINFO_FIELDS: element.setdefault(field, '') element['saltenv'] = kwargs.get('saltenv', 'base') element['relpath'] = relpath element['abspath'] = abspath element['is_pillar'] = kwargs.get('is_pillar', False) cachedir = kwargs.get('cachedir', '') if cachedir and os.path.commonprefix([abspath, cachedir]) == cachedir: element['cache_root'] = root else: element['file_root'] = root element_hook = kwargs.get('_element_hook', None) if element_hook: element = element_hook(self, element, **kwargs) return element def add_element(self, element, **kwargs): add_hook = kwargs.get('_add_hook', None) if add_hook: element = add_hook(self, element, **kwargs) if element['relpath'] not in self._elements: self._elements[element['relpath']] = element def filelist(self, roots, **kwargs): ''' :param roots: file_roots, pillar_roots, cache_roots, etc to walk. kwargs: Contains any extra variables to pass to element. ''' for env, destdirs in six.iteritems(roots): kwargs['saltenv'] = env super(PathInfoDict, self).filelist(destdirs, **kwargs) return self.as_sequence
def patch(self, force_load=True, kinds=None, **kwargs): ''' opts overriden opts context overriden context pillar overriden pillar grains overriden grains funcs override something in the dunders indexed by kind (modules, runners, etc) .. code-block:: python with self.patch(grains={1:2}, context={3: 4}): ret1 = self._('mc_remote.sls')() .. code-block:: python with self.patch(opts={1: 2}, pillar={'secret': 'secret'}): ret1 = self._('mc_remote.sls')() .. code-block:: python def _do(*args, **kw): return {} with self.patch(funcs={ 'modules': {'mc_remote.salt_call': Mock(side_effect=_do)}, 'pillars': {'mc_remote.salt_call': Mock(side_effect=_do)} }): ret1 = self._('mc_remote.sls')() ''' tpatchs = OrderedDict() globs = kwargs.get('globs', {}) mod_globs = {} funcs = kwargs.get('funcs', {}) for module, funcsdata in funcs.items(): for fun, callback in funcsdata.items(): if '.' in fun: module, sfun = fun.split('.') mglobs = mod_globs.setdefault(module, copy.deepcopy(globs)) mglobs[sfun] = callback k = 'patch_fun_{0}{1}'.format(module, fun) tpatchs[k] = patch.dict(self.salt, {fun: callback}) for opt in self.contextual: overriden = kwargs.get(opt, {}) tpatchs[opt] = patch.dict(getattr(self, opt+'_'), overriden) if not kinds: kinds = [a for a in self.dunders_] for kind in kinds: mods = self.dunders_[kind] # force lazyloader to load all if force_load: mods = [a for a in self.dunders_[kind]] filtered = kwargs.get('filtered', None) if filtered is None: filtered = mods[:] if not filtered: continue for mod in mods: matched = False for match in filtered: rmatch = re_cache.get(match) if not rmatch: rmatch = re_cache[match] = re.compile(match) if rmatch.search(mod): matched = True break if not matched: continue func = self.dunders_[kind][mod] if not isinstance(func, dict): try: _globals = func.__globals__ except AttributeError: continue thismod = mod.split('.')[0] globspatch = mod_globs.setdefault(thismod, {}) for i in globs: globspatch[i] = globs[i] k = '{0}.{1}'.format(kind, mod) for opt in self.contextual: gopt = '__{0}__'.format(opt) if gopt in _globals: globspatch[gopt] = getattr(self, opt+'_') tpatchs[k] = patch.dict(func.__globals__, globspatch) return contextlib.nested(*tuple(tpatchs.values()))
class PathInfoDict(fileinfo.FileInfo): def __init__(self, match_each=True, **patterns): ''' match_each: If True, each file path is matched which prevents uses less memory but sacrifices performance a little bit. If False, the complete list is matched after all the file information has been added to pathinfo. patterns: Contains the patterns to match. Example: { 'saltenv': 'base', 'relpath': ['*.sls'] } ''' super(PathInfoDict, self).__init__(fields=PATHINFO_FIELDS, match_each=match_each, **patterns) self._elements = OrderedDict() @property def as_sequence(self): if self.pattern and not self.match_each: return list( matcher.ifilter(self._elements.values(), _pattern=self.pattern)) return self._elements.values() def element(self, root=None, abspath=None, **kwargs): ''' kwargs contain extra information for custom methods. This method must return a valid empty object if no vars are passed to allow introspection to create patterns. :param root: :param abspath: ''' if root is None and abspath is None: root = os.path.abspath('.') abspath = os.path.abspath('.') relpath = os.path.relpath(abspath, root) try: element = self._elements.get(relpath, OrderedDict()) except AttributeError: element = OrderedDict() if not element: for field in PATHINFO_FIELDS: element.setdefault(field, '') element['saltenv'] = kwargs.get('saltenv', 'base') element['relpath'] = relpath element['abspath'] = abspath element['is_pillar'] = kwargs.get('is_pillar', False) cachedir = kwargs.get('cachedir', '') if cachedir and os.path.commonprefix([abspath, cachedir]) == cachedir: element['cache_root'] = root else: element['file_root'] = root element_hook = kwargs.get('_element_hook', None) if element_hook: element = element_hook(self, element, **kwargs) return element def add_element(self, element, **kwargs): add_hook = kwargs.get('_add_hook', None) if add_hook: element = add_hook(self, element, **kwargs) if element['relpath'] not in self._elements: self._elements[element['relpath']] = element def filelist(self, roots, **kwargs): ''' :param roots: file_roots, pillar_roots, cache_roots, etc to walk. kwargs: Contains any extra variables to pass to element. ''' for env, destdirs in six.iteritems(roots): kwargs['saltenv'] = env super(PathInfoDict, self).filelist(destdirs, **kwargs) return self.as_sequence