Source code for montague.loadwsgi

from __future__ import absolute_import

import os.path
import pkg_resources
from characteristic import attributes
from .ini import IniConfigLoader
from .vendor import reify
from .structs import LoadableConfig, Loadable, loadable_type_entry_points
from .exceptions import ConfigNotFound


scheme_loadable_types = {
    'application': 'app',
    'composite': 'composite',
    'server': 'server',
    'filter': 'filter',
}


[docs]def lookup_object(spec): """ Looks up a module or object from a some.module:func_name specification. To just look up a module, omit the colon and everything after it. """ parts, target = spec.split(':') if ':' in spec else (spec, None) module = __import__(parts) for part in parts.split('.')[1:] + ([target] if target else []): module = getattr(module, part) return module
[docs]class CompositeHelper(object): def __init__(self, loader): self.loader = loader
[docs] def get_app(self, name='main', global_conf=None): return self.loader.load_app(name)
@attributes(['path'], apply_with_init=False, apply_immutable=True)
[docs]class Loader(object): def __init__(self, path): self.path = path self.config_loader = Loader._find_config_loader(path) @staticmethod def _find_config_loader(config_path): suffix = os.path.splitext(config_path)[1] # returns ".ext" suffix = suffix[1:] # we just want "ext" entry_point_name = "montague.config_loader" eps = tuple(pkg_resources.iter_entry_points(entry_point_name, suffix)) if len(eps) == 0: # TODO log warning loader_cls = IniConfigLoader else: loader_cls = eps[0].load() return loader_cls(config_path) @reify def config(self): return self.config_loader.config() def _fallback_config_loader(self, schemes, kind, name): _configs = [] global_config = self.config.get('globals', {}) for scheme in schemes: scheme_config = self.config.get(scheme, {}) if name in scheme_config: loadable_type = scheme_loadable_types[scheme] constructor = getattr(LoadableConfig, loadable_type) _configs.append(constructor( name=name, config=scheme_config[name], global_config=global_config)) if len(_configs) == 0: not_found = "the {0} {1}".format(kind, name) msg = "Unable to find the config for {0}".format(not_found) raise ConfigNotFound(msg) app_config = _configs[0] return app_config
[docs] def app_config(self, name=None): try: if name is None: name = 'main' return self.config_loader.app_config(name) except (NotImplementedError, AttributeError): schemes = ['application', 'composite'] app_config = self._fallback_config_loader(schemes, 'application', name) return app_config
def _adapt_entry_point_factory(self, factory, entry_point_group): # TODO: use decorator or something to preserve signatures if entry_point_group in ['paste.composite_factory', 'paste.composit_factory']: def adapter(global_conf, **local_conf): helper = CompositeHelper(self) return factory(helper, global_conf, **local_conf) return adapter if entry_point_group in ['paste.server_runner', 'paste.filter_app_factory']: def outer(global_conf, **local_conf): def inner(wsgi_app): return factory(wsgi_app, global_conf, **local_conf) return inner return outer return factory def _load_entry_point_factory(self, resource, entry_point_groups): if "#" in resource: pkg, name = resource.split('#') else: pkg, name = resource, "main" entry_point_map = pkg_resources.get_entry_map(pkg) factory = None for group in entry_point_groups: if group in entry_point_map: group_map = entry_point_map[group] if name in group_map: factory = group_map[name].load() factory = self._adapt_entry_point_factory(factory, group) break if factory is None: raise Exception('TODO') return factory def _adapt_call_factory(self, factory, factory_type): # PasteDeploy assumes a call-type factory will meet the specification # for {type}_factory. That is, server_runner is not allowed. if factory_type == 'paste.composite_factory': def adapter(global_conf, **local_conf): helper = CompositeHelper(self) return factory(helper, global_conf, **local_conf) return adapter return factory def _load_call_factory(self, resource, factory_type): return self._adapt_call_factory(lookup_object(resource), factory_type) def _load_factory(self, location, factory_types): scheme, resource = location.split(':', 1) if scheme in ('egg', 'package'): factory = self._load_entry_point_factory(resource, factory_types) elif scheme == 'call': factory_type = [f for f in factory_types if f.endswith('_factory')][0] factory = self._load_call_factory(resource, factory_type) elif scheme == 'config': raise NotImplementedError("haven't implemented config delegation yet") else: raise NotImplementedError("assuming this is the 'import some code' type") return factory def _load_app_from_config(self, app_config): local_conf = dict(app_config.config) # resolve nested uses use = local_conf.pop('use') if ':' not in use: loadable = self._load_app(use) loadable.local_conf.update(local_conf) loadable.global_conf.update(app_config.global_config) else: factory = self._load_factory(use, app_config.entry_point_groups) loadable = Loadable(factory=factory, is_app=True, global_conf=app_config.global_config, local_conf=local_conf) filter_with = loadable.local_conf.pop('filter-with', None) loadable = loadable.normalize() if filter_with is not None: loadable.outer = self._load_filter(name=filter_with) return loadable def _load_app(self, name): if name is not None and name is not 'main' and ':' in name: # not a config section name, let's handle this factory = self._load_factory(name, loadable_type_entry_points['app']) loadable = Loadable( factory=factory, global_conf={}, local_conf={}) return loadable app_config = self.app_config(name) return self._load_app_from_config(app_config)
[docs] def load_app(self, name=None): loadable = self._load_app(name) return loadable.normalize().get()
[docs] def server_config(self, name=None): try: if name is None: name = 'main' return self.config_loader.server_config(name) except (NotImplementedError, AttributeError): schemes = ['server'] server_config = self._fallback_config_loader(schemes, 'server', name) return server_config
[docs] def load_server(self, name=None): server_config = self.server_config(name) scheme, resource = server_config.config['use'].split(':', 1) if scheme in ('egg', 'package'): factory = self._load_entry_point_factory( resource, server_config.entry_point_groups) elif scheme == 'call': factory = self._load_call_factory(resource, server_config.loadable_type) else: raise Exception('TODO: scheme type {}'.format(scheme)) local_conf = dict(server_config.config) del local_conf['use'] return factory(server_config.global_config, **local_conf)
[docs] def filter_config(self, name=None): try: if name is None: name = 'main' return self.config_loader.filter_config(name) except (NotImplementedError, AttributeError): schemes = ['filter'] filter_config = self._fallback_config_loader(schemes, 'filter', name) return filter_config
def _load_filter_from_config(self, filter_config): local_conf = dict(filter_config.config) # resolve nested uses use = local_conf.pop('use') if ':' not in use: loadable = self._load_filter(use) loadable.local_conf.update(local_conf) loadable.global_conf.update(filter_config.global_config) else: scheme, resource = filter_config.config['use'].split(':', 1) if scheme in ('egg', 'package'): factory = self._load_entry_point_factory( resource, filter_config.entry_point_groups) elif scheme == 'call': factory = self._load_call_factory(resource, filter_config.loadable_type) else: raise Exception('TODO: scheme type {}'.format(scheme)) loadable = Loadable( factory=factory, global_conf=filter_config.global_config, local_conf=local_conf) filter_with = loadable.local_conf.pop('filter-with', None) loadable = loadable.normalize() if filter_with is not None: loadable.outer = self._load_filter(name=filter_with) return loadable def _load_filter(self, name): if name is not None and name is not 'main' and ':' in name: # not a config section name, let's handle this factory = self._load_factory(name, loadable_type_entry_points['filter']) loadable = Loadable( factory=factory, global_conf={}, local_conf={}) return loadable filter_config = self.filter_config(name) return self._load_filter_from_config(filter_config)
[docs] def load_filter(self, name=None): return self._load_filter(name).normalize().get()
[docs] def logging_config(self, name=None): if name is None: name = 'main' try: return self.config_loader.logging_config(name) except (NotImplementedError, AttributeError): return self.config['logging'][name]