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 4 TestStartupShutdownEvents tests in services/ai-analysis/tests/test_main.py to 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.py tests failed under local Python 3.14 with RuntimeError: 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 raises RuntimeError instead. The 4 spots where the tests directly executed coroutines via the get_event_loop().run_until_complete() pattern were exposed to this change.
  • Secondarily, the Pydantic class Config: (V1 style) in src/config.py was 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), so asyncio.run() — which creates and tears down a fresh loop each time — fits best. It is more concise than manual new_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"), adding SettingsConfigDict to the import. Behavior is identical (.env loading); only the deprecation warning is removed. Existing logic such as @field_validator is unchanged.

D4. pytest_asyncio warning — out of scope (documentation only)

  • The get_event_loop_policy/set_event_loop_policy warnings (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)

  • 4d21d18 fix(ai-analysis) — resolve Python 3.14 asyncio.get_event_loop incompatibility (test_main 4 cases): replace with asyncio.run() (startup 1 · shutdown 3)
  • b1d0554 refactor(ai-analysis) — Pydantic class Config → SettingsConfigDict (V2 migration)

Verification

  • Local (Python 3.14): pytest tests/ -q327 passed, coverage 99.09% (gate 97%+), config.py 100%.
  • Our-code DeprecationWarning 0: confirmed PydanticDeprecatedSince20 (config.py) removed. The remaining 19 are all pytest_asyncio internal get_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.