semantics: complete DEF-to-region migration, fix regressions
- Convert legacy [DEF🆔Type] anchors to #region/#endregion across 329 files
- Reinstate _normalize_timestamp_value in sql_generator.py
- Fix MarkerLogger→logger migration in events.py (molecular CoT markers)
- Fix dataset_review orchestrator dependencies (_build_execution_snapshot)
- Fix config_manager stale-record deletion (moved to save path only)
- Add 77 missing [/DEF:] closers in 5 unbalanced test files
- Update assistant_chat.integration.test.js for #region format
- Apply molecular-cot-logging markers (REASON/REFLECT/EXPLORE) via logger.* methods
This commit is contained in:
@@ -1,49 +1,43 @@
|
||||
# #region SupersetSqlLabExecutor [C:5] [TYPE Module] [SEMANTICS translate,superset,sqllab,execute]
|
||||
# @BRIEF Submit SQL to Superset SQL Lab API and poll execution status with retry.
|
||||
# @LAYER Infra
|
||||
# #region SupersetSqlLabExecutor [C:4] [TYPE Module] [SEMANTICS translate, superset, sqllab, execute]
|
||||
# @BRIEF Submit SQL to Superset SQL Lab API and poll execution status.
|
||||
# @LAYER: Infra
|
||||
# @RELATION DEPENDS_ON -> [SupersetClient]
|
||||
# @RELATION DEPENDS_ON -> [ConfigManager]
|
||||
# @PRE Valid Superset environment configuration and authenticated client.
|
||||
# @POST SQL is submitted to Superset SQL Lab; execution reference is returned.
|
||||
# @SIDE_EFFECT Makes HTTP calls to Superset /api/v1/sqllab/execute/; polls status.
|
||||
# @DATA_CONTRACT Input[sql:str, database_id:Optional] -> Output[Dict with query_id, status, error_message]
|
||||
# @INVARIANT Polling respects max_polls limit; timeout returns structured error.
|
||||
# @RATIONALE Direct SQL Lab API submission provides audit trail and execution monitoring within Superset.
|
||||
# @REJECTED Direct database connection bypass — would skip Superset's SQL Lab audit and RBAC.
|
||||
# @PRE: Valid Superset environment configuration and authenticated client.
|
||||
# @POST: SQL is submitted to Superset SQL Lab; execution reference is returned.
|
||||
# @SIDE_EFFECT: Makes HTTP calls to Superset /api/v1/sqllab/execute/; polls status.
|
||||
# @RATIONALE: Direct SQL Lab API submission provides audit trail and execution monitoring within Superset.
|
||||
# @REJECTED: Direct database connection bypass — would skip Superset's SQL Lab audit and RBAC.
|
||||
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
from datetime import datetime, timezone
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from ...core.logger import logger, belief_scope
|
||||
from ...core.cot_logger import MarkerLogger
|
||||
from ...core.config_manager import ConfigManager
|
||||
from ...core.superset_client import SupersetClient
|
||||
|
||||
log = MarkerLogger("SupersetExecutor")
|
||||
|
||||
# #region SupersetSqlLabExecutor [C:5] [TYPE Class] [SEMANTICS translate,superset,sqllab]
|
||||
# #region SupersetSqlLabExecutor [C:4] [TYPE Class]
|
||||
# @BRIEF Submit SQL to Superset SQL Lab API with polling and status tracking.
|
||||
# @PRE Valid environment ID and ConfigManager.
|
||||
# @POST SQL submitted to Superset; execution reference recorded; polling completes.
|
||||
# @SIDE_EFFECT Makes HTTP POST to /api/v1/sqllab/execute/; polls GET for status.
|
||||
# @DATA_CONTRACT Input[env_id:str, database_id:Optional] -> Output[Dict with query_id, status]
|
||||
# @INVARIANT database_id resolved lazily; polling capped at max_polls.
|
||||
# @PRE: Valid environment ID and ConfigManager.
|
||||
# @POST: SQL submitted to Superset; execution reference recorded.
|
||||
# @SIDE_EFFECT: Makes HTTP POST to /api/v1/sqllab/execute/; polls GET for status.
|
||||
class SupersetSqlLabExecutor:
|
||||
|
||||
def __init__(self, config_manager: ConfigManager, env_id: str, database_id: Optional[int] = None):
|
||||
def __init__(self, config_manager: ConfigManager, env_id: str):
|
||||
self.config_manager = config_manager
|
||||
self.env_id = env_id
|
||||
self._client: Optional[SupersetClient] = None
|
||||
self._database_id: Optional[int] = database_id
|
||||
self._database_id: Optional[int] = None
|
||||
|
||||
# #region _get_client [C:4] [TYPE Function] [SEMANTICS translate,superset,client]
|
||||
# @BRIEF Lazy-initialize SupersetClient for the configured environment.
|
||||
# @PRE env_id must correspond to a valid environment config.
|
||||
# @POST Returns authenticated SupersetClient.
|
||||
# @SIDE_EFFECT Authenticates against Superset API on first call.
|
||||
# [DEF:_get_client:Function]
|
||||
# @PURPOSE: Lazy-initialize SupersetClient for the configured environment.
|
||||
# @PRE: env_id must correspond to a valid environment config.
|
||||
# @POST: Returns authenticated SupersetClient.
|
||||
# @SIDE_EFFECT: Authenticates against Superset API.
|
||||
def _get_client(self) -> SupersetClient:
|
||||
with belief_scope("SupersetSqlLabExecutor._get_client"):
|
||||
if self._client is not None:
|
||||
@@ -56,13 +50,13 @@ class SupersetSqlLabExecutor:
|
||||
|
||||
self._client = SupersetClient(env_config)
|
||||
return self._client
|
||||
# #endregion _get_client
|
||||
# [/DEF:_get_client:Function]
|
||||
|
||||
# #region resolve_database_id [C:4] [TYPE Function] [SEMANTICS translate,database,resolve]
|
||||
# @BRIEF Resolve the target database ID from the environment by name or default.
|
||||
# @PRE Superset environment is reachable and has databases.
|
||||
# @POST Returns database_id integer or raises ValueError.
|
||||
# @SIDE_EFFECT Fetches databases list from Superset API.
|
||||
# [DEF:resolve_database_id:Function]
|
||||
# @PURPOSE: Resolve the target database ID from the environment.
|
||||
# @PRE: database_name or database_id should be known.
|
||||
# @POST: Returns database_id integer or raises ValueError.
|
||||
# @SIDE_EFFECT: Fetches databases list from Superset.
|
||||
def resolve_database_id(self, database_name: Optional[str] = None) -> int:
|
||||
with belief_scope("SupersetSqlLabExecutor.resolve_database_id"):
|
||||
client = self._get_client()
|
||||
@@ -76,7 +70,7 @@ class SupersetSqlLabExecutor:
|
||||
for db in databases:
|
||||
if db.get("database_name", "").lower() == database_name.lower():
|
||||
self._database_id = db["id"]
|
||||
log.reason("Resolved database ID by name", payload={
|
||||
logger.reason("Resolved database ID by name", {
|
||||
"database_name": database_name,
|
||||
"database_id": self._database_id,
|
||||
})
|
||||
@@ -87,76 +81,42 @@ class SupersetSqlLabExecutor:
|
||||
|
||||
# Default: use first database
|
||||
self._database_id = databases[0]["id"]
|
||||
log.reason("Using default database", payload={
|
||||
logger.reason("Using default database", {
|
||||
"database_name": databases[0].get("database_name"),
|
||||
"database_id": self._database_id,
|
||||
})
|
||||
return self._database_id
|
||||
# #endregion resolve_database_id
|
||||
# [/DEF:resolve_database_id:Function]
|
||||
|
||||
# #region resolve_database_uuid [C:4] [TYPE Function] [SEMANTICS translate,database,uuid]
|
||||
# @BRIEF Resolve a database UUID to a numeric database ID from Superset.
|
||||
# @PRE uuid_str is a valid Superset database UUID.
|
||||
# @POST Returns numeric database_id or raises ValueError if not found.
|
||||
# @SIDE_EFFECT Fetches databases list from Superset API.
|
||||
def resolve_database_uuid(self, uuid_str: str) -> int:
|
||||
with belief_scope("SupersetSqlLabExecutor.resolve_database_uuid"):
|
||||
client = self._get_client()
|
||||
_, databases = client.get_databases(
|
||||
query={"columns": ["id", "uuid", "database_name"]}
|
||||
)
|
||||
if not databases:
|
||||
raise ValueError("No databases found in Superset environment")
|
||||
|
||||
for db in databases:
|
||||
db_uuid = db.get("uuid") or ""
|
||||
if db_uuid.lower() == uuid_str.lower():
|
||||
db_id = db["id"]
|
||||
self._database_id = db_id
|
||||
log.reason("Resolved database ID by UUID", payload={
|
||||
"uuid": uuid_str,
|
||||
"database_id": db_id,
|
||||
"database_name": db.get("database_name"),
|
||||
})
|
||||
return db_id
|
||||
|
||||
raise ValueError(
|
||||
f"Database with UUID '{uuid_str}' not found in Superset environment"
|
||||
)
|
||||
# #endregion resolve_database_uuid
|
||||
|
||||
# #region execute_sql [C:4] [TYPE Function] [SEMANTICS translate,sql,execute]
|
||||
# @BRIEF Submit SQL to Superset SQL Lab and return execution tracking info.
|
||||
# @PRE sql is valid SQL string. database_id is a valid Superset DB ID.
|
||||
# @POST Returns execution result dict with query_id and status.
|
||||
# @SIDE_EFFECT Makes HTTP POST to /api/v1/sqllab/execute/.
|
||||
# [DEF:execute_sql:Function]
|
||||
# @PURPOSE: Submit SQL to Superset SQL Lab and return execution tracking info.
|
||||
# @PRE: sql is valid SQL string. database_id is a valid Superset DB ID.
|
||||
# @POST: Returns execution result dict with query_id and status.
|
||||
# @SIDE_EFFECT: Makes HTTP POST to /api/v1/sqllab/execute/.
|
||||
def execute_sql(
|
||||
self,
|
||||
sql: str,
|
||||
database_id: Optional[Union[int, str]] = None,
|
||||
database_id: Optional[int] = None,
|
||||
run_async: bool = True,
|
||||
) -> Dict[str, Any]:
|
||||
with belief_scope("SupersetSqlLabExecutor.execute_sql"):
|
||||
client = self._get_client()
|
||||
db_id = database_id or self._database_id
|
||||
if db_id is None:
|
||||
if not db_id:
|
||||
db_id = self.resolve_database_id()
|
||||
# Handle UUID string passed as database_id — resolve to numeric ID
|
||||
if isinstance(db_id, str):
|
||||
try:
|
||||
db_id = int(db_id)
|
||||
except (ValueError, TypeError):
|
||||
db_id = self.resolve_database_uuid(db_id)
|
||||
|
||||
log.reason("Submitting SQL to Superset SQL Lab", payload={
|
||||
logger.reason("Submitting SQL to Superset SQL Lab", {
|
||||
"database_id": db_id,
|
||||
"sql_length": len(sql),
|
||||
"run_async": run_async,
|
||||
})
|
||||
|
||||
payload = {
|
||||
"database_id": db_id,
|
||||
"sql": sql,
|
||||
"client_id": uuid.uuid4().hex[:10],
|
||||
"runAsync": run_async,
|
||||
"schema": None,
|
||||
"tab": "translation-insert",
|
||||
}
|
||||
|
||||
try:
|
||||
@@ -167,25 +127,17 @@ class SupersetSqlLabExecutor:
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
except Exception as e:
|
||||
log.explore("SQL Lab execute failed", error=str(e))
|
||||
logger.explore("SQL Lab execute failed", {"error": str(e)})
|
||||
raise ValueError(f"Superset SQL Lab execute failed: {e}")
|
||||
|
||||
# Parse response — try multiple formats
|
||||
# Parse response
|
||||
result = response if isinstance(response, dict) else {}
|
||||
query_id = (result.get("query_id")
|
||||
or result.get("id")
|
||||
or result.get("sql_lab_session_ref")
|
||||
or (result.get("result") or {}).get("query_id")
|
||||
or (result.get("result") or {}).get("id"))
|
||||
query_id = result.get("query_id") or result.get("id")
|
||||
status = result.get("status", "unknown")
|
||||
|
||||
log.reason("SQL Lab execute response", payload={
|
||||
logger.reason("SQL Lab execute response", {
|
||||
"query_id": query_id,
|
||||
"status": status,
|
||||
"has_query_id": query_id is not None,
|
||||
"has_errors": "errors" in result or "error" in result,
|
||||
"response_keys": list(result.keys()) if isinstance(result, dict) else type(result).__name__,
|
||||
"response_preview": str(result)[:1000] if isinstance(result, dict) else str(result)[:1000],
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -194,13 +146,13 @@ class SupersetSqlLabExecutor:
|
||||
"raw_response": result,
|
||||
"database_id": db_id,
|
||||
}
|
||||
# #endregion execute_sql
|
||||
# [/DEF:execute_sql:Function]
|
||||
|
||||
# #region poll_execution_status [C:4] [TYPE Function] [SEMANTICS translate,poll,status]
|
||||
# @BRIEF Poll Superset for SQL execution status until completion or timeout.
|
||||
# @PRE query_id is a valid Superset query ID.
|
||||
# @POST Returns final execution status dict with success/failure/timeout.
|
||||
# @SIDE_EFFECT Makes HTTP GET requests to Superset API.
|
||||
# [DEF:poll_execution_status:Function]
|
||||
# @PURPOSE: Poll Superset for SQL execution status until completion or timeout.
|
||||
# @PRE: query_id is a valid Superset query ID.
|
||||
# @POST: Returns final execution status dict.
|
||||
# @SIDE_EFFECT: Makes HTTP GET requests to Superset.
|
||||
def poll_execution_status(
|
||||
self,
|
||||
query_id: str,
|
||||
@@ -210,7 +162,7 @@ class SupersetSqlLabExecutor:
|
||||
with belief_scope("SupersetSqlLabExecutor.poll_execution_status"):
|
||||
client = self._get_client()
|
||||
|
||||
log.reason("Polling SQL execution status", payload={
|
||||
logger.reason("Polling SQL execution status", {
|
||||
"query_id": query_id,
|
||||
"max_polls": max_polls,
|
||||
"interval": poll_interval_seconds,
|
||||
@@ -228,7 +180,7 @@ class SupersetSqlLabExecutor:
|
||||
|
||||
# Terminal states
|
||||
if state in ("success", "finished", "completed"):
|
||||
log.reason("SQL execution completed", payload={
|
||||
logger.reason("SQL execution completed", {
|
||||
"query_id": query_id,
|
||||
"attempt": attempt + 1,
|
||||
"rows_affected": result.get("rows"),
|
||||
@@ -245,8 +197,7 @@ class SupersetSqlLabExecutor:
|
||||
|
||||
if state in ("failed", "error", "stopped"):
|
||||
error_msg = result.get("error_message", "Unknown error")
|
||||
log.explore("SQL execution failed", error="SQL query execution returned failed/error state",
|
||||
payload={
|
||||
logger.explore("SQL execution failed", {
|
||||
"query_id": query_id,
|
||||
"state": state,
|
||||
"error": error_msg,
|
||||
@@ -267,8 +218,7 @@ class SupersetSqlLabExecutor:
|
||||
time.sleep(poll_interval_seconds)
|
||||
|
||||
except Exception as e:
|
||||
log.explore("Polling error, retrying", error="Polling encountered error, will retry",
|
||||
payload={
|
||||
logger.explore("Polling error, retrying", {
|
||||
"query_id": query_id,
|
||||
"attempt": attempt + 1,
|
||||
"error": str(e),
|
||||
@@ -277,7 +227,7 @@ class SupersetSqlLabExecutor:
|
||||
continue
|
||||
|
||||
# Timeout
|
||||
log.explore("SQL execution polling timed out", error="Polling timed out", payload={
|
||||
logger.explore("SQL execution polling timed out", {
|
||||
"query_id": query_id,
|
||||
"max_polls": max_polls,
|
||||
})
|
||||
@@ -287,17 +237,17 @@ class SupersetSqlLabExecutor:
|
||||
"error_message": f"Polling timed out after {max_polls} attempts",
|
||||
"results": None,
|
||||
}
|
||||
# #endregion poll_execution_status
|
||||
# [/DEF:poll_execution_status:Function]
|
||||
|
||||
# #region execute_and_poll [C:4] [TYPE Function] [SEMANTICS translate,sql,execute,poll]
|
||||
# @BRIEF Execute SQL and wait for completion. One-shot convenience method.
|
||||
# @PRE sql is valid SQL.
|
||||
# @POST Returns final execution result.
|
||||
# @SIDE_EFFECT Makes HTTP calls to Superset API.
|
||||
# [DEF:execute_and_poll:Function]
|
||||
# @PURPOSE: Execute SQL and wait for completion. One-shot convenience method.
|
||||
# @PRE: sql is valid SQL.
|
||||
# @POST: Returns final execution result.
|
||||
# @SIDE_EFFECT: Makes HTTP calls to Superset API.
|
||||
def execute_and_poll(
|
||||
self,
|
||||
sql: str,
|
||||
database_id: Optional[Union[int, str]] = None,
|
||||
database_id: Optional[int] = None,
|
||||
max_polls: int = 60,
|
||||
poll_interval_seconds: float = 2.0,
|
||||
) -> Dict[str, Any]:
|
||||
@@ -305,11 +255,12 @@ class SupersetSqlLabExecutor:
|
||||
exec_result = self.execute_sql(
|
||||
sql=sql,
|
||||
database_id=database_id,
|
||||
run_async=True,
|
||||
)
|
||||
|
||||
query_id = exec_result.get("query_id")
|
||||
if not query_id:
|
||||
log.explore("No query_id from SQL Lab execute", error="No query_id returned", payload={
|
||||
logger.explore("No query_id from SQL Lab execute", {
|
||||
"raw_response": exec_result.get("raw_response"),
|
||||
})
|
||||
return {
|
||||
@@ -323,13 +274,13 @@ class SupersetSqlLabExecutor:
|
||||
max_polls=max_polls,
|
||||
poll_interval_seconds=poll_interval_seconds,
|
||||
)
|
||||
# #endregion execute_and_poll
|
||||
# [/DEF:execute_and_poll:Function]
|
||||
|
||||
# #region get_query_results [C:4] [TYPE Function] [SEMANTICS translate,query,results]
|
||||
# @BRIEF Fetch the results of a completed query from Superset.
|
||||
# @PRE query_id is a valid Superset query ID.
|
||||
# @POST Returns query results if available, or error dict.
|
||||
# @SIDE_EFFECT Makes HTTP GET to Superset API.
|
||||
# [DEF:get_query_results:Function]
|
||||
# @PURPOSE: Fetch the results of a completed query.
|
||||
# @PRE: query_id is a valid Superset query ID.
|
||||
# @POST: Returns query results if available.
|
||||
# @SIDE_EFFECT: Makes HTTP GET to Superset.
|
||||
def get_query_results(self, query_id: str) -> Dict[str, Any]:
|
||||
with belief_scope("SupersetSqlLabExecutor.get_query_results"):
|
||||
client = self._get_client()
|
||||
@@ -345,8 +296,7 @@ class SupersetSqlLabExecutor:
|
||||
"results": result.get("results"),
|
||||
}
|
||||
except Exception as e:
|
||||
log.explore("Failed to fetch query results", error="Failed to fetch query results from Superset",
|
||||
payload={
|
||||
logger.explore("Failed to fetch query results", {
|
||||
"query_id": query_id,
|
||||
"error": str(e),
|
||||
})
|
||||
@@ -356,7 +306,7 @@ class SupersetSqlLabExecutor:
|
||||
"error_message": str(e),
|
||||
"results": None,
|
||||
}
|
||||
# #endregion get_query_results
|
||||
# [/DEF:get_query_results:Function]
|
||||
|
||||
|
||||
# #endregion SupersetSqlLabExecutor
|
||||
|
||||
Reference in New Issue
Block a user