In this post I will show you how to pass data to places where you cannot pass it as arguments.
As an example, let’s say we have a function that we cannot change which calls
our code (
MyAction.action). Because we cannot change
function_i_cannot_change, we cannot make it pass
dynamic_value through to
our code. Also note that we cannot pass something else than a number as the
arg parameter because of its implementation. (Yes, in this simple example
there would be alternative solutions, see note at the end.)
We can solve this by passing the data through a global variable.
Yes, you’re right… Using globals is bad. It makes your code hard to follow and breaks thread-safety.
To address the threading issue, we can use a
This solves threading issues, but we still have to know the thread local in the
consuming code and we have to trust all consumers to be well-behaved concerning
what they set on
DYNAMIC_TL.value and that they reset it to the original
Let’s use a context manager to make the usage more safe and clear for the consumers.
I also added
get_dynamic_value to hide the usage of the thread local from the
This sounds a bit crazy, does anybody really do this?
Yes. Have you ever wondered how
Can I really use this?
Well, I would say: Yes, if there is no simpler alternative. The pattern comes with the general problem of global variables:
a) They are available everywhere and thus people will starting using them in more and more places. This leads to unintended coupling.
b) The dataflow is not obvious. You will get surprising and hard to debug errors.
For me, this means that I only use the pattern in a very explicit way that makes sure it will be used responsibly. I would move the thread local and it’s accessors to their own module and make the thread local itself private.
I often find myself using this technique in tests, when I need to get 3rd party code to cooperate. In production code I would try very hard to find an alternative.
Note: Alternative solutions for the simple example
The simple example has a couple pretty simple, preferable solutions,
E.g. there is partial application of functions:
Or a function that returns a function:
Or a class: