Source code for pyapp.conf.loaders
"""
Loaders are used to load settings from an external source, eg a Python module
(using :py:class:`ModuleLoader`).
A loader provides key/value pairs to the settings container to merge into the
application settings.
"""
from __future__ import absolute_import, unicode_literals
import importlib
try:
from urllib.parse import urlparse
except ImportError:
# Fallback for Python 2
from urlparse import urlparse
from pyapp.exceptions import InvalidConfiguration
from .file_loader import FileLoader
[docs]class ModuleLoader(object):
"""
Load configuration from an importable module.
Loader will load all upper case attributes from the imported module.
Usage:
>>> loader = ModuleLoader("name.of.module")
>>> settings = dict(loader)
"""
scheme = 'python'
@classmethod
def from_url(cls, parse_result):
"""
Create an instance of :class:`ModuleLoader` from :class:`urllib.parse.ParseResult`.
:type parse_result: urllib.parse.ParseResult
:rtype: ModuleLoader
"""
return cls(parse_result.path)
def __init__(self, module):
"""
:param module: Fully qualify python module path.
:type module: str
"""
assert module
self.module = module
def __iter__(self):
try:
mod = importlib.import_module(self.module)
except ImportError as ex:
raise InvalidConfiguration("Unable to load module: {}\n{}".format(self, ex))
return ((k, getattr(mod, k)) for k in dir(mod) if k.isupper())
def __str__(self):
return "{}:{}".format(self.scheme, self.module)
class SettingsLoaderRegistry(object):
def __init__(self):
self.loaders = {
FileLoader.scheme: FileLoader,
ModuleLoader.scheme: ModuleLoader,
}
def register(self, loader=None, scheme=None):
"""
Register a new loader, this method can be used as decorator
:param loader: Loader to register
:param scheme: Scheme to register this loader for, if supplied scheme must be a attribute of the loader
"""
def inner(obj):
loader_scheme = scheme or getattr(obj, 'scheme', None)
assert loader_scheme, "Scheme has not been defined."
assert hasattr(obj, 'from_url'), "Settings loaders must implement a from_url method"
self.loaders[loader_scheme] = obj
return obj
return inner(loader) if loader else inner
def factory(self, settings_url):
"""
Factory method that returns a factory suitable for opening the settings uri reference.
The URI scheme (identifier prior to the first `:`) is used to determine the correct loader.
:param settings_url: URI that references a settings source.
:type settings_url: str
:return: Loader instance
:raises: ValueError
"""
result = urlparse(settings_url)
if not result.scheme:
# If no scheme is defined assume python module
return ModuleLoader.from_url(result)
try:
return self.loaders[result.scheme].from_url(result)
except KeyError:
raise InvalidConfiguration("Unknown scheme `{}` in settings URI: {}".format(result.scheme, result))
# Singleton instance
registry = SettingsLoaderRegistry()
register = registry.register
factory = registry.factory