Skip to content

Instantly share code, notes, and snippets.

@styoe
Created March 23, 2026 10:08
Show Gist options
  • Select an option

  • Save styoe/38d5445cfa482024af533a4079b703c1 to your computer and use it in GitHub Desktop.

Select an option

Save styoe/38d5445cfa482024af533a4079b703c1 to your computer and use it in GitHub Desktop.
Short answer: no—unittest.mock.patch itself is not inherently thread-safe, and there are only limited ways to make things effectively safe depending on what you mean by “thread-safe”.
Why patching is not thread-safe
patch() works by mutating global/module-level state (replacing an attribute with a mock, then restoring it later) .
That creates a few problems in multithreaded scenarios:
Global mutation: While a patch is active, all threads see the patched object.
Timing/race issues: If multiple threads start/stop patches concurrently, they can overwrite or restore each other’s changes unpredictably.
Non-atomic behavior: There’s no built-in locking around patch application/restoration.
So if one thread applies a patch while another thread is executing code that depends on that object, behavior becomes nondeterministic.
Separate issue: mocks themselves are not thread-safe
Even if patching were coordinated, Mock / MagicMock are not thread-safe for recording calls:
Concurrent calls can race when updating mock_calls, call_args, etc.
This can corrupt call history or produce inconsistent assertions.
What can be done?
There’s no single “make patch thread-safe” switch, but you have a few practical strategies:
1. Avoid patching across threads (best practice)
Keep patching strictly inside a single thread (typically the test thread):
with patch("module.func"):
# spawn threads AFTER patch is applied
...
Even then, all threads will see the patched object—so you must ensure that’s intended.
2. Apply patch before threads start
If multiple threads need to see the same patched object:
Start patch
Then start threads
Join threads
Stop patch
This avoids race conditions during patch/unpatch transitions.
3. Use dependency injection instead of patching
This is the cleanest and truly thread-safe approach:
def my_func(dep):
return dep()
# test
my_func(mock_dep)
No global mutation → no thread-safety issues.
4. Use locks around patching (limited usefulness)
You can serialize patch usage:
lock = threading.Lock()
with lock:
with patch("module.func"):
...
This prevents concurrent patch/unpatch, but:
It does NOT isolate threads from each other
It just avoids collisions
5. Use thread-safe mocks when needed
If multiple threads call the same mock:
Use a custom mock with a lock, or
Use ThreadingMock (if available in your environment), which records calls atomically
6. Patch per-thread via design (advanced)
If you truly need isolation:
Avoid globals entirely
Use thread-local storage or pass dependencies explicitly
Mental model
Think of patch like:
“Temporarily monkey-patch a global variable”
That’s fundamentally shared mutable state, which is the opposite of thread-safe design.
Bottom line
❌ patch() is not thread-safe
❌ Mock is not thread-safe for concurrent use
✅ You can manage risk by controlling timing (patch before threads)
✅ The real solution is avoiding global patching entirely (dependency injection)
If you want, I can show a concrete example where patching breaks under threads and how to redesign it safely.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment