From 25912eac0cd578f9ffdf149bcd3a1eabbe327666 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sat, 14 Feb 2026 11:21:49 +0000 Subject: [PATCH] Fix metric imports: use module-level access instead of name imports Type-annotated metric variables (e.g. `geojson_cache_operations: Counter`) don't exist as importable names until init_metrics() runs. Switch all `from api.metrics import ` to `import api.metrics as m` and access instruments as attributes at runtime to avoid ImportError. --- api/app.py | 7 ++++--- celery_app.py | 12 ++++++------ listing_processor.py | 6 +++--- rec/throttle_detector.py | 4 ++-- tasks/listing_tasks.py | 21 +++++++-------------- 5 files changed, 22 insertions(+), 28 deletions(-) diff --git a/api/app.py b/api/app.py index 5a677ba..2ddee0f 100644 --- a/api/app.py +++ b/api/app.py @@ -39,7 +39,8 @@ from services.listing_cache import ( from repositories.poi_repository import POIRepository from repositories.user_repository import UserRepository from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor -from api.metrics import init_metrics, get_metrics_asgi_app, geojson_cache_operations +from api.metrics import init_metrics, get_metrics_asgi_app +import api.metrics as app_metrics from logging_config import configure_logging @@ -324,10 +325,10 @@ async def stream_listing_geojson( cached_count = get_cached_count(query_parameters) if cached_count is not None and cached_count > 0 and not include_poi_distances: - geojson_cache_operations.add(1, {"result": "hit"}) + app_metrics.geojson_cache_operations.add(1, {"result": "hit"}) generator = _stream_from_cache(query_parameters, batch_size, limit) else: - geojson_cache_operations.add(1, {"result": "miss"}) + app_metrics.geojson_cache_operations.add(1, {"result": "miss"}) generator = _stream_from_db( query_parameters, batch_size, limit, poi_distances_lookup, skip_cache=include_poi_distances, diff --git a/celery_app.py b/celery_app.py index 31099b8..db2e5a5 100644 --- a/celery_app.py +++ b/celery_app.py @@ -47,9 +47,9 @@ def _start_metrics_server(**kwargs: object) -> None: @task_prerun.connect def _on_task_prerun(task_id: str, task: object, **kwargs: object) -> None: - from api.metrics import celery_tasks_active + import api.metrics as m task_name = getattr(task, "name", "unknown") - celery_tasks_active.add(1, {"task_name": task_name}) + m.celery_tasks_active.add(1, {"task_name": task_name}) _task_start_times[task_id] = time.monotonic() @@ -57,16 +57,16 @@ def _on_task_prerun(task_id: str, task: object, **kwargs: object) -> None: def _on_task_postrun( task_id: str, task: object, state: str | None = None, **kwargs: object ) -> None: - from api.metrics import celery_tasks_total, celery_task_duration_seconds, celery_tasks_active + import api.metrics as m task_name = getattr(task, "name", "unknown") status = state or "UNKNOWN" - celery_tasks_active.add(-1, {"task_name": task_name}) - celery_tasks_total.add(1, {"task_name": task_name, "status": status}) + m.celery_tasks_active.add(-1, {"task_name": task_name}) + m.celery_tasks_total.add(1, {"task_name": task_name, "status": status}) start = _task_start_times.pop(task_id, None) if start is not None: - celery_task_duration_seconds.record( + m.celery_task_duration_seconds.record( time.monotonic() - start, {"task_name": task_name} ) diff --git a/listing_processor.py b/listing_processor.py index a2efb7d..7ec07c3 100644 --- a/listing_processor.py +++ b/listing_processor.py @@ -326,10 +326,10 @@ class DetectFloorplanStep(Step): # Record OCR metrics try: - from api.metrics import ocr_attempts, ocr_successes - ocr_attempts.add(1) + import api.metrics as m + m.ocr_attempts.add(1) if max_sqm > 0: - ocr_successes.add(1) + m.ocr_successes.add(1) except Exception: pass # Metrics not initialised diff --git a/rec/throttle_detector.py b/rec/throttle_detector.py index ccebf04..d50b9fa 100644 --- a/rec/throttle_detector.py +++ b/rec/throttle_detector.py @@ -159,8 +159,8 @@ def reset_throttle_metrics() -> None: def _increment_throttle_metric(event_type: str) -> None: """Safely increment the OTel throttle counter if metrics are initialised.""" try: - from api.metrics import throttle_events_total - throttle_events_total.add(1, {"type": event_type}) + import api.metrics as m + m.throttle_events_total.add(1, {"type": event_type}) except Exception: pass # Metrics not yet initialised (e.g. during tests) diff --git a/tasks/listing_tasks.py b/tasks/listing_tasks.py index e9b1375..fedd29e 100644 --- a/tasks/listing_tasks.py +++ b/tasks/listing_tasks.py @@ -568,20 +568,13 @@ async def _dump_listings_full_inner( celery_logger.info("=" * 60) # Record scrape metrics - from api.metrics import ( - scrape_listings_found, - scrape_listings_processed, - scrape_listings_failed, - scrape_duration_seconds, - scrape_pages_fetched, - scrape_subqueries_total as scrape_subqueries_metric, - ) - scrape_listings_found.add(state.ids_collected) - scrape_listings_processed.add(state.processed_count) - scrape_listings_failed.add(state.failed_count) - scrape_duration_seconds.record(elapsed) - scrape_pages_fetched.add(state.total_pages_fetched) - scrape_subqueries_metric.add(state.completed_subqueries) + import api.metrics as m + m.scrape_listings_found.add(state.ids_collected) + m.scrape_listings_processed.add(state.processed_count) + m.scrape_listings_failed.add(state.failed_count) + m.scrape_duration_seconds.record(elapsed) + m.scrape_pages_fetched.add(state.total_pages_fetched) + m.scrape_subqueries_total.add(state.completed_subqueries) invalidate_cache()