Python Compatibility Fix — asyncio.get_event_loop + Pydantic class Config
SummaryFixes the 4 ai-analysis test_main.py tests that failed under local Python 3.14 due to asyncio.get_event_loop() incompatibility (RuntimeError: no current event loop). The 4 cases are migrated to asyncio.run(), and the Pydantic V2.0-deprecated class Config: is migrated to model_config = SettingsConfigDict. Proven to work across 3.12 (CI), 3.13 (Docker), and 3.14 (local); Critic 0 findings, CI 38 pass / 0 fail.
Date
ImpactHigh
Goal
- Resolve the
asyncio.get_event_loop()incompatibility that caused the 4TestStartupShutdownEventstests inservices/ai-analysis/tests/test_main.pyto fail under local Python 3.14. - CI passes because it runs Python 3.12, but the version gap across Dockerfile (3.13) and local (3.14) is cleaned up to guarantee common behavior across 3.12–3.14.
Background
- Carryover seed from Sprint 191: the 4
test_main.pytests failed under local Python 3.14 withRuntimeError: There is no current event loop in thread 'MainThread'(sprint-191 §carryover). Since CI runs Python 3.12, the build was unaffected, but the version gap between the local dev environment (3.14), CI (3.12), and Docker (3.13) remained a latent risk. - Root cause: starting in Python 3.10+,
asyncio.get_event_loop()emits a deprecation warning when there is no running loop, and in 3.14 it no longer auto-creates a loop when none is current — it raisesRuntimeErrorinstead. The 4 spots where the tests directly executed coroutines via theget_event_loop().run_until_complete()pattern were exposed to this change. - Secondarily, the Pydantic
class Config:(V1 style) insrc/config.pywas deprecated in V2.0 (PydanticDeprecatedSince20), emitting a "to be removed in V3.0" warning.
Decision
D1. Scope — focus on code compatibility, exclude the CI version bump (user)
- A scoping investigation reproduced and confirmed three compatibility issues: ① the 4 test_main.py failures, ② the config.py Pydantic deprecation, ③ the pytest_asyncio internal warning.
- The user was asked whether to include "bumping CI Python 3.12 → 3.13 (aligning with the Dockerfile)" → confirmed excluded. Rationale: the blast radius (re-validating the entire Python pipeline) is large, and aligning the version is safer as a separate sprint.
D2. asyncio pattern — adopt asyncio.run()
asyncio.get_event_loop().run_until_complete(coro)→asyncio.run(coro).- Each test awaits an independent coroutine once (
startup_event()×1 /shutdown_event()×3), soasyncio.run()— which creates and tears down a fresh loop each time — fits best. It is more concise than manualnew_event_loop()management and behaves identically across 3.12–3.14.
D3. Pydantic V2 migration — SettingsConfigDict
class Config: env_file = ".env"→model_config = SettingsConfigDict(env_file=".env"), addingSettingsConfigDictto the import. Behavior is identical (.envloading); only the deprecation warning is removed. Existing logic such as@field_validatoris unchanged.
D4. pytest_asyncio warning — out of scope (documentation only)
- The
get_event_loop_policy/set_event_loop_policywarnings (19 remaining) are internal calls within pytest-asyncio 0.26.0, so they cannot be fixed in our code. With removal slated for Python 3.16, there is no urgency. Recorded as a residual warning that will be resolved naturally by a newer pytest-asyncio version.
Implementation
Implementation commits (2 commits, PR #337 squash → 5370c78)
4d21d18fix(ai-analysis) — resolve Python 3.14 asyncio.get_event_loop incompatibility (test_main 4 cases): replace withasyncio.run()(startup 1 · shutdown 3)b1d0554refactor(ai-analysis) — Pydantic class Config → SettingsConfigDict (V2 migration)
Verification
- Local (Python 3.14):
pytest tests/ -q→ 327 passed, coverage 99.09% (gate 97%+),config.py100%. - Our-code DeprecationWarning 0: confirmed
PydanticDeprecatedSince20(config.py) removed. The remaining 19 are all pytest_asyncio internalget_event_loop_policy(out of scope, D4). - ruff: lint (src+tests)
All checks passed!· format --check (src) passed. - Critic:
codex review --base main— 0 findings ("The changes are limited to updating Pydantic settings configuration syntax and replacing event loop access in tests with asyncio.run. I did not identify any introduced behavior that would break existing code or tests"). - CI #337: Failed 0 / Passed 38. All ai-analysis jobs SUCCESS — Quality (ruff) · Test AI Analysis (Python 3.12) · Build AI Analysis Service (Docker 3.13) · Trivy. → proven to work across 3.12 · 3.13 · 3.14.