diff --git a/python/cdp/evm_local_account.py b/python/cdp/evm_local_account.py index 2c1cb0d0d..be6fdb0aa 100644 --- a/python/cdp/evm_local_account.py +++ b/python/cdp/evm_local_account.py @@ -1,7 +1,7 @@ import asyncio +from concurrent.futures import ThreadPoolExecutor from typing import Any -import nest_asyncio from eth_account.datastructures import SignedMessage, SignedTransaction from eth_account.messages import SignableMessage, _hash_eip191_message, encode_typed_data from eth_account.signers.base import BaseAccount @@ -11,35 +11,28 @@ from cdp.errors import UserInputValidationError from cdp.evm_server_account import EvmServerAccount -# Apply nest-asyncio to allow nested event loops, but only if compatible -try: - nest_asyncio.apply() -except ValueError as e: - # If nest_asyncio can't patch the loop (e.g., uvloop), silently continue - # This commonly happens when uvloop is installed and running - if "Can't patch loop" in str(e): - pass - else: - # Re-raise other ValueError exceptions - raise - def _run_async(coroutine): """Run an async coroutine synchronously. + If no event loop is running, asyncio.run is used directly. + If an event loop is already running, the coroutine is executed + in a background thread to avoid nested loop issues. + Args: - coroutine: The coroutine to run + coroutine: The coroutine to run. Returns: - Any: The result of the coroutine + Any: The result of the coroutine. """ try: - loop = asyncio.get_event_loop() + asyncio.get_running_loop() except RuntimeError: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - return loop.run_until_complete(coroutine) + return asyncio.run(coroutine) + + with ThreadPoolExecutor(max_workers=1) as executor: + return executor.submit(asyncio.run, coroutine).result() class EvmLocalAccount(BaseAccount): diff --git a/python/changelog.d/591.bugfix.md b/python/changelog.d/591.bugfix.md new file mode 100644 index 000000000..822e02625 --- /dev/null +++ b/python/changelog.d/591.bugfix.md @@ -0,0 +1 @@ +Removed `nest-asyncio` dependency and replaced it with a thread-safe fallback in `EvmLocalAccount`, restoring compatibility with Python 3.12+ and modern uvicorn. diff --git a/python/pyproject.toml b/python/pyproject.toml index b45386941..86826271c 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -17,7 +17,6 @@ dependencies = [ "web3>=7.6.0,<=7.10.0", "solana>=0.36.6", "solders>=0.26.0", - "nest-asyncio>=1.6.0,<2", "base58>=2.1.1", ] diff --git a/python/uv.lock b/python/uv.lock index 93bd95439..e439af70c 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.11'", @@ -310,7 +310,6 @@ dependencies = [ { name = "aiohttp-retry" }, { name = "base58" }, { name = "cryptography" }, - { name = "nest-asyncio" }, { name = "pydantic" }, { name = "pyjwt" }, { name = "python-dateutil" }, @@ -345,7 +344,6 @@ requires-dist = [ { name = "base58", specifier = ">=2.1.1" }, { name = "cryptography", specifier = ">=42.0.0" }, { name = "myst-parser", marker = "extra == 'dev'", specifier = ">=4.0.1" }, - { name = "nest-asyncio", specifier = ">=1.6.0,<2" }, { name = "pydantic", specifier = ">=2.10.3" }, { name = "pyjwt", specifier = ">=2.10.1" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, @@ -1307,15 +1305,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579, upload-time = "2025-02-12T10:53:02.078Z" }, ] -[[package]] -name = "nest-asyncio" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, -] - [[package]] name = "packaging" version = "24.2"