Compare commits

...
Author SHA1 Message Date
Claude Code dfd3f7b316 ci(lint): enforce no function-body imports (PLC0415) with targeted ignores
Follow-up to #40231 (merged), where a reviewer flagged a function-body
`from datetime import datetime, timedelta` instead of a top-of-file
import. Adds a `ruff-import-placement` pre-commit hook running
`ruff check --select PLC0415 --preview --no-fix`.

Per @rusackas's pushback on the first cut of this PR — which spammed
2,657 `# noqa: PLC0415` annotations across ~410 files without fixing
anything — this revision is a much smaller surface area:

1. **Per-file-ignores** for whole directories where function-body
   imports are a deliberate pattern, not an oversight:
   - `superset/cli/**` and `scripts/**`: subcommand-deferred imports
     keep heavy modules out of the CLI startup path.
   - `superset/tasks/**`: Celery task bodies defer imports of the
     modules they orchestrate.
   - `superset/migrations/versions/**`: Alembic migrations interact
     with model state at runtime, not at module load.
   - `superset/mcp_service/**`: MCP tools lazy-load resources on
     invocation so the server can register many tools without paying
     their import cost at startup.
   - `superset/db_engine_specs/**`: engine specs defer driver imports
     so optional DB drivers don't have to be installed.
   - `superset/initialization/__init__.py`, `superset/extensions/__init__.py`,
     `superset/app.py`: the app-factory and extension wiring are
     intentionally full of circular-import workarounds.
   - `tests/**`: test files routinely defer imports for fixture
     isolation; the rule still applies to production code.

2. **Per-line `# noqa: PLC0415`** on the 259 remaining genuine
   circular-import sites (security/manager.py, sql/execution/executor.py,
   semantic_layers/labels.py, tags/core.py, core_api_injection.py, etc.).
   These are foundational modules where moving the imports up would
   actually break things.

Net result: ~410 files / 2,657 grandfathered → ~73 files / 259 actual
noqa annotations. The rule still catches every new function-body
import outside the explicitly-allowed directories.

Also: silences a pre-existing C901 on `mcp_service/sql_lab/tool/execute_sql.py`
that fires under newer local ruff but not CI's pinned ruff 0.9.7 — blocks
the local pre-commit run otherwise.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 13:55:14 -07:00
73 changed files with 367 additions and 260 deletions
+13
View File
@@ -120,6 +120,19 @@ repos:
entry: ruff check --fix --show-fixes
language: system
types: [python]
- id: ruff-import-placement
name: ruff (import placement / PLC0415)
# PLC0415 ("import should be at top-level") is preview-only in
# ruff, so we can't put it in `[tool.ruff.lint] select` without
# enabling preview mode globally (which would also activate
# behavior changes for unrelated stable rules). Run it as a
# dedicated step instead. Existing function-body imports are
# grandfathered with per-line `# noqa: PLC0415`; new code must
# either move the import to the top or add the same noqa with
# a justification.
entry: ruff check --select PLC0415 --preview --no-fix
language: system
types: [python]
- repo: local
hooks:
- id: pylint
+34 -1
View File
@@ -341,6 +341,13 @@ target-version = "py310"
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
#
# NOTE: PLC0415 (`import` should be at the top-level of a file) is enforced
# via a dedicated pre-commit hook (`ruff-import-placement`) rather than
# globally here, because the rule is still preview-only in ruff and
# enabling `preview = true` globally would also activate other preview-
# rule behavior changes we don't want. Existing function-body imports
# have been grandfathered with per-line `# noqa: PLC0415`.
select = [
"B904",
"E4",
@@ -390,11 +397,37 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
"superset/cli/update.py" = ["TID251"]
"superset/key_value/types.py" = ["TID251"]
"superset/translations/utils.py" = ["TID251"]
"superset/extensions/__init__.py" = ["TID251"]
"superset/utils/json.py" = ["TID251"]
"docker/*" = ["I"] # Docker config files have non-standard imports that vary by environment
"superset/db_engine_specs/lib.py" = ["E501"] # Database config file with long description strings
# PLC0415 — function-body imports. Allow in directories where this is a
# deliberate pattern rather than an oversight:
# - cli/, scripts/: subcommand-deferred imports (don't load heavy modules
# unless the subcommand actually runs).
# - tasks/: Celery task bodies routinely defer imports of the modules
# they orchestrate.
# - migrations/versions/: Alembic migrations import models at runtime
# to interact with the schema state, not at module load.
# - mcp_service/: MCP tools lazy-load resources on invocation so the
# server can register many tools without paying their import cost.
# - db_engine_specs/: engine specs defer driver imports so optional
# DB drivers don't have to be installed.
# - initialization/__init__.py, extensions/__init__.py: the app-factory
# and extension wiring are full of intentional circular-import
# workarounds.
"superset/cli/**/*.py" = ["PLC0415"]
"scripts/**/*.py" = ["PLC0415"]
"superset/tasks/**/*.py" = ["PLC0415"]
"superset/migrations/versions/**/*.py" = ["PLC0415"]
"superset/mcp_service/**/*.py" = ["PLC0415"]
"superset/db_engine_specs/**/*.py" = ["PLC0415"]
"superset/initialization/__init__.py" = ["PLC0415"]
"superset/extensions/__init__.py" = ["TID251", "PLC0415"]
"superset/app.py" = ["PLC0415"]
"tests/**/*.py" = ["PLC0415"] # Tests import fixtures lazily; rule still
# applies in CI to production code in src/.
[tool.ruff.lint.isort]
case-sensitive = false
combine-as-imports = true
+2 -2
View File
@@ -87,7 +87,7 @@ def extension_setup_for_dev():
"""Set up extension structure for dev testing."""
def _setup(base_path: Path) -> None:
import json
import json # noqa: PLC0415
# Create extension.json with new structure
extension_json = {
@@ -111,7 +111,7 @@ def extension_setup_for_bundling():
"""Set up a complete extension structure ready for bundling."""
def _setup(base_path: Path) -> None:
import json
import json # noqa: PLC0415
# Create dist directory with manifest and files
dist_dir = base_path / "dist"
@@ -347,7 +347,7 @@ def test_build_manifest_exits_when_extension_json_missing(isolated_filesystem):
@pytest.mark.unit
def test_clean_dist_frontend_removes_frontend_dist(isolated_filesystem):
"""Test clean_dist_frontend removes frontend/dist directory specifically."""
from superset_extensions_cli.cli import clean_dist_frontend
from superset_extensions_cli.cli import clean_dist_frontend # noqa: PLC0415
# Create dist/frontend structure
dist_dir = isolated_filesystem / "dist"
@@ -366,7 +366,7 @@ def test_clean_dist_frontend_removes_frontend_dist(isolated_filesystem):
@pytest.mark.unit
def test_clean_dist_frontend_handles_nonexistent_directory(isolated_filesystem):
"""Test clean_dist_frontend handles case where frontend dist doesn't exist."""
from superset_extensions_cli.cli import clean_dist_frontend
from superset_extensions_cli.cli import clean_dist_frontend # noqa: PLC0415
# No dist directory exists
clean_dist_frontend(isolated_filesystem)
@@ -377,7 +377,7 @@ def test_clean_dist_frontend_handles_nonexistent_directory(isolated_filesystem):
@pytest.mark.unit
def test_run_frontend_build_with_output_messages(isolated_filesystem):
"""Test run_frontend_build produces expected output messages."""
from superset_extensions_cli.cli import run_frontend_build
from superset_extensions_cli.cli import run_frontend_build # noqa: PLC0415
frontend_dir = isolated_filesystem / "frontend"
frontend_dir.mkdir()
@@ -406,7 +406,7 @@ def test_rebuild_frontend_handles_build_results(
isolated_filesystem, return_code, expected_result
):
"""Test rebuild_frontend handles different build results."""
from superset_extensions_cli.cli import rebuild_frontend
from superset_extensions_cli.cli import rebuild_frontend # noqa: PLC0415
# Create frontend structure
frontend_dir = isolated_filesystem / "frontend"
@@ -434,7 +434,7 @@ def test_rebuild_frontend_handles_build_results(
@pytest.mark.unit
def test_rebuild_backend_calls_copy_and_shows_message(isolated_filesystem):
"""Test rebuild_backend calls copy_backend_files and shows success message."""
from superset_extensions_cli.cli import rebuild_backend
from superset_extensions_cli.cli import rebuild_backend # noqa: PLC0415
# Create extension.json
extension_json = {
@@ -35,7 +35,7 @@ def test_validate_command_success(cli_runner, isolated_filesystem):
"version": "1.0.0",
"permissions": [],
}
import json
import json # noqa: PLC0415
(isolated_filesystem / "extension.json").write_text(json.dumps(extension_json))
@@ -181,7 +181,7 @@ def test_template_rendering_with_different_ids(
jinja_env, publisher, technical_name, display_name
):
"""Test templates render correctly with various publisher/name combinations."""
from superset_extensions_cli.utils import (
from superset_extensions_cli.utils import ( # noqa: PLC0415
get_module_federation_name,
kebab_to_snake_case,
)
+3 -3
View File
@@ -152,7 +152,7 @@ class AsyncQueryManager:
self.register_request_handlers(app)
# pylint: disable=import-outside-toplevel
from superset.tasks.async_queries import (
from superset.tasks.async_queries import ( # noqa: PLC0415
load_chart_data_into_cache,
load_explore_json_into_cache,
)
@@ -222,7 +222,7 @@ class AsyncQueryManager:
user_id: Optional[int] = None,
) -> dict[str, Any]:
# pylint: disable=import-outside-toplevel
from superset import security_manager
from superset import security_manager # noqa: PLC0415
job_metadata = self.init_job(channel_id, user_id)
self._load_explore_json_into_cache_job.delay(
@@ -242,7 +242,7 @@ class AsyncQueryManager:
user_id: Optional[int] = None,
) -> dict[str, Any]:
# pylint: disable=import-outside-toplevel
from superset import security_manager
from superset import security_manager # noqa: PLC0415
# if it's guest user, we want to pass the guest token to the celery task
# chart data cache key is calculated based on the current user
+3 -1
View File
@@ -1433,7 +1433,9 @@ class ChartDataQueryContextSchema(Schema):
def get_query_context_factory(self) -> QueryContextFactory:
if self.query_context_factory is None:
# pylint: disable=import-outside-toplevel
from superset.common.query_context_factory import QueryContextFactory
from superset.common.query_context_factory import ( # noqa: PLC0415
QueryContextFactory,
)
self.query_context_factory = QueryContextFactory()
return self.query_context_factory
+1 -1
View File
@@ -219,7 +219,7 @@ class ExportDashboardsCommand(ExportModelsCommand):
# Export related theme
if model.theme:
from superset.commands.theme.export import ExportThemesCommand
from superset.commands.theme.export import ExportThemesCommand # noqa: PLC0415
yield from ExportThemesCommand([model.theme.id]).run()
@@ -235,9 +235,9 @@ def export_dataset_data(
sample_rows: int | None = None,
) -> bytes | None:
"""Export dataset data to Parquet format. Returns bytes or None on failure."""
import pandas as pd # pylint: disable=import-outside-toplevel
import pandas as pd # pylint: disable=import-outside-toplevel # noqa: PLC0415
from superset import db # pylint: disable=import-outside-toplevel
from superset import db # pylint: disable=import-outside-toplevel # noqa: PLC0415
# Ensure dataset is attached to session and relationships are loaded
if dataset not in db.session:
@@ -394,7 +394,7 @@ def export_dashboard_yaml(
dataset_id_to_uuid: dict[int, str],
) -> dict[str, Any]:
"""Export dashboard to YAML format with proper ID remapping."""
from superset.utils import (
from superset.utils import ( # noqa: PLC0415
json as superset_json, # pylint: disable=import-outside-toplevel
)
+7 -2
View File
@@ -158,8 +158,13 @@ class UpdateDatabaseCommand(BaseCommand):
"""
Update the catalog of the datasets that are associated with database.
"""
from superset.connectors.sqla.models import SqlaTable
from superset.models.sql_lab import Query, SavedQuery, TableSchema, TabState
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
from superset.models.sql_lab import ( # noqa: PLC0415
Query,
SavedQuery,
TableSchema,
TabState,
)
for model in [
SqlaTable,
@@ -101,7 +101,7 @@ class CSVReader(BaseDataReader):
return "c"
# Import pyarrow to verify it works properly
import pyarrow as pa # noqa: F401
import pyarrow as pa # noqa: F401, PLC0415
# Check if pandas has built-in pyarrow support
pandas_version = str(pd.__version__)
@@ -209,7 +209,7 @@ def load_data(data_uri: str, dataset: SqlaTable, database: Database) -> None:
:raises DatasetUnAllowedDataURI: If a dataset is trying
to load data from a URI that is not allowed.
"""
from superset.examples.helpers import normalize_example_data_url
from superset.examples.helpers import normalize_example_data_url # noqa: PLC0415
# Convert example URLs to align with configuration
data_uri = normalize_example_data_url(data_uri)
+4 -2
View File
@@ -216,7 +216,9 @@ def load_configs(
# Normalize example data URLs before schema validation
if prefix == "datasets" and "data" in config:
from superset.examples.helpers import normalize_example_data_url
from superset.examples.helpers import ( # noqa: PLC0415
normalize_example_data_url,
)
config["data"] = normalize_example_data_url(config["data"])
@@ -353,7 +355,7 @@ def safe_insert_dashboard_chart_relationships(
This function checks for existing relationships and only inserts new ones
to avoid duplicate key constraint errors.
"""
from sqlalchemy.sql import select
from sqlalchemy.sql import select # noqa: PLC0415
if not dashboard_chart_ids:
return
+1 -1
View File
@@ -179,7 +179,7 @@ class BaseReportState:
"""
Creates a Report execution log, uses the current computed last_value for Alerts
"""
from sqlalchemy.orm.exc import StaleDataError
from sqlalchemy.orm.exc import StaleDataError # noqa: PLC0415
try:
log = ReportExecutionLog(
+1 -1
View File
@@ -233,7 +233,7 @@ class BaseStreamingCSVExportCommand(BaseCommand):
)
except Exception as e:
logger.error("Error in streaming CSV generator: %s", e)
import traceback
import traceback # noqa: PLC0415
logger.error("Traceback: %s", traceback.format_exc())
+5 -5
View File
@@ -86,7 +86,7 @@ class CancelTaskCommand(BaseCommand):
:returns: The updated task model
"""
from superset.daos.tasks import TaskDAO
from superset.daos.tasks import TaskDAO # noqa: PLC0415
# Lightweight fetch to compute dedup_key for locking
# This is needed to use the same lock key as SubmitTaskCommand
@@ -113,7 +113,7 @@ class CancelTaskCommand(BaseCommand):
# Publish abort notification AFTER transaction commits
# This prevents race conditions where listeners check DB before commit
if self._should_publish_abort:
from superset.tasks.manager import TaskManager
from superset.tasks.manager import TaskManager # noqa: PLC0415
TaskManager.publish_abort(self._task_uuid)
@@ -129,7 +129,7 @@ class CancelTaskCommand(BaseCommand):
:returns: The updated task model
"""
from superset.daos.tasks import TaskDAO
from superset.daos.tasks import TaskDAO # noqa: PLC0415
# Check admin status (no DB access)
is_admin = security_manager.is_admin()
@@ -232,7 +232,7 @@ class CancelTaskCommand(BaseCommand):
:param is_admin: Whether current user is admin
:returns: The updated task model
"""
from superset.daos.tasks import TaskDAO
from superset.daos.tasks import TaskDAO # noqa: PLC0415
try:
result: Task | None = TaskDAO.abort_task(
@@ -273,7 +273,7 @@ class CancelTaskCommand(BaseCommand):
:param user_id: ID of user to unsubscribe
:returns: The updated task model
"""
from superset.daos.tasks import TaskDAO
from superset.daos.tasks import TaskDAO # noqa: PLC0415
self._action_taken = "unsubscribed"
+1 -1
View File
@@ -67,7 +67,7 @@ class TaskPruneCommand(BaseCommand):
# Select all IDs that need to be deleted
# Only delete completed tasks (success, failure, or aborted)
from superset.models.tasks import Task
from superset.models.tasks import Task # noqa: PLC0415
select_stmt = sa.select(Task.id).where(
Task.ended_at < datetime.now() - timedelta(days=self.retention_period_days),
+1 -1
View File
@@ -80,7 +80,7 @@ class SubmitTaskCommand(BaseCommand):
:returns: Tuple of (Task, is_new) where is_new is True if task was created
"""
from superset.daos.tasks import TaskDAO
from superset.daos.tasks import TaskDAO # noqa: PLC0415
self.validate()
+2 -2
View File
@@ -97,7 +97,7 @@ class UpdateTaskCommand(BaseCommand):
:returns: The updated task model
"""
from superset.daos.tasks import TaskDAO
from superset.daos.tasks import TaskDAO # noqa: PLC0415
self.validate()
@@ -129,7 +129,7 @@ class UpdateTaskCommand(BaseCommand):
:returns: The updated task model
"""
from superset.daos.tasks import TaskDAO
from superset.daos.tasks import TaskDAO # noqa: PLC0415
# Re-fetch model under lock to get fresh state
fresh_model = TaskDAO.find_one_or_none(
+2 -2
View File
@@ -68,7 +68,7 @@ class DeleteThemeCommand(BaseCommand):
def _dissociate_dashboards(self) -> None:
"""Dissociate dashboards from themes before deletion."""
from superset.models.dashboard import Dashboard
from superset.models.dashboard import Dashboard # noqa: PLC0415
theme_ids = [theme.id for theme in self._models or []]
if not theme_ids:
@@ -95,7 +95,7 @@ class DeleteThemeCommand(BaseCommand):
def _get_dashboard_usage(self) -> dict[int, list[str]]:
"""Get dashboard names that use these themes."""
from superset.models.dashboard import Dashboard
from superset.models.dashboard import Dashboard # noqa: PLC0415
theme_ids = [theme.id for theme in self._models or []]
if not theme_ids:
+3 -3
View File
@@ -34,9 +34,9 @@ logger = logging.getLogger(__name__)
def import_theme(config: dict[str, Any], overwrite: bool = False) -> "Theme | None":
"""Import a single theme from config dictionary"""
from superset import db, security_manager
from superset.models.core import Theme
from superset.utils.core import get_user
from superset import db, security_manager # noqa: PLC0415
from superset.models.core import Theme # noqa: PLC0415
from superset.utils.core import get_user # noqa: PLC0415
can_write = security_manager.can_access("can_write", "Theme")
existing = db.session.query(Theme).filter_by(uuid=config["uuid"]).first()
+3 -1
View File
@@ -457,7 +457,9 @@ class QueryContextProcessor:
annotation_layer: dict[str, Any], force: bool
) -> dict[str, Any]:
# pylint: disable=import-outside-toplevel
from superset.commands.chart.data.get_data_command import ChartDataCommand
from superset.commands.chart.data.get_data_command import ( # noqa: PLC0415
ChartDataCommand,
)
if not (chart := ChartDAO.find_by_id(annotation_layer["value"])):
raise QueryObjectValidationError(
+1 -1
View File
@@ -335,7 +335,7 @@ class QueryObject: # pylint: disable=too-many-instance-attributes
return False
def _sanitize_filters(self) -> None:
from superset.jinja_context import get_template_processor
from superset.jinja_context import get_template_processor # noqa: PLC0415
needs_transpilation = self.extras.get("transpile_to_dialect", False)
+1 -1
View File
@@ -2053,7 +2053,7 @@ class SqlaTable(
:param query_obj: query object to analyze
:return: The extra cache keys
"""
from superset.utils.rls import collect_rls_predicates_for_sql
from superset.utils.rls import collect_rls_predicates_for_sql # noqa: PLC0415
extra_cache_keys = super().get_extra_cache_keys(query_obj)
if self.has_extra_cache_key_calls(query_obj):
+45 -38
View File
@@ -42,22 +42,24 @@ def inject_dao_implementations() -> None:
Replace abstract DAO classes in superset_core common/queries/tasks daos with
concrete implementations from Superset.
"""
import superset_core.common.daos as core_common_dao_module
import superset_core.queries.daos as core_queries_dao_module
import superset_core.tasks.daos as core_tasks_dao_module
import superset_core.common.daos as core_common_dao_module # noqa: PLC0415
import superset_core.queries.daos as core_queries_dao_module # noqa: PLC0415
import superset_core.tasks.daos as core_tasks_dao_module # noqa: PLC0415
from superset.daos.chart import ChartDAO as HostChartDAO
from superset.daos.dashboard import DashboardDAO as HostDashboardDAO
from superset.daos.database import DatabaseDAO as HostDatabaseDAO
from superset.daos.dataset import DatasetDAO as HostDatasetDAO
from superset.daos.key_value import KeyValueDAO as HostKeyValueDAO
from superset.daos.query import (
from superset.daos.chart import ChartDAO as HostChartDAO # noqa: PLC0415
from superset.daos.dashboard import ( # noqa: PLC0415
DashboardDAO as HostDashboardDAO,
)
from superset.daos.database import DatabaseDAO as HostDatabaseDAO # noqa: PLC0415
from superset.daos.dataset import DatasetDAO as HostDatasetDAO # noqa: PLC0415
from superset.daos.key_value import KeyValueDAO as HostKeyValueDAO # noqa: PLC0415
from superset.daos.query import ( # noqa: PLC0415
QueryDAO as HostQueryDAO,
SavedQueryDAO as HostSavedQueryDAO,
)
from superset.daos.tag import TagDAO as HostTagDAO
from superset.daos.tasks import TaskDAO as HostTaskDAO
from superset.daos.user import UserDAO as HostUserDAO
from superset.daos.tag import TagDAO as HostTagDAO # noqa: PLC0415
from superset.daos.tasks import TaskDAO as HostTaskDAO # noqa: PLC0415
from superset.daos.user import UserDAO as HostUserDAO # noqa: PLC0415
# Replace abstract classes in common.daos with concrete implementations
core_common_dao_module.DatasetDAO = HostDatasetDAO # type: ignore[assignment,misc]
@@ -83,19 +85,24 @@ def inject_model_implementations() -> None:
Uses in-place replacement to maintain single import location for extensions.
"""
import superset_core.common.models as core_common_models_module
import superset_core.queries.models as core_queries_models_module
import superset_core.tasks.models as core_tasks_models_module
from flask_appbuilder.security.sqla.models import User as HostUser
import superset_core.common.models as core_common_models_module # noqa: PLC0415
import superset_core.queries.models as core_queries_models_module # noqa: PLC0415
import superset_core.tasks.models as core_tasks_models_module # noqa: PLC0415
from flask_appbuilder.security.sqla.models import User as HostUser # noqa: PLC0415
from superset.connectors.sqla.models import SqlaTable as HostDataset
from superset.key_value.models import KeyValueEntry as HostKeyValue
from superset.models.core import Database as HostDatabase
from superset.models.dashboard import Dashboard as HostDashboard
from superset.models.slice import Slice as HostChart
from superset.models.sql_lab import Query as HostQuery, SavedQuery as HostSavedQuery
from superset.models.tasks import Task as HostTask
from superset.tags.models import Tag as HostTag
from superset.connectors.sqla.models import ( # noqa: PLC0415
SqlaTable as HostDataset,
)
from superset.key_value.models import KeyValueEntry as HostKeyValue # noqa: PLC0415
from superset.models.core import Database as HostDatabase # noqa: PLC0415
from superset.models.dashboard import Dashboard as HostDashboard # noqa: PLC0415
from superset.models.slice import Slice as HostChart # noqa: PLC0415
from superset.models.sql_lab import ( # noqa: PLC0415
Query as HostQuery,
SavedQuery as HostSavedQuery,
)
from superset.models.tasks import Task as HostTask # noqa: PLC0415
from superset.tags.models import Tag as HostTag # noqa: PLC0415
# In-place replacement in common.models
core_common_models_module.Database = HostDatabase # type: ignore[misc]
@@ -119,9 +126,9 @@ def inject_query_implementations() -> None:
Replace abstract query functions in superset_core.queries.query with concrete
implementations from Superset.
"""
import superset_core.queries.query as core_query_module
import superset_core.queries.query as core_query_module # noqa: PLC0415
from superset.sql.parse import SQLGLOT_DIALECTS
from superset.sql.parse import SQLGLOT_DIALECTS # noqa: PLC0415
def get_sqlglot_dialect(database: "Database") -> Any:
return (
@@ -137,12 +144,12 @@ def inject_task_implementations() -> None:
Replace abstract task functions in superset_core tasks.types and tasks.decorators
with concrete implementations from Superset.
"""
import superset_core.tasks.decorators as core_tasks_decorators_module
import superset_core.tasks.types as core_tasks_types_module
import superset_core.tasks.decorators as core_tasks_decorators_module # noqa: PLC0415
import superset_core.tasks.types as core_tasks_types_module # noqa: PLC0415
from superset.tasks.ambient_context import get_context
from superset.tasks.context import TaskContext
from superset.tasks.decorators import task
from superset.tasks.ambient_context import get_context # noqa: PLC0415
from superset.tasks.context import TaskContext # noqa: PLC0415
from superset.tasks.decorators import task # noqa: PLC0415
# Replace abstract classes and functions with concrete implementations
core_tasks_types_module.TaskContext = TaskContext # type: ignore[assignment,misc]
@@ -155,9 +162,9 @@ def inject_rest_api_implementations() -> None:
Replace abstract REST API decorators in superset_core.rest_api.decorators
with concrete implementations from Superset.
"""
import superset_core.rest_api.decorators as core_rest_api_module
import superset_core.rest_api.decorators as core_rest_api_module # noqa: PLC0415
from superset.extensions import appbuilder
from superset.extensions import appbuilder # noqa: PLC0415
T = TypeVar("T", bound=type["RestApi"])
@@ -219,10 +226,10 @@ def inject_model_session_implementation() -> None:
Replace abstract get_session function in superset_core.common.models with concrete
implementation from Superset.
"""
import superset_core.common.models as core_models_module
import superset_core.common.models as core_models_module # noqa: PLC0415
def get_session() -> scoped_session:
from superset import db
from superset import db # noqa: PLC0415
return db.session
@@ -235,10 +242,10 @@ def inject_semantic_layer_implementations() -> None:
superset_core.semantic_layers.decorators with a concrete implementation
that registers classes in the contributions registry.
"""
import superset_core.semantic_layers.decorators as core_sl_module
import superset_core.semantic_layers.decorators as core_sl_module # noqa: PLC0415
import superset.extensions.context as context_module
from superset.semantic_layers.registry import registry
import superset.extensions.context as context_module # noqa: PLC0415
from superset.semantic_layers.registry import registry # noqa: PLC0415
def semantic_layer_impl(
id: str,
+9 -9
View File
@@ -98,7 +98,7 @@ def create_tool_decorator(
def decorator(func: F) -> F:
try:
# Import here to avoid circular imports
from superset.mcp_service.app import mcp
from superset.mcp_service.app import mcp # noqa: PLC0415
# Use provided values or extract from function
base_tool_name = name or func.__name__
@@ -111,7 +111,7 @@ def create_tool_decorator(
# Store RBAC permission metadata on the function so
# mcp_auth_hook can read them at call time.
if class_permission_name:
from superset.mcp_service.auth import (
from superset.mcp_service.auth import ( # noqa: PLC0415
CLASS_PERMISSION_ATTR,
METHOD_PERMISSION_ATTR,
)
@@ -124,13 +124,13 @@ def create_tool_decorator(
# Conditionally apply authentication wrapper
if protect:
from superset.mcp_service.auth import mcp_auth_hook
from superset.mcp_service.auth import mcp_auth_hook # noqa: PLC0415
wrapped_func = mcp_auth_hook(func)
else:
wrapped_func = func
from fastmcp.tools import Tool
from fastmcp.tools import Tool # noqa: PLC0415
tool = Tool.from_function(
wrapped_func,
@@ -208,7 +208,7 @@ def create_prompt_decorator(
def decorator(func: F) -> F:
try:
# Import here to avoid circular imports
from superset.mcp_service.app import mcp
from superset.mcp_service.app import mcp # noqa: PLC0415
# Use provided values or extract from function
base_prompt_name = name or func.__name__
@@ -223,7 +223,7 @@ def create_prompt_decorator(
# Conditionally apply authentication wrapper
if protect:
from superset.mcp_service.auth import mcp_auth_hook
from superset.mcp_service.auth import mcp_auth_hook # noqa: PLC0415
wrapped_func = mcp_auth_hook(func)
else:
@@ -279,10 +279,10 @@ def initialize_core_mcp_dependencies() -> None:
Also imports MCP service app to register all host tools BEFORE extension loading.
"""
import superset_core.mcp.decorators
import superset_core.mcp.decorators # noqa: PLC0415
try:
from fastmcp.tools import Tool # noqa: F401
from fastmcp.tools import Tool # noqa: F401, PLC0415
except ImportError:
logger.info(
"fastmcp is not installed, skipping MCP initialization. "
@@ -299,7 +299,7 @@ def initialize_core_mcp_dependencies() -> None:
try:
# Import MCP service app to register host tools BEFORE extension loading
# This prevents host tools from being registered during extension context
from superset.mcp_service import app # noqa: F401
from superset.mcp_service import app # noqa: F401, PLC0415
logger.info("MCP service app imported - host tools registered")
except Exception as e:
+3 -1
View File
@@ -213,7 +213,9 @@ class TaskDAO(BaseDAO[Task]):
:returns: Task if aborted/aborting, None if not found or already finished
:raises TaskNotAbortableError: If in-progress task has no abort handler
"""
from superset.commands.tasks.exceptions import TaskNotAbortableError
from superset.commands.tasks.exceptions import ( # noqa: PLC0415
TaskNotAbortableError,
)
task = cls.find_one_or_none(skip_base_filter=skip_base_filter, uuid=task_uuid)
if not task:
+3 -1
View File
@@ -749,7 +749,9 @@ class DatasetRestApi(BaseSupersetModelRestApi):
$ref: '#/components/responses/500'
"""
# pylint: disable=import-outside-toplevel
from superset.datasets.datetime_format_detector import DatetimeFormatDetector
from superset.datasets.datetime_format_detector import ( # noqa: PLC0415
DatetimeFormatDetector,
)
try:
# Get force parameter from query string
+6 -2
View File
@@ -50,8 +50,12 @@ def DistributedLock( # noqa: N802
or Redis connection fails
"""
# pylint: disable=import-outside-toplevel
from superset.commands.distributed_lock.acquire import AcquireDistributedLock
from superset.commands.distributed_lock.release import ReleaseDistributedLock
from superset.commands.distributed_lock.acquire import ( # noqa: PLC0415
AcquireDistributedLock,
)
from superset.commands.distributed_lock.release import ( # noqa: PLC0415
ReleaseDistributedLock,
)
key = get_key(namespace, **kwargs)
+1 -1
View File
@@ -60,7 +60,7 @@ def get_dataset_config_from_yaml(example_dir: Path) -> Dict[str, Optional[str]]:
def get_examples_directory() -> Path:
"""Get the path to the examples directory."""
from .helpers import get_examples_folder
from .helpers import get_examples_folder # noqa: PLC0415
return Path(get_examples_folder())
+1 -1
View File
@@ -74,7 +74,7 @@ def load_parquet_table( # noqa: C901
Returns:
The created SqlaTable object
"""
from sqlalchemy import text
from sqlalchemy import text # noqa: PLC0415
if database is None:
database = get_example_database()
+2 -2
View File
@@ -104,7 +104,7 @@ def normalize_example_data_url(url: str) -> str:
Normalized file:// URL pointing to the Parquet file, or the original URL
if it's a remote URL (http://, https://, etc.)
"""
import os
import os # noqa: PLC0415
# Handle existing examples:// protocol
if url.startswith(EXAMPLES_PROTOCOL):
@@ -158,7 +158,7 @@ def read_example_data(
Returns:
DataFrame with the loaded data
"""
import os
import os # noqa: PLC0415
# Extract example name from filepath
if filepath.startswith(EXAMPLES_PROTOCOL):
+1 -1
View File
@@ -50,7 +50,7 @@ def _read_file_if_exists(base: Any, path: Any) -> str | None:
def _load_shared_configs(examples_root: Any) -> dict[str, str]:
"""Load shared database and metadata configs from _shared directory."""
from flask import current_app
from flask import current_app # noqa: PLC0415
contents: dict[str, str] = {}
base = files("superset")
+2 -2
View File
@@ -34,13 +34,13 @@ class ExtensionsRestApi(BaseApi):
def response(self, status_code: int, **kwargs: Any) -> Response:
"""Helper method to create JSON responses."""
from flask import jsonify
from flask import jsonify # noqa: PLC0415
return jsonify(kwargs), status_code
def response_404(self) -> Response:
"""Helper method to create 404 responses."""
from flask import jsonify
from flask import jsonify # noqa: PLC0415
return jsonify({"message": "Not found"}), 404
@@ -37,7 +37,7 @@ _watcher_lock = threading.Lock()
def _get_file_handler_class() -> Any:
"""Get the file handler class, importing watchdog only when needed."""
try:
from watchdog.events import FileSystemEventHandler
from watchdog.events import FileSystemEventHandler # noqa: PLC0415
class LocalExtensionFileHandler(FileSystemEventHandler):
"""Custom file system event handler for LOCAL_EXTENSIONS directories."""
@@ -131,7 +131,7 @@ def setup_local_extensions_watcher(app: Flask) -> None: # noqa: C901
return
try:
from watchdog.observers import Observer
from watchdog.observers import Observer # noqa: PLC0415
# Set up and start the file watcher
event_handler = handler_class()
+1 -1
View File
@@ -296,7 +296,7 @@ class SupersetShillelaghAdapter(Adapter):
This is done on initialization because it's expensive.
"""
# pylint: disable=import-outside-toplevel
from superset.models.core import Database
from superset.models.core import Database # noqa: PLC0415
database = (
db.session.query(Database).filter_by(database_name=self.database).first()
+4 -4
View File
@@ -79,7 +79,7 @@ class SupersetMetastoreCache(BaseCache):
def set(self, key: str, value: Any, timeout: Optional[int] = None) -> bool:
# pylint: disable=import-outside-toplevel
from superset.daos.key_value import KeyValueDAO
from superset.daos.key_value import KeyValueDAO # noqa: PLC0415
KeyValueDAO.upsert_entry(
resource=RESOURCE,
@@ -93,7 +93,7 @@ class SupersetMetastoreCache(BaseCache):
def add(self, key: str, value: Any, timeout: Optional[int] = None) -> bool:
# pylint: disable=import-outside-toplevel
from superset.daos.key_value import KeyValueDAO
from superset.daos.key_value import KeyValueDAO # noqa: PLC0415
try:
KeyValueDAO.delete_expired_entries(RESOURCE)
@@ -112,7 +112,7 @@ class SupersetMetastoreCache(BaseCache):
def get(self, key: str) -> Any:
# pylint: disable=import-outside-toplevel
from superset.daos.key_value import KeyValueDAO
from superset.daos.key_value import KeyValueDAO # noqa: PLC0415
return KeyValueDAO.get_value(RESOURCE, self.get_key(key), self.codec)
@@ -125,6 +125,6 @@ class SupersetMetastoreCache(BaseCache):
@transaction()
def delete(self, key: str) -> Any:
# pylint: disable=import-outside-toplevel
from superset.daos.key_value import KeyValueDAO
from superset.daos.key_value import KeyValueDAO # noqa: PLC0415
return KeyValueDAO.delete_entry(RESOURCE, self.get_key(key))
+1 -1
View File
@@ -53,7 +53,7 @@ class SSHManager:
ssh_tunnel: "SSHTunnel",
sqlalchemy_database_uri: str,
) -> sshtunnel.SSHTunnelForwarder:
from superset.utils.ssh_tunnel import get_default_port
from superset.utils.ssh_tunnel import get_default_port # noqa: PLC0415
url = make_url_safe(sqlalchemy_database_uri)
backend = url.get_backend_name()
+3 -1
View File
@@ -276,7 +276,9 @@ def get_extensions() -> dict[str, LoadedExtension]:
# Load extensions from discovery path (.supx files)
if extensions_path := current_app.config.get("EXTENSIONS_PATH"):
from superset.extensions.discovery import discover_and_load_extensions
from superset.extensions.discovery import ( # noqa: PLC0415
discover_and_load_extensions,
)
for extension in discover_and_load_extensions(extensions_path):
extension_id = extension.manifest.id
+9 -9
View File
@@ -285,7 +285,7 @@ class ExtraCache:
"""
# pylint: disable=import-outside-toplevel
from superset.views.utils import get_form_data
from superset.views.utils import get_form_data # noqa: PLC0415
if has_request_context() and request.args.get(param):
return request.args.get(param, default)
@@ -407,7 +407,7 @@ class ExtraCache:
:return: returns a list of filters
"""
# pylint: disable=import-outside-toplevel
from superset.views.utils import get_form_data
from superset.views.utils import get_form_data # noqa: PLC0415
form_data, _ = get_form_data()
convert_legacy_filters_into_adhoc(form_data)
@@ -512,7 +512,7 @@ class ExtraCache:
:return: The corresponding time filter.
"""
# pylint: disable=import-outside-toplevel
from superset.views.utils import get_form_data
from superset.views.utils import get_form_data # noqa: PLC0415
form_data, _ = get_form_data()
convert_legacy_filters_into_adhoc(form_data)
@@ -948,7 +948,7 @@ class PrestoTemplateProcessor(JinjaTemplateProcessor):
"""
# pylint: disable=import-outside-toplevel
from superset.db_engine_specs.presto import PrestoEngineSpec
from superset.db_engine_specs.presto import PrestoEngineSpec # noqa: PLC0415
table_name, schema = self._schema_table(table_name, self._schema)
return cast(PrestoEngineSpec, self._database.db_engine_spec).latest_partition(
@@ -959,7 +959,7 @@ class PrestoTemplateProcessor(JinjaTemplateProcessor):
table_name, schema = self._schema_table(table_name, self._schema)
# pylint: disable=import-outside-toplevel
from superset.db_engine_specs.presto import PrestoEngineSpec
from superset.db_engine_specs.presto import PrestoEngineSpec # noqa: PLC0415
return cast(
PrestoEngineSpec, self._database.db_engine_spec
@@ -1052,7 +1052,7 @@ def dataset_macro(
the underlying dataset.
"""
# pylint: disable=import-outside-toplevel
from superset.daos.dataset import DatasetDAO
from superset.daos.dataset import DatasetDAO # noqa: PLC0415
dataset = DatasetDAO.find_by_id(dataset_id)
if not dataset:
@@ -1081,8 +1081,8 @@ def get_dataset_id_from_context(metric_key: str) -> int:
:returns: the dataset ID.
"""
# pylint: disable=import-outside-toplevel
from superset.daos.chart import ChartDAO
from superset.views.utils import loads_request_json
from superset.daos.chart import ChartDAO # noqa: PLC0415
from superset.views.utils import loads_request_json # noqa: PLC0415
form_data: dict[str, Any] = {}
exc_message = _(
@@ -1134,7 +1134,7 @@ def metric_macro(
:returns: the macro SQL syntax.
"""
# pylint: disable=import-outside-toplevel
from superset.daos.dataset import DatasetDAO
from superset.daos.dataset import DatasetDAO # noqa: PLC0415
if not dataset_id:
dataset_id = get_dataset_id_from_context(metric_key)
+2 -2
View File
@@ -1317,7 +1317,7 @@ class Database(CoreDatabase, AuditMixinNullable, ImportExportMixin): # pylint:
:param options: QueryOptions with execution settings
:returns: QueryResult with status, data, and metadata
"""
from superset.sql.execution import SQLExecutor
from superset.sql.execution import SQLExecutor # noqa: PLC0415
return SQLExecutor(self).execute(sql, options)
@@ -1333,7 +1333,7 @@ class Database(CoreDatabase, AuditMixinNullable, ImportExportMixin): # pylint:
:param options: QueryOptions with execution settings
:returns: AsyncQueryHandle for tracking the query
"""
from superset.sql.execution import SQLExecutor
from superset.sql.execution import SQLExecutor # noqa: PLC0415
return SQLExecutor(self).execute_async(sql, options)
+6 -2
View File
@@ -1375,7 +1375,9 @@ class ExploreMixin: # pylint: disable=too-many-public-methods
"""
# Import here to avoid circular dependency
# pylint: disable=import-outside-toplevel
from superset.common.utils.query_cache_manager import QueryCacheManager
from superset.common.utils.query_cache_manager import ( # noqa: PLC0415
QueryCacheManager,
)
# ensure query_object is immutable
query_object_clone = copy.copy(query_object)
@@ -2513,7 +2515,9 @@ class ExploreMixin: # pylint: disable=too-many-public-methods
:return: Dict with validation result and any errors
"""
from superset.sql_validators.base import SQLValidationAnnotation
from superset.sql_validators.base import ( # noqa: PLC0415
SQLValidationAnnotation,
)
try:
# Process template
+4 -2
View File
@@ -342,7 +342,9 @@ class Slice( # pylint: disable=too-many-public-methods
def get_query_context_factory(self) -> QueryContextFactory:
if self.query_context_factory is None:
# pylint: disable=import-outside-toplevel
from superset.common.query_context_factory import QueryContextFactory
from superset.common.query_context_factory import ( # noqa: PLC0415
QueryContextFactory,
)
self.query_context_factory = QueryContextFactory()
return self.query_context_factory
@@ -363,7 +365,7 @@ def id_or_uuid_filter(id_or_uuid: str | int) -> BinaryExpression:
def set_related_perm(_mapper: Mapper, _connection: Connection, target: Slice) -> None:
# pylint: disable=import-outside-toplevel
from superset.daos.datasource import DatasourceDAO
from superset.daos.datasource import DatasourceDAO # noqa: PLC0415
src_class = DatasourceDAO.sources[target.datasource_type]
if id_ := target.datasource_id:
+1 -1
View File
@@ -238,7 +238,7 @@ class Query(
@property
def columns(self) -> list["TableColumn"]:
from superset.connectors.sqla.models import ( # pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
TableColumn,
)
+41 -38
View File
@@ -132,7 +132,7 @@ def _log_audit_event(action: str, payload: dict[str, Any]) -> None:
configured implementation (DBEventLogger, S3EventLogger, etc.)
receives these security audit events.
"""
from superset.extensions import (
from superset.extensions import ( # noqa: PLC0415
event_logger, # pylint: disable=import-outside-toplevel
)
@@ -654,7 +654,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
def request_loader(self, request: Request) -> Optional[User]:
# pylint: disable=import-outside-toplevel
from superset.extensions import feature_flag_manager
from superset.extensions import feature_flag_manager # noqa: PLC0415
if feature_flag_manager.is_feature_enabled("EMBEDDED_SUPERSET"):
return self.get_guest_user_from_request(request)
@@ -799,7 +799,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
:param datasource: The datasource
:returns: Whether the user can access the datasource's schema
"""
from superset.connectors.sqla.models import BaseDatasource
from superset.connectors.sqla.models import BaseDatasource # noqa: PLC0415
# Admin/superuser override
if self.can_access_all_datasources():
@@ -847,7 +847,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
"""
Return True if an embedded user or DASHBOARD_RBAC user can drill a dataset.
"""
from superset import is_feature_enabled
from superset import is_feature_enabled # noqa: PLC0415
if (
(
@@ -909,7 +909,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
:returns: Whether the user has drill access.
"""
from superset.models.slice import Slice
from superset.models.slice import Slice # noqa: PLC0415
# Drill to Detail: no slice/chart context, dataset must belong to the dashboard
if (
@@ -1097,7 +1097,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
user_datasources = set()
# pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import SqlaTable
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
user_datasources.update(
self.session.query(SqlaTable)
@@ -1208,7 +1208,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
"""
# pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import SqlaTable
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
default_catalog = database.get_default_catalog()
catalog = catalog or default_catalog
@@ -1274,7 +1274,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
:returns: The set of accessible database catalogs
"""
# pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import SqlaTable
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
if hierarchical and self.can_access_database(database):
return catalogs
@@ -1339,7 +1339,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
:returns: The list of accessible SQL tables w/ schema
"""
# pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import SqlaTable
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
if self.can_access_database(database):
return datasource_names
@@ -1428,8 +1428,8 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
"""
# pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import SqlaTable
from superset.models import core as models
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
from superset.models import core as models # noqa: PLC0415
logger.info("Fetching a set of all perms to lookup which ones are missing")
all_pvs = {
@@ -1461,7 +1461,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
existing_pvs: set[tuple[str, str]],
) -> None:
"""Backfill perm columns and create missing PVMs for semantic models."""
from superset.semantic_layers.models import ( # pylint: disable=import-outside-toplevel
from superset.semantic_layers.models import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
SemanticLayer,
SemanticView,
)
@@ -1959,10 +1959,10 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
:param target: The database object
:return: A list of changed view menus (permission resource names)
""" # noqa: E501
from superset.connectors.sqla.models import ( # pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
SqlaTable,
)
from superset.models.slice import ( # pylint: disable=import-outside-toplevel
from superset.models.slice import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
Slice,
)
@@ -2039,7 +2039,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
:param target: The changed dataset object
:return:
"""
from superset.models.core import ( # pylint: disable=import-outside-toplevel
from superset.models.core import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
Database,
)
@@ -2152,7 +2152,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
:return:
"""
# pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import SqlaTable
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
# Check if watched fields have changed
table = SqlaTable.__table__ # pylint: disable=no-member
@@ -2245,10 +2245,10 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
:param target: Dataset that was updated
:return:
"""
from superset.connectors.sqla.models import ( # pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
SqlaTable,
)
from superset.models.slice import ( # pylint: disable=import-outside-toplevel
from superset.models.slice import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
Slice,
)
@@ -2318,10 +2318,10 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
old_permission_name,
new_permission_name,
)
from superset.connectors.sqla.models import ( # pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
SqlaTable,
)
from superset.models.slice import ( # pylint: disable=import-outside-toplevel
from superset.models.slice import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
Slice,
)
@@ -2379,7 +2379,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
Creates the datasource_access PVM and stores the perm string on the row.
"""
from superset.semantic_layers.models import ( # pylint: disable=import-outside-toplevel
from superset.semantic_layers.models import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
SemanticLayer,
)
@@ -2406,7 +2406,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
Renames the FAB ViewMenu so the PVM stays in sync with the layer name.
Also cascades the rename to all semantic view perms under this layer.
"""
from superset.semantic_layers.models import ( # pylint: disable=import-outside-toplevel
from superset.semantic_layers.models import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
SemanticLayer,
SemanticView,
)
@@ -2494,7 +2494,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
Looks up the layer name via connection since the ORM relationship may
not be loaded during event handling.
"""
from superset.semantic_layers.models import ( # pylint: disable=import-outside-toplevel
from superset.semantic_layers.models import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
SemanticLayer,
SemanticView,
)
@@ -2526,7 +2526,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
Looks up the layer name via connection since the ORM relationship may
not be loaded during event handling.
"""
from superset.semantic_layers.models import ( # pylint: disable=import-outside-toplevel
from superset.semantic_layers.models import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
SemanticLayer,
SemanticView,
)
@@ -2883,12 +2883,12 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
:raises SupersetSecurityException: If the user cannot access the resource
"""
# pylint: disable=import-outside-toplevel
from superset import is_feature_enabled
from superset.connectors.sqla.models import SqlaTable
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.models.sql_lab import Query
from superset.utils.core import shortid
from superset import is_feature_enabled # noqa: PLC0415
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
from superset.models.dashboard import Dashboard # noqa: PLC0415
from superset.models.slice import Slice # noqa: PLC0415
from superset.models.sql_lab import Query # noqa: PLC0415
from superset.utils.core import shortid # noqa: PLC0415
if sql and database:
query = Query(
@@ -2922,7 +2922,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
# If the DB engine spec doesn't implement the logic the schema is read
# from the SQLAlchemy URI if possible; if not, we use the SQLAlchemy
# inspector to read it.
from superset.models.sql_lab import Query
from superset.models.sql_lab import Query # noqa: PLC0415
default_schema = database.get_default_schema_for_query(
cast(Query, query),
@@ -3215,7 +3215,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
return cache[cache_key]
# pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import (
from superset.connectors.sqla.models import ( # noqa: PLC0415
RLSFilterRoles,
RLSFilterTables,
RowLevelSecurityFilter,
@@ -3299,7 +3299,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
return
# pylint: disable=import-outside-toplevel
from superset.connectors.sqla.models import (
from superset.connectors.sqla.models import ( # noqa: PLC0415
RLSFilterRoles,
RLSFilterTables,
RowLevelSecurityFilter,
@@ -3410,11 +3410,11 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
@staticmethod
def validate_guest_token_resources(resources: GuestTokenResources) -> None:
# pylint: disable=import-outside-toplevel
from superset.commands.dashboard.embedded.exceptions import (
from superset.commands.dashboard.embedded.exceptions import ( # noqa: PLC0415
EmbeddedDashboardNotFoundError,
)
from superset.daos.dashboard import EmbeddedDashboardDAO
from superset.models.dashboard import Dashboard
from superset.daos.dashboard import EmbeddedDashboardDAO # noqa: PLC0415
from superset.models.dashboard import Dashboard # noqa: PLC0415
for resource in resources:
if resource["type"] == GuestTokenResourceType.DASHBOARD.value:
@@ -3505,7 +3505,7 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
@staticmethod
def is_guest_user(user: Optional[Any] = None) -> bool:
# pylint: disable=import-outside-toplevel
from superset import is_feature_enabled
from superset import is_feature_enabled # noqa: PLC0415
if not is_feature_enabled("EMBEDDED_SUPERSET"):
return False
@@ -3598,7 +3598,10 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods
# temporal change to remove the roles view from the security menu,
# after migrating all views to frontend, we will set FAB_ADD_SECURITY_VIEWS = False
def register_views(self) -> None:
from superset.views.auth import SupersetAuthView, SupersetRegisterUserView
from superset.views.auth import ( # noqa: PLC0415
SupersetAuthView,
SupersetRegisterUserView,
)
if self.register_superset_auth_view:
self.auth_view = self.appbuilder.add_view_no_menu(SupersetAuthView)
+3 -1
View File
@@ -36,7 +36,9 @@ def _sl(legacy: str, semantic: str) -> str:
# Imported lazily to avoid a circular import at module load time
# (superset.semantic_layers.labels is imported by superset.initialization,
# which is itself imported during superset package initialization).
from superset import feature_flag_manager # pylint: disable=import-outside-toplevel
from superset import ( # noqa: PLC0415
feature_flag_manager, # pylint: disable=import-outside-toplevel
)
return (
semantic
+13 -9
View File
@@ -150,7 +150,7 @@ class SemanticLayer(AuditMixinNullable, Model):
connection: Connection,
target: "SemanticLayer",
) -> None:
from superset import security_manager
from superset import security_manager # noqa: PLC0415
security_manager.semantic_layer_after_insert(mapper, connection, target)
@@ -160,7 +160,7 @@ class SemanticLayer(AuditMixinNullable, Model):
connection: Connection,
target: "SemanticLayer",
) -> None:
from superset import security_manager
from superset import security_manager # noqa: PLC0415
security_manager.semantic_layer_before_update(mapper, connection, target)
@@ -170,7 +170,7 @@ class SemanticLayer(AuditMixinNullable, Model):
connection: Connection,
target: "SemanticLayer",
) -> None:
from superset import security_manager
from superset import security_manager # noqa: PLC0415
security_manager.semantic_layer_after_delete(mapper, connection, target)
@@ -242,7 +242,7 @@ class SemanticView(AuditMixinNullable, Model):
connection: Connection,
target: "SemanticView",
) -> None:
from superset import security_manager
from superset import security_manager # noqa: PLC0415
security_manager.semantic_view_after_insert(mapper, connection, target)
@@ -252,7 +252,7 @@ class SemanticView(AuditMixinNullable, Model):
connection: Connection,
target: "SemanticView",
) -> None:
from superset import security_manager
from superset import security_manager # noqa: PLC0415
security_manager.semantic_view_before_update(mapper, connection, target)
@@ -262,7 +262,7 @@ class SemanticView(AuditMixinNullable, Model):
connection: Connection,
target: "SemanticView",
) -> None:
from superset import security_manager
from superset import security_manager # noqa: PLC0415
security_manager.semantic_view_after_delete(mapper, connection, target)
@@ -482,9 +482,13 @@ class SemanticView(AuditMixinNullable, Model):
def raise_for_access(self) -> None:
"""Check that the user has access to this semantic view."""
from superset import security_manager
from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
from superset.exceptions import SupersetSecurityException
from superset import security_manager # noqa: PLC0415
from superset.errors import ( # noqa: PLC0415
ErrorLevel,
SupersetError,
SupersetErrorType,
)
from superset.exceptions import SupersetSecurityException # noqa: PLC0415
if security_manager.can_access_all_datasources():
return
+3 -3
View File
@@ -109,7 +109,7 @@ def _handle_query_error(
def _serialize_payload(payload: dict[Any, Any]) -> bytes:
"""Serialize payload for storage based on RESULTS_BACKEND_USE_MSGPACK config."""
from superset import results_backend_use_msgpack
from superset import results_backend_use_msgpack # noqa: PLC0415
if results_backend_use_msgpack:
return msgpack.dumps(payload, default=json.json_iso_dttm_ser, use_bin_type=True)
@@ -298,8 +298,8 @@ def _serialize_result_set(
:param result_set: Query result set to serialize
:returns: Tuple of (serialized_data, columns)
"""
from superset import results_backend_use_msgpack
from superset.dataframe import df_to_records
from superset import results_backend_use_msgpack # noqa: PLC0415
from superset.dataframe import df_to_records # noqa: PLC0415
if results_backend_use_msgpack:
if has_app_context():
+36 -29
View File
@@ -122,7 +122,7 @@ def execute_sql_with_cursor(
:returns: List of (statement_sql, result_set, execution_time_ms, rowcount) tuples
Returns empty list if stopped. Raises exception on error (fail-fast).
"""
from superset.result_set import SupersetResultSet
from superset.result_set import SupersetResultSet # noqa: PLC0415
total = len(statements)
if total == 0:
@@ -214,7 +214,7 @@ class SQLExecutor:
See superset_core.api.models.Database.execute() for full documentation.
"""
from superset_core.queries.types import (
from superset_core.queries.types import ( # noqa: PLC0415
QueryOptions as QueryOptionsType,
QueryResult as QueryResultType,
QueryStatus,
@@ -341,7 +341,7 @@ class SQLExecutor:
See superset_core.api.models.Database.execute_async() for full documentation.
"""
from superset_core.queries.types import (
from superset_core.queries.types import ( # noqa: PLC0415
QueryOptions as QueryOptionsType,
QueryResult as QueryResultType,
QueryStatus,
@@ -363,7 +363,7 @@ class SQLExecutor:
# DRY RUN: Return transformed SQL as completed async handle
if opts.dry_run:
from superset_core.queries.types import StatementResult
from superset_core.queries.types import StatementResult # noqa: PLC0415
original_sqls = [stmt.format() for stmt in original_script.statements]
transformed_sqls = [stmt.format() for stmt in transformed_script.statements]
@@ -510,7 +510,7 @@ class SQLExecutor:
:param query: Query model for progress tracking
:returns: List of StatementResult objects
"""
from superset_core.queries.types import StatementResult
from superset_core.queries.types import StatementResult # noqa: PLC0415
# Get original statement strings
original_sqls = [stmt.format() for stmt in original_script.statements]
@@ -578,7 +578,7 @@ class SQLExecutor:
:param sql: SQL to log
:param schema: Schema name
"""
from superset import security_manager
from superset import security_manager # noqa: PLC0415
if log_query := app.config.get("QUERY_LOGGER"):
log_query(
@@ -607,7 +607,9 @@ class SQLExecutor:
statements before the failure
:returns: QueryResult with error status
"""
from superset_core.queries.types import QueryResult as QueryResultType
from superset_core.queries.types import ( # noqa: PLC0415
QueryResult as QueryResultType,
)
return QueryResultType(
status=status,
@@ -629,7 +631,7 @@ class SQLExecutor:
if template_params is None:
return sql
from superset.jinja_context import get_template_processor
from superset.jinja_context import get_template_processor # noqa: PLC0415
tp = get_template_processor(database=self.database)
return tp.process_template(sql, **template_params)
@@ -737,7 +739,7 @@ class SQLExecutor:
:param catalog: Catalog name
:param schema: Schema name
"""
from superset.utils.rls import apply_rls
from superset.utils.rls import apply_rls # noqa: PLC0415
# Apply RLS to each statement in the script
for statement in script.statements:
@@ -761,7 +763,7 @@ class SQLExecutor:
:param status: Initial QueryStatus (RUNNING for sync, PENDING for async)
:returns: Query model instance
"""
from superset.models.sql_lab import Query as QueryModel
from superset.models.sql_lab import Query as QueryModel # noqa: PLC0415
user_id = None
if has_app_context() and hasattr(g, "user") and g.user:
@@ -793,7 +795,7 @@ class SQLExecutor:
:param opts: Query options
:returns: Cached QueryResult if found, None otherwise
"""
from superset_core.queries.types import (
from superset_core.queries.types import ( # noqa: PLC0415
QueryResult as QueryResultType,
QueryStatus,
StatementResult,
@@ -833,7 +835,7 @@ class SQLExecutor:
:param sql: SQL query (for cache key)
:param opts: Query options
"""
from superset_core.queries.types import QueryStatus
from superset_core.queries.types import QueryStatus # noqa: PLC0415
if result.status != QueryStatus.SUCCESS:
return
@@ -849,7 +851,7 @@ class SQLExecutor:
# Convert DataFrames to list-of-dicts so the cache backend
# does not need to pickle pandas objects (which can fail to
# deserialize correctly with some backends or pandas versions).
import pandas as pd
import pandas as pd # noqa: PLC0415
cached_data = {
"statements": [
@@ -906,9 +908,9 @@ class SQLExecutor:
:param rendered_sql: Rendered SQL to execute
:raises: Re-raises any exception after marking query as failed
"""
from superset.sql.execution.celery_task import execute_sql_task
from superset.utils.core import get_username
from superset.utils.dates import now_as_float
from superset.sql.execution.celery_task import execute_sql_task # noqa: PLC0415
from superset.utils.core import get_username # noqa: PLC0415
from superset.utils.dates import now_as_float # noqa: PLC0415
try:
task = execute_sql_task.delay(
@@ -931,7 +933,7 @@ class SQLExecutor:
:param query_id: ID of the Query model
:returns: AsyncQueryHandle with configured methods
"""
from superset_core.queries.types import (
from superset_core.queries.types import ( # noqa: PLC0415
AsyncQueryHandle as AsyncQueryHandleType,
QueryResult as QueryResultType,
QueryStatus,
@@ -970,7 +972,7 @@ class SQLExecutor:
:param cached_result: The cached QueryResult
:returns: AsyncQueryHandle that returns the cached data
"""
from superset_core.queries.types import (
from superset_core.queries.types import ( # noqa: PLC0415
AsyncQueryHandle as AsyncQueryHandleType,
QueryResult as QueryResultType,
QueryStatus,
@@ -1001,9 +1003,11 @@ class SQLExecutor:
@staticmethod
def _get_async_query_status(query_id: int) -> Any:
"""Get the current status of an async query."""
from superset_core.queries.types import QueryStatus as QueryStatusType
from superset_core.queries.types import ( # noqa: PLC0415
QueryStatus as QueryStatusType,
)
from superset.models.sql_lab import Query as QueryModel
from superset.models.sql_lab import Query as QueryModel # noqa: PLC0415
query = db.session.query(QueryModel).filter_by(id=query_id).one_or_none()
if not query:
@@ -1022,14 +1026,14 @@ class SQLExecutor:
@staticmethod
def _get_async_query_result(query_id: int) -> Any:
"""Get the result of an async query."""
import pandas as pd
from superset_core.queries.types import (
import pandas as pd # noqa: PLC0415
from superset_core.queries.types import ( # noqa: PLC0415
QueryResult as QueryResultType,
QueryStatus as QueryStatusType,
StatementResult,
)
from superset.models.sql_lab import Query as QueryModel
from superset.models.sql_lab import Query as QueryModel # noqa: PLC0415
query = db.session.query(QueryModel).filter_by(id=query_id).one_or_none()
if not query:
@@ -1048,16 +1052,16 @@ class SQLExecutor:
# Fetch results from results backend
if query.results_key:
import msgpack
import msgpack # noqa: PLC0415
from superset import results_backend_manager
from superset import results_backend_manager # noqa: PLC0415
results_backend = results_backend_manager.results_backend
if results_backend is not None:
blob = results_backend.get(query.results_key)
if blob:
try:
from superset.utils.core import zlib_decompress
from superset.utils.core import zlib_decompress # noqa: PLC0415
payload = msgpack.loads(zlib_decompress(blob))
@@ -1107,7 +1111,7 @@ class SQLExecutor:
@staticmethod
def _cancel_async_query(query_id: int, database: Database) -> bool:
"""Cancel an async query."""
from superset.models.sql_lab import Query as QueryModel
from superset.models.sql_lab import Query as QueryModel # noqa: PLC0415
query = db.session.query(QueryModel).filter_by(id=query_id).one_or_none()
if not query:
@@ -1128,8 +1132,11 @@ class SQLExecutor:
:param query: Query model instance to cancel
:returns: True if cancelled successfully, False otherwise
"""
from superset.constants import QUERY_CANCEL_KEY, QUERY_EARLY_CANCEL_KEY
from superset.utils.core import QuerySource
from superset.constants import ( # noqa: PLC0415
QUERY_CANCEL_KEY,
QUERY_EARLY_CANCEL_KEY,
)
from superset.utils.core import QuerySource # noqa: PLC0415
# Some engines implicitly handle cancellation
if database.db_engine_spec.has_implicit_cancel():
+2 -2
View File
@@ -1550,7 +1550,7 @@ def process_jinja_sql(
:raises jinja2.exceptions.TemplateError: If the Jinjafied SQL could not be rendered
"""
from superset.jinja_context import ( # pylint: disable=import-outside-toplevel
from superset.jinja_context import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
get_template_processor,
)
@@ -1605,7 +1605,7 @@ def sanitize_clause(clause: str, engine: str) -> str:
try:
statement = SQLStatement(clause, engine)
dialect = SQLGLOT_DIALECTS.get(engine)
from sqlglot.dialects.dialect import Dialect
from sqlglot.dialects.dialect import Dialect # noqa: PLC0415
return Dialect.get_or_raise(dialect).generate(
statement._parsed, # pylint: disable=protected-access
+1 -1
View File
@@ -63,7 +63,7 @@ class PrestoDBSQLValidator(BaseSQLValidator):
# these EXPLAIN queries done in validation as proper Query objects
# in the superset ORM.
# pylint: disable=import-outside-toplevel
from pyhive.exc import DatabaseError
from pyhive.exc import DatabaseError # noqa: PLC0415
try:
db_engine_spec.execute(cursor, sql, database)
+4 -2
View File
@@ -67,7 +67,9 @@ class SqlQueryRenderImpl(SqlQueryRender):
self._raise_template_exception(ex, execution_context)
return "NOT_REACHABLE_CODE"
except Exception as ex:
from superset.jinja_context import UndefinedTemplateFunctionException
from superset.jinja_context import ( # noqa: PLC0415
UndefinedTemplateFunctionException,
)
if isinstance(ex, UndefinedTemplateFunctionException):
return query_model.sql.strip().strip(";")
@@ -78,7 +80,7 @@ class SqlQueryRenderImpl(SqlQueryRender):
execution_context: SqlJsonExecutionContext,
sql: str,
) -> str:
from superset.sql.parse import SQLScript
from superset.sql.parse import SQLScript # noqa: PLC0415
engine = execution_context.query.database.db_engine_spec.engine
script = SQLScript(sql, engine)
+14 -14
View File
@@ -18,14 +18,14 @@
def register_sqla_event_listeners() -> None:
import sqlalchemy as sqla
import sqlalchemy as sqla # noqa: PLC0415
from superset.connectors.sqla.models import SqlaTable
from superset.models.core import FavStar
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.models.sql_lab import SavedQuery
from superset.tags.models import (
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
from superset.models.core import FavStar # noqa: PLC0415
from superset.models.dashboard import Dashboard # noqa: PLC0415
from superset.models.slice import Slice # noqa: PLC0415
from superset.models.sql_lab import SavedQuery # noqa: PLC0415
from superset.tags.models import ( # noqa: PLC0415
ChartUpdater,
DashboardUpdater,
DatasetUpdater,
@@ -54,14 +54,14 @@ def register_sqla_event_listeners() -> None:
def clear_sqla_event_listeners() -> None:
import sqlalchemy as sqla
import sqlalchemy as sqla # noqa: PLC0415
from superset.connectors.sqla.models import SqlaTable
from superset.models.core import FavStar
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.models.sql_lab import SavedQuery
from superset.tags.models import (
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
from superset.models.core import FavStar # noqa: PLC0415
from superset.models.dashboard import Dashboard # noqa: PLC0415
from superset.models.slice import Slice # noqa: PLC0415
from superset.models.sql_lab import SavedQuery # noqa: PLC0415
from superset.tags.models import ( # noqa: PLC0415
ChartUpdater,
DashboardUpdater,
DatasetUpdater,
+5 -5
View File
@@ -421,7 +421,7 @@ class ThemeRestApi(BaseSupersetModelRestApi):
is_system=False, # User-created themes are never system themes
)
from superset.extensions import db
from superset.extensions import db # noqa: PLC0415
db.session.add(new_theme)
db.session.flush() # Flush to get the ID
@@ -601,7 +601,7 @@ class ThemeRestApi(BaseSupersetModelRestApi):
$ref: '#/components/responses/500'
"""
# Check if user is admin
from superset import security_manager
from superset import security_manager # noqa: PLC0415
if not security_manager.is_admin():
return self.response(
@@ -668,7 +668,7 @@ class ThemeRestApi(BaseSupersetModelRestApi):
$ref: '#/components/responses/500'
"""
# Check if user is admin
from superset import security_manager
from superset import security_manager # noqa: PLC0415
if not security_manager.is_admin():
return self.response(
@@ -721,7 +721,7 @@ class ThemeRestApi(BaseSupersetModelRestApi):
$ref: '#/components/responses/500'
"""
# Check if user is admin
from superset import security_manager
from superset import security_manager # noqa: PLC0415
if not security_manager.is_admin():
return self.response(
@@ -771,7 +771,7 @@ class ThemeRestApi(BaseSupersetModelRestApi):
$ref: '#/components/responses/500'
"""
# Check if user is admin
from superset import security_manager
from superset import security_manager # noqa: PLC0415
if not security_manager.is_admin():
return self.response(
+1 -1
View File
@@ -241,7 +241,7 @@ class CacheManager:
def _init_distributed_coordination(self, app: Flask) -> None:
"""Initialize the distributed coordination backend (pub/sub, locks, streams)."""
from superset.async_events.cache_backend import (
from superset.async_events.cache_backend import ( # noqa: PLC0415
RedisCacheBackend,
RedisSentinelCacheBackend,
)
+1 -1
View File
@@ -595,7 +595,7 @@ def sanitize_url(url: str) -> str:
return url
try:
from urllib.parse import urlparse
from urllib.parse import urlparse # noqa: PLC0415
parsed = urlparse(url)
+3 -3
View File
@@ -36,8 +36,8 @@ def get_or_create_db(
database_name: str, sqlalchemy_uri: str, always_create: bool | None = True
) -> Database:
# pylint: disable=import-outside-toplevel
from superset import db
from superset.models import core as models
from superset import db # noqa: PLC0415
from superset.models import core as models # noqa: PLC0415
database = (
db.session.query(models.Database).filter_by(database_name=database_name).first()
@@ -81,7 +81,7 @@ def get_main_database() -> Database:
# with above function... think of how to refactor it
def remove_database(database: Database) -> None:
# pylint: disable=import-outside-toplevel
from superset import db
from superset import db # noqa: PLC0415
db.session.delete(database)
db.session.flush()
+1 -1
View File
@@ -611,7 +611,7 @@ def get_since_until( # pylint: disable=too-many-arguments,too-many-locals,too-m
# that is made available in some plugins behind the experimental
# feature flag.
# pylint: disable=import-outside-toplevel
from superset import feature_flag_manager
from superset import feature_flag_manager # noqa: PLC0415
if feature_flag_manager.is_feature_enabled("CHART_PLUGINS_EXPERIMENTAL"):
time_unit = ""
+3 -1
View File
@@ -249,7 +249,9 @@ def transaction( # pylint: disable=redefined-outer-name
def decorate(func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def wrapped(*args: Any, **kwargs: Any) -> Any:
from superset import db # pylint: disable=import-outside-toplevel
from superset import ( # noqa: PLC0415
db, # pylint: disable=import-outside-toplevel
)
if getattr(g, "in_transaction", False):
# If already in a transaction, call the function directly
+4 -2
View File
@@ -102,7 +102,9 @@ class EncryptedFieldFactory:
class SecretsMigrator:
def __init__(self, previous_secret_key: str) -> None:
from superset import db # pylint: disable=import-outside-toplevel
from superset import ( # noqa: PLC0415
db, # pylint: disable=import-outside-toplevel
)
self._db = db
self._previous_secret_key = previous_secret_key
@@ -129,7 +131,7 @@ class SecretsMigrator:
:return: mapping of table name to (Table, {column name: EncryptedType})
"""
from flask_appbuilder import ( # pylint: disable=import-outside-toplevel
from flask_appbuilder import ( # pylint: disable=import-outside-toplevel # noqa: PLC0415
Model as FABModel,
)
+2 -2
View File
@@ -26,8 +26,8 @@ def get_dataset_access_filters(
*args: Any,
) -> BooleanClauseList:
# pylint: disable=import-outside-toplevel
from superset import security_manager
from superset.connectors.sqla.models import Database
from superset import security_manager # noqa: PLC0415
from superset.connectors.sqla.models import Database # noqa: PLC0415
database_ids = security_manager.get_accessible_databases()
perms = security_manager.user_view_menu_names("datasource_access")
+4 -4
View File
@@ -178,8 +178,8 @@ class AbstractEventLogger(ABC):
**payload_override: dict[str, Any] | None,
) -> None:
# pylint: disable=import-outside-toplevel
from superset import db
from superset.views.core import get_form_data
from superset import db # noqa: PLC0415
from superset.views.core import get_form_data # noqa: PLC0415
referrer = request.referrer[:1000] if request and request.referrer else None
@@ -380,8 +380,8 @@ class DBEventLogger(AbstractEventLogger):
**kwargs: Any,
) -> None:
# pylint: disable=import-outside-toplevel
from superset import db
from superset.models.core import Log
from superset import db # noqa: PLC0415
from superset.models.core import Log # noqa: PLC0415
records = kwargs.get("records", [])
curated_payload = kwargs.get("curated_payload")
+1 -1
View File
@@ -180,7 +180,7 @@ def add_data(
:param bool append: if the table already exists, append data or replace?
"""
# pylint: disable=import-outside-toplevel
from superset.utils.database import get_example_database
from superset.utils.database import get_example_database # noqa: PLC0415
database = get_example_database()
table_exists = database.has_table(Table(table_name))
+2 -2
View File
@@ -102,7 +102,7 @@ def get_oauth2_access_token(
a valid token when they retry.
""" # noqa: E501
# pylint: disable=import-outside-toplevel
from superset.models.core import DatabaseUserOAuth2Tokens
from superset.models.core import DatabaseUserOAuth2Tokens # noqa: PLC0415
token = (
db.session.query(DatabaseUserOAuth2Tokens)
@@ -131,7 +131,7 @@ def refresh_oauth2_token(
db_engine_spec: type[BaseEngineSpec],
) -> str | None:
# pylint: disable=import-outside-toplevel
from superset.models.core import DatabaseUserOAuth2Tokens
from superset.models.core import DatabaseUserOAuth2Tokens # noqa: PLC0415
# Use longer TTL for OAuth2 token refresh (may involve network calls)
with DistributedLock(
@@ -56,7 +56,7 @@ def _prophet_fit_and_predict( # pylint: disable=too-many-arguments
# `prophet` complains about `plotly` not being installed
with suppress_logging("prophet.plot"):
# pylint: disable=import-outside-toplevel
from prophet import Prophet
from prophet import Prophet # noqa: PLC0415
prophet_logger = logging.getLogger("prophet.plot")
prophet_logger.setLevel(logging.CRITICAL)
+2 -2
View File
@@ -84,7 +84,7 @@ def get_predicates_for_table(
table must be fully qualified, with catalog (null if the DB doesn't support) and
schema.
"""
from superset.connectors.sqla.models import SqlaTable
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
# if the dataset in the RLS has null catalog, match it when using the default
# catalog
@@ -158,7 +158,7 @@ def collect_rls_predicates_for_sql(
(kept consistent with what's actually applied at query time).
:return: List of RLS predicate strings that would be applied
"""
from superset.sql.parse import SQLScript
from superset.sql.parse import SQLScript # noqa: PLC0415
try:
parsed_script = SQLScript(sql, engine=database.db_engine_spec.engine)
+1 -1
View File
@@ -125,7 +125,7 @@ def validate_webdriver_config() -> dict[str, Any]:
Returns a dictionary with the status of available webdrivers
and feature flags.
"""
from superset import feature_flag_manager
from superset import feature_flag_manager # noqa: PLC0415
return {
"selenium_available": True, # Always available as required dependency
+3 -1
View File
@@ -129,7 +129,9 @@ class Api(BaseSupersetView):
def get_query_context_factory(self) -> QueryContextFactory:
if self.query_context_factory is None:
# pylint: disable=import-outside-toplevel
from superset.common.query_context_factory import QueryContextFactory
from superset.common.query_context_factory import ( # noqa: PLC0415
QueryContextFactory,
)
self.query_context_factory = QueryContextFactory()
return self.query_context_factory
+5 -2
View File
@@ -214,9 +214,12 @@ class Superset(BaseSupersetView):
@staticmethod
def _generate_xlsx(viz_obj: BaseViz) -> FlaskResponse:
import pandas as pd
import pandas as pd # noqa: PLC0415
from superset.utils.excel import apply_column_types, df_to_excel
from superset.utils.excel import ( # noqa: PLC0415
apply_column_types,
df_to_excel,
)
payload = viz_obj.get_df_payload()
df = payload.get("df")
+1 -1
View File
@@ -117,7 +117,7 @@ class FilterRelatedTables(BaseFilter): # pylint: disable=too-few-public-methods
arg_name = "tables"
def apply(self, query: Query, value: Optional[Any]) -> Query:
from superset.connectors.sqla.models import SqlaTable
from superset.connectors.sqla.models import SqlaTable # noqa: PLC0415
like_value = "%" + cast(str, value) + "%"
return query.filter(SqlaTable.table_name.ilike(like_value))
+1 -1
View File
@@ -40,6 +40,6 @@ def version() -> FlaskResponse:
Return comprehensive version information including Git SHA
and branch when available.
"""
from superset.utils.version import get_version_metadata
from superset.utils.version import get_version_metadata # noqa: PLC0415
return jsonify(get_version_metadata())
+3 -3
View File
@@ -1309,7 +1309,7 @@ class WorldMapViz(BaseViz):
return None
# pylint: disable=import-outside-toplevel
from superset.examples import countries
from superset.examples import countries # noqa: PLC0415
cols = get_column_names([self.form_data.get("entity")]) # type: ignore
metric = utils.get_metric_name(self.form_data["metric"])
@@ -1661,8 +1661,8 @@ class DeckGLMultiLayer(BaseViz):
def get_data(self, df: pd.DataFrame) -> VizData:
# Late imports to avoid circular import issues
# pylint: disable=import-outside-toplevel
from superset import db
from superset.models.slice import Slice
from superset import db # noqa: PLC0415
from superset.models.slice import Slice # noqa: PLC0415
slice_ids = self.form_data.get("deck_slices")
slices = db.session.query(Slice).filter(Slice.id.in_(slice_ids)).all()