from glob import glob import fnmatch class Registry(dict): unregister = dict.__delitem__ def check_key(self, key): pass def check_value(self, value): pass def check(self, key, value): try: self.check_key(key) except KeyError: raise except Exception as e: raise KeyError(e.message) try: self.check_value(value) except ValueError: raise except Exception as e: raise ValueError(e.message) def register(self, key, value): self.__setitem__(key, value) def __setitem__(self, key, value): self.check(key, value) super().__setitem__(key, value) class PathMatcher(object): """ A configurable glob. """ def __init__(self, config): if isinstance(config, str): self.include = [config, ] elif isinstance(config, list): self.include = config elif not isinstance(config, dict): raise ValueError('Invalid pattern configuration') if not config.get('include'): raise ValueError('At least one inclusion pattern required') elif isinstance(config['include'], str): self.include = [config['include'], ] elif isinstance(config['include'], list): self.include = config['include'] else: raise ValueError( '"include" must be a pattern or list of patterns' ) config.setdefault('exclude', []) if isinstance(config['exclude'], str): self.exclude = [config['exclude'], ] elif isinstance(config['exclude'], list): self.exclude = config['exclude'] else: raise ValueError( '"exclude" must be a pattern or list of patterns' ) self._found = None def all(self): if self._found is not None: return self._found self._found = set() for include_pattern in self.include: self._found |= set(glob(include_pattern)) for exclude_pattern in self.exclude: self._found -= set(fnmatch.filter(self._found, exclude_pattern)) self._found = list(map(Path, self._found)) return self._found def get(self): if not self.all(): raise ValueError('No matching paths found') if len(self.all()) != 1: raise ValueError('More than one matching path found') return self.all()[0]