import argparse
from abc import abstractmethod
from typing import Any, Callable, Dict, Type
from everett import NO_VALUE, ConfigurationMissingError
from everett.manager import ConfigManager, ConfigOSEnv, ListOf, generate_uppercase_key
_config = ConfigManager([])
[docs]class ConfigEnv:
register = True
on_top = True
[docs] @abstractmethod
def get(self, key, namespace=None):
raise NotImplementedError # pragma: no cover
def __init_subclass__(cls: Type['ConfigEnv']):
if cls.register:
inst = cls()
if cls.on_top:
_config.envs.insert(0, inst)
else:
_config.envs.append(inst)
class _ConfigArgParseEnv(ConfigEnv):
on_top = False
def __init__(self):
self.cache = dict()
def get(self, key, namespace=None):
name = generate_uppercase_key(key, namespace).lower()
if name in self.cache:
return self.cache[name]
parser = argparse.ArgumentParser()
parser.add_argument(f'--{name}')
args, _ = parser.parse_known_args()
res = getattr(args, name) or NO_VALUE
self.cache[name] = res
return res
class _NamespacedOSEnv(ConfigOSEnv, ConfigEnv):
namespace = 'EBONITE'
def get(self, key, namespace=None):
return super(_NamespacedOSEnv, self).get(key, namespace or self.namespace)
[docs]class Param:
def __init__(self, key, namespace=None, default=NO_VALUE,
alternate_keys=NO_VALUE, doc='', parser: Callable = str, raise_error=True,
raw_value=False):
self.key = key
self.namespace = namespace
self.default = default
self.alternate_keys = alternate_keys
self.doc = doc
self.parser = parser
self.raise_error = raise_error
self.raw_value = raw_value
def __get__(self, instance: 'Config', owner: Type['Config']):
if instance is None:
return self
return _config(key=self.key, namespace=self.namespace or instance.namespace,
default=self.default, alternate_keys=self.alternate_keys,
doc=self.doc, parser=self.parser,
raise_error=self.raise_error, raw_value=self.raw_value)
class _ConfigMeta(type):
def __new__(mcs, name, bases, namespace):
meta = super().__new__(mcs, name + 'Meta', (mcs,) + bases, namespace)
res = super().__new__(meta, name, bases, {})
return res
[docs]class Config(metaclass=_ConfigMeta):
namespace = None
@classmethod
def _try__get__(cls, value, default):
try:
return value.__get__(cls, type(cls))
except ConfigurationMissingError:
return default
@classmethod
def get_params(cls) -> Dict[str, Any]:
return {
name: cls._try__get__(value, '--NOT-SET--')
for name, value in cls.__dict__.items() if isinstance(value, Param)
}
@classmethod
def log_params(cls):
from ebonite.utils.log import logger
logger.debug('%s environment:', cls.__name__)
for name, value in cls.get_params().items():
logger.debug('%s: %s', name, value)
[docs]class Core(Config):
DEBUG = Param('debug', default='false', doc='turn debug on', parser=bool)
ADDITIONAL_EXTENSIONS = Param('extensions', default='',
doc='comma-separated list of additional ebonite extensions to load',
parser=ListOf(str),
raise_error=False)
AUTO_IMPORT_EXTENSIONS = Param('auto_import_extensions', default='true',
doc='Set to true to automatically load available extensions on ebonite import',
parser=bool)
RUNTIME = Param('runtime', default='false', doc='is this instance a runtime', parser=bool)
[docs]class Logging(Config):
LOG_LEVEL = Param('log_level', default='INFO' if not Core.DEBUG else 'DEBUG',
doc='Logging level for ebonite',
parser=str)
[docs]class Runtime(Config):
SERVER = Param('server', doc='server for runtime')
LOADER = Param('loader', doc='interface loader for runtime')
if Core.DEBUG:
Logging.log_params()
Core.log_params()
Runtime.log_params()