codeant-ai-for-open-source[bot] commented on code in PR #37516:
URL: https://github.com/apache/superset/pull/37516#discussion_r3577078352
##########
superset/common/query_context_processor.py:
##########
@@ -438,37 +774,39 @@ def get_annotation_data(self, query_obj: QueryObject) ->
dict[str, Any]:
@staticmethod
def get_native_annotation_data(query_obj: QueryObject) -> dict[str, Any]:
- annotation_data = {}
- annotation_layers = [
- layer
- for layer in query_obj.annotation_layers
- if layer["sourceType"] == "NATIVE"
- ]
- layer_ids = [layer["value"] for layer in annotation_layers]
- layer_objects = {
- layer_object.id: layer_object
- for layer_object in AnnotationLayerDAO.find_by_ids(layer_ids)
- }
+ with source_phase("planning"):
+ annotation_layers = [
+ layer
+ for layer in query_obj.annotation_layers
+ if layer["sourceType"] == "NATIVE"
+ ]
+ layer_ids = [layer["value"] for layer in annotation_layers]
+
+ with source_phase("execution"):
+ layer_objects = {
+ layer_object.id: list(layer_object.annotation)
+ for layer_object in AnnotationLayerDAO.find_by_ids(layer_ids)
+ }
- # annotations
- for layer in annotation_layers:
- layer_id = layer["value"]
- layer_name = layer["name"]
+ with source_phase("processing"):
+ annotation_data = {}
Review Comment:
**Suggestion:** Add an explicit type annotation to this newly introduced
dictionary variable to comply with the type-hint requirement for relevant
variables. [custom_rule]
**Severity Level:** Minor ๐งน
<details>
<summary><b>Why it matters? โญ </b></summary>
This is newly added Python code that introduces a local dictionary without a
type hint. The custom rule requires type hints for relevant variables that can
be annotated, so this is a real violation.
</details>
<details>
<summary><b>Rule source ๐ </b></summary>
.cursor/rules/dev-standard.mdc (line 28)
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=d18990761b7c4009aad9db1ca7d67f74&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=d18990761b7c4009aad9db1ca7d67f74&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent ๐ค </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/common/query_context_processor.py
**Line:** 792:792
**Comment:**
*Custom Rule: Add an explicit type annotation to this newly introduced
dictionary variable to comply with the type-hint requirement for relevant
variables.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=129c647c661c3aa16c5a2a91b22734853fab504732cb975750b2b07efb594103&reaction=like'>๐</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=129c647c661c3aa16c5a2a91b22734853fab504732cb975750b2b07efb594103&reaction=dislike'>๐</a>
##########
superset/commands/chart/data/get_data_command.py:
##########
@@ -25,49 +27,115 @@
ChartDataQueryFailedError,
)
from superset.common.chart_data import ChartDataResultType
+from superset.common.chart_data_timing import (
+ CacheWriteOutcome,
+ chart_data_execution_scope,
+ ChartDataExecutionResult,
+ emit_query_timing,
+)
+from superset.common.query_actions import get_effective_result_type
from superset.common.query_context import QueryContext
-from superset.exceptions import CacheLoadError
+from superset.exceptions import CacheLoadError, QueryObjectValidationError
logger = logging.getLogger(__name__)
+class ChartDataExecutionMode(str, Enum):
+ """Controls whether query data is returned or only persisted to cache."""
+
+ MATERIALIZE = "materialize"
+ CACHE_ONLY = "cache_only"
+
+
+@dataclass(frozen=True)
+class ChartDataExecutionOptions:
+ """Immutable options for one chart-data execution."""
+
+ mode: ChartDataExecutionMode = ChartDataExecutionMode.MATERIALIZE
+ force_cached: bool = False
+ cache_query_context: bool = False
+ require_cache_writes: bool = False
+
+
class ChartDataCommand(BaseCommand):
_query_context: QueryContext
def __init__(self, query_context: QueryContext):
self._query_context = query_context
def run(self, **kwargs: Any) -> dict[str, Any]:
- # caching is handled in query_context.get_df_payload
- # (also evals `force` property)
- cache_query_context = kwargs.get("cache", False)
- force_cached = kwargs.get("force_cached", False)
- try:
- payload = self._query_context.get_payload(
- cache_query_context=cache_query_context,
force_cached=force_cached
+ """Execute and return the historical payload shape."""
+ options = ChartDataExecutionOptions(
+ force_cached=kwargs.get("force_cached", False),
+ cache_query_context=kwargs.get("cache", False),
+ )
+ return self.execute(options).materialize()
+
+ def execute(
+ self, options: ChartDataExecutionOptions | None = None
+ ) -> ChartDataExecutionResult:
+ """Execute using a typed result with timing outside query payloads."""
+ options = options or ChartDataExecutionOptions()
Review Comment:
**Suggestion:** Add an explicit type annotation to `options` in `execute`
after optional-coalescing so the narrowed type is declared. [custom_rule]
**Severity Level:** Minor ๐งน
<details>
<summary><b>Why it matters? โญ </b></summary>
The new local `options` is assigned from an optional-coalescing expression
in modified code, and it has no explicit type annotation even though it can be
annotated. This matches the rule requiring type hints on relevant variables in
changed Python code.
</details>
<details>
<summary><b>Rule source ๐ </b></summary>
.cursor/rules/dev-standard.mdc (line 28)
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=e1e3597a2e2b446bb7a60976822e5ac9&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=e1e3597a2e2b446bb7a60976822e5ac9&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent ๐ค </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/commands/chart/data/get_data_command.py
**Line:** 78:78
**Comment:**
*Custom Rule: Add an explicit type annotation to `options` in `execute`
after optional-coalescing so the narrowed type is declared.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=480d1fab40f48215022c458c190d57884eb377eb8cfb6004a498db6304bcf2de&reaction=like'>๐</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=480d1fab40f48215022c458c190d57884eb377eb8cfb6004a498db6304bcf2de&reaction=dislike'>๐</a>
##########
superset/commands/chart/data/get_data_command.py:
##########
@@ -25,49 +27,115 @@
ChartDataQueryFailedError,
)
from superset.common.chart_data import ChartDataResultType
+from superset.common.chart_data_timing import (
+ CacheWriteOutcome,
+ chart_data_execution_scope,
+ ChartDataExecutionResult,
+ emit_query_timing,
+)
+from superset.common.query_actions import get_effective_result_type
from superset.common.query_context import QueryContext
-from superset.exceptions import CacheLoadError
+from superset.exceptions import CacheLoadError, QueryObjectValidationError
logger = logging.getLogger(__name__)
+class ChartDataExecutionMode(str, Enum):
+ """Controls whether query data is returned or only persisted to cache."""
+
+ MATERIALIZE = "materialize"
+ CACHE_ONLY = "cache_only"
+
+
+@dataclass(frozen=True)
+class ChartDataExecutionOptions:
+ """Immutable options for one chart-data execution."""
+
+ mode: ChartDataExecutionMode = ChartDataExecutionMode.MATERIALIZE
+ force_cached: bool = False
+ cache_query_context: bool = False
+ require_cache_writes: bool = False
+
+
class ChartDataCommand(BaseCommand):
_query_context: QueryContext
def __init__(self, query_context: QueryContext):
self._query_context = query_context
def run(self, **kwargs: Any) -> dict[str, Any]:
- # caching is handled in query_context.get_df_payload
- # (also evals `force` property)
- cache_query_context = kwargs.get("cache", False)
- force_cached = kwargs.get("force_cached", False)
- try:
- payload = self._query_context.get_payload(
- cache_query_context=cache_query_context,
force_cached=force_cached
+ """Execute and return the historical payload shape."""
+ options = ChartDataExecutionOptions(
+ force_cached=kwargs.get("force_cached", False),
+ cache_query_context=kwargs.get("cache", False),
+ )
+ return self.execute(options).materialize()
+
+ def execute(
+ self, options: ChartDataExecutionOptions | None = None
+ ) -> ChartDataExecutionResult:
+ """Execute using a typed result with timing outside query payloads."""
+ options = options or ChartDataExecutionOptions()
+ with chart_data_execution_scope() as owns_telemetry:
+ try:
+ result = self._query_context.get_payload_result(
+ cache_query_context=options.cache_query_context,
+ force_cached=options.force_cached,
+ materialize=options.mode ==
ChartDataExecutionMode.MATERIALIZE,
+ )
+ except CacheLoadError as ex:
+ raise ChartDataCacheLoadError(ex.message) from ex
+ except QueryObjectValidationError as ex:
+ raise ChartDataQueryFailedError(ex.message) from ex
+
+ execution = ChartDataExecutionResult(
+ query_context=self._query_context,
+ queries=result.queries,
+ cache_key=result.cache_key,
)
- except CacheLoadError as ex:
- raise ChartDataCacheLoadError(ex.message) from ex
-
- # Skip error check for query-only requests - errors are returned in
payload
- # This allows View Query modal to display validation errors
- for query in payload["queries"]:
- if (
- query.get("error")
- and self._query_context.result_type !=
ChartDataResultType.QUERY
+
+ if owns_telemetry:
+ for query in result.queries:
+ emit_query_timing(query.timing)
+
+ # Query-only errors remain in the payload for the View Query modal.
+ for query_obj, query in zip(
+ self._query_context.queries, result.queries, strict=True
):
- raise ChartDataQueryFailedError(
- _("Error: %(error)s", error=query["error"])
- )
+ result_type = get_effective_result_type(self._query_context,
query_obj)
+ if (
+ query.payload.get("error")
+ and result_type != ChartDataResultType.QUERY
+ ):
+ raise ChartDataQueryFailedError(
+ _("Error: %(error)s", error=query.payload["error"])
+ )
- return_value = {
- "query_context": self._query_context,
- "queries": payload["queries"],
- }
- if cache_query_context:
- return_value.update(cache_key=payload["cache_key"])
+ if options.mode == ChartDataExecutionMode.CACHE_ONLY:
+ query_cache_failed = any(
+ query.timing.cache_write_outcome ==
CacheWriteOutcome.FAILED
+ or (
+ options.require_cache_writes
+ and query.timing.cache_hit is not True
+ and query.timing.cache_write_outcome
+ != CacheWriteOutcome.SUCCEEDED
+ )
+ for query in result.queries
+ )
Review Comment:
**Suggestion:** Add a boolean type annotation for `query_cache_failed` to
satisfy the rule for relevant new variables. [custom_rule]
**Severity Level:** Minor ๐งน
<details>
<summary><b>Why it matters? โญ </b></summary>
This is a newly added boolean local variable in modified Python code with no
explicit annotation. It is a relevant variable that can be typed as `bool`, so
the suggestion correctly identifies a real rule violation.
</details>
<details>
<summary><b>Rule source ๐ </b></summary>
.cursor/rules/dev-standard.mdc (line 28)
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=f2e4213c8a54470c916a60680d28cb39&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=f2e4213c8a54470c916a60680d28cb39&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent ๐ค </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/commands/chart/data/get_data_command.py
**Line:** 115:124
**Comment:**
*Custom Rule: Add a boolean type annotation for `query_cache_failed` to
satisfy the rule for relevant new variables.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=1e098e94aa943abd5d1daec1e521bfbea33b42d1b3b2c421fea09364a052a088&reaction=like'>๐</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=1e098e94aa943abd5d1daec1e521bfbea33b42d1b3b2c421fea09364a052a088&reaction=dislike'>๐</a>
##########
superset/commands/chart/data/get_data_command.py:
##########
@@ -25,49 +27,115 @@
ChartDataQueryFailedError,
)
from superset.common.chart_data import ChartDataResultType
+from superset.common.chart_data_timing import (
+ CacheWriteOutcome,
+ chart_data_execution_scope,
+ ChartDataExecutionResult,
+ emit_query_timing,
+)
+from superset.common.query_actions import get_effective_result_type
from superset.common.query_context import QueryContext
-from superset.exceptions import CacheLoadError
+from superset.exceptions import CacheLoadError, QueryObjectValidationError
logger = logging.getLogger(__name__)
+class ChartDataExecutionMode(str, Enum):
+ """Controls whether query data is returned or only persisted to cache."""
+
+ MATERIALIZE = "materialize"
+ CACHE_ONLY = "cache_only"
+
+
+@dataclass(frozen=True)
+class ChartDataExecutionOptions:
+ """Immutable options for one chart-data execution."""
+
+ mode: ChartDataExecutionMode = ChartDataExecutionMode.MATERIALIZE
+ force_cached: bool = False
+ cache_query_context: bool = False
+ require_cache_writes: bool = False
+
+
class ChartDataCommand(BaseCommand):
_query_context: QueryContext
def __init__(self, query_context: QueryContext):
self._query_context = query_context
def run(self, **kwargs: Any) -> dict[str, Any]:
- # caching is handled in query_context.get_df_payload
- # (also evals `force` property)
- cache_query_context = kwargs.get("cache", False)
- force_cached = kwargs.get("force_cached", False)
- try:
- payload = self._query_context.get_payload(
- cache_query_context=cache_query_context,
force_cached=force_cached
+ """Execute and return the historical payload shape."""
+ options = ChartDataExecutionOptions(
+ force_cached=kwargs.get("force_cached", False),
+ cache_query_context=kwargs.get("cache", False),
+ )
+ return self.execute(options).materialize()
+
+ def execute(
+ self, options: ChartDataExecutionOptions | None = None
+ ) -> ChartDataExecutionResult:
+ """Execute using a typed result with timing outside query payloads."""
+ options = options or ChartDataExecutionOptions()
+ with chart_data_execution_scope() as owns_telemetry:
+ try:
+ result = self._query_context.get_payload_result(
+ cache_query_context=options.cache_query_context,
+ force_cached=options.force_cached,
+ materialize=options.mode ==
ChartDataExecutionMode.MATERIALIZE,
+ )
+ except CacheLoadError as ex:
+ raise ChartDataCacheLoadError(ex.message) from ex
+ except QueryObjectValidationError as ex:
+ raise ChartDataQueryFailedError(ex.message) from ex
+
+ execution = ChartDataExecutionResult(
+ query_context=self._query_context,
+ queries=result.queries,
+ cache_key=result.cache_key,
)
- except CacheLoadError as ex:
- raise ChartDataCacheLoadError(ex.message) from ex
-
- # Skip error check for query-only requests - errors are returned in
payload
- # This allows View Query modal to display validation errors
- for query in payload["queries"]:
- if (
- query.get("error")
- and self._query_context.result_type !=
ChartDataResultType.QUERY
+
+ if owns_telemetry:
+ for query in result.queries:
+ emit_query_timing(query.timing)
+
+ # Query-only errors remain in the payload for the View Query modal.
+ for query_obj, query in zip(
+ self._query_context.queries, result.queries, strict=True
):
- raise ChartDataQueryFailedError(
- _("Error: %(error)s", error=query["error"])
- )
+ result_type = get_effective_result_type(self._query_context,
query_obj)
+ if (
+ query.payload.get("error")
+ and result_type != ChartDataResultType.QUERY
+ ):
+ raise ChartDataQueryFailedError(
+ _("Error: %(error)s", error=query.payload["error"])
+ )
- return_value = {
- "query_context": self._query_context,
- "queries": payload["queries"],
- }
- if cache_query_context:
- return_value.update(cache_key=payload["cache_key"])
+ if options.mode == ChartDataExecutionMode.CACHE_ONLY:
+ query_cache_failed = any(
+ query.timing.cache_write_outcome ==
CacheWriteOutcome.FAILED
+ or (
+ options.require_cache_writes
+ and query.timing.cache_hit is not True
+ and query.timing.cache_write_outcome
+ != CacheWriteOutcome.SUCCEEDED
+ )
+ for query in result.queries
+ )
+ context_cache_failed = (
+ result.context_cache_write_outcome ==
CacheWriteOutcome.FAILED
+ or (
+ options.require_cache_writes
+ and result.context_cache_write_outcome
+ != CacheWriteOutcome.SUCCEEDED
+ )
+ )
Review Comment:
**Suggestion:** Add a boolean type annotation for `context_cache_failed` to
keep new cache-failure state variables fully typed. [custom_rule]
**Severity Level:** Minor ๐งน
<details>
<summary><b>Why it matters? โญ </b></summary>
This is another new boolean local variable in the changed code, and it is
unannotated even though it can be typed. That satisfies the type-hint rule
violation for relevant variables.
</details>
<details>
<summary><b>Rule source ๐ </b></summary>
.cursor/rules/dev-standard.mdc (line 28)
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=5b43d1de6c1143a5a214b8abc00944cc&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=5b43d1de6c1143a5a214b8abc00944cc&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent ๐ค </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/commands/chart/data/get_data_command.py
**Line:** 125:132
**Comment:**
*Custom Rule: Add a boolean type annotation for `context_cache_failed`
to keep new cache-failure state variables fully typed.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=d87c5c0b97dcac72c77df4a2ac6ec11441ae75fe73f214a779ad69f3700ff9bd&reaction=like'>๐</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=d87c5c0b97dcac72c77df4a2ac6ec11441ae75fe73f214a779ad69f3700ff9bd&reaction=dislike'>๐</a>
##########
superset/common/chart_data_timing.py:
##########
@@ -0,0 +1,803 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import logging
+import time
+from collections.abc import Callable, Iterator, Mapping
+from contextlib import contextmanager
+from contextvars import ContextVar
+from dataclasses import dataclass, field, replace
+from enum import Enum
+from typing import Any, Literal, TYPE_CHECKING
+
+from flask import current_app
+
+if TYPE_CHECKING:
+ from superset.common.query_context import QueryContext
+
+NANOSECONDS_PER_MILLISECOND = 1_000_000
+SOURCE_TRACE_VERSION = 2
+MAX_SOURCE_TRACE_DEPTH = 8
+MAX_SOURCE_TRACE_NODES = 64
+
+SourcePhase = Literal["planning", "execution", "processing"]
+
+logger: logging.Logger = logging.getLogger(__name__)
+
+
+class SourceKind(str, Enum):
+ """The reason a data source was contacted."""
+
+ PRIMARY = "primary"
+ SERIES_LIMIT = "series_limit"
+ TIME_OFFSET = "time_offset"
+ ANNOTATION = "annotation"
+ CURRENCY_DETECTION = "currency_detection"
+
+
+class SourceProvider(str, Enum):
+ """The implementation family that supplied data."""
+
+ SQL = "sql"
+ SEMANTIC = "semantic"
+ OTHER = "other"
+
+
+class SourceOrigin(str, Enum):
+ """Whether a source trace was captured during this execution or
replayed."""
+
+ CURRENT = "current"
+ CACHE = "cache"
+
+
+class CacheWriteOutcome(str, Enum):
+ """Outcome of a requested cache write."""
+
+ SUCCEEDED = "succeeded"
+ FAILED = "failed"
+ SKIPPED = "skipped"
+ NOT_ATTEMPTED = "not_attempted"
+
+
+_CACHE_WRITE_OUTCOME_PRIORITY: dict[CacheWriteOutcome, int] = {
+ CacheWriteOutcome.NOT_ATTEMPTED: 0,
+ CacheWriteOutcome.SUCCEEDED: 1,
+ CacheWriteOutcome.SKIPPED: 2,
+ CacheWriteOutcome.FAILED: 3,
+}
+
+
+def combine_cache_write_outcomes(
+ *outcomes: CacheWriteOutcome,
+) -> CacheWriteOutcome:
+ """Return the most restrictive outcome for a compound acquisition."""
+
+ return max(outcomes, key=_CACHE_WRITE_OUTCOME_PRIORITY.__getitem__)
+
+
+def _elapsed_ns(start_ns: int, clock: Callable[[], int]) -> int:
+ return max(0, clock() - start_ns)
+
+
+def _to_ms(value_ns: int | None) -> float | None:
+ if value_ns is None:
+ return None
+ return round(value_ns / NANOSECONDS_PER_MILLISECOND, 2)
+
+
+@dataclass(frozen=True)
+class SourceTiming:
+ """Bounded, content-free timing trace for one source operation."""
+
+ kind: SourceKind
+ provider: SourceProvider
+ origin: SourceOrigin
+ planning_ns: int = 0
+ execution_ns: int = 0
+ processing_ns: int = 0
+ total_ns: int = 0
+ children: tuple[SourceTiming, ...] = ()
+ truncated: bool = False
+
+ def as_public_dict(self) -> dict[str, Any]:
+ """Return the stable public representation in milliseconds."""
+ return {
+ "kind": self.kind.value,
+ "provider": self.provider.value,
+ "origin": self.origin.value,
+ "planning_ms": _to_ms(self.planning_ns),
+ "execution_ms": _to_ms(self.execution_ns),
+ "processing_ms": _to_ms(self.processing_ns),
+ "total_ms": _to_ms(self.total_ns),
+ "children": [child.as_public_dict() for child in self.children],
+ "truncated": self.truncated,
+ }
+
+ def as_cache_value(self) -> dict[str, Any]:
+ """Return a versioned cache representation with integer durations."""
+ return {
+ "kind": self.kind.value,
+ "provider": self.provider.value,
+ "planning_ns": self.planning_ns,
+ "execution_ns": self.execution_ns,
+ "processing_ns": self.processing_ns,
+ "total_ns": self.total_ns,
+ "children": [child.as_cache_value() for child in self.children],
+ "truncated": self.truncated,
+ }
+
+
+@dataclass(frozen=True)
+class QueryAcquisitionTiming:
+ """Timing captured before response materialization."""
+
+ cache_key_ns: int
+ cache_read_ns: int
+ source_ns: int
+ cache_write_ns: int | None
+ cache_write_outcome: CacheWriteOutcome
+ cache_hit: bool | None
+ sources: tuple[SourceTiming, ...] | None
+ elapsed_ns: int | None = None
+
+ @property
+ def total_ns(self) -> int:
+ if self.elapsed_ns is not None:
+ return self.elapsed_ns
+ return (
+ self.cache_key_ns
+ + self.cache_read_ns
+ + self.source_ns
+ + (self.cache_write_ns or 0)
+ )
+
+ def materialized(self, materialization_ns: int) -> QueryTiming:
+ """Create the completed immutable query timing snapshot."""
+ return QueryTiming(
+ cache_key_ns=self.cache_key_ns,
+ cache_read_ns=self.cache_read_ns,
+ source_ns=self.source_ns,
+ cache_write_ns=self.cache_write_ns,
+ cache_write_outcome=self.cache_write_outcome,
+ materialization_ns=materialization_ns,
+ total_ns=self.total_ns + materialization_ns,
+ cache_hit=self.cache_hit,
+ sources=self.sources,
+ )
+
+
+@dataclass(frozen=True)
+class QueryAcquisitionResult:
+ """A dataframe payload paired with acquisition timing."""
+
+ payload: dict[str, Any]
+ timing: QueryAcquisitionTiming
+
+
+def combine_acquisition_timings(
+ primary: QueryAcquisitionTiming,
+ dependency: QueryAcquisitionTiming,
+) -> QueryAcquisitionTiming:
+ """Combine sequential acquisition phases without overlapping query
totals."""
+
+ def combine_optional_int(left: int | None, right: int | None) -> int |
None:
+ if left is None and right is None:
+ return None
+ return (left or 0) + (right or 0)
+
+ cache_hits = {primary.cache_hit, dependency.cache_hit}
+ if False in cache_hits:
+ cache_hit: bool | None = False
+ elif cache_hits == {True}:
+ cache_hit = True
+ else:
+ cache_hit = None
+
+ known_sources = bound_source_trace(
+ tuple(
+ source
+ for sources in (primary.sources, dependency.sources)
+ if sources is not None
+ for source in sources
+ )
+ )
+ sources = (
+ None
+ if primary.sources is None and dependency.sources is None and not
known_sources
+ else known_sources
+ )
+ return QueryAcquisitionTiming(
+ cache_key_ns=primary.cache_key_ns + dependency.cache_key_ns,
+ cache_read_ns=primary.cache_read_ns + dependency.cache_read_ns,
+ source_ns=primary.source_ns + dependency.source_ns,
+ cache_write_ns=combine_optional_int(
+ primary.cache_write_ns, dependency.cache_write_ns
+ ),
+ cache_write_outcome=combine_cache_write_outcomes(
+ primary.cache_write_outcome,
+ dependency.cache_write_outcome,
+ ),
+ cache_hit=cache_hit,
+ sources=sources,
+ elapsed_ns=primary.total_ns + dependency.total_ns,
+ )
+
+
+@dataclass(frozen=True)
+class QueryTiming:
+ """Completed timing snapshot for one independently materialized query."""
+
+ cache_key_ns: int
+ cache_read_ns: int
+ source_ns: int
+ cache_write_ns: int | None
+ cache_write_outcome: CacheWriteOutcome
+ materialization_ns: int
+ total_ns: int
+ cache_hit: bool | None
+ sources: tuple[SourceTiming, ...] | None
+
+ def as_public_dict(self) -> dict[str, Any]:
+ """Return the versioned chart-data API representation."""
+ return {
+ "version": 1,
+ "query": {
+ "cache_key_ms": _to_ms(self.cache_key_ns),
+ "cache_read_ms": _to_ms(self.cache_read_ns),
+ "source_ms": _to_ms(self.source_ns),
+ "cache_write_ms": _to_ms(self.cache_write_ns),
+ "cache_write_status": self.cache_write_outcome.value,
+ "materialization_ms": _to_ms(self.materialization_ns),
+ "total_ms": _to_ms(self.total_ns),
+ "cache_hit": self.cache_hit,
+ },
+ "sources": (
+ None
+ if self.sources is None
+ else [
+ source.as_public_dict()
+ for source in bound_source_trace(self.sources)
+ ]
+ ),
+ }
+
+
+@dataclass(frozen=True)
+class QueryDataResult:
+ """A query payload paired with its completed timing sidecar."""
+
+ payload: dict[str, Any]
+ timing: QueryTiming
+
+
+@dataclass(frozen=True)
+class QueryContextExecutionResult:
+ """Execution result before the command attaches its query context."""
+
+ queries: tuple[QueryDataResult, ...]
+ cache_key: str | None = None
+ context_cache_write_outcome: CacheWriteOutcome =
CacheWriteOutcome.NOT_ATTEMPTED
+
+
+@dataclass(frozen=True)
+class ChartDataExecutionResult:
+ """Typed result of executing a chart data command."""
+
+ query_context: QueryContext
+ queries: tuple[QueryDataResult, ...]
+ cache_key: str | None = None
+
+ def materialize(self) -> dict[str, Any]:
+ """Return the historical command payload without timing metadata."""
+ result: dict[str, Any] = {
+ "query_context": self.query_context,
+ # Response projection adds timing and client-processing fields at
the
+ # query-dict level. Keep those mutations outside the reusable
execution
+ # result without copying potentially large data values.
+ "queries": [dict(query.payload) for query in self.queries],
+ }
+ if self.cache_key is not None:
+ result["cache_key"] = self.cache_key
+ return result
+
+
+@dataclass
+class _MutableSourceTiming:
+ kind: SourceKind
+ provider: SourceProvider
+ start_ns: int
+ planning_ns: int = 0
+ execution_ns: int = 0
+ processing_ns: int = 0
+ children: list[SourceTiming] = field(default_factory=list)
+ truncated: bool = False
+
+
+@dataclass
+class _MutableSourcePhase:
+ node: _MutableSourceTiming
+ phase: SourcePhase
+ active_since_ns: int | None
+
+
+class _SourceParseState(str, Enum):
+ VALID = "valid"
+ TRUNCATED = "truncated"
+ INVALID = "invalid"
+
+
+@dataclass(frozen=True)
+class _SourceParseResult:
+ state: _SourceParseState
+ source: SourceTiming | None = None
+
+
+@dataclass(frozen=True)
+class _ParsedSourceFields:
+ kind: SourceKind
+ provider: SourceProvider
+ planning_ns: int
+ execution_ns: int
+ processing_ns: int
+ total_ns: int
+ children: list[Any]
+ truncated: bool
+
+
+_active_source_collector: ContextVar[SourceTimingCollector | None] =
ContextVar(
+ "chart_data_source_timing_collector",
+ default=None,
+)
+_chart_data_execution_depth: ContextVar[int] = ContextVar(
+ "chart_data_execution_depth", default=0
+)
+
+
+@contextmanager
+def chart_data_execution_scope() -> Iterator[bool]:
+ """Identify the outer command execution that owns telemetry emission."""
+
+ depth = _chart_data_execution_depth.get()
+ token = _chart_data_execution_depth.set(depth + 1)
+ try:
+ yield depth == 0
+ finally:
+ _chart_data_execution_depth.reset(token)
+
+
+class SourceTimingCollector:
+ """Collect a bounded source tree without retaining query content."""
+
+ def __init__(self, clock: Callable[[], int] = time.perf_counter_ns) ->
None:
+ self._clock = clock
Review Comment:
**Suggestion:** Add an explicit instance-attribute type annotation for this
callable clock field when assigning it in the constructor. [custom_rule]
**Severity Level:** Minor ๐งน
<details>
<summary><b>Why it matters? โญ </b></summary>
The rule requires type hints on relevant Python variables that can be
annotated. This constructor assigns an instance attribute without an explicit
type annotation, so the suggestion correctly identifies a real violation.
</details>
<details>
<summary><b>Rule source ๐ </b></summary>
.cursor/rules/dev-standard.mdc (line 28)
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=5510a97380934406aed26c1c1b547699&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=5510a97380934406aed26c1c1b547699&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent ๐ค </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/common/chart_data_timing.py
**Line:** 387:387
**Comment:**
*Custom Rule: Add an explicit instance-attribute type annotation for
this callable clock field when assigning it in the constructor.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=9f9c3ba69d98d797d1474853640db8bd51751022a9d1a046d882d5dbc12b1616&reaction=like'>๐</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=9f9c3ba69d98d797d1474853640db8bd51751022a9d1a046d882d5dbc12b1616&reaction=dislike'>๐</a>
##########
superset/common/chart_data_timing.py:
##########
@@ -0,0 +1,803 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import logging
+import time
+from collections.abc import Callable, Iterator, Mapping
+from contextlib import contextmanager
+from contextvars import ContextVar
+from dataclasses import dataclass, field, replace
+from enum import Enum
+from typing import Any, Literal, TYPE_CHECKING
+
+from flask import current_app
+
+if TYPE_CHECKING:
+ from superset.common.query_context import QueryContext
+
+NANOSECONDS_PER_MILLISECOND = 1_000_000
+SOURCE_TRACE_VERSION = 2
+MAX_SOURCE_TRACE_DEPTH = 8
+MAX_SOURCE_TRACE_NODES = 64
+
+SourcePhase = Literal["planning", "execution", "processing"]
+
+logger: logging.Logger = logging.getLogger(__name__)
+
+
+class SourceKind(str, Enum):
+ """The reason a data source was contacted."""
+
+ PRIMARY = "primary"
+ SERIES_LIMIT = "series_limit"
+ TIME_OFFSET = "time_offset"
+ ANNOTATION = "annotation"
+ CURRENCY_DETECTION = "currency_detection"
+
+
+class SourceProvider(str, Enum):
+ """The implementation family that supplied data."""
+
+ SQL = "sql"
+ SEMANTIC = "semantic"
+ OTHER = "other"
+
+
+class SourceOrigin(str, Enum):
+ """Whether a source trace was captured during this execution or
replayed."""
+
+ CURRENT = "current"
+ CACHE = "cache"
+
+
+class CacheWriteOutcome(str, Enum):
+ """Outcome of a requested cache write."""
+
+ SUCCEEDED = "succeeded"
+ FAILED = "failed"
+ SKIPPED = "skipped"
+ NOT_ATTEMPTED = "not_attempted"
+
+
+_CACHE_WRITE_OUTCOME_PRIORITY: dict[CacheWriteOutcome, int] = {
+ CacheWriteOutcome.NOT_ATTEMPTED: 0,
+ CacheWriteOutcome.SUCCEEDED: 1,
+ CacheWriteOutcome.SKIPPED: 2,
+ CacheWriteOutcome.FAILED: 3,
+}
+
+
+def combine_cache_write_outcomes(
+ *outcomes: CacheWriteOutcome,
+) -> CacheWriteOutcome:
+ """Return the most restrictive outcome for a compound acquisition."""
+
+ return max(outcomes, key=_CACHE_WRITE_OUTCOME_PRIORITY.__getitem__)
+
+
+def _elapsed_ns(start_ns: int, clock: Callable[[], int]) -> int:
+ return max(0, clock() - start_ns)
+
+
+def _to_ms(value_ns: int | None) -> float | None:
+ if value_ns is None:
+ return None
+ return round(value_ns / NANOSECONDS_PER_MILLISECOND, 2)
+
+
+@dataclass(frozen=True)
+class SourceTiming:
+ """Bounded, content-free timing trace for one source operation."""
+
+ kind: SourceKind
+ provider: SourceProvider
+ origin: SourceOrigin
+ planning_ns: int = 0
+ execution_ns: int = 0
+ processing_ns: int = 0
+ total_ns: int = 0
+ children: tuple[SourceTiming, ...] = ()
+ truncated: bool = False
+
+ def as_public_dict(self) -> dict[str, Any]:
+ """Return the stable public representation in milliseconds."""
+ return {
+ "kind": self.kind.value,
+ "provider": self.provider.value,
+ "origin": self.origin.value,
+ "planning_ms": _to_ms(self.planning_ns),
+ "execution_ms": _to_ms(self.execution_ns),
+ "processing_ms": _to_ms(self.processing_ns),
+ "total_ms": _to_ms(self.total_ns),
+ "children": [child.as_public_dict() for child in self.children],
+ "truncated": self.truncated,
+ }
+
+ def as_cache_value(self) -> dict[str, Any]:
+ """Return a versioned cache representation with integer durations."""
+ return {
+ "kind": self.kind.value,
+ "provider": self.provider.value,
+ "planning_ns": self.planning_ns,
+ "execution_ns": self.execution_ns,
+ "processing_ns": self.processing_ns,
+ "total_ns": self.total_ns,
+ "children": [child.as_cache_value() for child in self.children],
+ "truncated": self.truncated,
+ }
+
+
+@dataclass(frozen=True)
+class QueryAcquisitionTiming:
+ """Timing captured before response materialization."""
+
+ cache_key_ns: int
+ cache_read_ns: int
+ source_ns: int
+ cache_write_ns: int | None
+ cache_write_outcome: CacheWriteOutcome
+ cache_hit: bool | None
+ sources: tuple[SourceTiming, ...] | None
+ elapsed_ns: int | None = None
+
+ @property
+ def total_ns(self) -> int:
+ if self.elapsed_ns is not None:
+ return self.elapsed_ns
+ return (
+ self.cache_key_ns
+ + self.cache_read_ns
+ + self.source_ns
+ + (self.cache_write_ns or 0)
+ )
+
+ def materialized(self, materialization_ns: int) -> QueryTiming:
+ """Create the completed immutable query timing snapshot."""
+ return QueryTiming(
+ cache_key_ns=self.cache_key_ns,
+ cache_read_ns=self.cache_read_ns,
+ source_ns=self.source_ns,
+ cache_write_ns=self.cache_write_ns,
+ cache_write_outcome=self.cache_write_outcome,
+ materialization_ns=materialization_ns,
+ total_ns=self.total_ns + materialization_ns,
+ cache_hit=self.cache_hit,
+ sources=self.sources,
+ )
+
+
+@dataclass(frozen=True)
+class QueryAcquisitionResult:
+ """A dataframe payload paired with acquisition timing."""
+
+ payload: dict[str, Any]
+ timing: QueryAcquisitionTiming
+
+
+def combine_acquisition_timings(
+ primary: QueryAcquisitionTiming,
+ dependency: QueryAcquisitionTiming,
+) -> QueryAcquisitionTiming:
+ """Combine sequential acquisition phases without overlapping query
totals."""
+
+ def combine_optional_int(left: int | None, right: int | None) -> int |
None:
+ if left is None and right is None:
+ return None
+ return (left or 0) + (right or 0)
+
+ cache_hits = {primary.cache_hit, dependency.cache_hit}
+ if False in cache_hits:
+ cache_hit: bool | None = False
+ elif cache_hits == {True}:
+ cache_hit = True
+ else:
+ cache_hit = None
+
+ known_sources = bound_source_trace(
+ tuple(
+ source
+ for sources in (primary.sources, dependency.sources)
+ if sources is not None
+ for source in sources
+ )
+ )
+ sources = (
+ None
+ if primary.sources is None and dependency.sources is None and not
known_sources
+ else known_sources
+ )
+ return QueryAcquisitionTiming(
+ cache_key_ns=primary.cache_key_ns + dependency.cache_key_ns,
+ cache_read_ns=primary.cache_read_ns + dependency.cache_read_ns,
+ source_ns=primary.source_ns + dependency.source_ns,
+ cache_write_ns=combine_optional_int(
+ primary.cache_write_ns, dependency.cache_write_ns
+ ),
+ cache_write_outcome=combine_cache_write_outcomes(
+ primary.cache_write_outcome,
+ dependency.cache_write_outcome,
+ ),
+ cache_hit=cache_hit,
+ sources=sources,
+ elapsed_ns=primary.total_ns + dependency.total_ns,
+ )
+
+
+@dataclass(frozen=True)
+class QueryTiming:
+ """Completed timing snapshot for one independently materialized query."""
+
+ cache_key_ns: int
+ cache_read_ns: int
+ source_ns: int
+ cache_write_ns: int | None
+ cache_write_outcome: CacheWriteOutcome
+ materialization_ns: int
+ total_ns: int
+ cache_hit: bool | None
+ sources: tuple[SourceTiming, ...] | None
+
+ def as_public_dict(self) -> dict[str, Any]:
+ """Return the versioned chart-data API representation."""
+ return {
+ "version": 1,
+ "query": {
+ "cache_key_ms": _to_ms(self.cache_key_ns),
+ "cache_read_ms": _to_ms(self.cache_read_ns),
+ "source_ms": _to_ms(self.source_ns),
+ "cache_write_ms": _to_ms(self.cache_write_ns),
+ "cache_write_status": self.cache_write_outcome.value,
+ "materialization_ms": _to_ms(self.materialization_ns),
+ "total_ms": _to_ms(self.total_ns),
+ "cache_hit": self.cache_hit,
+ },
+ "sources": (
+ None
+ if self.sources is None
+ else [
+ source.as_public_dict()
+ for source in bound_source_trace(self.sources)
+ ]
+ ),
+ }
+
+
+@dataclass(frozen=True)
+class QueryDataResult:
+ """A query payload paired with its completed timing sidecar."""
+
+ payload: dict[str, Any]
+ timing: QueryTiming
+
+
+@dataclass(frozen=True)
+class QueryContextExecutionResult:
+ """Execution result before the command attaches its query context."""
+
+ queries: tuple[QueryDataResult, ...]
+ cache_key: str | None = None
+ context_cache_write_outcome: CacheWriteOutcome =
CacheWriteOutcome.NOT_ATTEMPTED
+
+
+@dataclass(frozen=True)
+class ChartDataExecutionResult:
+ """Typed result of executing a chart data command."""
+
+ query_context: QueryContext
+ queries: tuple[QueryDataResult, ...]
+ cache_key: str | None = None
+
+ def materialize(self) -> dict[str, Any]:
+ """Return the historical command payload without timing metadata."""
+ result: dict[str, Any] = {
+ "query_context": self.query_context,
+ # Response projection adds timing and client-processing fields at
the
+ # query-dict level. Keep those mutations outside the reusable
execution
+ # result without copying potentially large data values.
+ "queries": [dict(query.payload) for query in self.queries],
+ }
+ if self.cache_key is not None:
+ result["cache_key"] = self.cache_key
+ return result
+
+
+@dataclass
+class _MutableSourceTiming:
+ kind: SourceKind
+ provider: SourceProvider
+ start_ns: int
+ planning_ns: int = 0
+ execution_ns: int = 0
+ processing_ns: int = 0
+ children: list[SourceTiming] = field(default_factory=list)
+ truncated: bool = False
+
+
+@dataclass
+class _MutableSourcePhase:
+ node: _MutableSourceTiming
+ phase: SourcePhase
+ active_since_ns: int | None
+
+
+class _SourceParseState(str, Enum):
+ VALID = "valid"
+ TRUNCATED = "truncated"
+ INVALID = "invalid"
+
+
+@dataclass(frozen=True)
+class _SourceParseResult:
+ state: _SourceParseState
+ source: SourceTiming | None = None
+
+
+@dataclass(frozen=True)
+class _ParsedSourceFields:
+ kind: SourceKind
+ provider: SourceProvider
+ planning_ns: int
+ execution_ns: int
+ processing_ns: int
+ total_ns: int
+ children: list[Any]
+ truncated: bool
+
+
+_active_source_collector: ContextVar[SourceTimingCollector | None] =
ContextVar(
+ "chart_data_source_timing_collector",
+ default=None,
+)
+_chart_data_execution_depth: ContextVar[int] = ContextVar(
+ "chart_data_execution_depth", default=0
+)
+
+
+@contextmanager
+def chart_data_execution_scope() -> Iterator[bool]:
+ """Identify the outer command execution that owns telemetry emission."""
+
+ depth = _chart_data_execution_depth.get()
+ token = _chart_data_execution_depth.set(depth + 1)
+ try:
+ yield depth == 0
+ finally:
+ _chart_data_execution_depth.reset(token)
+
+
+class SourceTimingCollector:
+ """Collect a bounded source tree without retaining query content."""
+
+ def __init__(self, clock: Callable[[], int] = time.perf_counter_ns) ->
None:
+ self._clock = clock
+ self._roots: list[SourceTiming] = []
+ self._stack: list[_MutableSourceTiming | None] = []
+ self._phases: list[_MutableSourcePhase] = []
+ self._activation_parents: list[SourceTimingCollector | None] = []
+ self._node_count = 0
Review Comment:
**Suggestion:** Add an explicit `int` type annotation for this
constructor-assigned counter attribute. [custom_rule]
**Severity Level:** Minor ๐งน
<details>
<summary><b>Why it matters? โญ </b></summary>
This is another constructor-assigned instance attribute with no explicit
type hint. Since it is a relevant variable that can be annotated, the
suggestion matches the type-hint requirement.
</details>
<details>
<summary><b>Rule source ๐ </b></summary>
.cursor/rules/dev-standard.mdc (line 28)
</details>
[](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=ee7be46263ae42b3bf387462637f593b&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
[](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=ee7be46263ae42b3bf387462637f593b&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent ๐ค </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset/common/chart_data_timing.py
**Line:** 392:392
**Comment:**
*Custom Rule: Add an explicit `int` type annotation for this
constructor-assigned counter attribute.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask
user if the user wants to fix the rest of the comments as well. if said yes,
then fetch all the comments validate the correctness and implement a minimal fix
```
</details>
<a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=e78f58cb01232bbe527d40bafcbae7f83c832fc77f5d7eb1d3a1e2ce9a3bbb4f&reaction=like'>๐</a>
| <a
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37516&comment_hash=e78f58cb01232bbe527d40bafcbae7f83c832fc77f5d7eb1d3a1e2ce9a3bbb4f&reaction=dislike'>๐</a>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]