import abc import collections import re import threading from typing import MutableMapping from typing import MutableSet import stevedore def coerce_string_conf(d): result = {} for k, v in d.items(): if not isinstance(v, str): result[k] = v continue v = v.strip() if re.match(r"^[-+]?\d+$", v): result[k] = int(v) elif re.match(r"^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?$", v): result[k] = float(v) elif v.lower() in ("false", "true"): result[k] = v.lower() == "true" elif v == "None": result[k] = None else: result[k] = v return result class PluginLoader: def __init__(self, group): self.group = group self.impls = {} # loaded plugins self._mgr = None # lazily defined stevedore manager self._unloaded = {} # plugins registered but not loaded def load(self, name): if name in self._unloaded: self.impls[name] = self._unloaded[name]() return self.impls[name] if name in self.impls: return self.impls[name] else: # pragma NO COVERAGE if self._mgr is None: self._mgr = stevedore.ExtensionManager(self.group) try: self.impls[name] = self._mgr[name].plugin return self.impls[name] except KeyError: raise self.NotFound( "Can't load plugin %s %s" % (self.group, name) ) def register(self, name, modulepath, objname): def load(): mod = __import__(modulepath, fromlist=[objname]) return getattr(mod, objname) self._unloaded[name] = load class NotFound(Exception): """The specified plugin could not be found.""" class memoized_property: """A read-only @property that is only evaluated once.""" def __init__(self, fget, doc=None): self.fget = fget self.__doc__ = doc or fget.__doc__ self.__name__ = fget.__name__ def __get__(self, obj, cls): if obj is None: return self obj.__dict__[self.__name__] = result = self.fget(obj) return result def to_list(x, default=None): """Coerce to a list.""" if x is None: return default if not isinstance(x, (list, tuple)): return [x] else: return x class Mutex(abc.ABC): @abc.abstractmethod def acquire(self, wait: bool = True) -> bool: raise NotImplementedError() @abc.abstractmethod def release(self) -> None: raise NotImplementedError() class KeyReentrantMutex: def __init__( self, key: str, mutex: Mutex, keys: MutableMapping[int, MutableSet[str]], ): self.key = key self.mutex = mutex self.keys = keys @classmethod def factory(cls, mutex): # this collection holds zero or one # thread idents as the key; a set of # keynames held as the value. keystore: MutableMapping[ int, MutableSet[str] ] = collections.defaultdict(set) def fac(key): return KeyReentrantMutex(key, mutex, keystore) return fac def acquire(self, wait=True): current_thread = threading.get_ident() keys = self.keys.get(current_thread) if keys is not None and self.key not in keys: # current lockholder, new key. add it in keys.add(self.key) return True elif self.mutex.acquire(wait=wait): # after acquire, create new set and add our key self.keys[current_thread].add(self.key) return True else: return False def release(self): current_thread = threading.get_ident() keys = self.keys.get(current_thread) assert keys is not None, "this thread didn't do the acquire" assert self.key in keys, "No acquire held for key '%s'" % self.key keys.remove(self.key) if not keys: # when list of keys empty, remove # the thread ident and unlock. del self.keys[current_thread] self.mutex.release() def locked(self): current_thread = threading.get_ident() keys = self.keys.get(current_thread) if keys is None: return False return self.key in keys