asgiref 3.9.2__py3-none-any.whl → 3.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- asgiref/__init__.py +1 -1
- asgiref/sync.py +65 -2
- {asgiref-3.9.2.dist-info → asgiref-3.10.0.dist-info}/METADATA +1 -1
- {asgiref-3.9.2.dist-info → asgiref-3.10.0.dist-info}/RECORD +7 -7
- {asgiref-3.9.2.dist-info → asgiref-3.10.0.dist-info}/WHEEL +0 -0
- {asgiref-3.9.2.dist-info → asgiref-3.10.0.dist-info}/licenses/LICENSE +0 -0
- {asgiref-3.9.2.dist-info → asgiref-3.10.0.dist-info}/top_level.txt +0 -0
asgiref/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "3.
|
|
1
|
+
__version__ = "3.10.0"
|
asgiref/sync.py
CHANGED
|
@@ -69,6 +69,45 @@ else:
|
|
|
69
69
|
return func
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
class AsyncSingleThreadContext:
|
|
73
|
+
"""Context manager to run async code inside the same thread.
|
|
74
|
+
|
|
75
|
+
Normally, AsyncToSync functions run either inside a separate ThreadPoolExecutor or
|
|
76
|
+
the main event loop if it exists. This context manager ensures that all AsyncToSync
|
|
77
|
+
functions execute within the same thread.
|
|
78
|
+
|
|
79
|
+
This context manager is re-entrant, so only the outer-most call to
|
|
80
|
+
AsyncSingleThreadContext will set the context.
|
|
81
|
+
|
|
82
|
+
Usage:
|
|
83
|
+
|
|
84
|
+
>>> import asyncio
|
|
85
|
+
>>> with AsyncSingleThreadContext():
|
|
86
|
+
... async_to_sync(asyncio.sleep(1))()
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self):
|
|
90
|
+
self.token = None
|
|
91
|
+
|
|
92
|
+
def __enter__(self):
|
|
93
|
+
try:
|
|
94
|
+
AsyncToSync.async_single_thread_context.get()
|
|
95
|
+
except LookupError:
|
|
96
|
+
self.token = AsyncToSync.async_single_thread_context.set(self)
|
|
97
|
+
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
def __exit__(self, exc, value, tb):
|
|
101
|
+
if not self.token:
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
executor = AsyncToSync.context_to_thread_executor.pop(self, None)
|
|
105
|
+
if executor:
|
|
106
|
+
executor.shutdown()
|
|
107
|
+
|
|
108
|
+
AsyncToSync.async_single_thread_context.reset(self.token)
|
|
109
|
+
|
|
110
|
+
|
|
72
111
|
class ThreadSensitiveContext:
|
|
73
112
|
"""Async context manager to manage context for thread sensitive mode
|
|
74
113
|
|
|
@@ -131,6 +170,14 @@ class AsyncToSync(Generic[_P, _R]):
|
|
|
131
170
|
# inside create_task, we'll look it up here from the running event loop.
|
|
132
171
|
loop_thread_executors: "Dict[asyncio.AbstractEventLoop, CurrentThreadExecutor]" = {}
|
|
133
172
|
|
|
173
|
+
async_single_thread_context: "contextvars.ContextVar[AsyncSingleThreadContext]" = (
|
|
174
|
+
contextvars.ContextVar("async_single_thread_context")
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
context_to_thread_executor: "weakref.WeakKeyDictionary[AsyncSingleThreadContext, ThreadPoolExecutor]" = (
|
|
178
|
+
weakref.WeakKeyDictionary()
|
|
179
|
+
)
|
|
180
|
+
|
|
134
181
|
def __init__(
|
|
135
182
|
self,
|
|
136
183
|
awaitable: Union[
|
|
@@ -246,8 +293,24 @@ class AsyncToSync(Generic[_P, _R]):
|
|
|
246
293
|
running_in_main_event_loop = False
|
|
247
294
|
|
|
248
295
|
if not running_in_main_event_loop:
|
|
249
|
-
|
|
250
|
-
|
|
296
|
+
loop_executor = None
|
|
297
|
+
|
|
298
|
+
if self.async_single_thread_context.get(None):
|
|
299
|
+
single_thread_context = self.async_single_thread_context.get()
|
|
300
|
+
|
|
301
|
+
if single_thread_context in self.context_to_thread_executor:
|
|
302
|
+
loop_executor = self.context_to_thread_executor[
|
|
303
|
+
single_thread_context
|
|
304
|
+
]
|
|
305
|
+
else:
|
|
306
|
+
loop_executor = ThreadPoolExecutor(max_workers=1)
|
|
307
|
+
self.context_to_thread_executor[
|
|
308
|
+
single_thread_context
|
|
309
|
+
] = loop_executor
|
|
310
|
+
else:
|
|
311
|
+
# Make our own event loop - in a new thread - and run inside that.
|
|
312
|
+
loop_executor = ThreadPoolExecutor(max_workers=1)
|
|
313
|
+
|
|
251
314
|
loop_future = loop_executor.submit(asyncio.run, new_loop_wrap())
|
|
252
315
|
# Run the CurrentThreadExecutor until the future is done.
|
|
253
316
|
current_executor.run_until_future(loop_future)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
asgiref/__init__.py,sha256=
|
|
1
|
+
asgiref/__init__.py,sha256=iKJAvc5i0UTDDSSefTGL0Tq-kWQ4S3OJJgvyaQfQNF8,23
|
|
2
2
|
asgiref/compatibility.py,sha256=DhY1SOpOvOw0Y1lSEjCqg-znRUQKecG3LTaV48MZi68,1606
|
|
3
3
|
asgiref/current_thread_executor.py,sha256=42CU1VODLTk-_PYise-cP1XgyAvI5Djc8f97owFzdrs,4157
|
|
4
4
|
asgiref/local.py,sha256=ZZeWWIXptVU4GbNApMMWQ-skuglvodcQA5WpzJDMxh4,4912
|
|
5
5
|
asgiref/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
asgiref/server.py,sha256=3A68169Nuh2sTY_2O5JzRd_opKObWvvrEFcrXssq3kA,6311
|
|
7
|
-
asgiref/sync.py,sha256=
|
|
7
|
+
asgiref/sync.py,sha256=CEKxFyePiksUoA7MronOKaF6mmNQxUYZjXlfJZXEQCM,22551
|
|
8
8
|
asgiref/testing.py,sha256=U5wcs_-ZYTO5SIGfl80EqRAGv_T8BHrAhvAKRuuztT4,4421
|
|
9
9
|
asgiref/timeout.py,sha256=LtGL-xQpG8JHprdsEUCMErJ0kNWj4qwWZhEHJ3iKu4s,3627
|
|
10
10
|
asgiref/typing.py,sha256=Zi72AZlOyF1C7N14LLZnpAdfUH4ljoBqFdQo_bBKMq0,6290
|
|
11
11
|
asgiref/wsgi.py,sha256=J8OAgirfsYHZmxxqIGfFiZ43uq1qKKv2xGMkRISNIo4,6742
|
|
12
|
-
asgiref-3.
|
|
13
|
-
asgiref-3.
|
|
14
|
-
asgiref-3.
|
|
15
|
-
asgiref-3.
|
|
16
|
-
asgiref-3.
|
|
12
|
+
asgiref-3.10.0.dist-info/licenses/LICENSE,sha256=uEZBXRtRTpwd_xSiLeuQbXlLxUbKYSn5UKGM0JHipmk,1552
|
|
13
|
+
asgiref-3.10.0.dist-info/METADATA,sha256=TlcKOCn3FwSCGD62jZkbckPRh-RjAhkCLLDnfmDZTyA,9287
|
|
14
|
+
asgiref-3.10.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
asgiref-3.10.0.dist-info/top_level.txt,sha256=bokQjCzwwERhdBiPdvYEZa4cHxT4NCeAffQNUqJ8ssg,8
|
|
16
|
+
asgiref-3.10.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|