From 87826b354286bd6c269ee9308310c3eb6b7d75cf Mon Sep 17 00:00:00 2001 From: Jakub Stasiak Date: Sun, 12 Feb 2023 13:51:15 +0100 Subject: [PATCH] Fix an Injector.get() thread safety regression (#215) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One of the thread safety tests has been failing on various PyPy versions[1]: _____________ TestThreadSafety.test_singleton_scope_is_thread_safe _____________ Traceback (most recent call last): File "/home/runner/work/injector/injector/injector_test.py", line 863, in test_singleton_scope_is_thread_safe assert a is b AssertionError: assert .XXX object at 0x0000000006b80d08> is .XXX object at 0x0000000006b810c0> SingletonScope's get() was protected with a lock already but Injector.get() wasn't and there are things in there that are totally likely to produce invalid results if executed from multiple contexts concurrently. Rather than try to nail down the exact issue that caused this, figure out why it's been failing only on PyPy etc. I figured it's ok to go for the big hammer here and protect the whole method with the lock. This way we don't have to wonder about the details that can be *really* difficult to figure out. I don't think this is gonna make anything meaningfully slower – ClassProvider and CallableProvider are already locked (indirecty, through Injector.args_to_inject() being synchronized) and I expect these to be involved in vast majority of the injection work. [1] https://github.com/python-injector/injector/actions/runs/4156039132/jobs/7189544034 --- injector/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/injector/__init__.py b/injector/__init__.py index d296d57..8adf6be 100644 --- a/injector/__init__.py +++ b/injector/__init__.py @@ -898,6 +898,7 @@ class Injector: def _log_prefix(self) -> str: return '>' * (len(self._stack) + 1) + ' ' + @synchronized(lock) def get(self, interface: Type[T], scope: Union[ScopeDecorator, Type[Scope], None] = None) -> T: """Get an instance of the given interface.