Source

appspace / appspace / utils.py

from inspect import isclass

from functools import partial
from itertools import starmap
from keyword import iskeyword
from importlib import import_module

from twoq import twoq
from stuf import stuf
from stuf.six import items, strings
from stuf.utils import getcls, selfname, exhaust, exhaustmap, twoway, imap

from appspace.imps import appifies
from appspace.registry import Manager, StrictManager
from appspace.keys import ABranch, ANamespace, AApp, AService


class key(appifies):

    '''appifier'''

    def __init__(self, key, **metadata):
        exhaustmap(metadata, key.setTaggedValue)
        appifies.__init__(self, key)


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)
    
class _PatternsMixin(object):

    @classmethod
    def _key(cls, label, manager):
        try:
            # lazily load key
            key = cls.key
            if isinstance(key, strings):
                key = lazyimport(key)
            # register class key

        except AttributeError:
            key = manager.key(ANamespace, label)

    
class _PatternMixin(_Filter):

    @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)


@appifies(ANamespace)
class Branch(_PatternMixin):

    '''branch configuration'''

    @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(ANamespace)
class Namespace(_PatternMixin):

    '''configuration namespace'''

    @classmethod
    def build(cls, manager):
        '''gather namespace configuration'''
        label = selfname(cls)
        cls._key(label, manager)
        m, n = manager.set, partial(manager.keyed, ANamespace)
        t = lambda k, v: v.build(manager) if n(v) else m(v, k, label)
        exhaustmap(vars(cls), t, cls._filter)

def localize(self, thing, *args, **kw):
    '''
    local settings from thing and its base classes plus any custom settings

    @param thing: some thing with local settings
    '''
    return (twoq([type.mro(getcls(thing)), [thing]]).smash().pick('Meta')
    .tap(lambda x: not x[0].startswith('__')).members()
    .reup().wrap(stuf).map().invoke('update', *args, **kw).value())


def lazyimport(path, attribute=None):
    '''
    deferred module loader

    @param path: something to load
    @param attribute: attribute on loaded module to return
    '''
    if isinstance(path, strings):
        try:
            dot = path.rindex('.')
            # import module
            path = getattr(import_module(path[:dot]), path[dot + 1:])
        # If nothing but module name, import the module
        except (AttributeError, ValueError):
            path = import_module(path)
        if attribute:
            path = getattr(path, attribute)
    return path


class CheckName(object):

    '''ensures string is legal Python name'''

    # Illegal characters for Python names
    ic = '()[]{}@,:`=;+*/%&|^><\'"#\\$?!~'

    def __call__(self, name):
        '''
        ensures string is legal python name

        @param name: name to check
        '''
        # Remove characters that are illegal in a Python name
        name = name.strip().lower().replace('-', '_').replace(
            '.', '_'
        ).replace(' ', '_')
        name = ''.join(i for i in name if i not in self.ic)
        # Add _ if value is a Python keyword
        return name + '_' if iskeyword(name) else name


checkname = CheckName()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.