Skip to content

Commit 70c80f5

Browse files
committed
First implementation of the context mapping passed to resolve and build.
1 parent 8a7bc18 commit 70c80f5

File tree

10 files changed

+137
-157
lines changed

10 files changed

+137
-157
lines changed

.flake8

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
[flake8]
22
ignore = E501, E402
3+
exclude = docs

pyioc/containers.py

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,7 @@ class InstanceLifetime(Enum):
3434
"""
3535
New instance will be created every time container will be ask for object on given key.
3636
"""
37-
SingletonEager = 1
38-
"""
39-
New instance will be created on registering the callable object to container and the result will be
40-
stored in the container.
41-
"""
42-
SingletonLazy = 2
37+
Singleton = 1
4338
"""
4439
New instance will be created the first time container will be asked for object under given key. Both the callable
4540
and object will be stored in the container.
@@ -111,7 +106,7 @@ def register_object(self, key, obj):
111106
def register_callable(self, key, callable_object, lifetime=InstanceLifetime.NewInstancePerCall):
112107
"""
113108
Registers a callable object that will be used to create a new instance of an object that will be returned upon
114-
calling the get() method.
109+
calling the get_instance() method.
115110
116111
Based on the lifetime parameter, either the callable will be stored, and called whenever object is needed, or
117112
the callable will be called on registering, and the returned object will be stored.
@@ -123,9 +118,7 @@ def register_callable(self, key, callable_object, lifetime=InstanceLifetime.NewI
123118
"""
124119
if lifetime == InstanceLifetime.NewInstancePerCall:
125120
provider = providers.NewInstancesProvider(callable_object)
126-
elif lifetime == InstanceLifetime.SingletonEager:
127-
provider = providers.EagerSingleInstanceProvider(callable_object)
128-
elif lifetime == InstanceLifetime.SingletonLazy:
121+
elif lifetime == InstanceLifetime.Singleton:
129122
provider = providers.LazySingleInstanceProvider(callable_object)
130123
else:
131124
raise TypeError('Unsupported instance lifetime.')
@@ -135,33 +128,31 @@ def register_callable(self, key, callable_object, lifetime=InstanceLifetime.NewI
135128
def register_callable_with_deps(self, key, callable_object, lifetime=InstanceLifetime.NewInstancePerCall):
136129
if lifetime == InstanceLifetime.NewInstancePerCall:
137130
provider = providers.NewInstancesWithDepsProvider(callable_object, self)
138-
elif lifetime == InstanceLifetime.SingletonEager:
139-
provider = providers.EagerSingleInstanceWithDepsProvider(callable_object, self)
140-
elif lifetime == InstanceLifetime.SingletonLazy:
131+
elif lifetime == InstanceLifetime.Singleton:
141132
provider = providers.LazySingleInstanceWithDepsProvider(callable_object, self)
142133
else:
143134
raise TypeError('Unsupported instance lifetime.')
144135

145136
self._register_provider_for_key(key, provider)
146137

147-
def resolve(self, key):
138+
def resolve(self, key, context=None):
148139
"""
149140
Return instance based on what was registered for a given key.
150141
151142
:param key: Key under which the object or callable was registered.
152-
:return: Instance related to that key.
143+
:return: Instance related to that key.
153144
"""
154-
return self._resolve(key)
145+
return self._resolve(key, context)
155146

156-
def build(self, cls):
147+
def build(self, cls, context=None):
157148
"""
158149
Build a new instance of class cls injecting dependencies of an object from objects registered in the container.
159150
160151
:param cls: Class of which object to build.
161152
:return:
162153
"""
163154
provider = providers.NewInstancesWithDepsProvider(cls, self)
164-
return provider.get()
155+
return provider.get_instance(context)
165156

166157
@property
167158
def name(self):
@@ -180,9 +171,17 @@ def get_keys(self):
180171
"""
181172
return self._locator.get_keys()
182173

183-
def _resolve(self, key):
184-
instance_provider = self._locator.get(key)
185-
return instance_provider.get()
174+
def _resolve(self, key, context=None):
175+
if context:
176+
try:
177+
item = context[key]
178+
except KeyError:
179+
pass
180+
else:
181+
return item
182+
183+
instance_provider = self._locator.locate(key)
184+
return instance_provider.get_instance(context)
186185

187186
def _register_provider_for_key(self, id, provider):
188187
self._locator.register(id, provider)
@@ -226,15 +225,25 @@ def get_all_keys(self):
226225

227226
return result
228227

229-
def _resolve(self, id):
228+
def _resolve(self, id, context=None):
230229
if isinstance(id, str):
231230
instance_id = self._name_resolver.parse(id)
231+
232232
if instance_id.namespace:
233233
container = self._sub_containers[instance_id.namespace]
234-
return container.resolve(instance_id.id)
234+
return container.resolve(instance_id.id, context)
235235
else:
236-
provider = self._locator.get(instance_id.id)
237-
return provider.get()
236+
if context:
237+
try:
238+
item = context[instance_id.id]
239+
except KeyError:
240+
pass
241+
else:
242+
return item
243+
244+
provider = self._locator.locate(instance_id.id)
245+
return provider.get_instance(context)
246+
238247
else:
239-
provider = self._locator.get(id)
240-
return provider.get()
248+
provider = self._locator.locate(id)
249+
return provider.get_instance(context)

pyioc/locators.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# coding=utf-8
22
"""
3-
Module containing implementation of the service locator pattern.
3+
Module containing implementation of the service locator.
44
"""
55
from __future__ import absolute_import
66

@@ -23,6 +23,28 @@ def generate_key(self, obj):
2323
return type(obj).__name__
2424

2525

26+
class UnregisteredKeyError(KeyError):
27+
def __init__(self, key):
28+
self._key = key
29+
30+
def __str__(self):
31+
return 'There is no object registered for the given "%s" key' % self._key
32+
33+
def __unicode__(self):
34+
return self.__str__()
35+
36+
37+
class KeyAlreadyRegisteredError(KeyError):
38+
def __init__(self, key):
39+
self._key = key
40+
41+
def __str__(self):
42+
return 'There is already an object registered for the "%s" key' % self._key
43+
44+
def __unicode__(self):
45+
return self.__str__()
46+
47+
2648
@six.add_metaclass(abc.ABCMeta)
2749
class LocatorBase(object):
2850
"""
@@ -34,7 +56,7 @@ def register(self, key, obj):
3456
pass
3557

3658
@abc.abstractmethod
37-
def get(self, key):
59+
def locate(self, key):
3860
pass
3961

4062
@abc.abstractmethod
@@ -55,27 +77,27 @@ def __init__(self):
5577

5678
def register(self, key, obj):
5779
"""
58-
Register object in locator for a specified key.
80+
Register object in locator under a specified key.
5981
6082
:param key: Key under which object will be registered.
6183
:param obj: Object to register.
6284
"""
6385
if key in self._objects:
64-
raise KeyError('There is already an object registered for the "%s" key' % key)
86+
raise KeyAlreadyRegisteredError(key)
6587

6688
self._set_instance(key, obj)
6789

68-
def get(self, key):
90+
def locate(self, key):
6991
"""
70-
Gets the object for a given key.
92+
Returns the object registered for a given key.
7193
7294
:param key: Key under which object was registered.
7395
:return: Object registered under the given key.
7496
"""
7597
try:
7698
instance = self._get_instance(key)
7799
except KeyError:
78-
raise KeyError('There is no object registered for the given"%s" key' % key)
100+
raise UnregisteredKeyError(key)
79101

80102
return instance
81103

@@ -101,7 +123,11 @@ def is_key_registered(self, key):
101123
:param key: Key to look for.
102124
:return: True if there is a key in the locator, otherwise False.
103125
"""
104-
return key in self._objects
126+
try:
127+
self._objects[key]
128+
except KeyError:
129+
return False
130+
return True
105131

106132
def get_keys(self):
107133
return list(self._objects.keys())

pyioc/providers.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ def validate_if_callable_without_args(obj):
4949
@six.add_metaclass(abc.ABCMeta)
5050
class ProviderBase(object):
5151
@abc.abstractmethod
52-
def get(self):
52+
def get_instance(self, context=None):
5353
pass
5454

5555

5656
class ObjectProvider(ProviderBase):
5757
def __init__(self, obj):
5858
self._obj = obj
5959

60-
def get(self):
60+
def get_instance(self, context=None):
6161
return self._obj
6262

6363

@@ -66,7 +66,7 @@ def __init__(self, callable_object):
6666
validate_if_callable_without_args(callable_object)
6767
self._callable_object = callable_object
6868

69-
def get(self):
69+
def get_instance(self, context=None):
7070
return self._callable_object()
7171

7272

@@ -76,7 +76,7 @@ def __init__(self, callable_object):
7676
self._instance = None
7777
self._callable_object = callable_object
7878

79-
def get(self):
79+
def get_instance(self, context=None):
8080
if not self._instance:
8181
self._instance = self._callable_object()
8282
return self._instance
@@ -87,7 +87,7 @@ def __init__(self, callable_object):
8787
validate_if_callable_without_args(callable_object)
8888
self._instance = callable_object()
8989

90-
def get(self):
90+
def get_instance(self, context=None):
9191
return self._instance
9292

9393

@@ -99,10 +99,10 @@ def __init__(self, callable_object, container):
9999
self._callable_object = callable_object
100100
self._container = container
101101

102-
def get(self):
103-
return self._build_object()
102+
def get_instance(self, context=None):
103+
return self._build_object(context)
104104

105-
def _build_object(self):
105+
def _build_object(self, context):
106106
if inspect.isclass(self._callable_object):
107107
if _check_if_init_implemented(self._callable_object):
108108
args = inspect.getargspec(self._callable_object.__init__).args
@@ -116,29 +116,20 @@ def _build_object(self):
116116
if arg == 'self':
117117
continue
118118

119-
new_args.append(self._container.resolve(arg))
119+
new_args.append(self._container.resolve(arg, context))
120120

121121
if new_args:
122122
return self._callable_object(*new_args)
123123

124124
return self._callable_object()
125125

126126

127-
class EagerSingleInstanceWithDepsProvider(NewInstancesWithDepsProvider):
128-
def __init__(self, callable_object, container):
129-
super(EagerSingleInstanceWithDepsProvider, self).__init__(callable_object, container)
130-
self._instance = self._build_object()
131-
132-
def get(self):
133-
return self._instance
134-
135-
136127
class LazySingleInstanceWithDepsProvider(NewInstancesWithDepsProvider):
137128
def __init__(self, callable_object, container):
138129
super(LazySingleInstanceWithDepsProvider, self).__init__(callable_object, container)
139130
self._instance = None
140131

141-
def get(self):
132+
def get_instance(self, context=None):
142133
if not self._instance:
143-
self._instance = self._build_object()
134+
self._instance = self._build_object(context)
144135
return self._instance

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
setup(
55
name='pyioc',
6-
version='0.2.1',
6+
version='0.3.0',
77
packages=['pyioc'],
88
url='https://github.com/MrUPGrade/pyioc',
99
license='MIT',

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def mock_container():
3636
:return: mocked locator with testclass1 and testclass2 registered
3737
"""
3838

39-
def side_efect(name):
39+
def side_efect(name, context=None):
4040
type_dict = {
4141
TEST_CLASS_1_NAME: TEST_CLASS_1_INSTANCE,
4242
TEST_CLASS_3_NAME: TEST_CLASS_3_INSTANCE

tests/functional_tests/test_containers.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ def get_container(cls):
1313
def test_registering_singleton(self):
1414
locator = ObjectLocator()
1515
container_class = self.get_container()
16-
container = container_class()
17-
container._locator = locator
18-
container.register_callable(TEST_CLASS_1_NAME, TestClass1, lifetime=InstanceLifetime.SingletonLazy)
16+
container = container_class(locator=locator)
17+
container.register_callable(TEST_CLASS_1_NAME, TestClass1, lifetime=InstanceLifetime.Singleton)
1918

2019
ret1 = locator.is_key_registered(TEST_CLASS_1_NAME)
2120
assert ret1 is True
@@ -24,13 +23,12 @@ def test_registering_singleton(self):
2423
ret3 = container.resolve(TEST_CLASS_1_NAME)
2524

2625
assert isinstance(ret2, TestClass1)
27-
assert id(ret2) == id(ret3)
26+
assert ret2 is ret3
2827

2928
def test_registering_class(self):
3029
locator = ObjectLocator()
3130
container_class = self.get_container()
32-
container = container_class()
33-
container._locator = locator
31+
container = container_class(locator=locator)
3432
container.register_callable(TEST_CLASS_1_NAME, TestClass1)
3533

3634
ret1 = locator.is_key_registered(TEST_CLASS_1_NAME)
@@ -40,13 +38,11 @@ def test_registering_class(self):
4038
ret3 = container.resolve(TEST_CLASS_1_NAME)
4139

4240
assert isinstance(ret2, TestClass1)
43-
assert id(ret2) != id(ret3)
41+
assert ret2 is not ret3
4442

4543
def test_registering_function_as_object(self):
46-
locator = ObjectLocator()
4744
container_class = self.get_container()
4845
container = container_class()
49-
container._locator = locator
5046
container.register_object(TEST_FUNC_1_NAME, TestFunc1)
5147

5248
ret1 = container.resolve(TEST_FUNC_1_NAME)

0 commit comments

Comments
 (0)