247 lines
9.0 KiB
Plaintext
247 lines
9.0 KiB
Plaintext
Metadata-Version: 2.1
|
|
Name: asgiref
|
|
Version: 3.7.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-3-Clause
|
|
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: Programming Language :: Python :: 3.11
|
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
Requires-Python: >=3.7
|
|
License-File: LICENSE
|
|
Requires-Dist: typing-extensions (>=4) ; python_version < "3.11"
|
|
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>`_.
|