Source code for haps.config

import os
from functools import partial
from threading import RLock
from types import FunctionType
from typing import Any, Optional, Type

from haps.exceptions import ConfigurationError, UnknownConfigVariable

_NONE = object()


def _env_resolver(var_name: str, env_name: str = None,
                  default: Any = _NONE) -> Any:
    try:
        return os.environ[env_name or f'HAPS_{var_name}']
    except KeyError:
        if default is not _NONE:
            if callable(default):
                return default()
            else:
                return default
        else:
            raise UnknownConfigVariable


[docs]class Configuration: """ Configuration container, a simple object to manage application config variables. Variables can be set manually, from the environment, or resolved via custom function. """ _lock = RLock() _instance: 'Configuration' = None def __new__(cls, *args, **kwargs) -> 'Configuration': with cls._lock: if cls._instance is None: cls._instance = object.__new__(cls) cls._instance.cache = {} cls._instance.resolvers = {} return cls._instance def _resolve_var(self, var_name: str) -> Any: if var_name in self.resolvers: return self.resolvers[var_name]() else: raise UnknownConfigVariable( f'No resolver registered for {var_name}')
[docs] def get_var(self, var_name: str, default: Optional[Any] = _NONE) -> Any: """ Get a config variable. If a variable is not set, a resolver is not set, and no default is given :class:`~haps.exceptions.UnknownConfigVariable` is raised. :param var_name: Name of variable :param default: Default value :return: Value of config variable """ try: return self.cache[var_name] except KeyError: try: var = self._resolve_var(var_name) except UnknownConfigVariable as e: if default is not _NONE: if callable(default): return default() else: return default else: raise e else: self.cache[var_name] = var return var
[docs] @classmethod def resolver(cls, var_name: str) -> FunctionType: """ Variable resolver decorator. Function or method decorated with it is used to resolve the config variable. .. note:: Variable is resolved only once. Next gets are returned from the cache. :param var_name: Variable name :return: Function decorator """ def dec(f): if var_name in cls().resolvers: raise ConfigurationError( f'Resolver for {var_name} already registered') cls().resolvers[var_name] = f return f return dec
[docs] @classmethod def env_resolver(cls, var_name: str, env_name: str = None, default: Any = _NONE) -> 'Configuration': """ Method for configuring environment resolver. :param var_name: Variable name :param env_name: An optional environment variable name. If not set\ haps looks for `HAPS_var_name` :param default: Default value for variable. If it's a callable,\ it is called before return. If not provided\ :class:`~haps.exceptions.UnknownConfigVariable` is raised :return: :class:`~haps.config.Configuration` instance for easy\ chaining """ cls.resolver(var_name)( partial( _env_resolver, var_name=var_name, env_name=env_name, default=default)) return cls()
[docs] @classmethod def set(cls, var_name: str, value: Any) -> 'Configuration': """ Set the variable :param var_name: Variable name :param value: Value of variable :return: :class:`~haps.config.Configuration` instance for easy\ chaining """ with cls._lock: if var_name not in cls().cache: cls().cache[var_name] = value else: raise ConfigurationError( f'Value for {var_name} already set') return cls()
[docs]class Config: """ Descriptor providing config variables as a class properties. .. code-block:: python class SomeClass: my_var: VarType = Config() custom_property_name: VarType = Config('var_name') """
[docs] def __init__(self, var_name: str = None, default=_NONE) -> None: """ :param var_name: An optional variable name. If not set the property\ name is used. :param default: Default value for variable. If it's a callable,\ it is called before return. If not provided\ :class:`~haps.exceptions.UnknownConfigVariable` is raised """ self._default = default self._var_name = var_name self._type = None self._name = None
def __get__(self, instance: 'Config', owner: Type) -> Any: var = Configuration().get_var(self._var_name, self._default) setattr(instance, self._var_name, var) return var def __set_name__(self, owner: Type, name: str) -> None: self._name = name if self._var_name is None: self._var_name = name