fix(cli,session): remove nest_asyncio, make chat-profile config async-safe#2953
Open
pidefrem wants to merge 6 commits into
Open
fix(cli,session): remove nest_asyncio, make chat-profile config async-safe#2953pidefrem wants to merge 6 commits into
pidefrem wants to merge 6 commits into
Conversation
nest_asyncio <=1.6.0 patches loop.run_until_complete() via
asyncio.ensure_future(future, loop=self). The loop= keyword was
deprecated in Python 3.8 and removed in Python 3.14 (bpo-39529).
Calling nest_asyncio.apply() at import time silently corrupted asyncio
task registration: asyncio.current_task() returned None inside running
coroutines, causing anyio.NoEventLoopError on every static-asset
request (HTTP 500 / white screen) on Python 3.14.
The fix removes nest_asyncio entirely. asyncio.run(start()) is a
top-level entry point and has never needed re-entrant loop support.
The existing comment in the code ("Not sure if it is necessary...")
confirms this usage was always uncertain.
Also removes the nest-asyncio dependency from pyproject.toml.
Adds a regression test to prevent reintroduction.
Refs Chainlit#2767
Replace the synchronous get_config() call to asyncio.get_event_loop().run_until_complete() with a new async resolve_config() method. The old approach required nest_asyncio to work inside the already-running event loop and silently failed without it. - get_config() now returns the cached config immediately - resolve_config() awaits set_chat_profiles() properly - connect() handler calls await session.resolve_config() - Add 9 tests covering overrides, idempotency, error handling, and a regression test verifying run_until_complete is not called - Add hatchling to mypy optional deps (ships py.typed, fixes pre-existing import-not-found error in build.py)
nest_asyncio was removed from chainlit's direct dependencies in the previous commit. The ignore_missing_imports override in the root pyproject.toml mypy config is now dead — chainlit no longer imports it anywhere. nest_asyncio remains in uv.lock as a transitive dependency of llama-index-core and semantic-kernel, so no lockfile change is needed.
Contributor
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
This PR is stale because it has been open for 14 days with no activity. |
Author
|
Not stale — this PR is active. I just pushed It's been waiting on a maintainer review. Could someone take a look when they have a chance? Happy to rebase or address feedback. Thanks! 🙏 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Problem 1 —
nest_asynciobreaks Python 3.14nest_asyncio.apply()is called at import time inchainlit/cli/__init__.py.nest_asyncio<= 1.6.0 internally callsasyncio.ensure_future(future, loop=self),where the
loop=keyword was removed in Python 3.14 (bpo-39529).This corrupts asyncio task registration, causing
asyncio.current_task()to returnNoneinside running coroutines.The symptom is a white page on every Chainlit app running on Python 3.14:
starlette's
FileResponsecallsanyio.to_thread.run_sync(), which callssniffio.current_async_library(), which callsasyncio.current_task()->None->
anyio.NoEventLoopError-> HTTP 500 on every static asset.Problem 2 —
get_config()relied onnest_asynciofor re-entrant event loopWebsocketSession.get_config()calledasyncio.get_event_loop().run_until_complete()from within the already-running event loop (the SocketIO
connecthandler).This only worked because
nest_asynciopatchedrun_until_completeto be re-entrant.Removing
nest_asynciowithout fixing this would silently break chat-profileconfig overrides on all Python versions (3.10-3.13), not just 3.14.
Fix
Commit 1 (
b1a6bb4): Removenest_asyncioentirely.asyncio.run(start())is a top-level entry point; it does not need re-entrantloop support. Also removes the
nest-asynciodependency frompyproject.toml.Adds a regression test to prevent silent reintroduction.
Commit 2 (
55bf45b): Exclude.pytest_cachefrom Prettier checks.Commit 3 (
2406b99): Make chat-profile config resolution async-safe.get_config()now returns the cached config immediately (norun_until_complete)async resolve_config()method properlyawaitsset_chat_profiles()connect()handler callsawait session.resolve_config()after constructionand a regression test verifying
run_until_completeis never calledhatchlingto themypyoptional deps (it shipspy.typed;fixes a pre-existing
import-not-foundmypy error inbuild.py)Testing
All 798 tests pass on Python 3.10, 3.11, 3.12, and 3.13.
Python 3.14 cannot be tested yet (
pydantic-corelacks a 3.14 wheel).Note: the
requires-python < 3.14upper bound is intentionally left unchanged.Lifting it is a separate decision once the full dependency chain supports 3.14.
Refs #2767
Known pre-existing issue (out of scope)
cypress/support/run.ts:27has a bug:access()fromfs/promisesreturns aPromise (always truthy), so the
if (!access(entryPointPath))check is dead codeand the file-existence guard never triggers. This predates this PR and should be
addressed separately.