Repository: ettoreleandrotognoli/python-cdi

Issue:
InjectionPoint
Create a structure to represent the injection point, that can make the producers more dynamic

Comments:

Patch:
diff --git a/.travis.yml b/.travis.yml
index 1cb1433..8d92ed9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,8 +7,6 @@ python:
   - 3.5
   - 3.6
   - 3.7-dev
-  - pypy
-  #- pypy3
 install:
   - pip install -U pip six setuptools codecov
   - python setup.py install
diff --git a/pycdi/core.py b/pycdi/core.py
index 43b99f7..b479b16 100644
--- a/pycdi/core.py
+++ b/pycdi/core.py
@@ -10,6 +10,14 @@ INJECT_ARGS = '_inject_args'
 INJECT_KWARGS = '_inject_kwargs'
 
 
+class InjectionPoint(object):
+    def __init__(self, member=None, name=None, type=object, context=DEFAULT_CONTEXT):
+        self.context = context
+        self.name = name
+        self.member = member
+        self.type = type
+
+
 class CDIContainer(object):
     def register_instance(self, instance, product_type=None, context=DEFAULT_CONTEXT):
         raise NotImplementedError()
@@ -23,6 +31,9 @@ class CDIContainer(object):
     def sub_container(self, *args, **kwargs):
         raise NotImplementedError()
 
+    def resolve(self, injection_point, **kwargs):
+        raise NotImplementedError()
+
     def produce(self, produce_type, context=DEFAULT_CONTEXT):
         raise NotImplementedError()
 
@@ -95,16 +106,31 @@ class PyCDIContainer(CDIContainer):
                 container.register_instance(instance, context=context)
         return container
 
+    def resolve(self, injection_point, kwargs=None):
+        if kwargs and injection_point.name in kwargs:
+            return kwargs[injection_point.name]
+        producer = self.get_producer(injection_point.type, injection_point.context)
+        return self.call(producer, injection_point=injection_point)
+
     def produce(self, produce_type, context=DEFAULT_CONTEXT):
         producer = self.get_producer(produce_type, context)
         return self.call(producer)
 
+    def _resolve_di_args(self, member, di_args, args):
+        injection_points = map(lambda kv: InjectionPoint(member, kv[0], *kv[1]), zip(range(len(di_args)), di_args))
+        inject_args = list(map(lambda ij: self.resolve(ij), injection_points)) + list(args)
+        return inject_args
+
+    def _resolve_di_kwargs(self, member, di_kwargs, kwargs):
+        injection_points = map(lambda kv: InjectionPoint(member, kv[0], *kv[1]), di_kwargs.items())
+        inject_kwargs = dict(map(lambda ij: (ij.name, self.resolve(ij, kwargs)), injection_points))
+        return inject_kwargs
+
     def call(self, function, *args, **kwargs):
         di_args = get_di_args(function)
         di_kwargs = get_di_kwargs(function)
-        inject_args = list(map(lambda tc: self.produce(tc[0], tc[1]), di_args)) + list(args)
-        inject_kwargs = dict(map(lambda kv: (kv[0], self.produce(*kv[1])), di_kwargs.items()))
-        inject_kwargs.update(kwargs)
+        inject_args = self._resolve_di_args(function, di_args, args)
+        inject_kwargs = self._resolve_di_kwargs(function, di_kwargs, kwargs)
         return function(*inject_args, **inject_kwargs)
 
 
diff --git a/setup.py b/setup.py
index acfe9e6..a608c13 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
 # -*- encoding: utf-8 -*-
 __author__ = 'ettore'
-__version__ = (0, 0, 5)
+__version__ = (0, 1, 0)
 
 import os
 


Test patch:
diff --git a/tests/common/test_injection_point.py b/tests/common/test_injection_point.py
new file mode 100644
index 0000000..af2f327
--- /dev/null
+++ b/tests/common/test_injection_point.py
@@ -0,0 +1,50 @@
+# -*- encoding: utf-8 -*-
+import unittest
+from random import random, choice
+
+from pycdi import Producer, Inject
+from pycdi.core import InjectionPoint, PyCDIContainer
+
+
+class InjectionPointTest(unittest.TestCase):
+    float_injection_point = None
+    str_injection_point = None
+
+    def test_injection_points(self):
+        container = PyCDIContainer()
+
+        @Producer(produce_type=float, _container=container)
+        @Inject(injection_point=InjectionPoint)
+        def float_producer(injection_point):
+            self.assertEqual(injection_point.type, float)
+            self.assertIsNotNone(injection_point.member)
+            self.assertIsNotNone(injection_point.name)
+            self.assertIsNotNone(injection_point.context)
+            self.float_injection_point = injection_point
+            return random()
+
+        @Producer(produce_type=str, _container=container)
+        @Inject(injection_point=InjectionPoint)
+        def float_producer(injection_point):
+            self.assertEqual(injection_point.type, str)
+            self.assertIsNotNone(injection_point.member)
+            self.assertIsNotNone(injection_point.name)
+            self.assertIsNotNone(injection_point.context)
+            self.str_injection_point = injection_point
+            letters = [chr(i) for i in range(97, 97 + 26)]
+            return choice(letters)
+
+        @Inject(random_float=float)
+        def receive_float(random_float):
+            self.assertIsNotNone(random_float)
+
+        container.call(receive_float)
+
+        @Inject(random_str=str)
+        def receive_str(random_str):
+            self.assertIsNotNone(random_str)
+
+        container.call(receive_str)
+
+        self.assertIsNotNone(self.float_injection_point)
+        self.assertIsNotNone(self.str_injection_point)
diff --git a/tests/common/test_interface.py b/tests/common/test_interface.py
index d276126..48dbce9 100644
--- a/tests/common/test_interface.py
+++ b/tests/common/test_interface.py
@@ -21,6 +21,8 @@ class InterfaceCDITest(unittest.TestCase):
             container.register_producer(None)
         with self.assertRaises(NotImplementedError):
             container.sub_container()
+        with self.assertRaises(NotImplementedError):
+            container.resolve(None)
 
     def test_decorator(self):
         with self.assertRaises(NotImplementedError):