diff --git a/.pylintrc b/.pylintrc index 6083060624..5de0030192 100644 --- a/.pylintrc +++ b/.pylintrc @@ -75,6 +75,7 @@ enable= # --disable=W" disable= cyclic-import, # re-enable once this no longer raises false positives + no-member, # re-enable once this no longer raises false positives. This will become redundant after the min required version is 3.11 missing-docstring, duplicate-code, unspecified-encoding, diff --git a/superset/common/chart_data.py b/superset/common/chart_data.py index 65c0c43c11..e03f51eee2 100644 --- a/superset/common/chart_data.py +++ b/superset/common/chart_data.py @@ -14,10 +14,10 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -from enum import Enum +from superset.utils.backports import StrEnum -class ChartDataResultFormat(str, Enum): +class ChartDataResultFormat(StrEnum): """ Chart data response format """ @@ -31,7 +31,7 @@ class ChartDataResultFormat(str, Enum): return {cls.CSV} | {cls.XLSX} -class ChartDataResultType(str, Enum): +class ChartDataResultType(StrEnum): """ Chart data response type """ diff --git a/superset/common/db_query_status.py b/superset/common/db_query_status.py index 82bb437f65..fa893a1717 100644 --- a/superset/common/db_query_status.py +++ b/superset/common/db_query_status.py @@ -14,10 +14,10 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -from enum import Enum +from superset.utils.backports import StrEnum -class QueryStatus(str, Enum): +class QueryStatus(StrEnum): """Enum-type class for query statuses""" STOPPED: str = "stopped" diff --git a/superset/connectors/base/models.py b/superset/connectors/base/models.py index ec2443398a..bd730c2406 100644 --- a/superset/connectors/base/models.py +++ b/superset/connectors/base/models.py @@ -21,7 +21,6 @@ import json import logging from collections.abc import Hashable from datetime import datetime -from enum import Enum from json.decoder import JSONDecodeError from typing import Any, TYPE_CHECKING @@ -44,6 +43,7 @@ from superset.superset_typing import ( ResultSetColumnType, ) from superset.utils import core as utils +from superset.utils.backports import StrEnum from superset.utils.core import GenericDataType, MediumText if TYPE_CHECKING: @@ -75,7 +75,7 @@ COLUMN_FORM_DATA_PARAMS = [ ] -class DatasourceKind(str, Enum): +class DatasourceKind(StrEnum): VIRTUAL = "virtual" PHYSICAL = "physical" diff --git a/superset/constants.py b/superset/constants.py index 48b08506ca..4f01674bd4 100644 --- a/superset/constants.py +++ b/superset/constants.py @@ -20,6 +20,8 @@ # string to use when None values *need* to be converted to/from strings from enum import Enum +from superset.utils.backports import StrEnum + USER_AGENT = "Apache Superset" NULL_STRING = "" @@ -185,7 +187,7 @@ EXTRA_FORM_DATA_OVERRIDE_KEYS = ( ) -class TimeGrain(str, Enum): +class TimeGrain(StrEnum): SECOND = "PT1S" FIVE_SECONDS = "PT5S" THIRTY_SECONDS = "PT30S" @@ -214,13 +216,13 @@ class PandasAxis(int, Enum): COLUMN = 1 -class PandasPostprocessingCompare(str, Enum): +class PandasPostprocessingCompare(StrEnum): DIFF = "difference" PCT = "percentage" RAT = "ratio" -class CacheRegion(str, Enum): +class CacheRegion(StrEnum): DEFAULT = "default" DATA = "data" THUMBNAIL = "thumbnail" diff --git a/superset/errors.py b/superset/errors.py index 6f68f2466c..6661e98183 100644 --- a/superset/errors.py +++ b/superset/errors.py @@ -15,13 +15,14 @@ # specific language governing permissions and limitations # under the License. from dataclasses import dataclass -from enum import Enum from typing import Any, Optional from flask_babel import lazy_gettext as _ +from superset.utils.backports import StrEnum -class SupersetErrorType(str, Enum): + +class SupersetErrorType(StrEnum): """ Types of errors that can exist within Superset. @@ -183,7 +184,7 @@ ERROR_TYPES_TO_ISSUE_CODES_MAPPING = { } -class ErrorLevel(str, Enum): +class ErrorLevel(StrEnum): """ Levels of errors that can exist within Superset. diff --git a/superset/key_value/types.py b/superset/key_value/types.py index b2a47336c3..f5865de323 100644 --- a/superset/key_value/types.py +++ b/superset/key_value/types.py @@ -20,7 +20,6 @@ import json import pickle from abc import ABC, abstractmethod from dataclasses import dataclass -from enum import Enum from typing import Any, TypedDict from uuid import UUID @@ -30,6 +29,7 @@ from superset.key_value.exceptions import ( KeyValueCodecDecodeException, KeyValueCodecEncodeException, ) +from superset.utils.backports import StrEnum @dataclass @@ -44,14 +44,14 @@ class KeyValueFilter(TypedDict, total=False): uuid: UUID | None -class KeyValueResource(str, Enum): +class KeyValueResource(StrEnum): APP = "app" DASHBOARD_PERMALINK = "dashboard_permalink" EXPLORE_PERMALINK = "explore_permalink" METASTORE_CACHE = "superset_metastore_cache" -class SharedKey(str, Enum): +class SharedKey(StrEnum): DASHBOARD_PERMALINK_SALT = "dashboard_permalink_salt" EXPLORE_PERMALINK_SALT = "explore_permalink_salt" diff --git a/superset/models/core.py b/superset/models/core.py index 345d06584c..e76da0dcd5 100755 --- a/superset/models/core.py +++ b/superset/models/core.py @@ -20,7 +20,6 @@ from __future__ import annotations import builtins -import enum import json import logging import textwrap @@ -74,6 +73,7 @@ from superset.models.helpers import AuditMixinNullable, ImportExportMixin from superset.result_set import SupersetResultSet from superset.superset_typing import ResultSetColumnType from superset.utils import cache as cache_util, core as utils +from superset.utils.backports import StrEnum from superset.utils.core import get_username config = app.config @@ -116,7 +116,7 @@ class CssTemplate(Model, AuditMixinNullable): css = Column(Text, default="") -class ConfigurationMethod(str, enum.Enum): +class ConfigurationMethod(StrEnum): SQLALCHEMY_FORM = "sqlalchemy_form" DYNAMIC_FORM = "dynamic_form" @@ -1007,7 +1007,7 @@ class Log(Model): # pylint: disable=too-few-public-methods referrer = Column(String(1024)) -class FavStarClassName(str, enum.Enum): +class FavStarClassName(StrEnum): CHART = "slice" DASHBOARD = "Dashboard" diff --git a/superset/reports/models.py b/superset/reports/models.py index a13ded6223..5c47f41456 100644 --- a/superset/reports/models.py +++ b/superset/reports/models.py @@ -15,8 +15,6 @@ # specific language governing permissions and limitations # under the License. """A collection of ORM sqlalchemy models for Superset""" -import enum - from cron_descriptor import get_description from flask_appbuilder import Model from flask_appbuilder.models.decorators import renders @@ -41,28 +39,29 @@ from superset.models.dashboard import Dashboard from superset.models.helpers import AuditMixinNullable, ExtraJSONMixin from superset.models.slice import Slice from superset.reports.types import ReportScheduleExtra +from superset.utils.backports import StrEnum metadata = Model.metadata # pylint: disable=no-member -class ReportScheduleType(str, enum.Enum): +class ReportScheduleType(StrEnum): ALERT = "Alert" REPORT = "Report" -class ReportScheduleValidatorType(str, enum.Enum): +class ReportScheduleValidatorType(StrEnum): """Validator types for alerts""" NOT_NULL = "not null" OPERATOR = "operator" -class ReportRecipientType(str, enum.Enum): +class ReportRecipientType(StrEnum): EMAIL = "Email" SLACK = "Slack" -class ReportState(str, enum.Enum): +class ReportState(StrEnum): SUCCESS = "Success" WORKING = "Working" ERROR = "Error" @@ -70,19 +69,19 @@ class ReportState(str, enum.Enum): GRACE = "On Grace" -class ReportDataFormat(str, enum.Enum): +class ReportDataFormat(StrEnum): VISUALIZATION = "PNG" DATA = "CSV" TEXT = "TEXT" -class ReportCreationMethod(str, enum.Enum): +class ReportCreationMethod(StrEnum): CHARTS = "charts" DASHBOARDS = "dashboards" ALERTS_REPORTS = "alerts_reports" -class ReportSourceFormat(str, enum.Enum): +class ReportSourceFormat(StrEnum): CHART = "chart" DASHBOARD = "dashboard" diff --git a/superset/sql_parse.py b/superset/sql_parse.py index 70a612d0b4..c45a3a3544 100644 --- a/superset/sql_parse.py +++ b/superset/sql_parse.py @@ -18,7 +18,6 @@ import logging import re from collections.abc import Iterator from dataclasses import dataclass -from enum import Enum from typing import Any, cast, Optional from urllib import parse @@ -49,6 +48,7 @@ from sqlparse.tokens import ( from sqlparse.utils import imt from superset.exceptions import QueryClauseValidationException +from superset.utils.backports import StrEnum try: from sqloxide import parse_sql as sqloxide_parse @@ -71,7 +71,7 @@ sqlparser_sql_regex.insert(25, (r"'(''|\\\\|\\|[^'])*'", sqlparse.tokens.String. lex.set_SQL_REGEX(sqlparser_sql_regex) -class CtasMethod(str, Enum): +class CtasMethod(StrEnum): TABLE = "TABLE" VIEW = "VIEW" @@ -483,7 +483,7 @@ def sanitize_clause(clause: str) -> str: return clause -class InsertRLSState(str, Enum): +class InsertRLSState(StrEnum): """ State machine that scans for WHERE and ON clauses referencing tables. """ diff --git a/superset/sqllab/limiting_factor.py b/superset/sqllab/limiting_factor.py index 46cbc9bd81..638f9e347a 100644 --- a/superset/sqllab/limiting_factor.py +++ b/superset/sqllab/limiting_factor.py @@ -14,10 +14,10 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -import enum +from superset.utils.backports import StrEnum -class LimitingFactor(str, enum.Enum): +class LimitingFactor(StrEnum): QUERY = "QUERY" DROPDOWN = "DROPDOWN" QUERY_AND_DROPDOWN = "QUERY_AND_DROPDOWN" diff --git a/superset/tasks/types.py b/superset/tasks/types.py index cc337a81ed..84a3e7b01f 100644 --- a/superset/tasks/types.py +++ b/superset/tasks/types.py @@ -14,11 +14,10 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. - -from enum import Enum +from superset.utils.backports import StrEnum -class ExecutorType(str, Enum): +class ExecutorType(StrEnum): """ Which user should scheduled tasks be executed as. Used as follows: For Alerts & Reports: the "model" refers to the AlertSchedule object diff --git a/superset/utils/backports.py b/superset/utils/backports.py new file mode 100644 index 0000000000..b48f76d235 --- /dev/null +++ b/superset/utils/backports.py @@ -0,0 +1,26 @@ +# 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. +import sys +from enum import Enum + +if sys.version_info >= (3, 11): + # pylint: disable=unused-import + from enum import StrEnum # nopycln: import +else: + + class StrEnum(str, Enum): + pass diff --git a/superset/utils/core.py b/superset/utils/core.py index f05d7b780d..80af7cde3c 100644 --- a/superset/utils/core.py +++ b/superset/utils/core.py @@ -98,6 +98,7 @@ from superset.superset_typing import ( FormData, Metric, ) +from superset.utils.backports import StrEnum from superset.utils.database import get_example_database from superset.utils.date_parser import parse_human_timedelta from superset.utils.dates import datetime_to_epoch, EPOCH @@ -133,12 +134,12 @@ class LenientEnum(Enum): return None -class AdhocMetricExpressionType(str, Enum): +class AdhocMetricExpressionType(StrEnum): SIMPLE = "SIMPLE" SQL = "SQL" -class AnnotationType(str, Enum): +class AnnotationType(StrEnum): FORMULA = "FORMULA" INTERVAL = "INTERVAL" EVENT = "EVENT" @@ -160,7 +161,7 @@ class GenericDataType(IntEnum): # ROW = 7 -class DatasourceType(str, Enum): +class DatasourceType(StrEnum): SLTABLE = "sl_table" TABLE = "table" DATASET = "dataset" @@ -169,7 +170,7 @@ class DatasourceType(str, Enum): VIEW = "view" -class LoggerLevel(str, Enum): +class LoggerLevel(StrEnum): INFO = "info" WARNING = "warning" EXCEPTION = "exception" @@ -208,19 +209,19 @@ class QueryObjectFilterClause(TypedDict, total=False): isExtra: bool | None -class ExtraFiltersTimeColumnType(str, Enum): +class ExtraFiltersTimeColumnType(StrEnum): TIME_COL = "__time_col" TIME_GRAIN = "__time_grain" TIME_ORIGIN = "__time_origin" TIME_RANGE = "__time_range" -class ExtraFiltersReasonType(str, Enum): +class ExtraFiltersReasonType(StrEnum): NO_TEMPORAL_COLUMN = "no_temporal_column" COL_NOT_IN_DATASOURCE = "not_in_datasource" -class FilterOperator(str, Enum): +class FilterOperator(StrEnum): """ Operators used filter controls """ @@ -242,7 +243,7 @@ class FilterOperator(str, Enum): TEMPORAL_RANGE = "TEMPORAL_RANGE" -class FilterStringOperators(str, Enum): +class FilterStringOperators(StrEnum): EQUALS = ("EQUALS",) NOT_EQUALS = ("NOT_EQUALS",) LESS_THAN = ("LESS_THAN",) @@ -260,7 +261,7 @@ class FilterStringOperators(str, Enum): IS_FALSE = ("IS_FALSE",) -class PostProcessingBoxplotWhiskerType(str, Enum): +class PostProcessingBoxplotWhiskerType(StrEnum): """ Calculate cell contribution to row/column total """ @@ -270,7 +271,7 @@ class PostProcessingBoxplotWhiskerType(str, Enum): PERCENTILE = "percentile" -class PostProcessingContributionOrientation(str, Enum): +class PostProcessingContributionOrientation(StrEnum): """ Calculate cell contribution to row/column total """ @@ -298,7 +299,7 @@ class QuerySource(Enum): SQL_LAB = 2 -class QueryStatus(str, Enum): +class QueryStatus(StrEnum): """Enum-type class for query statuses""" STOPPED: str = "stopped" @@ -311,14 +312,14 @@ class QueryStatus(str, Enum): TIMED_OUT: str = "timed_out" -class DashboardStatus(str, Enum): +class DashboardStatus(StrEnum): """Dashboard status used for frontend filters""" PUBLISHED = "published" DRAFT = "draft" -class ReservedUrlParameters(str, Enum): +class ReservedUrlParameters(StrEnum): """ Reserved URL parameters that are used internally by Superset. These will not be passed to chart queries, as they control the behavior of the UI. @@ -336,7 +337,7 @@ class ReservedUrlParameters(str, Enum): return standalone -class RowLevelSecurityFilterType(str, Enum): +class RowLevelSecurityFilterType(StrEnum): REGULAR = "Regular" BASE = "Base" diff --git a/tests/unit_tests/utils/test_decorators.py b/tests/unit_tests/utils/test_decorators.py index 3aafc7a91d..f600334414 100644 --- a/tests/unit_tests/utils/test_decorators.py +++ b/tests/unit_tests/utils/test_decorators.py @@ -17,7 +17,6 @@ from contextlib import nullcontext -from enum import Enum from inspect import isclass from typing import Any, Optional from unittest.mock import call, Mock, patch @@ -26,9 +25,10 @@ import pytest from superset import app from superset.utils import decorators +from superset.utils.backports import StrEnum -class ResponseValues(str, Enum): +class ResponseValues(StrEnum): FAIL = "fail" WARN = "warn" OK = "ok"