246 lines
8.9 KiB
Plaintext
246 lines
8.9 KiB
Plaintext
|
Metadata-Version: 2.1
|
||
|
Name: asgiref
|
||
|
Version: 3.5.2
|
||
|
Summary: ASGI specs, helper code, and adapters
|
||
|
Home-page: https://github.com/django/asgiref/
|
||
|
Author: Django Software Foundation
|
||
|
Author-email: foundation@djangoproject.com
|
||
|
License: BSD
|
||
|
Project-URL: Documentation, https://asgi.readthedocs.io/
|
||
|
Project-URL: Further Documentation, https://docs.djangoproject.com/en/stable/topics/async/#async-adapter-functions
|
||
|
Project-URL: Changelog, https://github.com/django/asgiref/blob/master/CHANGELOG.txt
|
||
|
Classifier: Development Status :: 5 - Production/Stable
|
||
|
Classifier: Environment :: Web Environment
|
||
|
Classifier: Intended Audience :: Developers
|
||
|
Classifier: License :: OSI Approved :: BSD License
|
||
|
Classifier: Operating System :: OS Independent
|
||
|
Classifier: Programming Language :: Python
|
||
|
Classifier: Programming Language :: Python :: 3
|
||
|
Classifier: Programming Language :: Python :: 3 :: Only
|
||
|
Classifier: Programming Language :: Python :: 3.7
|
||
|
Classifier: Programming Language :: Python :: 3.8
|
||
|
Classifier: Programming Language :: Python :: 3.9
|
||
|
Classifier: Programming Language :: Python :: 3.10
|
||
|
Classifier: Topic :: Internet :: WWW/HTTP
|
||
|
Requires-Python: >=3.7
|
||
|
License-File: LICENSE
|
||
|
Requires-Dist: typing-extensions ; python_version < "3.8"
|
||
|
Provides-Extra: tests
|
||
|
Requires-Dist: pytest ; extra == 'tests'
|
||
|
Requires-Dist: pytest-asyncio ; extra == 'tests'
|
||
|
Requires-Dist: mypy (>=0.800) ; extra == 'tests'
|
||
|
|
||
|
asgiref
|
||
|
=======
|
||
|
|
||
|
.. image:: https://api.travis-ci.org/django/asgiref.svg
|
||
|
:target: https://travis-ci.org/django/asgiref
|
||
|
|
||
|
.. image:: https://img.shields.io/pypi/v/asgiref.svg
|
||
|
:target: https://pypi.python.org/pypi/asgiref
|
||
|
|
||
|
ASGI is a standard for Python asynchronous web apps and servers to communicate
|
||
|
with each other, and positioned as an asynchronous successor to WSGI. You can
|
||
|
read more at https://asgi.readthedocs.io/en/latest/
|
||
|
|
||
|
This package includes ASGI base libraries, such as:
|
||
|
|
||
|
* Sync-to-async and async-to-sync function wrappers, ``asgiref.sync``
|
||
|
* Server base classes, ``asgiref.server``
|
||
|
* A WSGI-to-ASGI adapter, in ``asgiref.wsgi``
|
||
|
|
||
|
|
||
|
Function wrappers
|
||
|
-----------------
|
||
|
|
||
|
These allow you to wrap or decorate async or sync functions to call them from
|
||
|
the other style (so you can call async functions from a synchronous thread,
|
||
|
or vice-versa).
|
||
|
|
||
|
In particular:
|
||
|
|
||
|
* AsyncToSync lets a synchronous subthread stop and wait while the async
|
||
|
function is called on the main thread's event loop, and then control is
|
||
|
returned to the thread when the async function is finished.
|
||
|
|
||
|
* SyncToAsync lets async code call a synchronous function, which is run in
|
||
|
a threadpool and control returned to the async coroutine when the synchronous
|
||
|
function completes.
|
||
|
|
||
|
The idea is to make it easier to call synchronous APIs from async code and
|
||
|
asynchronous APIs from synchronous code so it's easier to transition code from
|
||
|
one style to the other. In the case of Channels, we wrap the (synchronous)
|
||
|
Django view system with SyncToAsync to allow it to run inside the (asynchronous)
|
||
|
ASGI server.
|
||
|
|
||
|
Note that exactly what threads things run in is very specific, and aimed to
|
||
|
keep maximum compatibility with old synchronous code. See
|
||
|
"Synchronous code & Threads" below for a full explanation. By default,
|
||
|
``sync_to_async`` will run all synchronous code in the program in the same
|
||
|
thread for safety reasons; you can disable this for more performance with
|
||
|
``@sync_to_async(thread_sensitive=False)``, but make sure that your code does
|
||
|
not rely on anything bound to threads (like database connections) when you do.
|
||
|
|
||
|
|
||
|
Threadlocal replacement
|
||
|
-----------------------
|
||
|
|
||
|
This is a drop-in replacement for ``threading.local`` that works with both
|
||
|
threads and asyncio Tasks. Even better, it will proxy values through from a
|
||
|
task-local context to a thread-local context when you use ``sync_to_async``
|
||
|
to run things in a threadpool, and vice-versa for ``async_to_sync``.
|
||
|
|
||
|
If you instead want true thread- and task-safety, you can set
|
||
|
``thread_critical`` on the Local object to ensure this instead.
|
||
|
|
||
|
|
||
|
Server base classes
|
||
|
-------------------
|
||
|
|
||
|
Includes a ``StatelessServer`` class which provides all the hard work of
|
||
|
writing a stateless server (as in, does not handle direct incoming sockets
|
||
|
but instead consumes external streams or sockets to work out what is happening).
|
||
|
|
||
|
An example of such a server would be a chatbot server that connects out to
|
||
|
a central chat server and provides a "connection scope" per user chatting to
|
||
|
it. There's only one actual connection, but the server has to separate things
|
||
|
into several scopes for easier writing of the code.
|
||
|
|
||
|
You can see an example of this being used in `frequensgi <https://github.com/andrewgodwin/frequensgi>`_.
|
||
|
|
||
|
|
||
|
WSGI-to-ASGI adapter
|
||
|
--------------------
|
||
|
|
||
|
Allows you to wrap a WSGI application so it appears as a valid ASGI application.
|
||
|
|
||
|
Simply wrap it around your WSGI application like so::
|
||
|
|
||
|
asgi_application = WsgiToAsgi(wsgi_application)
|
||
|
|
||
|
The WSGI application will be run in a synchronous threadpool, and the wrapped
|
||
|
ASGI application will be one that accepts ``http`` class messages.
|
||
|
|
||
|
Please note that not all extended features of WSGI may be supported (such as
|
||
|
file handles for incoming POST bodies).
|
||
|
|
||
|
|
||
|
Dependencies
|
||
|
------------
|
||
|
|
||
|
``asgiref`` requires Python 3.7 or higher.
|
||
|
|
||
|
|
||
|
Contributing
|
||
|
------------
|
||
|
|
||
|
Please refer to the
|
||
|
`main Channels contributing docs <https://github.com/django/channels/blob/master/CONTRIBUTING.rst>`_.
|
||
|
|
||
|
|
||
|
Testing
|
||
|
'''''''
|
||
|
|
||
|
To run tests, make sure you have installed the ``tests`` extra with the package::
|
||
|
|
||
|
cd asgiref/
|
||
|
pip install -e .[tests]
|
||
|
pytest
|
||
|
|
||
|
|
||
|
Building the documentation
|
||
|
''''''''''''''''''''''''''
|
||
|
|
||
|
The documentation uses `Sphinx <http://www.sphinx-doc.org>`_::
|
||
|
|
||
|
cd asgiref/docs/
|
||
|
pip install sphinx
|
||
|
|
||
|
To build the docs, you can use the default tools::
|
||
|
|
||
|
sphinx-build -b html . _build/html # or `make html`, if you've got make set up
|
||
|
cd _build/html
|
||
|
python -m http.server
|
||
|
|
||
|
...or you can use ``sphinx-autobuild`` to run a server and rebuild/reload
|
||
|
your documentation changes automatically::
|
||
|
|
||
|
pip install sphinx-autobuild
|
||
|
sphinx-autobuild . _build/html
|
||
|
|
||
|
|
||
|
Releasing
|
||
|
'''''''''
|
||
|
|
||
|
To release, first add details to CHANGELOG.txt and update the version number in ``asgiref/__init__.py``.
|
||
|
|
||
|
Then, build and push the packages::
|
||
|
|
||
|
python -m build
|
||
|
twine upload dist/*
|
||
|
rm -r build/ dist/
|
||
|
|
||
|
|
||
|
Implementation Details
|
||
|
----------------------
|
||
|
|
||
|
Synchronous code & threads
|
||
|
''''''''''''''''''''''''''
|
||
|
|
||
|
The ``asgiref.sync`` module provides two wrappers that let you go between
|
||
|
asynchronous and synchronous code at will, while taking care of the rough edges
|
||
|
for you.
|
||
|
|
||
|
Unfortunately, the rough edges are numerous, and the code has to work especially
|
||
|
hard to keep things in the same thread as much as possible. Notably, the
|
||
|
restrictions we are working with are:
|
||
|
|
||
|
* All synchronous code called through ``SyncToAsync`` and marked with
|
||
|
``thread_sensitive`` should run in the same thread as each other (and if the
|
||
|
outer layer of the program is synchronous, the main thread)
|
||
|
|
||
|
* If a thread already has a running async loop, ``AsyncToSync`` can't run things
|
||
|
on that loop if it's blocked on synchronous code that is above you in the
|
||
|
call stack.
|
||
|
|
||
|
The first compromise you get to might be that ``thread_sensitive`` code should
|
||
|
just run in the same thread and not spawn in a sub-thread, fulfilling the first
|
||
|
restriction, but that immediately runs you into the second restriction.
|
||
|
|
||
|
The only real solution is to essentially have a variant of ThreadPoolExecutor
|
||
|
that executes any ``thread_sensitive`` code on the outermost synchronous
|
||
|
thread - either the main thread, or a single spawned subthread.
|
||
|
|
||
|
This means you now have two basic states:
|
||
|
|
||
|
* If the outermost layer of your program is synchronous, then all async code
|
||
|
run through ``AsyncToSync`` will run in a per-call event loop in arbitrary
|
||
|
sub-threads, while all ``thread_sensitive`` code will run in the main thread.
|
||
|
|
||
|
* If the outermost layer of your program is asynchronous, then all async code
|
||
|
runs on the main thread's event loop, and all ``thread_sensitive`` synchronous
|
||
|
code will run in a single shared sub-thread.
|
||
|
|
||
|
Crucially, this means that in both cases there is a thread which is a shared
|
||
|
resource that all ``thread_sensitive`` code must run on, and there is a chance
|
||
|
that this thread is currently blocked on its own ``AsyncToSync`` call. Thus,
|
||
|
``AsyncToSync`` needs to act as an executor for thread code while it's blocking.
|
||
|
|
||
|
The ``CurrentThreadExecutor`` class provides this functionality; rather than
|
||
|
simply waiting on a Future, you can call its ``run_until_future`` method and
|
||
|
it will run submitted code until that Future is done. This means that code
|
||
|
inside the call can then run code on your thread.
|
||
|
|
||
|
|
||
|
Maintenance and Security
|
||
|
------------------------
|
||
|
|
||
|
To report security issues, please contact security@djangoproject.com. For GPG
|
||
|
signatures and more security process information, see
|
||
|
https://docs.djangoproject.com/en/dev/internals/security/.
|
||
|
|
||
|
To report bugs or request new features, please open a new GitHub issue.
|
||
|
|
||
|
This repository is part of the Channels project. For the shepherd and maintenance team, please see the
|
||
|
`main Channels readme <https://github.com/django/channels/blob/master/README.rst>`_.
|