# -*- coding: utf-8 -*- import yaml from .module import Module __all__ = ["Config"] class _ModuleIndex(list): """List class that supports attribute access to its elements by key.""" def __init__(self): super().__init__() self._index = {} def __getitem__(self, key): try: return super().__getitem__(key) except TypeError: return super().__getitem__(self._index[key]) def __getattr__(self, attr): return self[self._index[attr]] def append(self, key, value): self._index[key] = len(self) super().append(value) class Config: """Stores application-wide configuration info.""" def __init__(self, basedir): self._dir = basedir self._filename = basedir / "config" / "config.yml" self._data = None self._modules = _ModuleIndex() self._load() def _load(self): """Load config from the config file.""" with self._filename.open("rb") as fp: self._data = yaml.load(fp) self._modules = _ModuleIndex() for name in self.get("modules.enabled", []): self._modules.append(name, Module(self, name)) def get(self, key="", default=None): """Acts like a dict lookup in the config file. Dots can be used to separate keys. For example, config["foo.bar"] == config["foo"]["bar"]. """ obj = self._data for item in key.split("."): if item not in obj: return default obj = obj[item] return obj @property def dir(self): """Return the application's base directory.""" return self._dir @property def modules(self): """Return a list-like object (a _ModuleIndex) of loaded modules.""" return self._modules @property def scheme(self): """Return the site's configured scheme, either "http" or "https".""" return "https" if self.get("site.https") else "http" def install(self, app): """Install relevant config into the application, including modules.""" app.config["SERVER_NAME"] = self.get("site.canonical") app.config["PREFERRED_URL_SCHEME"] = self.scheme app.config["MAKO_MODULE_DIRECTORY"] = str( self._dir / "templates" / ".cache") app.secret_key = self.get("auth.session_key") for module in self.modules: module.install(app) def load_module_config(self, name): """Load and return a module config file. Returns a YAML parse of {confdir}/modules/{name}.yml, or None. """ filename = self._dir / "config" / "modules" / (name + ".yml") try: with filename.open("rb") as fp: return yaml.load(fp) except FileNotFoundError: return None def collect_scopes(self): """Return a list of SSO scopes required for all/regular users.""" scopes = set() for module in self.modules: scopes |= module.scopes() if not scopes: scopes.add("publicData") return sorted(list(scopes))