chore: eliminate all deprecation warnings from tests and linter

Warnings fixed:
- datetime.utcnow() → datetime.now(UTC) across 48+ files (src/ + tests/)
- datetime.utcnow (callback ref) → lambda: datetime.now(UTC) in model fields (18 files)
- Pydantic class Config → model_config = ConfigDict(...) (16 files)
- Pydantic .dict() → .model_dump() (8 files)
- ConfigDict(allow_population_by_field_name=True) → validate_by_name=True
- SQLAlchemy declarative_base() import path updated
- FastAPI on_event → lifespan context manager (app.py)
- Import sorting (ruff I001) auto-fixed across all files
- Fixed broken re-export chains that ruff F401 cleanup broke:
  _validate_bcp47: service.py now imports from dictionary_validation directly
  job_to_response: _job_routes.py and test imports from service_utils directly
  fetch_datasource_metadata: restored re-export in service.py
- Added missing TranslateJobService import in _job_routes.py (was deleted by F401)
- Added ConfigDict(protected_namespaces=()) for DashboardDatasetItem schema field
- pytest.ini: replaced deprecated importmode with asyncio_mode

All 440 tests pass with zero deprecation warnings.
This commit is contained in:
2026-05-26 19:18:28 +03:00
parent eda346979b
commit 4205618ee6
182 changed files with 779 additions and 897 deletions

View File

@@ -3,7 +3,7 @@
# @LAYER Domain
# @RELATION DEPENDS_ON -> [EXT:Library:pydantic]
from datetime import datetime
from datetime import UTC, datetime
from enum import Enum
from pydantic import BaseModel, Field
@@ -53,7 +53,7 @@ class DetectedIssue(BaseModel):
class ValidationResult(BaseModel):
id: str | None = None
dashboard_id: str
timestamp: datetime = Field(default_factory=datetime.utcnow)
timestamp: datetime = Field(default_factory=lambda: datetime.now(UTC))
status: ValidationStatus
screenshot_path: str | None = None
issues: list[DetectedIssue]

View File

@@ -9,7 +9,7 @@
# @INVARIANT All LLM interactions must be executed as asynchronous tasks.
# @DATA_CONTRACT AnalysisRequest -> AnalysisResult
from datetime import datetime, timedelta
from datetime import UTC, datetime, timedelta
import json
import os
from typing import Any
@@ -99,7 +99,7 @@ class DashboardValidationPlugin(PluginBase):
# @SIDE_EFFECT Captures a screenshot, calls LLM API, and writes to the database.
async def execute(self, params: dict[str, Any], context: TaskContext | None = None):
with belief_scope("execute", f"plugin_id={self.id}"):
validation_started_at = datetime.utcnow()
validation_started_at = datetime.now(UTC)
# Use TaskContext logger if available, otherwise fall back to app logger
log = context.logger if context else logger
@@ -165,15 +165,15 @@ class DashboardValidationPlugin(PluginBase):
filename = f"{dashboard_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
screenshot_path = os.path.join(screenshots_dir, filename)
screenshot_started_at = datetime.utcnow()
screenshot_started_at = datetime.now(UTC)
screenshot_log.info(f"Capturing screenshot for dashboard {dashboard_id}")
await screenshot_service.capture_dashboard(dashboard_id, screenshot_path)
screenshot_log.debug(f"Screenshot saved to: {screenshot_path}")
screenshot_finished_at = datetime.utcnow()
screenshot_finished_at = datetime.now(UTC)
# 4. Fetch Logs (from Environment /api/v1/log/)
logs = []
logs_fetch_started_at = datetime.utcnow()
logs_fetch_started_at = datetime.now(UTC)
try:
client = SupersetClient(env)
@@ -214,7 +214,7 @@ class DashboardValidationPlugin(PluginBase):
except Exception as e:
superset_log.warning(f"Failed to fetch logs from environment: {e}")
logs = [f"Error fetching remote logs: {e!s}"]
logs_fetch_finished_at = datetime.utcnow()
logs_fetch_finished_at = datetime.now(UTC)
# 5. Analyze with LLM
llm_client = LLMClient(
@@ -230,13 +230,13 @@ class DashboardValidationPlugin(PluginBase):
"dashboard_validation_prompt",
DEFAULT_LLM_PROMPTS["dashboard_validation_prompt"],
)
llm_call_started_at = datetime.utcnow()
llm_call_started_at = datetime.now(UTC)
analysis = await llm_client.analyze_dashboard(
screenshot_path,
logs,
prompt_template=dashboard_prompt,
)
llm_call_finished_at = datetime.utcnow()
llm_call_finished_at = datetime.now(UTC)
# Log analysis summary to task logs for better visibility
llm_log.info(f"[ANALYSIS_SUMMARY] Status: {analysis['status']}")
@@ -254,9 +254,9 @@ class DashboardValidationPlugin(PluginBase):
screenshot_path=screenshot_path,
raw_response=str(analysis)
)
validation_finished_at = datetime.utcnow()
validation_finished_at = datetime.now(UTC)
result_payload = _json_safe_value(validation_result.dict())
result_payload = _json_safe_value(validation_result.model_dump())
result_payload["screenshot_paths"] = [screenshot_path]
result_payload["logs_sent_to_llm"] = logs
result_payload["logs_sent_count"] = len(logs)
@@ -291,7 +291,7 @@ class DashboardValidationPlugin(PluginBase):
environment_id=env_id,
status=validation_result.status.value,
summary=validation_result.summary,
issues=[issue.dict() for issue in validation_result.issues],
issues=[issue.model_dump() for issue in validation_result.issues],
screenshot_path=validation_result.screenshot_path,
raw_response=json.dumps(result_payload, ensure_ascii=False)
)

View File

@@ -842,7 +842,7 @@ class ScreenshotService:
await asyncio.sleep(0.5)
except Exception:
pass
except asyncio.TimeoutError:
except TimeoutError:
logger.error(f"Screenshot capture timed out after {SCREENSHOT_SERVICE_TIMEOUT_MS}ms")
try:
await page.screenshot(path=output_path, timeout=PLAYWRIGHT_SHORT_TIMEOUT_MS)

View File

@@ -12,7 +12,6 @@
# @TEST_EDGE non_timestamp_string -> passed through unchanged
import logging
from unittest.mock import MagicMock, patch
from src.plugins.translate.sql_generator import (

View File

@@ -11,10 +11,10 @@
# @REJECTED Keeping 1199-line test module violates module < 400 lines constraint.
# All tests have been migrated to the following modules:
from .test_dictionary_correction import * # noqa: F401, F403
from .test_dictionary_crud import * # noqa: F401, F403
from .test_dictionary_filter import * # noqa: F401, F403
from .test_dictionary_import import * # noqa: F401, F403
from .test_dictionary_prompt_builder import * # noqa: F401, F403
from .test_dictionary_utils import * # noqa: F401, F403
from .test_dictionary_correction import * # noqa: F403
from .test_dictionary_crud import * # noqa: F403
from .test_dictionary_filter import * # noqa: F403
from .test_dictionary_import import * # noqa: F403
from .test_dictionary_prompt_builder import * # noqa: F403
from .test_dictionary_utils import * # noqa: F403
# #endregion TestDictionaryLegacyHub

View File

@@ -10,7 +10,6 @@ from sqlalchemy.orm import Session, sessionmaker
from src.models.translate import (
Base,
DictionaryEntry,
TerminologyDictionary,
TranslationBatch,
TranslationJob,
TranslationLanguage,

View File

@@ -12,7 +12,7 @@ import pytest
from sqlalchemy import create_engine, event
from sqlalchemy.orm import Session, sessionmaker
from src.models.translate import Base, DictionaryEntry, TerminologyDictionary, TranslationJob, TranslationJobDictionary
from src.models.translate import Base, TranslationJob, TranslationJobDictionary
from src.plugins.translate.dictionary import DictionaryManager

View File

@@ -14,7 +14,6 @@ from src.models.translate import (
TranslationJob,
TranslationRun,
)
from src.plugins.translate._batch_sizer import AdaptiveBatchSizer
from src.plugins.translate.executor import TranslationExecutor, estimate_row_tokens

View File

@@ -298,9 +298,7 @@ class TestBulkFindReplaceService:
from unittest.mock import MagicMock
from src.models.translate import TranslationLanguage, TranslationRecord
from src.plugins.translate.prompt_builder import ContextAwarePromptBuilder
from src.plugins.translate.service import InlineCorrectionService
# region TestContextCapture [TYPE Class]

View File

@@ -16,7 +16,7 @@
from datetime import UTC, datetime
import pytest
from unittest.mock import MagicMock, PropertyMock, patch
from unittest.mock import MagicMock, patch
from src.models.translate import (
TranslationJob,

View File

@@ -18,7 +18,6 @@ from src.plugins.translate.service_target_schema import (
validate_target_table_schema,
)
from src.schemas.translate import (
TargetSchemaColumnInfo,
TargetSchemaValidationRequest,
)

View File

@@ -13,7 +13,6 @@
# @REJECTED Inline insert logic in process_batch — made the function 583 lines.
import json
from typing import Any
from sqlalchemy.orm import Session, joinedload

View File

@@ -24,7 +24,7 @@ from ...core.logger import belief_scope, logger
from ...models.translate import TranslationBatch, TranslationJob, TranslationLanguage, TranslationRecord
from ...services.llm_provider import LLMProviderService
from ._batch_insert import insert_batch_to_target
from ._lang_detect import batch_detect, get_detector
from ._lang_detect import batch_detect
from ._llm_call import LLMTranslationService
from ._token_budget import estimate_token_budget
from ._utils import _check_translation_cache, _compute_key_hash, _compute_source_hash

View File

@@ -11,8 +11,6 @@
# @REJECTED Fixed batch_size of 50 — causes truncation on long-content rows.
# Single monolithic batch — would lose all progress on any failure.
import time
from typing import Any
from sqlalchemy.orm import Session

View File

@@ -30,7 +30,6 @@ from ...services.llm_prompt_templates import render_prompt
from ...services.llm_provider import LLMProviderService
from ._llm_http import call_openai_compatible
from ._llm_parse import parse_llm_response
from ._token_budget import estimate_token_budget
from ._utils import _enforce_dictionary
from .preview import DEFAULT_EXECUTION_PROMPT_TEMPLATE
from .prompt_builder import ContextAwarePromptBuilder

View File

@@ -35,7 +35,6 @@ def call_openai_compatible(
if not base_url:
raise ValueError("LLM provider has no base_url configured")
import requests as http_requests
url = f"{base_url.rstrip('/')}/chat/completions"
headers = {

View File

@@ -8,7 +8,6 @@
import json
import re
from typing import Any
from ...core.logger import logger

View File

@@ -12,15 +12,13 @@
# @REJECTED Keeping all run orchestration inside TranslationExecutor — caused class to exceed INV_7.
from collections.abc import Callable
from datetime import UTC, datetime
from typing import Any
from sqlalchemy.orm import Session
from ...core.config_manager import ConfigManager
from ...core.logger import belief_scope, logger
from ...models.translate import TranslationJob, TranslationRecord, TranslationRun, TranslationRunLanguageStats
from ._batch_sizer import AdaptiveBatchSizer
from ...models.translate import TranslationRecord, TranslationRun, TranslationRunLanguageStats
from ._run_source import _extract_chart_data_rows, fetch_source_rows
@@ -97,7 +95,7 @@ class RunExecutionService:
def _load_preview_edits(self, job_id: str) -> None:
"""Load preview edits for carry-forward during execution."""
from ...models.translate import TranslationPreviewLanguage, TranslationPreviewRecord, TranslationPreviewSession
from ...models.translate import TranslationPreviewRecord, TranslationPreviewSession
with belief_scope("RunExecutionService._load_preview_edits"):
session = (

View File

@@ -17,7 +17,7 @@ import unicodedata
from sqlalchemy.orm import Session, joinedload
from ...core.logger import logger
from ...models.translate import TranslationLanguage, TranslationRecord
from ...models.translate import TranslationRecord
# #region _normalize_term [TYPE Function]

View File

@@ -12,18 +12,13 @@
# @PRE Database session is open and valid.
# @SIDE_EFFECT Creates, updates, deletes TerminologyDictionary and DictionaryEntry rows; enforces deletion guards.
from typing import Any
from sqlalchemy.orm import Session
from ...core.logger import belief_scope
from ...models.translate import DictionaryEntry, TerminologyDictionary
from .dictionary_correction import DictionaryCorrectionService
from .dictionary_crud import DictionaryCRUD
from .dictionary_entries import DictionaryEntryCRUD
from .dictionary_filter import DictionaryBatchFilter
from .dictionary_import_export import DictionaryImportExport
from .dictionary_validation import _validate_bcp47
# #region DictionaryManager [C:4] [TYPE Class]

View File

@@ -28,12 +28,14 @@ from ...core.logger import logger
from ...models.translate import TranslationJob, TranslationRun, TranslationRunLanguageStats
from ...services.llm_provider import LLMProviderService
from ._run_service import RunExecutionService
from ._token_budget import estimate_token_budget
from ._utils import _check_translation_cache, _compute_source_hash, _enforce_dictionary, estimate_row_tokens
__all__ = [
"TranslationExecutor", "estimate_row_tokens",
"_enforce_dictionary", "_compute_source_hash", "_check_translation_cache",
"TranslationExecutor",
"_check_translation_cache",
"_compute_source_hash",
"_enforce_dictionary",
"estimate_row_tokens",
]

View File

@@ -11,7 +11,6 @@
# @REJECTED Querying all events for all time — would be slow; MetricSnapshot provides historical summary.
# @COMPLEXITY 4
from datetime import UTC, datetime
from typing import Any
from sqlalchemy import func
@@ -20,7 +19,6 @@ from sqlalchemy.orm import Session
from ...core.logger import belief_scope
from ...models.translate import (
MetricSnapshot,
TranslationEvent,
TranslationRun,
TranslationRunLanguageStats,
TranslationSchedule,

View File

@@ -23,12 +23,11 @@ from typing import Any
from sqlalchemy.orm import Session
from ...core.config_manager import ConfigManager
from ...core.logger import belief_scope, logger
from ...core.logger import belief_scope
from ...models.translate import (
TranslationRun,
)
from .events import TranslationEventLog
from .executor import TranslationExecutor
from .orchestrator_aggregator import TranslationResultAggregator
from .orchestrator_planner import TranslationPlanner
from .orchestrator_runner import TranslationStageRunner

View File

@@ -6,7 +6,6 @@
# @RELATION DEPENDS_ON -> [TranslationRunLanguageStats]
from collections import defaultdict
from typing import Any
from sqlalchemy.orm import Session

View File

@@ -19,7 +19,6 @@ from sqlalchemy.orm import Session
from ...core.logger import belief_scope, logger
from ...models.translate import (
TranslationJob,
TranslationPreviewSession,
TranslationRun,
)
from .events import TranslationEventLog

View File

@@ -7,7 +7,6 @@
# @RELATION DEPENDS_ON -> [TranslationEventLog]
from datetime import UTC, datetime
from typing import Any
from ...core.logger import logger
from ...models.translate import TranslationJob, TranslationRun, TranslationRunLanguageStats

View File

@@ -4,7 +4,6 @@
# @RELATION DEPENDS_ON -> [TranslationJob]
import json
from typing import Any
from ...models.translate import TranslationJob, TranslationRecord
from .sql_generator import _normalize_timestamp_value

View File

@@ -40,7 +40,6 @@ from .preview_response_parser import (
parse_llm_response as _parse_llm_response_module,
)
from .preview_review import PreviewSessionManager
from .preview_token_estimator import TokenEstimator
# #region TranslationPreview [C:4] [TYPE Class]
@@ -272,5 +271,4 @@ class TranslationPreview:
# #endregion TranslationPreview
# Re-export for backward compatibility
from .preview_token_estimator import TokenEstimator # noqa: E402, F401
# #endregion TranslationPreview

View File

@@ -5,8 +5,6 @@
from typing import Any
from sqlalchemy.orm import Session
from ...core.logger import logger
from ...models.translate import TranslationJob
from ._token_budget import DEFAULT_CONTEXT_WINDOW, estimate_token_budget

View File

@@ -10,9 +10,8 @@ from typing import Any
from sqlalchemy.orm import Session
from ...core.logger import belief_scope, logger
from ...core.logger import belief_scope
from ...models.translate import (
TranslationJob,
TranslationPreviewLanguage,
TranslationPreviewRecord,
TranslationPreviewSession,

View File

@@ -12,7 +12,6 @@
# @REJECTED Monolithic service.py — violated INV_7. Decomposed into service_datasource, service_inline_correction, service_bulk_replace, service_utils.
from datetime import UTC, datetime
from typing import Any
import uuid
from sqlalchemy.orm import Session
@@ -21,9 +20,9 @@ from ...core.config_manager import ConfigManager
from ...core.logger import logger
from ...models.translate import TranslationJob, TranslationJobDictionary
from ...schemas.translate import TranslateJobCreate, TranslateJobUpdate
from .dictionary import _validate_bcp47
from .dictionary_validation import _validate_bcp47
from .service_datasource import fetch_datasource_metadata
from .service_utils import _extract_dialect, job_to_response
from .service_utils import _extract_dialect
# #region TranslateJobService [TYPE Class]
@@ -267,10 +266,8 @@ class TranslateJobService:
from .service_bulk_replace import BulkFindReplaceService # noqa: E402, F401
from .service_datasource import ( # noqa: E402, F401
detect_virtual_columns,
fetch_datasource_metadata,
get_datasource_columns,
get_dialect_from_database,
)
from .service_inline_correction import InlineCorrectionService # noqa: E402, F401
from .service_utils import _extract_dialect, job_to_response # noqa: E402, F401
# #endregion TranslateJobService

View File

@@ -8,7 +8,6 @@
# @RELATION DEPENDS_ON -> [DictionaryEntry]
# @RELATION DEPENDS_ON -> [_normalize_term]
from typing import Any
from sqlalchemy.orm import Session

View File

@@ -1,7 +1,6 @@
# #region ServiceUtils [C:1] [TYPE Module] [SEMANTICS utils, dialect, response]
# @BRIEF Utility functions for the translate service layer.
from typing import Any
from ...models.translate import TranslationJob
from ...schemas.translate import TranslateJobResponse