joyider/NoJoy_DI

View on GitHub
NoJoy_DI/service.py

Summary

Maintainability
A
2 hrs
Test Coverage
# -*- coding: utf-8 -*-
# NoJoy_DI (c) 2016 by Andre Karlsson<andre.karlsson@protractus.se>
#
# This file is part of NoJoy_DI.
#
#    NoJoy_DI is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Foobar is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
#
# Filename:  by: andrek
# Timesamp: 5/1/16 :: 10:25 PM

import sys
from NoJoy_DI.utils import *
from NoJoy_DI.patterns import *
from functools import wraps
from importlib import import_module

#version hack
if sys.version_info >= (3,3):
    from inspect import signature, Parameter
    signature_empty = Parameter.empty
else:
    from funcsigs import signature
    from funcsigs import _empty as signature_empty


class Service(object):

    _mypattern = DefaultPattern
    _factory = None

    _classification = None
    _classification_getter = None
    _inject_signature = False

    _locked = False

    def __init__(self, mycallable, classification=None):
        super(Service, self).__init__()

        self._input = {}
        self._sets = {}
        self._callers = []
        self._injectors = []
        self._arguments_injectors = []

        self.name = object_name_standard(mycallable)

        if classification:
            self._classification = classification
        else:
            if callable(mycallable):
                self._classification = mycallable
            else:
                self._classification_getter = self._lazy_loader(mycallable)


    def _get_classification(self):
        if self._classification:
            return  self._classification
        if self._classification_getter:
            self._classification = self._classification_getter()
            return self._classification

        raise Exception()

    def _lazy_loader(self, class_hierarchy):
        module, cls = class_hierarchy.split('.', 1)
        @wraps(self._lazy_loader)
        def wrapper(*args, **kwargs):
            return getattr(import_module(module), cls)
        return  wrapper


    def _lazymarker(self, myclone=None, myservice=None, myfunction=None, myvariable=None):
        if myclone is not None:
            return myclone
        elif myservice or myvariable:
            return LazyMarker(service=myservice, function=myfunction, variable=myvariable)

    def _input_maker(self, kwargs):
        types = {}
        for key, value in kwargs.items():
            if key.endswith("__svc"):
                types[key[:-5]] = self._lazymarker(myservice=value)
            elif key.endswith("__param"):
                types[key[:-7]] = self._lazymarker(myvariable=value)
            else:
                types[key] = value
        return types

    @private
    def set_classification(self, value):
        """
        Classify the service callable

        :param value:A callable method/function to be classified as service main callable
        :return:
        """
        self._classification = value

    @private
    def set_factory(self, service=None, function=None, acallable=None):
        """
        Create a factory callable to by assigning a Factory to a Service(Class)
        and set the private factroy accordingly

        >>> di.set(AFactory_Class)
        >>> di.set(A_Class).set_factory(service=AFactory_Class, function="return_class_method")

        :param service: The factory Service(class) it self
        :param function: Function/method to be called in the factory
        :param acallable: A already created LazyMarker
        :return:
        """
        self._factory = self._lazymarker(myclone=acallable, myservice=service, myfunction=function)

    @private
    def input(self, **kwargs):
        """
        Add constructor args for the service

        >>> di.set(AFactory_Class)
        >>> di.set(A_Class).set_factory(service=AFactory_Class, function="return_class_method").input(variable="abc")

        :param kwargs: Input arguments for the Service
        :return:
        """
        self._input.update(self._input_maker(kwargs))

    @private
    def call(self, function, arg=False, **kwargs):
        """
        Call method adds a method call with arguments on an existing Service

        :param function: The callable function/method
        :param arg: If True Argements will be detected using signature (used with set_signature) Default:False
        :param kwargs: Arguments for function/method
        :return:
        """
        if isinstance(arg, bool):
            self._callers.append((arg, function, self._input_maker(kwargs)))
        else:
            raise Exception("Undefined Argument (arg)")


    @private
    def set(self, **kwargs):
        """
        Using setattr to set the values to the instanstiated service

        >>> di.set(A_Class).set(variable="abc")

        :param kwargs: KeyWord arguments for the Service
        :return:
        """
        self._sets.update(self._input_maker(kwargs))

    @private
    def injector(self, service=None, function=None, function_args=None,
                 acallable=None, callable_args=None):
        """
        Injects a Service or callable into self Service

        >>> di.set(AnInjector_Class)
        >>> di.set(A_Class).injector(service=AnInjector, function="a_method", function_args="a_method_with_kwargs")

        :param service: Service to be injected to self
        :param function: method to to called for injected service
        :param function_args: method to to called WITH INPUTS for injected service
        :param acallable: callable to to called for injected service
        :param callable_args: callable to to called WITH INPUTS for injected service
        :return:
        """
        if function or acallable:
            self._injectors.append(self._lazymarker(myclone=acallable, myservice=service, myfunction=function))
        if function_args or callable_args:
            self._arguments_injectors.append(self._lazymarker(myclone=callable_args,
                                                              myservice=service,
                                                              myfunction=function_args))

    @private
    def set_signature(self):
        self._inject_signature = True

    @private
    def set_pattern(self, pattern_cls):
        self._mypattern = pattern_cls