then/helper.py

Summary

Maintainability
A
1 hr
Test Coverage
import copy
from collections import defaultdict
from typing import Type

from then.configs.components import LoadComponentConfigs
from then.configs.templates import LoadTemplates
from then.exceptions import ThenError, InvalidUsage
from then.params import Params
from then.templates.base import TemplateBase
from then.templates.format import FormatTemplate
from then.utils import flat_list


DEFAULT = 'default'


class UseBase:
    _use = DEFAULT

    def use(self, use_name):
        instance = self.copy()
        instance._use = use_name
        return instance

    def _get_use_name(self, use=None, default_template=True):
        parts = (use or self._use).split('@', 1)
        if len(parts) > 1:
            template_name, component_name = parts
        else:
            template_name, component_name = None, parts[0]
        if default_template:
            template_name = template_name or DEFAULT
        component_name = component_name or DEFAULT
        return template_name, component_name

    def copy(self):
        raise NotImplementedError


class Templates(UseBase):
    default_template_class = FormatTemplate

    def __init__(self, then, *args):
        self.then = then
        self._templates = defaultdict(list)
        for arg in args:
            self._templates[arg.get_template_name()].append(arg)
        self._args = args
        self._render_params = {}

    def render(self, **kwargs):
        templates = self.copy()
        templates._render_params = kwargs
        return templates

    def get_template(self) -> TemplateBase:
        if self._use in self._templates:
            # TODO: use component_name for get the best template using params
            return self._templates[self._use][-1]
        template_name, component_name = self._get_use_name(default_template=False)
        component = None
        if not template_name:
            component = self.then.get_component(component_name)
        if not template_name and component and component._type != 'message':
            return self.default_template_class()
        elif not template_name:
            template_name = component_name
        if template_name in self._templates:
            return self._templates[template_name][-1]
        elif DEFAULT in self._templates:
            return self._templates[DEFAULT][-1]
        return self.default_template_class()

    def copy(self):
        templates = Templates(self.then, *copy.copy(self._args))
        templates._render_params = self._render_params
        templates._use = self._use
        return templates

    def send(self):
        component_name = self._get_use_name()[1]
        params = self.get_template().args(**self._render_params)
        self.then.use(component_name).send(params)


class Then(UseBase):

    def __init__(self, *args):
        if not args:
            raise ThenError('Sets configurations for components in Then class. '
                            'Currently there are no configurations')
        components_list = flat_list(args, (tuple, list, LoadComponentConfigs))
        self.components = {}
        for component in components_list:
            self.components[component.get_use_as()] = component
        self._args = args

    def templates(self, *args):
        args = flat_list(args, (tuple, list, LoadTemplates))
        return Templates(self, *args)

    def copy(self):
        then = Then(*self._args)
        then._use = self._use
        return then

    def get_component(self, use=None):
        use = use or self._use
        if not self.components:
            raise InvalidUsage('There are no configurations. Registers configs using '
                               'Then(<component config>)')
        if use == DEFAULT and len(self.components) > 1:
            raise InvalidUsage('There is more than one configuration available. '
                               'Use the use("<config>") option')
        elif use == DEFAULT:
            return next(iter(self.components.values()))
        template_name, component_name = self._get_use_name()
        if component_name in self.components:
            return self.components[component_name]
        return self.components[use]

    def send(self, params=None):
        self.get_component().send(params)