injector icon indicating copy to clipboard operation
injector copied to clipboard

ThreadLocalScope doesn't clear manually set Keys

Open jhorman opened this issue 6 years ago • 3 comments

Manually bound Keys end up caching a value you might expect to be thread local with ThreadLocalScope. The problem is that for manually bound items, a InstanceProvider(to) is created. Then later ThreadLocalScope does

provider = InstanceProvider(provider.get(self.injector))

Where provider.get just returns that to always. I would have expected something fancier to happen such that thread2 would never see the binding.

TEST_KEY = Key('TEST_KEY')

thread1
injector.binder.bind(TEST_KEY, 'test', threadlocal)
assert(injector.get(TEST_KEY) == 'test')

thread2

assert(injector.get(TEST_KEY) == 'test')

https://github.com/alecthomas/injector/blob/master/injector.py#L442 https://github.com/alecthomas/injector/blob/master/injector.py#L608

Not really sure what the best approach would be. I would have thought that the thread local scope sort of owned the binding created when I did

injector.binder.bind(TEST_KEY, 'test', threadlocal)

jhorman avatar Jun 21 '18 19:06 jhorman

I also think ThreadLocalScope could use a clear/reset method, since if you are using thread pooling, you could leak scopes unless they get cleared.

jhorman avatar Jun 21 '18 19:06 jhorman

There's a test case for threadlocal that passes. What's the difference between how the test runs and how you're using it?

Also, I would expect your example to do what it does, right? It's effectively a constant, so it's always going to return the same thing.

alecthomas avatar Jun 21 '18 23:06 alecthomas

I would think that

injector.binder.bind(TEST_KEY, 'test', threadlocal)

Would behave a lot like just setting a thread local variable.

tl_local = threading.local()

# thread1
tl_local.x = 'y'

# thread2
tl_local.x is None

The test is binding a class. That works fine. The problem is when binding a value. InstanceProvider is treating it like a constant vs a thread local value.

My specific use case is I am setting a user object as a key.

injector.binder.bind(AuthenticatedUser, user_instance, threadlocal)

And I need to make sure that other thread/requests don't ever return that user.

jhorman avatar Jun 21 '18 23:06 jhorman