appspace / appspace /

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

import uuid
import hashlib
from inspect import isclass

from stuf.six import u, strings
from stuf.utils import exhaustmap

from appspace.utils import lazyimport, checkname
from appspace.keys import (
    ALazyLoad, AppStore, InterfaceClass, AApp, StrictAppStore, ANamespace,
    AManager, appifies, keys)

class LazyLoad(object):

    '''lazy import loader'''

    __slots__ = ['_path']

    def __init__(self, path):

        @param path: path to component
        self._path = path

    def __repr__(self):
        return 'lazy import from "{}"'.format(self.path)

    def path(self):
        module = self._path
        app = lazyimport(module[-1]) if isinstance(
            module, tuple
        ) else lazyimport(module)
        return app, next(list(keys(app)))

class RegistryMixin(object):

    def __init__(self, label, key=AApp):

        @param label: label for internal namespace
        @param key: registry key (default: AApp)
        super(RegistryMixin, self).__init__()
        self._key = key
        # root and current label
        self._root = self._current = label
        # register key under namespace
        self.ez_register(ANamespace, label, key)
        # register manager under label
        self.ez_register(AManager, label, self)

    def _lazy(self, thing):
        return LazyLoad(thing) if isinstance(
            thing, (strings, tuple)
        ) else thing

    def create(cls):
        '''create new key'''
        return InterfaceClass(cls.uuid())

    def ez_id(this):
        easy unique identifier for an object

        @param this: an object
        return hashlib.sha1(u(id(this))).digest()

    def ez_lookup(self, key, label):
        streamlined app lookup

        @param key: key to lookup
        @param label: label to lookup
        app = self.lookup1(key, key, label)
        return self.load(
            label, key, app.path
        ) if self.keyed(ALazyLoad, app) else app

    def ez_register(self, key=None, label=None, app=None):
        streamlined app registration

        @param key: key to register
        @param label: label to register
        @param app: app to register
        self.register([key], key, label, app)

    def ez_subscribe(self, key, label, app):
        streamlined app subscription

        @param key: key to extend to
        @param label: label to extend to
        self.subscribe(key, self.key(key, label), app)

    def ez_unregister(self, key, label):
        streamlined app unregistration

        @param key: key to lookup
        @param label: label to lookup
        self.unregister([key], key, label, self.ez_lookup(key, label))

    unkey = ez_unregister

    def ez_unsubscribe(self, key, label):
        streamlined app unsubscription

        @param key: key to lookup
        @param label: label to lookup
        self.unsubscribe(key, self.ez_lookup(key, label))

    def keyed(k=False, v=False):
        check if item has an app key

        @param key: app key
        @param thing: thing to check
            return k.implementedBy(v) if isclass(v) else k.providedBy(v)
        except AttributeError:
            return False

    def key(self, key, label):
        create or fetch key

        @param key: key to register
        @param label: label to register
        this = self.lookup1(key, key, label)
        if this is None:
            this = self.create()
            self.register([key], key, label, this)
        return this

    def load(self, label, key, thing):
        import thing into appspace

        @param label: appspaced thing label
        @param key: appspace key
        @param thing: some thing
        if not self.keyed(ALazyLoad, thing):
            return thing
        app, appkey = thing.path
        key = key if not appkey else appkey
        self.register([key], key, label, app)
        return app

    safename = staticmethod(checkname)

    def uuid():
        '''universal unique identifier'''
        return uuid.uuid4().hex.upper()

    def pack(self, label, call):
        pack things into registry

        @param label: event label
        @param call: some thing
        self.ez_register(self._key, label, self._lazy(call))

    def update(self, labels):
        update event with other registries

        @param labels: eventconf
        exhaustmap(vars(labels), self.pack, lambda x: not x[0].startswith('_'))

class Registry(RegistryMixin, AppStore):

    '''easy registry'''

    __slots__ = ('_current', '_root', '_key')

class StrictRegistry(RegistryMixin, StrictAppStore):

    '''strict registry'''

    __slots__ = ('_current', '_root', '_key')