diff --git a/docs/discussions.md b/docs/discussions.md index b2202a96a..a3f008f73 100644 --- a/docs/discussions.md +++ b/docs/discussions.md @@ -193,11 +193,11 @@ When you define an asynchronous connector, Procrastinate will try to seamlessly give you the right connector for your context. When you call the synchronous API, it will either create a sync connector based on your async connector, or let you use the async connector directly with -`asgiref.sync.async_to_sync`. +`asyncio.run`. ::: For running jobs, support of synchronous task functions is through -`asgiref.sync.sync_to_async`. This means your synchronous function will be +`asyncio.to_thread`. This means your synchronous function will be executed by an asynchronous worker in a thread. Because of the [Global Interpreter Lock][global interpreter lock], you will not benefit from parallelism, but you will still be able to parallelize (thread-safe) I/Os. @@ -290,7 +290,7 @@ Procrastinate: driver. Under the hood, we have factored as much as possible the non-I/O parts of the code, so that the synchronous and asynchronous versions are only separate in the way they handle I/Os. -- For executing a synchronous task: we use `asgiref.sync.sync_to_async` to run the +- For executing a synchronous task: we use `asyncio.to_thread` to run the synchronous code in a thread. - There are a few case where we facilitate calling Procrastinate from synchronous codebases, by providing a synchronous API, where we'll create an diff --git a/docs/howto/advanced/sync_defer.md b/docs/howto/advanced/sync_defer.md index 629cfb4b9..a573dbb63 100644 --- a/docs/howto/advanced/sync_defer.md +++ b/docs/howto/advanced/sync_defer.md @@ -71,7 +71,7 @@ be using a synchronous connector. If you request the synchronous connector after opening the app, you will get the asynchronous connector, with a compatibility layer to make synchronous -operations. This will only work if you call it inside a function decorated -with `asgiref.sync.sync_to_async` (such as inside a sync job). Otherwise, -you will likely get a `RuntimeError`. +operations. This will only work if you call it inside a function that runs +in its own thread (such as inside a sync job). Otherwise, you will likely +get a `RuntimeError`. ::: diff --git a/poetry.lock b/poetry.lock index 1074696b3..28432bb4c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1835,7 +1835,7 @@ type = ["pytest-mypy"] [extras] aiopg = ["aiopg", "psycopg2-binary"] -django = ["django"] +django = ["asgiref", "django"] psycopg2 = ["psycopg2-binary"] sphinx = ["sphinx"] sqlalchemy = ["sqlalchemy"] @@ -1843,4 +1843,4 @@ sqlalchemy = ["sqlalchemy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "8d4350e830593b495e8caa03a3be60037cdcd221d240b26adf75199a2eb02ff1" +content-hash = "3624f62ad4cec7f623dcf8341125f75543d37981010008f738f2c4b437fc549d" diff --git a/procrastinate/utils.py b/procrastinate/utils.py index 4a3422ddb..e58708603 100644 --- a/procrastinate/utils.py +++ b/procrastinate/utils.py @@ -22,7 +22,6 @@ ) import dateutil.parser -from asgiref import sync from procrastinate import exceptions from procrastinate.types import TimeDeltaParams @@ -96,7 +95,11 @@ def async_to_sync(awaitable: Callable[..., Awaitable[T]], *args, **kwargs) -> T: Given a callable returning an awaitable, call the callable, await it synchronously. Returns the result after it's done. """ - return sync.async_to_sync(awaitable)(*args, **kwargs) + + async def wrapper() -> T: + return await awaitable(*args, **kwargs) + + return asyncio.run(wrapper()) async def sync_to_async(func: Callable[..., T], *args, **kwargs) -> T: @@ -104,7 +107,7 @@ async def sync_to_async(func: Callable[..., T], *args, **kwargs) -> T: Given a callable, return a callable that will call the original one in an async context. """ - return await sync.sync_to_async(func, thread_sensitive=False)(*args, **kwargs) + return await asyncio.to_thread(func, *args, **kwargs) def causes(exc: BaseException | None): diff --git a/procrastinate_demos/demo_async/__main__.py b/procrastinate_demos/demo_async/__main__.py index 71d1706e9..87f4fb71f 100644 --- a/procrastinate_demos/demo_async/__main__.py +++ b/procrastinate_demos/demo_async/__main__.py @@ -4,13 +4,9 @@ import json import logging -import asgiref.sync - from . import app as app_module from . import tasks -ainput = asgiref.sync.sync_to_async(input) - async def main(): logging.info("Running app in async context") @@ -22,7 +18,7 @@ async def main(): print("Enter an empty line to quit") print() while True: - response = (await ainput("Your input: ")).strip() + response = (await asyncio.to_thread(input, "Your input: ")).strip() if not response: break command, *args = (response).split(maxsplit=1) diff --git a/pyproject.toml b/pyproject.toml index 63a014496..d87cbe335 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ procrastinate = 'procrastinate.cli:main' python = "^3.9" aiopg = { version = "*", optional = true } anyio = "*" -asgiref = "*" +asgiref = { version = "*", optional = true } attrs = "*" contextlib2 = { version = "*", python = "<3.10" } croniter = "*" @@ -39,7 +39,7 @@ typing-extensions = "*" sphinx = { version = "*", optional = true } [tool.poetry.extras] -django = ["django"] +django = ["asgiref", "django"] sqlalchemy = ["sqlalchemy"] aiopg = ["aiopg", "psycopg2-binary"] psycopg2 = ["psycopg2-binary"] diff --git a/tests/acceptance/test_sync.py b/tests/acceptance/test_sync.py index f74607743..cece47372 100644 --- a/tests/acceptance/test_sync.py +++ b/tests/acceptance/test_sync.py @@ -4,7 +4,6 @@ import time import pytest -from asgiref.sync import sync_to_async import procrastinate from procrastinate.contrib import psycopg2 @@ -70,7 +69,7 @@ def _inner_sum_task_sync(a, b): sum_results.append(a + b) # Only works if the worker runs the sync task in a separate thread - await sync_to_async(_inner_sum_task_sync)(a, b) + await asyncio.to_thread(_inner_sum_task_sync, a, b) asyncio.run(_sum_task_async(a, b)) diff --git a/tests/integration/contrib/aiopg/test_aiopg_connector.py b/tests/integration/contrib/aiopg/test_aiopg_connector.py index b413bdb0b..7c23b5053 100644 --- a/tests/integration/contrib/aiopg/test_aiopg_connector.py +++ b/tests/integration/contrib/aiopg/test_aiopg_connector.py @@ -4,7 +4,6 @@ import functools import json -import asgiref.sync import attr import pytest @@ -101,7 +100,6 @@ async def test_get_sync_connector(aiopg_connector_factory): aiopg_connector = await aiopg_connector_factory(open=False) - @asgiref.sync.sync_to_async def f(): sync_conn = aiopg_connector.get_sync_connector() sync_conn.open() @@ -110,7 +108,7 @@ def f(): finally: sync_conn.close() - await f() + await asyncio.to_thread(f) assert list(result[0].values()) == [1] diff --git a/tests/integration/test_psycopg_connector.py b/tests/integration/test_psycopg_connector.py index 9539e50ef..5cf51aa5c 100644 --- a/tests/integration/test_psycopg_connector.py +++ b/tests/integration/test_psycopg_connector.py @@ -4,7 +4,6 @@ import functools import json -import asgiref.sync import attr import pytest @@ -112,7 +111,6 @@ async def test_wrap_exceptions(psycopg_connector): async def test_execute_query_sync(psycopg_connector): - @asgiref.sync.sync_to_async() def sync(): assert ( psycopg_connector.execute_query( @@ -130,7 +128,7 @@ def sync(): ) assert result == [{"obj_description": "foo"}] - await sync() + await asyncio.to_thread(sync) async def test_execute_query_interpolate(psycopg_connector):