Source

appspace / appspace / builders.py

# -*- coding: utf-8 -*-
'''appspace builder'''

from functools import partial
from itertools import starmap

from stuf.six import strings
from stuf.utils import selfname, exhaust, exhaustmap, twoway

from appspace.utils import lazyimport
from appspace.registry import Manager, StrictManager
from appspace.keys import (
    AAppspace, AppLookupError, NoAppError, ABranch, ANamespace, AApp, appifies)


class _Filter(object):

    @classmethod
    def _filter(self, x):
        return not x[0].startswith('_')


class Patterns(_Filter):

    '''patterns for manager configured by class'''

    key = AApp
    strict = False

    @twoway
    def _manager(self):
        '''manager class'''
        return StrictManager if self.strict else Manager

    @classmethod
    def build(cls):
        '''build manager configuration from class'''
        l = selfname(cls)
        # set key
        key = cls.key
        if isinstance(key, strings):
            # load key if string
            key = lazyimport(key)
        manager = cls._manager(l, key)  # pylint: disable-msg=e1121
        b = partial(manager.keyed, ABranch)
        m, n = manager.set, partial(manager.keyed, ANamespace)
        t = lambda x, y: y.build(manager) if (n(y) or b(y)) else m(y, x, l)
        exhaustmap(vars(cls), t, cls._filter)
        return manager

    @staticmethod
    def factory(label, manager, *args):
        '''
        factory for manager

        @param label: label for manager
        '''
        # build manager
        manager = manager(label)
        # register things in manager
        exhaust(starmap(lambda x, y: manager.set(y, x), iter(args)))
        return manager

    @classmethod
    def patterns(cls, label, *args):
        '''
        configure appspace

        @param label: name of branch appspace
        @param *args: tuple of module paths or component inclusions
        '''
        return cls.factory(label, cls._manager, *args)


@appifies(ANamespace)
class Branch(object):

    '''branch configuration'''
    
    @classmethod
    def _key(cls, label, manager):
        try:
            # lazily load key
            key = cls.key
            if isinstance(key, strings):
                key = lazyimport(key)
            # register class key
            manager.ez_register(ANamespace, label, key)
        except AttributeError:
            key = manager.key(ANamespace, label)

    @classmethod
    def build(cls, manager):
        '''gather branch configuration'''
        cls._key(selfname(cls), manager)
        i, m = cls.include, manager.set
        t = lambda x: not x[0].startswith('_') or isinstance(x[1], strings)
        exhaustmap(vars(cls), lambda x, y: m(i(y), x), t)

    @staticmethod
    def include(module):
        '''
        configure branch appspace

        @param module: module import path
        '''
        return ('include', module)


@appifies(AAppspace)
class Appspace(object):

    '''appspace interface'''

    __slots__ = ['manager']

    def __init__(self, manager):
        '''
        init

        @param manager: appspace manager
        '''
        self.manager = manager

    def __getattr__(self, label):
        try:
            return object.__getattribute__(self, label)
        except AttributeError:
            return self.__getitem__(label)

    def __getitem__(self, label):
        try:
            item = self.manager.get(label, self.manager._current)
        except AppLookupError:
            try:
                # try finding namespace
                self.manager.namespace(label)
            except AppLookupError:
                raise NoAppError(label)
            else:
                # temporarily swap primary label
                self.manager._current = label
                return self
        else:
            # ensure current label is set back to default
            self.manager._current = self.manager._root
            return item

    def __call__(self, label, *args, **kw):
        try:
            result = self.__getitem__(label)
            return result(*args, **kw)
        except TypeError:
            return result


factory = Patterns.factory
include = Branch.include
space_patterns = Patterns.patterns

def patterns(label, *args, **kw):
    '''
    factory for manager

    @param label: label for manager
    '''
    return Appspace(space_patterns(label, *args, **kw))


def class_patterns(clspatterns):
    '''
    factory for manager configured with class patterns

    @param clspatterns: class patterns
    '''
    return Appspace(clspatterns.build())