Make injection and singleton scope thread safe
This commit is contained in:
parent
40fe997913
commit
9cd643964a
|
@ -410,6 +410,14 @@ class instance on the time of method call::
|
||||||
After such call all ``inject``-decorated methods will work just as you'd expect
|
After such call all ``inject``-decorated methods will work just as you'd expect
|
||||||
them to work.
|
them to work.
|
||||||
|
|
||||||
|
Thread safety
|
||||||
|
=============
|
||||||
|
|
||||||
|
The following functions are thread safe:
|
||||||
|
|
||||||
|
* ``Injector.get``
|
||||||
|
* injection provided by ``inject`` decorator (please note, however, that it doesn't say anything about decorated function thread safety)
|
||||||
|
|
||||||
Footnote
|
Footnote
|
||||||
========
|
========
|
||||||
This framework is similar to snake-guice, but aims for simplification.
|
This framework is similar to snake-guice, but aims for simplification.
|
||||||
|
|
13
injector.py
13
injector.py
|
@ -29,6 +29,17 @@ __author__ = 'Alec Thomas <alec@swapoff.org>'
|
||||||
__version__ = '0.5.2'
|
__version__ = '0.5.2'
|
||||||
__version_tag__ = ''
|
__version_tag__ = ''
|
||||||
|
|
||||||
|
def synchronized(lock):
|
||||||
|
def outside_wrapper(function):
|
||||||
|
@functools.wraps(function)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
with lock:
|
||||||
|
return function(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
return outside_wrapper
|
||||||
|
|
||||||
|
lock = threading.RLock()
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
"""Base exception."""
|
"""Base exception."""
|
||||||
|
@ -353,6 +364,7 @@ class SingletonScope(Scope):
|
||||||
def configure(self):
|
def configure(self):
|
||||||
self._context = {}
|
self._context = {}
|
||||||
|
|
||||||
|
@synchronized(lock)
|
||||||
def get(self, key, provider):
|
def get(self, key, provider):
|
||||||
try:
|
try:
|
||||||
return self._context[key]
|
return self._context[key]
|
||||||
|
@ -493,6 +505,7 @@ class Injector(object):
|
||||||
"""
|
"""
|
||||||
instance.__injector__ = self
|
instance.__injector__ = self
|
||||||
|
|
||||||
|
@synchronized(lock)
|
||||||
def args_to_inject(self, function, bindings, owner_key):
|
def args_to_inject(self, function, bindings, owner_key):
|
||||||
"""Inject arguments into a function.
|
"""Inject arguments into a function.
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"""Functional tests for the Pollute dependency injection framework."""
|
"""Functional tests for the Pollute dependency injection framework."""
|
||||||
|
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
from time import sleep
|
||||||
import abc
|
import abc
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
@ -664,3 +665,44 @@ def test_assisted_builder_works_when_injected():
|
||||||
injector = Injector()
|
injector = Injector()
|
||||||
x = injector.get(X)
|
x = injector.get(X)
|
||||||
assert ((x.obj.a, x.obj.b) == (str(), 234))
|
assert ((x.obj.a, x.obj.b) == (str(), 234))
|
||||||
|
|
||||||
|
class TestThreadSafety(object):
|
||||||
|
def setup(self):
|
||||||
|
def configure(binder):
|
||||||
|
binder.bind(str, to=lambda: sleep(1) and 'this is str')
|
||||||
|
|
||||||
|
class XXX(object):
|
||||||
|
@inject(s=str)
|
||||||
|
def __init__(self, s):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.injector = Injector(configure)
|
||||||
|
self.cls = XXX
|
||||||
|
|
||||||
|
def gather_results(self, count):
|
||||||
|
objects = []
|
||||||
|
lock = threading.Lock()
|
||||||
|
|
||||||
|
def target():
|
||||||
|
o = self.injector.get(self.cls)
|
||||||
|
with lock:
|
||||||
|
objects.append(o)
|
||||||
|
|
||||||
|
threads = [threading.Thread(target=target) for i in range(2)]
|
||||||
|
|
||||||
|
for t in threads:
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
for t in threads:
|
||||||
|
t.join()
|
||||||
|
|
||||||
|
return objects
|
||||||
|
|
||||||
|
def test_injection_is_thread_safe(self):
|
||||||
|
objects = self.gather_results(2)
|
||||||
|
assert (len(objects) == 2)
|
||||||
|
|
||||||
|
def test_singleton_scope_is_thread_safe(self):
|
||||||
|
self.injector.binder.bind(self.cls, scope=singleton)
|
||||||
|
a, b = self.gather_results(2)
|
||||||
|
assert (a is b)
|
||||||
|
|
Loading…
Reference in New Issue