diff --git a/pyproject.toml b/pyproject.toml index 81d56f8..6350d40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dbt-core-interface" -version = "1.1.8" +version = "1.2.0" dynamic = [] description = "Dbt Core Interface" authors = [ @@ -31,7 +31,7 @@ Changelog = "https://github.com/z3z1ma/dbt-core-interface/releases" [project.optional-dependencies] sqlfluff = [ - "sqlfluff>=2.3.2,<4" + "sqlfluff>=2.3.2,<5" ] server = [ "fastapi~=0.115.14", @@ -42,13 +42,13 @@ dev = [ "ruff~=0.12.0", "coverage[toml]>=6.2,<7", "pytest>=8.0.0,<9", - "sqlfluff>=2.3.2,<4" + "sqlfluff>=2.3.2,<5" ] test = [ "dbt-postgres~=1.8.0", "dbt-core~=1.8.0", "psycopg2-binary>=2.8,<3.0", - "sqlfluff>=2.3.2,<4" + "sqlfluff>=2.3.2,<5" ] [[tool.uv.index]] diff --git a/src/dbt_core_interface/server.py b/src/dbt_core_interface/server.py index c658455..7a7f18b 100644 --- a/src/dbt_core_interface/server.py +++ b/src/dbt_core_interface/server.py @@ -529,7 +529,7 @@ def status(runner: DbtProject = Depends(_get_runner)) -> dict[str, t.Any]: @app.get("/api/v1/heartbeat") def heartbeat() -> dict[str, t.Any]: """Heartbeat endpoint to check server availability.""" - return {"result": {"status": "ready"}} + return {"result": {"status": "ready", **DbtProjectWatcher.reload_status()}} class ServerLintResult(BaseModel): diff --git a/src/dbt_core_interface/watcher.py b/src/dbt_core_interface/watcher.py index b3dbe81..ea18cfc 100644 --- a/src/dbt_core_interface/watcher.py +++ b/src/dbt_core_interface/watcher.py @@ -6,6 +6,7 @@ import logging import os import threading +import time import typing as t import weakref from pathlib import Path @@ -50,6 +51,9 @@ def __init__( self.reader = project.create_reader() self._mtimes: dict[Path, float] = {} + self.reload_count: int = 0 + self.last_reload_at: float = 0.0 + self.last_reload_path: str = "" self._running = False self._thread: threading.Thread | None = None self._stop_event = threading.Event() @@ -120,6 +124,9 @@ def _monitor_loop(self) -> None: self._project.parse_project( write_manifest=True, reparse_configuration=change_level > 1 ) + if change_level > 1: + self.reload_count += 1 + self.last_reload_at = time.time() except Exception as e: logger.error(f"Error in project watcher loop: {e}") @@ -161,6 +168,7 @@ def _check_for_changes(self) -> int: stamped_mtime = self._mtimes.get(path) if stamped_mtime is None or current_mtime != stamped_mtime: self._mtimes[path] = current_mtime + self.last_reload_path = str(path) logger.info(f"Config change detected: {path}") return 2 except OSError as e: @@ -235,6 +243,20 @@ def stop_path(cls, path: Path | str) -> None: else: logger.warning(f"No watcher found for project at {path}") + @classmethod + def reload_status(cls) -> dict[str, t.Any]: + """Aggregate configuration reload counters across every active watcher.""" + with cls._instance_lock: + watchers = list(cls._instances.values()) + if not watchers: + return {"config_reload_count": 0, "config_reload_at": 0.0, "config_reload_path": ""} + latest = max(watchers, key=lambda w: w.last_reload_at) + return { + "config_reload_count": sum(w.reload_count for w in watchers), + "config_reload_at": latest.last_reload_at, + "config_reload_path": latest.last_reload_path, + } + @classmethod def active_watchers(cls) -> list[DbtProjectWatcher]: """Return a list of currently active project paths being watched.""" diff --git a/uv.lock b/uv.lock index 43e7ccc..48c0fe0 100644 --- a/uv.lock +++ b/uv.lock @@ -283,7 +283,7 @@ wheels = [ [[package]] name = "dbt-core-interface" -version = "1.1.7" +version = "1.2.0" source = { editable = "." } dependencies = [ { name = "dbt-adapters" }, @@ -326,9 +326,9 @@ requires-dist = [ { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0,<9" }, { name = "rich", specifier = ">10.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = "~=0.12.0" }, - { name = "sqlfluff", marker = "extra == 'dev'", specifier = ">=2.3.2,<4" }, - { name = "sqlfluff", marker = "extra == 'sqlfluff'", specifier = ">=2.3.2,<4" }, - { name = "sqlfluff", marker = "extra == 'test'", specifier = ">=2.3.2,<4" }, + { name = "sqlfluff", marker = "extra == 'dev'", specifier = ">=2.3.2,<5" }, + { name = "sqlfluff", marker = "extra == 'sqlfluff'", specifier = ">=2.3.2,<5" }, + { name = "sqlfluff", marker = "extra == 'test'", specifier = ">=2.3.2,<5" }, { name = "typing-extensions", marker = "python_full_version < '3.10'" }, { name = "uvicorn", marker = "extra == 'server'", specifier = "~=0.35.0" }, ]