Skip to content

Commit 3323e76

Browse files
committed
Simplify code leveraging the fact that ContextVar is automatically thread-local
1 parent 16d4ae2 commit 3323e76

File tree

1 file changed

+5
-63
lines changed

1 file changed

+5
-63
lines changed

asgiref/local.py

Lines changed: 5 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def __delattr__(self, key: str) -> None:
3838
raise AttributeError(f"{self!r} object has no attribute {key!r}")
3939

4040

41-
class Local:
41+
def Local(thread_critical: bool = False) -> threading.local | _CVar:
4242
"""Local storage for async tasks.
4343
4444
This is a namespace object (similar to `threading.local`) where data is
@@ -65,65 +65,7 @@ class Local:
6565
6666
Unlike plain `contextvars` objects, this utility is threadsafe.
6767
"""
68-
69-
def __init__(self, thread_critical: bool = False) -> None:
70-
self._thread_critical = thread_critical
71-
self._thread_lock = threading.RLock()
72-
73-
self._storage: "Union[threading.local, _CVar]"
74-
75-
if thread_critical:
76-
# Thread-local storage
77-
self._storage = threading.local()
78-
else:
79-
# Contextvar storage
80-
self._storage = _CVar()
81-
82-
@contextlib.contextmanager
83-
def _lock_storage(self):
84-
# Thread safe access to storage
85-
if self._thread_critical:
86-
try:
87-
# this is a test for are we in a async or sync
88-
# thread - will raise RuntimeError if there is
89-
# no current loop
90-
asyncio.get_running_loop()
91-
except RuntimeError:
92-
# We are in a sync thread, the storage is
93-
# just the plain thread local (i.e, "global within
94-
# this thread" - it doesn't matter where you are
95-
# in a call stack you see the same storage)
96-
yield self._storage
97-
else:
98-
# We are in an async thread - storage is still
99-
# local to this thread, but additionally should
100-
# behave like a context var (is only visible with
101-
# the same async call stack)
102-
103-
# Ensure context exists in the current thread
104-
if not hasattr(self._storage, "cvar"):
105-
self._storage.cvar = _CVar()
106-
107-
# self._storage is a thread local, so the members
108-
# can't be accessed in another thread (we don't
109-
# need any locks)
110-
yield self._storage.cvar
111-
else:
112-
# Lock for thread_critical=False as other threads
113-
# can access the exact same storage object
114-
with self._thread_lock:
115-
yield self._storage
116-
117-
def __getattr__(self, key):
118-
with self._lock_storage() as storage:
119-
return getattr(storage, key)
120-
121-
def __setattr__(self, key, value):
122-
if key in ("_local", "_storage", "_thread_critical", "_thread_lock"):
123-
return super().__setattr__(key, value)
124-
with self._lock_storage() as storage:
125-
setattr(storage, key, value)
126-
127-
def __delattr__(self, key):
128-
with self._lock_storage() as storage:
129-
delattr(storage, key)
68+
if thread_critical:
69+
return threading.local()
70+
else:
71+
return _CVar()

0 commit comments

Comments
 (0)