Repository navigation
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The ZIP loader needs failed-import cache cleanup and regression coverage for the new compatibility path.
2 open issues
What changed in this PR
Adds Python 3.15 compatibility to the external-function server and extends CI coverage to the new interpreter.
Changes:
- Replaces ZIP environment loading with
find_spec()/exec_module(), retaining the Python 3.9 fallback. - Uses the running event loop to create the uvicorn startup future.
- Adds Python 3.15 CI jobs and upgrades cibuildwheel, raising the Linux wheel requirement to glibc 2.28.
| File | Description |
|---|---|
singlestoredb/functions/ext/asgi.py |
Updates ZIP environment loading for Python 3.15. |
singlestoredb/apps/_uvicorn_util.py |
Creates the startup future on the running loop. |
.github/workflows/smoke-test.yml |
Adds Python 3.15 smoke coverage. |
.github/workflows/publish.yml |
Upgrades cibuildwheel and selects Python 3.15 for wheel testing. |
.github/workflows/pre-commit.yml |
Adds Python 3.15 to the matrix. |
Review effort: Balanced
kesmit13
left a comment
There was a problem hiding this comment.
I'd like to see this split into two PRs. One for 3.15 support and a separate one for the update of cibuildwheel. The 3.15 changes seem straightforward, but we need to evaluate any side-effects of cibuildwheel updates more thoroughly.
|
Can you rebase this? We've done some refactoring recently and there is an issue with resource cleanup in PRs. I pushed a fix for it so that the code checks should run cleanly now. Sorry for the inconvenience. |
Python 3.15 removed zipimport.zipimporter.load_module(), which the external function server used to load a zipped environment file. Load it with find_spec()/exec_module() instead. Those methods only exist from Python 3.10, so 3.9 keeps using load_module(); that branch can be removed when 3.9 support is dropped. Use asyncio.get_running_loop() in AwaitableUvicornServer rather than get_event_loop(). Both callers already create it inside a running event loop, so behaviour is unchanged. Add 3.15 to the smoke-test and pre-commit matrices. The cibuildwheel upgrade needed to build and test wheels on 3.15 is in singlestore-labs#147. Tested locally on 3.15.0 against SingleStore 9.1.1 (management tests excluded): 967 passed, 19 skipped, 1 failed, the same results as 3.14. The failure, TestServerProcessMode::test_process_mode_worker_handles_request, also fails on 3.14 on macOS and is unrelated to this change.
If the module in a zipped environment file raised while importing, main() left the half-initialized module in sys.modules, and a later import in the same process would return it without retrying. Remove the entry and re-raise, as load_module() did. Add test_asgi_env_file, which runs a zip environment through main() with uvicorn mocked out and checks that its functions are found, and that a failed import leaves nothing in sys.modules. The smoke-test job runs it on every Python version, 3.9 through 3.15, since the loader takes a different path on 3.9 than on 3.10+.
281c47e to
6ccbc33
Compare
|
@kesmit13 No prob! Rebased onto main |

Python 3.15 removed
zipimport.zipimporter.load_module(), which the external function server used to load a zipped environment file. It's now loaded withfind_spec()/exec_module()instead. Those methods only exist from Python 3.10, so 3.9 keeps usingload_module(); that branch can be removed when 3.9 support is dropped. If the module raises while importing, itssys.modulesentry is removed before the error is re-raised, asload_module()did.AwaitableUvicornServernow usesasyncio.get_running_loop()rather thanget_event_loop(). Both callers already create it inside a running event loop, so behaviour is unchanged.CI
smoke-testandpre-commitmatrices.test_asgi_env_fileon every Python version from 3.9 to 3.15. It loads a zip environment throughmain()with uvicorn mocked, and checks both a successful import and a failed one.The cibuildwheel upgrade needed to build and test wheels on 3.15 is in a separate PR: #NNN.
Testing
Full suite run locally on 3.15.0 against SingleStore 9.1.1, with the management tests excluded:
The one failure,
TestServerProcessMode::test_process_mode_worker_handles_request, also fails on 3.14 on macOS and is unrelated to this change.test_asgi_env_filepasses on 3.9, 3.14 and 3.15. Onmainit fails on 3.15 with theload_moduleerror, which confirms it catches the original bug.Note
Medium Risk
Changes how zipped external-function environment files are imported—a deployment path—with version-specific branches, though behavior is covered by new tests.
Overview
Adds Python 3.15 to pre-commit and smoke-test CI matrices and removes the old note that 3.15 was intentionally excluded.
External function zip environment loading in
asgi.main()no longer calls removedzipimporter.load_module()on 3.10+; it usesfind_spec()/exec_module()instead, with 3.9 still onload_module(). Failed imports clearsys.moduleslike the old API.AwaitableUvicornServercreates its startup future withasyncio.get_running_loop()instead ofget_event_loop().New
test_asgi_env_fileexercises zipped env import throughmain()(uvicorn mocked) and runs on every smoke-test Python version alongsidetest_basics.Reviewed by Cursor Bugbot for commit ac40b8e. Bugbot is set up for automated code reviews on this repo. Configure here.