-
Notifications
You must be signed in to change notification settings - Fork 124
Description
Uvicorn is expecting a function signature that includes a loop_factory in Python3.12 - and nest_async is unfortunately patching the function and does not incorporate that feature and argument.
Sadly the project is no longer maintained.. see findings below (unverified)
... .venv/lib/python3.12/site-packages/uvicorn/server.py", line 67, in run
return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
TypeError: _patch_asyncio.<locals>.run() got an unexpected keyword argument 'loop_factory'
Here's what Claude says:
● Now I can see the issue. nest_asyncio's patched run() function (line 25) only accepts (main, *, debug=False) — it doesn't support the loop_factory parameter that
Python 3.12+ and modern uvicorn use. But who's calling nest_asyncio.apply()? Let me check cdp's transitive imports.● Read 1 file (ctrl+o to expand)
● Explore(Find nest_asyncio.apply caller)
⎿ Done (12 tool uses · 18.4k tokens · 20s)● Found it. The culprit is cdp/evm_local_account.py. Let me confirm.
● Read 1 file (ctrl+o to expand)
● Here's the root cause:
cdp/evm_local_account.py:16 calls nest_asyncio.apply() at module import time (line 16). This is loaded because cdp/init.py imports EvmLocalAccount from this
module (line 7 of cdp/init.py).When you from cdp.x402 import create_facilitator_config, Python first initializes the cdp package by running cdp/init.py, which imports EvmLocalAccount, which
triggers nest_asyncio.apply().nest_asyncio.apply() monkey-patches asyncio.run with a replacement that has the signature run(main, *, debug=False) — it does not accept the loop_factory keyword
argument that was added in Python 3.12. Modern uvicorn passes loop_factory=... to asyncio.run, so it crashes with:TypeError: _patch_asyncio..run() got an unexpected keyword argument 'loop_factory'
The fix options:
- Reorder imports — move uvicorn.run() before the cdp.x402 import isn't practical since you need the app built first.
- Lazy import — defer the cdp.x402 import so it doesn't run at module level, or import it only where needed.
- Upgrade nest_asyncio — version 1.6.0 is installed but it's outdated and doesn't support loop_factory. However, as of the last release, this was never fixed
upstream.- Patch around it — re-save the original asyncio.run before the cdp import and restore it after:
import asyncio
_original_run = asyncio.runfrom cdp.x402 import create_facilitator_config # triggers nest_asyncio.apply()
asyncio.run = _original_run # restore the real asyncio.run
Option 4 is the most straightforward workaround. Want me to apply it to server.py?
✻ Cooked for 2m 1s
❯ what version of python is nest_ayncio meant to support.. because 3.12 isn't it.
● Read 1 file (ctrl+o to expand)
● It actually claims to support 3.12 (line 20 of the metadata). But that's a lie — or at least incomplete. The classifiers say 3.12, but the actual code was never
updated for the loop_factory parameter that was added to asyncio.run() in Python 3.12 (via
https://peps.python.org/pep-0690//https://github.com/python/cpython/issues/89943).Look at their patched run() in nest_asyncio.py:25:
def run(main, *, debug=False):
The real asyncio.run signature since Python 3.12 is:
def run(main, *, debug=None, loop_factory=None):
The project is also effectively abandoned — the author (erdewit) passed away, and the repo at github.com/erdewit/nest_asyncio hasn't seen updates since. Version
1.6.0 was the last release (Jan 2024), and this loop_factory incompatibility was a known open issue that was never fixed.So the real problem is that the cdp-sdk package depends on a dead library with a broken asyncio monkey-patch, and it applies it unconditionally at import time.
This is a bug in the cdp-sdk package — worth filing an issue with Coinbase's CDP SDK.