From 56f0fc4ec2a0f92d4956ff1f224a6ed58ed87236 Mon Sep 17 00:00:00 2001 From: Eyal Ezer Date: Mon, 20 May 2024 12:27:22 -0500 Subject: [PATCH 01/59] refactor: Migration of json utilities from core (#28522) Co-authored-by: Eyal Ezer --- .../annotation_layers/annotations/schemas.py | 4 +- superset/charts/data/api.py | 7 +- superset/common/query_object.py | 2 +- superset/dashboards/schemas.py | 4 +- superset/embedded/view.py | 7 +- superset/key_value/utils.py | 2 +- superset/models/core.py | 6 +- superset/models/dashboard.py | 4 +- superset/result_set.py | 4 +- superset/sql_lab.py | 11 +- superset/sqllab/api.py | 11 +- .../sqllab/execution_context_convertor.py | 31 +-- superset/utils/cache.py | 2 +- superset/utils/core.py | 146 +----------- superset/utils/json.py | 211 ++++++++++++++++++ superset/utils/schema.py | 4 +- superset/views/api.py | 6 +- superset/views/base.py | 40 ++-- superset/views/chart/views.py | 6 +- superset/views/core.py | 19 +- superset/views/dashboard/views.py | 7 +- superset/views/sql_lab/views.py | 4 +- superset/views/tags.py | 8 +- superset/viz.py | 10 +- tests/integration_tests/conftest.py | 2 +- tests/integration_tests/core_tests.py | 6 +- tests/integration_tests/sqllab_tests.py | 6 +- tests/integration_tests/utils_tests.py | 12 +- tests/unit_tests/utils/json_tests.py | 114 ++++++++++ tests/unit_tests/utils/test_core.py | 51 ----- 30 files changed, 431 insertions(+), 316 deletions(-) create mode 100644 superset/utils/json.py create mode 100644 tests/unit_tests/utils/json_tests.py diff --git a/superset/annotation_layers/annotations/schemas.py b/superset/annotation_layers/annotations/schemas.py index 7aa553d2a16bb..516241b9e3b44 100644 --- a/superset/annotation_layers/annotations/schemas.py +++ b/superset/annotation_layers/annotations/schemas.py @@ -20,7 +20,7 @@ from marshmallow.validate import Length from superset.exceptions import SupersetException -from superset.utils import core as utils +from superset.utils import json as json_utils openapi_spec_methods_override = { "get": {"get": {"summary": "Get an annotation layer"}}, @@ -51,7 +51,7 @@ def validate_json(value: Union[bytes, bytearray, str]) -> None: try: - utils.validate_json(value) + json_utils.validate_json(value) except SupersetException as ex: raise ValidationError("JSON not valid") from ex diff --git a/superset/charts/data/api.py b/superset/charts/data/api.py index 2e46eb2737a3a..e902d5738a65c 100644 --- a/superset/charts/data/api.py +++ b/superset/charts/data/api.py @@ -21,7 +21,6 @@ import logging from typing import Any, TYPE_CHECKING -import simplejson from flask import current_app, g, make_response, request, Response from flask_appbuilder.api import expose, protect from flask_babel import gettext as _ @@ -47,11 +46,11 @@ from superset.exceptions import QueryObjectValidationError from superset.extensions import event_logger from superset.models.sql_lab import Query +from superset.utils import json as json_utils from superset.utils.core import ( create_zip, DatasourceType, get_user_id, - json_int_dttm_ser, ) from superset.utils.decorators import logs_context from superset.views.base import CsvResponse, generate_download_headers, XlsxResponse @@ -396,9 +395,9 @@ def _process_data(query_data: Any) -> Any: ) if result_format == ChartDataResultFormat.JSON: - response_data = simplejson.dumps( + response_data = json_utils.dumps( {"result": result["queries"]}, - default=json_int_dttm_ser, + default=json_utils.json_int_dttm_ser, ignore_nan=True, ) resp = make_response(response_data, 200) diff --git a/superset/common/query_object.py b/superset/common/query_object.py index b183532ebf936..35b1d2974fcd3 100644 --- a/superset/common/query_object.py +++ b/superset/common/query_object.py @@ -43,10 +43,10 @@ get_column_names, get_metric_names, is_adhoc_metric, - json_int_dttm_ser, QueryObjectFilterClause, ) from superset.utils.hashing import md5_sha_from_dict +from superset.utils.json import json_int_dttm_ser if TYPE_CHECKING: from superset.connectors.sqla.models import BaseDatasource diff --git a/superset/dashboards/schemas.py b/superset/dashboards/schemas.py index 6a6debe397541..c90da33734a9c 100644 --- a/superset/dashboards/schemas.py +++ b/superset/dashboards/schemas.py @@ -24,7 +24,7 @@ from superset import security_manager from superset.exceptions import SupersetException from superset.tags.models import TagType -from superset.utils import core as utils +from superset.utils import json as json_utils get_delete_ids_schema = {"type": "array", "items": {"type": "integer"}} get_export_ids_schema = {"type": "array", "items": {"type": "integer"}} @@ -88,7 +88,7 @@ def validate_json(value: Union[bytes, bytearray, str]) -> None: try: - utils.validate_json(value) + json_utils.validate_json(value) except SupersetException as ex: raise ValidationError("JSON not valid") from ex diff --git a/superset/embedded/view.py b/superset/embedded/view.py index 462c6046faaf0..a260e33b5e553 100644 --- a/superset/embedded/view.py +++ b/superset/embedded/view.py @@ -14,7 +14,6 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -import json from typing import Callable from flask import abort, request @@ -25,7 +24,7 @@ from superset import event_logger, is_feature_enabled from superset.daos.dashboard import EmbeddedDashboardDAO from superset.superset_typing import FlaskResponse -from superset.utils import core as utils +from superset.utils import json as json_utils from superset.views.base import BaseSupersetView, common_bootstrap_payload @@ -87,7 +86,7 @@ def embedded( return self.render_template( "superset/spa.html", entry="embedded", - bootstrap_data=json.dumps( - bootstrap_data, default=utils.pessimistic_json_iso_dttm_ser + bootstrap_data=json_utils.dumps( + bootstrap_data, default=json_utils.pessimistic_json_iso_dttm_ser ), ) diff --git a/superset/key_value/utils.py b/superset/key_value/utils.py index 6b487c278c0d0..1a22cfaa747b3 100644 --- a/superset/key_value/utils.py +++ b/superset/key_value/utils.py @@ -26,7 +26,7 @@ from superset.key_value.exceptions import KeyValueParseKeyError from superset.key_value.types import KeyValueFilter, KeyValueResource -from superset.utils.core import json_dumps_w_dates +from superset.utils.json import json_dumps_w_dates HASHIDS_MIN_LENGTH = 11 diff --git a/superset/models/core.py b/superset/models/core.py index bbf30523420a6..2d7e57929526d 100755 --- a/superset/models/core.py +++ b/superset/models/core.py @@ -76,7 +76,7 @@ from superset.result_set import SupersetResultSet from superset.sql_parse import Table from superset.superset_typing import OAuth2ClientConfig, ResultSetColumnType -from superset.utils import cache as cache_util, core as utils +from superset.utils import cache as cache_util, core as utils, json as json_utils from superset.utils.backports import StrEnum from superset.utils.core import DatasourceName, get_username from superset.utils.oauth2 import get_oauth2_access_token @@ -601,7 +601,7 @@ def column_needs_conversion(df_series: pd.Series) -> bool: for col, coltype in df.dtypes.to_dict().items(): if coltype == numpy.object_ and column_needs_conversion(df[col]): - df[col] = df[col].apply(utils.json_dumps_w_dates) + df[col] = df[col].apply(json_utils.json_dumps_w_dates) return df @property @@ -957,7 +957,7 @@ def get_pk_constraint(self, table: Table) -> dict[str, Any]: def _convert(value: Any) -> Any: try: - return utils.base_json_conv(value) + return json_utils.base_json_conv(value) except TypeError: return None diff --git a/superset/models/dashboard.py b/superset/models/dashboard.py index 991d2d41a46f9..f478583246e21 100644 --- a/superset/models/dashboard.py +++ b/superset/models/dashboard.py @@ -51,7 +51,7 @@ from superset.tasks.thumbnails import cache_dashboard_thumbnail from superset.tasks.utils import get_current_user from superset.thumbnails.digest import get_dashboard_digest -from superset.utils import core as utils +from superset.utils import core as utils, json as json_utils metadata = Model.metadata # pylint: disable=no-member config = app.config @@ -372,7 +372,7 @@ def export_dashboards( # pylint: disable=too-many-locals return json.dumps( {"dashboards": copied_dashboards, "datasources": eager_datasources}, - cls=utils.DashboardEncoder, + cls=json_utils.DashboardEncoder, indent=4, ) diff --git a/superset/result_set.py b/superset/result_set.py index 061656720e4e2..38ca9453a59c4 100644 --- a/superset/result_set.py +++ b/superset/result_set.py @@ -28,7 +28,7 @@ from superset.db_engine_specs import BaseEngineSpec from superset.superset_typing import DbapiDescription, DbapiResult, ResultSetColumnType -from superset.utils import core as utils +from superset.utils import core as utils, json as json_utils from superset.utils.core import GenericDataType logger = logging.getLogger(__name__) @@ -61,7 +61,7 @@ def dedup(l: list[str], suffix: str = "__", case_sensitive: bool = True) -> list def stringify(obj: Any) -> str: - return json.dumps(obj, default=utils.json_iso_dttm_ser) + return json_utils.dumps(obj, default=json_utils.json_iso_dttm_ser) def stringify_values(array: NDArray[Any]) -> NDArray[Any]: diff --git a/superset/sql_lab.py b/superset/sql_lab.py index 3f8c1cc73709c..d2e6680fbb82a 100644 --- a/superset/sql_lab.py +++ b/superset/sql_lab.py @@ -24,7 +24,6 @@ import backoff import msgpack -import simplejson as json from celery.exceptions import SoftTimeLimitExceeded from flask_babel import gettext as __ @@ -59,8 +58,8 @@ ) from superset.sqllab.limiting_factor import LimitingFactor from superset.sqllab.utils import write_ipc_buffer +from superset.utils import json as json_utils from superset.utils.core import ( - json_iso_dttm_ser, override_user, QuerySource, zlib_compress, @@ -349,9 +348,13 @@ def _serialize_payload( ) -> Union[bytes, str]: logger.debug("Serializing to msgpack: %r", use_msgpack) if use_msgpack: - return msgpack.dumps(payload, default=json_iso_dttm_ser, use_bin_type=True) + return msgpack.dumps( + payload, default=json_utils.json_iso_dttm_ser, use_bin_type=True + ) - return json.dumps(payload, default=json_iso_dttm_ser, ignore_nan=True) + return json_utils.dumps( + payload, default=json_utils.json_iso_dttm_ser, ignore_nan=True + ) def _serialize_and_expand_data( diff --git a/superset/sqllab/api.py b/superset/sqllab/api.py index 6d6579c9498f9..df6506a5d2689 100644 --- a/superset/sqllab/api.py +++ b/superset/sqllab/api.py @@ -18,7 +18,6 @@ from typing import Any, cast, Optional from urllib import parse -import simplejson as json from flask import request, Response from flask_appbuilder import permission_name from flask_appbuilder.api import expose, protect, rison, safe @@ -62,7 +61,7 @@ from superset.sqllab.utils import bootstrap_sqllab_data from superset.sqllab.validators import CanAccessQueryValidatorImpl from superset.superset_typing import FlaskResponse -from superset.utils import core as utils +from superset.utils import core as utils, json as json_utils from superset.views.base import CsvResponse, generate_download_headers, json_success from superset.views.base_api import BaseSupersetApi, requires_json, statsd_metrics @@ -132,9 +131,9 @@ def get(self) -> Response: result = bootstrap_sqllab_data(user_id) return json_success( - json.dumps( + json_utils.dumps( {"result": result}, - default=utils.json_iso_dttm_ser, + default=json_utils.json_iso_dttm_ser, ignore_nan=True, ), 200, @@ -343,9 +342,9 @@ def get_results(self, **kwargs: Any) -> FlaskResponse: # Using pessimistic json serialization since some database drivers can return # unserializeable types at times - payload = json.dumps( + payload = json_utils.dumps( result, - default=utils.pessimistic_json_iso_dttm_ser, + default=json_utils.pessimistic_json_iso_dttm_ser, ignore_nan=True, ) return json_success(payload, 200) diff --git a/superset/sqllab/execution_context_convertor.py b/superset/sqllab/execution_context_convertor.py index 06bf3b847462c..9453faa152fc2 100644 --- a/superset/sqllab/execution_context_convertor.py +++ b/superset/sqllab/execution_context_convertor.py @@ -19,11 +19,9 @@ import logging from typing import Any, TYPE_CHECKING -import simplejson as json - -import superset.utils.core as utils from superset.sqllab.command_status import SqlJsonExecutionStatus from superset.sqllab.utils import apply_display_max_row_configuration_if_require +from superset.utils import json as json_utils logger = logging.getLogger(__name__) @@ -52,23 +50,16 @@ def set_payload( def serialize_payload(self) -> str: if self._exc_status == SqlJsonExecutionStatus.HAS_RESULTS: - sql_results = apply_display_max_row_configuration_if_require( - self.payload, self._max_row_in_display_configuration + return json_utils.dumps( + apply_display_max_row_configuration_if_require( + self.payload, self._max_row_in_display_configuration + ), + default=json_utils.pessimistic_json_iso_dttm_ser, + ignore_nan=True, ) - try: - return json.dumps( - sql_results, - default=utils.pessimistic_json_iso_dttm_ser, - ignore_nan=True, - ) - except UnicodeDecodeError: - return json.dumps( - sql_results, - default=utils.pessimistic_json_iso_dttm_ser, - ensure_ascii=False, - ignore_nan=True, - ) - return json.dumps( - {"query": self.payload}, default=utils.json_int_dttm_ser, ignore_nan=True + return json_utils.dumps( + {"query": self.payload}, + default=json_utils.json_int_dttm_ser, + ignore_nan=True, ) diff --git a/superset/utils/cache.py b/superset/utils/cache.py index 00216fc4b1d1f..15f334e128aee 100644 --- a/superset/utils/cache.py +++ b/superset/utils/cache.py @@ -30,8 +30,8 @@ from superset import db from superset.extensions import cache_manager from superset.models.cache import CacheKey -from superset.utils.core import json_int_dttm_ser from superset.utils.hashing import md5_sha_from_dict +from superset.utils.json import json_int_dttm_ser if TYPE_CHECKING: from superset.stats_logger import BaseStatsLogger diff --git a/superset/utils/core.py b/superset/utils/core.py index bf457bf5b3e43..37a11ea7ff592 100644 --- a/superset/utils/core.py +++ b/superset/utils/core.py @@ -21,9 +21,7 @@ import _thread import collections -import decimal import errno -import json import logging import os import platform @@ -40,7 +38,7 @@ from collections.abc import Iterable, Iterator, Sequence from contextlib import closing, contextmanager from dataclasses import dataclass -from datetime import date, datetime, time, timedelta +from datetime import timedelta from email.mime.application import MIMEApplication from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart @@ -56,7 +54,6 @@ import markdown as md import nh3 -import numpy as np import pandas as pd import sqlalchemy as sa from cryptography.hazmat.backends import default_backend @@ -65,7 +62,6 @@ from flask_appbuilder import SQLA from flask_appbuilder.security.sqla.models import User from flask_babel import gettext as __ -from flask_babel.speaklater import LazyString from markupsafe import Markup from pandas.api.types import infer_dtype from pandas.core.dtypes.common import is_numeric_dtype @@ -103,7 +99,6 @@ 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 from superset.utils.hashing import md5_sha_from_dict, md5_sha_from_str if TYPE_CHECKING: @@ -418,136 +413,6 @@ def cast_to_boolean(value: Any) -> bool | None: return False -class DashboardEncoder(json.JSONEncoder): - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - self.sort_keys = True - - def default(self, o: Any) -> dict[Any, Any] | str: - if isinstance(o, uuid.UUID): - return str(o) - try: - vals = {k: v for k, v in o.__dict__.items() if k != "_sa_instance_state"} - return {f"__{o.__class__.__name__}__": vals} - except Exception: # pylint: disable=broad-except - if isinstance(o, datetime): - return {"__datetime__": o.replace(microsecond=0).isoformat()} - return json.JSONEncoder(sort_keys=True).default(o) - - -def format_timedelta(time_delta: timedelta) -> str: - """ - Ensures negative time deltas are easily interpreted by humans - - >>> td = timedelta(0) - timedelta(days=1, hours=5,minutes=6) - >>> str(td) - '-2 days, 18:54:00' - >>> format_timedelta(td) - '-1 day, 5:06:00' - """ - if time_delta < timedelta(0): - return "-" + str(abs(time_delta)) - - # Change this to format positive time deltas the way you want - return str(time_delta) - - -def base_json_conv(obj: Any) -> Any: - """ - Tries to convert additional types to JSON compatible forms. - - :param obj: The serializable object - :returns: The JSON compatible form - :raises TypeError: If the object cannot be serialized - :see: https://docs.python.org/3/library/json.html#encoders-and-decoders - """ - - if isinstance(obj, memoryview): - obj = obj.tobytes() - if isinstance(obj, np.int64): - return int(obj) - if isinstance(obj, np.bool_): - return bool(obj) - if isinstance(obj, np.ndarray): - return obj.tolist() - if isinstance(obj, set): - return list(obj) - if isinstance(obj, decimal.Decimal): - return float(obj) - if isinstance(obj, (uuid.UUID, time, LazyString)): - return str(obj) - if isinstance(obj, timedelta): - return format_timedelta(obj) - if isinstance(obj, bytes): - try: - return obj.decode("utf-8") - except Exception: # pylint: disable=broad-except - try: - return obj.decode("utf-16") - except Exception: # pylint: disable=broad-except - return "[bytes]" - - raise TypeError(f"Unserializable object {obj} of type {type(obj)}") - - -def json_iso_dttm_ser(obj: Any, pessimistic: bool = False) -> Any: - """ - A JSON serializer that deals with dates by serializing them to ISO 8601. - - >>> json.dumps({'dttm': datetime(1970, 1, 1)}, default=json_iso_dttm_ser) - '{"dttm": "1970-01-01T00:00:00"}' - - :param obj: The serializable object - :param pessimistic: Whether to be pessimistic regarding serialization - :returns: The JSON compatible form - :raises TypeError: If the non-pessimistic object cannot be serialized - """ - - if isinstance(obj, (datetime, date, pd.Timestamp)): - return obj.isoformat() - - try: - return base_json_conv(obj) - except TypeError as ex: - if pessimistic: - logger.error("Failed to serialize %s", obj) - return f"Unserializable [{type(obj)}]" - raise ex - - -def pessimistic_json_iso_dttm_ser(obj: Any) -> Any: - """Proxy to call json_iso_dttm_ser in a pessimistic way - - If one of object is not serializable to json, it will still succeed""" - return json_iso_dttm_ser(obj, pessimistic=True) - - -def json_int_dttm_ser(obj: Any) -> Any: - """ - A JSON serializer that deals with dates by serializing them to EPOCH. - - >>> json.dumps({'dttm': datetime(1970, 1, 1)}, default=json_int_dttm_ser) - '{"dttm": 0.0}' - - :param obj: The serializable object - :returns: The JSON compatible form - :raises TypeError: If the object cannot be serialized - """ - - if isinstance(obj, (datetime, pd.Timestamp)): - return datetime_to_epoch(obj) - - if isinstance(obj, date): - return (obj - EPOCH.date()).total_seconds() * 1000 - - return base_json_conv(obj) - - -def json_dumps_w_dates(payload: dict[Any, Any], sort_keys: bool = False) -> str: - """Dumps payload to JSON with Datetime objects properly converted""" - return json.dumps(payload, default=json_int_dttm_ser, sort_keys=sort_keys) - - def error_msg_from_exception(ex: Exception) -> str: """Translate exception into error message @@ -691,15 +556,6 @@ def get_datasource_full_name( return ".".join([f"[{part}]" for part in parts if part]) -def validate_json(obj: bytes | bytearray | str) -> None: - if obj: - try: - json.loads(obj) - except Exception as ex: - logger.error("JSON is not valid %s", str(ex), exc_info=True) - raise SupersetException("JSON is not valid") from ex - - class SigalrmTimeout: """ To be used in a ``with`` block and timeout its content. diff --git a/superset/utils/json.py b/superset/utils/json.py new file mode 100644 index 0000000000000..9068a84bb306a --- /dev/null +++ b/superset/utils/json.py @@ -0,0 +1,211 @@ +# 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 decimal +import json +import logging +import uuid +from datetime import date, datetime, time, timedelta +from typing import Any, Callable, Optional, Union + +import numpy as np +import pandas as pd +import simplejson +from flask_babel.speaklater import LazyString + +from superset.exceptions import SupersetException +from superset.utils.dates import datetime_to_epoch, EPOCH + +logging.getLogger("MARKDOWN").setLevel(logging.INFO) +logger = logging.getLogger(__name__) + + +class DashboardEncoder(json.JSONEncoder): + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.sort_keys = True + + def default(self, o: Any) -> Union[dict[Any, Any], str]: + if isinstance(o, uuid.UUID): + return str(o) + try: + vals = {k: v for k, v in o.__dict__.items() if k != "_sa_instance_state"} + return {f"__{o.__class__.__name__}__": vals} + except Exception: # pylint: disable=broad-except + if isinstance(o, datetime): + return {"__datetime__": o.replace(microsecond=0).isoformat()} + return json.JSONEncoder(sort_keys=True).default(o) + + +def format_timedelta(time_delta: timedelta) -> str: + """ + Ensures negative time deltas are easily interpreted by humans + + >>> td = timedelta(0) - timedelta(days=1, hours=5,minutes=6) + >>> str(td) + '-2 days, 18:54:00' + >>> format_timedelta(td) + '-1 day, 5:06:00' + """ + if time_delta < timedelta(0): + return "-" + str(abs(time_delta)) + + # Change this to format positive time deltas the way you want + return str(time_delta) + + +def base_json_conv(obj: Any) -> Any: + """ + Tries to convert additional types to JSON compatible forms. + + :param obj: The serializable object + :returns: The JSON compatible form + :raises TypeError: If the object cannot be serialized + :see: https://docs.python.org/3/library/json.html#encoders-and-decoders + """ + + if isinstance(obj, memoryview): + obj = obj.tobytes() + if isinstance(obj, np.int64): + return int(obj) + if isinstance(obj, np.bool_): + return bool(obj) + if isinstance(obj, np.ndarray): + return obj.tolist() + if isinstance(obj, set): + return list(obj) + if isinstance(obj, decimal.Decimal): + return float(obj) + if isinstance(obj, (uuid.UUID, time, LazyString)): + return str(obj) + if isinstance(obj, timedelta): + return format_timedelta(obj) + if isinstance(obj, bytes): + try: + return obj.decode("utf-8") + except Exception: # pylint: disable=broad-except + try: + return obj.decode("utf-16") + except Exception: # pylint: disable=broad-except + return "[bytes]" + + raise TypeError(f"Unserializable object {obj} of type {type(obj)}") + + +def json_iso_dttm_ser(obj: Any, pessimistic: bool = False) -> Any: + """ + A JSON serializer that deals with dates by serializing them to ISO 8601. + + >>> json.dumps({'dttm': datetime(1970, 1, 1)}, default=json_iso_dttm_ser) + '{"dttm": "1970-01-01T00:00:00"}' + + :param obj: The serializable object + :param pessimistic: Whether to be pessimistic regarding serialization + :returns: The JSON compatible form + :raises TypeError: If the non-pessimistic object cannot be serialized + """ + + if isinstance(obj, (datetime, date, pd.Timestamp)): + return obj.isoformat() + + try: + return base_json_conv(obj) + except TypeError as ex: + if pessimistic: + logger.error("Failed to serialize %s", obj) + return f"Unserializable [{type(obj)}]" + raise ex + + +def pessimistic_json_iso_dttm_ser(obj: Any) -> Any: + """Proxy to call json_iso_dttm_ser in a pessimistic way + + If one of object is not serializable to json, it will still succeed""" + return json_iso_dttm_ser(obj, pessimistic=True) + + +def json_int_dttm_ser(obj: Any) -> Any: + """ + A JSON serializer that deals with dates by serializing them to EPOCH. + + >>> json.dumps({'dttm': datetime(1970, 1, 1)}, default=json_int_dttm_ser) + '{"dttm": 0.0}' + + :param obj: The serializable object + :returns: The JSON compatible form + :raises TypeError: If the object cannot be serialized + """ + + if isinstance(obj, (datetime, pd.Timestamp)): + return datetime_to_epoch(obj) + + if isinstance(obj, date): + return (obj - EPOCH.date()).total_seconds() * 1000 + + return base_json_conv(obj) + + +def json_dumps_w_dates(payload: dict[Any, Any], sort_keys: bool = False) -> str: + """Dumps payload to JSON with Datetime objects properly converted""" + return dumps(payload, default=json_int_dttm_ser, sort_keys=sort_keys) + + +def validate_json(obj: Union[bytes, bytearray, str]) -> None: + """ + A JSON Validator that validates an object of bytes, bytes array or string + to be in valid JSON format + + :raises SupersetException: if obj is not serializable to JSON + :param obj: an object that should be parseable to JSON + """ + if obj: + try: + json.loads(obj) + except Exception as ex: + logger.error("JSON is not valid %s", str(ex), exc_info=True) + raise SupersetException("JSON is not valid") from ex + + +def dumps( + obj: Any, + default: Optional[Callable[[Any], Any]] = json_iso_dttm_ser, + ignore_nan: bool = True, + sort_keys: bool = False, +) -> str: + """ + Dumps object to compatible JSON format + + :param obj: The serializable object + :param default: function that should return a serializable version of obj + :param ignore_nan: when set to True nan values will be ignored + :param sort_keys: when set to True keys will be sorted + :returns: String object in the JSON compatible form + """ + + results_string = "" + try: + results_string = simplejson.dumps( + obj, default=default, ignore_nan=ignore_nan, sort_keys=sort_keys + ) + except UnicodeDecodeError: + results_string = simplejson.dumps( # type: ignore[call-overload] + obj, + default=default, + ignore_nan=ignore_nan, + sort_keys=sort_keys, + encoding=None, + ) + return results_string diff --git a/superset/utils/schema.py b/superset/utils/schema.py index c082ae017c211..7fd83d715b887 100644 --- a/superset/utils/schema.py +++ b/superset/utils/schema.py @@ -19,7 +19,7 @@ from marshmallow import validate, ValidationError from superset.exceptions import SupersetException -from superset.utils import core as utils +from superset.utils import json as json_utils class OneOfCaseInsensitive(validate.OneOf): @@ -49,6 +49,6 @@ def validate_json(value: Union[bytes, bytearray, str]) -> None: :param value: an object that should be parseable to JSON """ try: - utils.validate_json(value) + json_utils.validate_json(value) except SupersetException as ex: raise ValidationError("JSON not valid") from ex diff --git a/superset/views/api.py b/superset/views/api.py index d5dd0eca4c5ec..eef2b72bc73ca 100644 --- a/superset/views/api.py +++ b/superset/views/api.py @@ -33,7 +33,7 @@ from superset.legacy import update_time_range from superset.models.slice import Slice from superset.superset_typing import FlaskResponse -from superset.utils import core as utils +from superset.utils import json as json_utils from superset.utils.date_parser import get_since_until from superset.views.base import api, BaseSupersetView, handle_api_exception @@ -73,8 +73,8 @@ def query(self) -> FlaskResponse: query_context.raise_for_access() result = query_context.get_payload() payload_json = result["queries"] - return json.dumps( - payload_json, default=utils.json_int_dttm_ser, ignore_nan=True + return json_utils.dumps( + payload_json, default=json_utils.json_int_dttm_ser, ignore_nan=True ) @event_logger.log_this diff --git a/superset/views/base.py b/superset/views/base.py index 06e330ab64853..04583387bd8cb 100644 --- a/superset/views/base.py +++ b/superset/views/base.py @@ -25,7 +25,6 @@ from importlib.resources import files from typing import Any, Callable, cast -import simplejson as json import yaml from babel import Locale from flask import ( @@ -51,14 +50,13 @@ ) from flask_appbuilder.security.sqla.models import User from flask_appbuilder.widgets import ListWidget -from flask_babel import get_locale, gettext as __, lazy_gettext as _ +from flask_babel import get_locale, gettext as __ from flask_jwt_extended.exceptions import NoAuthorizationError from flask_wtf.csrf import CSRFError from flask_wtf.form import FlaskForm from sqlalchemy import exc from sqlalchemy.orm import Query from werkzeug.exceptions import HTTPException -from wtforms import Form from wtforms.fields.core import Field, UnboundField from superset import ( @@ -85,7 +83,7 @@ from superset.reports.models import ReportRecipientType from superset.superset_typing import FlaskResponse from superset.translations.utils import get_language_pack -from superset.utils import core as utils +from superset.utils import core as utils, json as json_utils from superset.utils.filters import get_dataset_access_filters from .utils import bootstrap_user_data @@ -155,7 +153,9 @@ def json_error_response( payload = payload or {"error": f"{msg}"} return Response( - json.dumps(payload, default=utils.json_iso_dttm_ser, ignore_nan=True), + json_utils.dumps( + payload, default=json_utils.json_iso_dttm_ser, ignore_nan=True + ), status=status, mimetype="application/json", ) @@ -170,7 +170,9 @@ def json_errors_response( payload["errors"] = [dataclasses.asdict(error) for error in errors] return Response( - json.dumps(payload, default=utils.json_iso_dttm_ser, ignore_nan=True), + json_utils.dumps( + payload, default=json_utils.json_iso_dttm_ser, ignore_nan=True + ), status=status, mimetype="application/json; charset=utf-8", ) @@ -292,7 +294,9 @@ class BaseSupersetView(BaseView): @staticmethod def json_response(obj: Any, status: int = 200) -> FlaskResponse: return Response( - json.dumps(obj, default=utils.json_int_dttm_ser, ignore_nan=True), + json_utils.dumps( + obj, default=json_utils.json_int_dttm_ser, ignore_nan=True + ), status=status, mimetype="application/json", ) @@ -308,8 +312,8 @@ def render_app_template( return self.render_template( "superset/spa.html", entry="spa", - bootstrap_data=json.dumps( - payload, default=utils.pessimistic_json_iso_dttm_ser + bootstrap_data=json_utils.dumps( + payload, default=json_utils.pessimistic_json_iso_dttm_ser ), ) @@ -543,9 +547,9 @@ def show_unexpected_exception(ex: Exception) -> FlaskResponse: @superset_app.context_processor def get_common_bootstrap_data() -> dict[str, Any]: def serialize_bootstrap_data() -> str: - return json.dumps( + return json_utils.dumps( {"common": common_bootstrap_payload()}, - default=utils.pessimistic_json_iso_dttm_ser, + default=json_utils.pessimistic_json_iso_dttm_ser, ) return {"bootstrap_data": serialize_bootstrap_data} @@ -626,8 +630,8 @@ def render_app_template(self) -> FlaskResponse: return self.render_template( "superset/spa.html", entry="spa", - bootstrap_data=json.dumps( - payload, default=utils.pessimistic_json_iso_dttm_ser + bootstrap_data=json_utils.dumps( + payload, default=json_utils.pessimistic_json_iso_dttm_ser ), ) @@ -640,16 +644,6 @@ class ListWidgetWithCheckboxes(ListWidget): # pylint: disable=too-few-public-me template = "superset/fab_overrides/list_with_checkboxes.html" -def validate_json(form: Form, field: Field) -> None: # pylint: disable=unused-argument - try: - json.loads(field.data) - except Exception as ex: - logger.exception(ex) - raise Exception( # pylint: disable=broad-exception-raised - _("json isn't valid") - ) from ex - - class YamlExportMixin: # pylint: disable=too-few-public-methods """ Override this if you want a dict response instead, with a certain key. diff --git a/superset/views/chart/views.py b/superset/views/chart/views.py index 00d591587207f..e8ea621902001 100644 --- a/superset/views/chart/views.py +++ b/superset/views/chart/views.py @@ -22,7 +22,7 @@ from superset.constants import MODEL_VIEW_RW_METHOD_PERMISSION_MAP, RouteMethod from superset.models.slice import Slice from superset.superset_typing import FlaskResponse -from superset.utils import core as utils +from superset.utils import json as json_utils from superset.views.base import DeleteMixin, DeprecateModelViewMixin, SupersetModelView from superset.views.chart.mixin import SliceMixin @@ -41,10 +41,10 @@ class SliceModelView( method_permission_name = MODEL_VIEW_RW_METHOD_PERMISSION_MAP def pre_add(self, item: "SliceModelView") -> None: - utils.validate_json(item.params) + json_utils.validate_json(item.params) def pre_update(self, item: "SliceModelView") -> None: - utils.validate_json(item.params) + json_utils.validate_json(item.params) security_manager.raise_for_ownership(item) def pre_delete(self, item: "SliceModelView") -> None: diff --git a/superset/views/core.py b/superset/views/core.py index 35ba3d042889b..83dcbf0a0df2f 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -71,10 +71,9 @@ from superset.models.sql_lab import Query from superset.models.user_attributes import UserAttribute from superset.superset_typing import FlaskResponse -from superset.utils import core as utils +from superset.utils import core as utils, json as json_utils from superset.utils.cache import etag_cache from superset.utils.core import ( - base_json_conv, DatasourceType, get_user_id, ReservedUrlParameters, @@ -576,8 +575,8 @@ def explore( return self.render_template( "superset/basic.html", - bootstrap_data=json.dumps( - bootstrap_data, default=utils.pessimistic_json_iso_dttm_ser + bootstrap_data=json_utils.dumps( + bootstrap_data, default=json_utils.pessimistic_json_iso_dttm_ser ), entry="explore", title=title, @@ -753,7 +752,7 @@ def warm_up_cache(self) -> FlaskResponse: ) return json_success( - json.dumps( + json_utils.dumps( [ { "slice_id" if key == "chart_id" else key: value @@ -765,7 +764,7 @@ def warm_up_cache(self) -> FlaskResponse: } for slc in slices ], - default=base_json_conv, + default=json_utils.base_json_conv, ), ) @@ -814,12 +813,12 @@ def dashboard( "superset/spa.html", entry="spa", title=dashboard.dashboard_title, # dashboard title is always visible - bootstrap_data=json.dumps( + bootstrap_data=json_utils.dumps( { "user": bootstrap_user_data(g.user, include_perms=True), "common": common_bootstrap_payload(), }, - default=utils.pessimistic_json_iso_dttm_ser, + default=json_utils.pessimistic_json_iso_dttm_ser, ), standalone_mode=ReservedUrlParameters.is_standalone_mode(), ) @@ -919,8 +918,8 @@ def welcome(self) -> FlaskResponse: return self.render_template( "superset/spa.html", entry="spa", - bootstrap_data=json.dumps( - payload, default=utils.pessimistic_json_iso_dttm_ser + bootstrap_data=json_utils.dumps( + payload, default=json_utils.pessimistic_json_iso_dttm_ser ), ) diff --git a/superset/views/dashboard/views.py b/superset/views/dashboard/views.py index 3b405fea0af5f..97735b9081b4b 100644 --- a/superset/views/dashboard/views.py +++ b/superset/views/dashboard/views.py @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. import builtins -import json from typing import Callable, Union from flask import g, redirect, request, Response @@ -35,7 +34,7 @@ from superset.constants import MODEL_VIEW_RW_METHOD_PERMISSION_MAP, RouteMethod from superset.models.dashboard import Dashboard as DashboardModel from superset.superset_typing import FlaskResponse -from superset.utils import core as utils +from superset.utils import json as json_utils from superset.views.base import ( BaseSupersetView, common_bootstrap_payload, @@ -160,8 +159,8 @@ def embedded( return self.render_template( "superset/spa.html", entry="embedded", - bootstrap_data=json.dumps( - bootstrap_data, default=utils.pessimistic_json_iso_dttm_ser + bootstrap_data=json_utils.dumps( + bootstrap_data, default=json_utils.pessimistic_json_iso_dttm_ser ), ) diff --git a/superset/views/sql_lab/views.py b/superset/views/sql_lab/views.py index b481f191b8227..b2c05095914da 100644 --- a/superset/views/sql_lab/views.py +++ b/superset/views/sql_lab/views.py @@ -28,7 +28,7 @@ from superset.constants import MODEL_VIEW_RW_METHOD_PERMISSION_MAP, RouteMethod from superset.models.sql_lab import Query, SavedQuery, TableSchema, TabState from superset.superset_typing import FlaskResponse -from superset.utils import core as utils +from superset.utils import json as json_utils from superset.utils.core import get_user_id from superset.views.base import ( BaseSupersetView, @@ -140,7 +140,7 @@ def get(self, tab_state_id: int) -> FlaskResponse: if tab_state is None: return Response(status=404) return json_success( - json.dumps(tab_state.to_dict(), default=utils.json_iso_dttm_ser) + json.dumps(tab_state.to_dict(), default=json_utils.json_iso_dttm_ser) ) @has_access_api diff --git a/superset/views/tags.py b/superset/views/tags.py index 3b27510ff3ee6..f78b2b5edefda 100644 --- a/superset/views/tags.py +++ b/superset/views/tags.py @@ -17,16 +17,16 @@ import logging -import simplejson as json from flask_appbuilder import expose from flask_appbuilder.hooks import before_request from flask_appbuilder.models.sqla.interface import SQLAInterface from flask_appbuilder.security.decorators import has_access, has_access_api from werkzeug.exceptions import NotFound -from superset import db, is_feature_enabled, utils +from superset import db, is_feature_enabled from superset.superset_typing import FlaskResponse from superset.tags.models import Tag +from superset.utils import json as json_utils from superset.views.base import SupersetModelView from .base import BaseSupersetView, json_success @@ -74,4 +74,6 @@ def tags(self) -> FlaskResponse: } for obj in query ] - return json_success(json.dumps(results, default=utils.core.json_int_dttm_ser)) + return json_success( + json_utils.dumps(results, default=json_utils.json_int_dttm_ser) + ) diff --git a/superset/viz.py b/superset/viz.py index 5a4b323079169..b06bb42186b00 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -66,7 +66,7 @@ VizData, VizPayload, ) -from superset.utils import core as utils, csv +from superset.utils import core as utils, csv, json as json_utils from superset.utils.cache import set_and_log_cache from superset.utils.core import ( apply_max_row_limit, @@ -440,8 +440,8 @@ def cache_timeout(self) -> int: @deprecated(deprecated_in="3.0") def get_json(self) -> str: - return json.dumps( - self.get_payload(), default=utils.json_int_dttm_ser, ignore_nan=True + return json_utils.dumps( + self.get_payload(), default=json_utils.json_int_dttm_ser, ignore_nan=True ) @deprecated(deprecated_in="3.0") @@ -641,9 +641,9 @@ def get_df_payload( # pylint: disable=too-many-statements @staticmethod @deprecated(deprecated_in="3.0") def json_dumps(query_obj: Any, sort_keys: bool = False) -> str: - return json.dumps( + return json_utils.dumps( query_obj, - default=utils.json_int_dttm_ser, + default=json_utils.json_int_dttm_ser, ignore_nan=True, sort_keys=sort_keys, ) diff --git a/tests/integration_tests/conftest.py b/tests/integration_tests/conftest.py index 185f7d410fc4b..84c579310556b 100644 --- a/tests/integration_tests/conftest.py +++ b/tests/integration_tests/conftest.py @@ -29,8 +29,8 @@ from superset import db, security_manager from superset.extensions import feature_flag_manager -from superset.utils.core import json_dumps_w_dates from superset.utils.database import get_example_database, remove_database +from superset.utils.json import json_dumps_w_dates from tests.integration_tests.test_app import app, login if TYPE_CHECKING: diff --git a/tests/integration_tests/core_tests.py b/tests/integration_tests/core_tests.py index 90873d49b9f9e..ed683c95c5b91 100644 --- a/tests/integration_tests/core_tests.py +++ b/tests/integration_tests/core_tests.py @@ -51,7 +51,7 @@ from superset.models.sql_lab import Query from superset.result_set import SupersetResultSet from superset.sql_parse import Table -from superset.utils import core as utils +from superset.utils import core as utils, json as json_utils from superset.utils.core import backend from superset.utils.database import get_example_database from superset.views.database.views import DatabaseView @@ -502,7 +502,7 @@ def test_dataframe_timezone(self): results = SupersetResultSet(list(data), [["data"]], BaseEngineSpec) df = results.to_pandas_df() data = dataframe.df_to_records(df) - json_str = json.dumps(data, default=utils.pessimistic_json_iso_dttm_ser) + json_str = json.dumps(data, default=json_utils.pessimistic_json_iso_dttm_ser) self.assertDictEqual( data[0], {"data": pd.Timestamp("2017-11-18 21:53:00.219225+0100", tz=tz)} ) @@ -943,7 +943,7 @@ def test_feature_flag_serialization(self): encoded = json.dumps( {"FOO": lambda x: 1, "super": "set"}, - default=utils.pessimistic_json_iso_dttm_ser, + default=json_utils.pessimistic_json_iso_dttm_ser, ) html_string = ( html.escape(encoded, quote=False) diff --git a/tests/integration_tests/sqllab_tests.py b/tests/integration_tests/sqllab_tests.py index 96019c16cc500..8bbf7023a4044 100644 --- a/tests/integration_tests/sqllab_tests.py +++ b/tests/integration_tests/sqllab_tests.py @@ -44,10 +44,8 @@ apply_limit_if_exists, ) from superset.sql_parse import CtasMethod -from superset.utils.core import ( - backend, - datetime_to_epoch, # noqa: F401 -) +from superset.utils.core import backend +from superset.utils.json import datetime_to_epoch # noqa: F401 from superset.utils.database import get_example_database, get_main_database from tests.integration_tests.base_tests import SupersetTestCase diff --git a/tests/integration_tests/utils_tests.py b/tests/integration_tests/utils_tests.py index 67feb27141140..8916d30631066 100644 --- a/tests/integration_tests/utils_tests.py +++ b/tests/integration_tests/utils_tests.py @@ -45,20 +45,16 @@ from superset.models.dashboard import Dashboard # noqa: F401 from superset.models.slice import Slice # noqa: F401 from superset.utils.core import ( - base_json_conv, cast_to_num, convert_legacy_filters_into_adhoc, create_ssl_cert_file, DTTM_ALIAS, extract_dataframe_dtypes, - format_timedelta, GenericDataType, get_form_data_token, as_list, get_email_address_list, get_stacktrace, - json_int_dttm_ser, - json_iso_dttm_ser, merge_extra_filters, merge_extra_form_data, merge_request_params, @@ -66,11 +62,17 @@ parse_ssl_cert, parse_js_uri_path_item, split, - validate_json, zlib_compress, zlib_decompress, DateColumn, ) +from superset.utils.json import ( + base_json_conv, + format_timedelta, + json_int_dttm_ser, + json_iso_dttm_ser, + validate_json, +) from superset.utils.database import get_or_create_db from superset.utils import schema from superset.utils.hashing import md5_sha_from_str diff --git a/tests/unit_tests/utils/json_tests.py b/tests/unit_tests/utils/json_tests.py new file mode 100644 index 0000000000000..a3d0a6d9d9ed9 --- /dev/null +++ b/tests/unit_tests/utils/json_tests.py @@ -0,0 +1,114 @@ +# 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 datetime +import json +from unittest.mock import MagicMock + +import pytest + +from superset.exceptions import SupersetException +from superset.utils.json import ( + dumps, + json_iso_dttm_ser, + pessimistic_json_iso_dttm_ser, + validate_json, +) + + +def test_json_dumps(): + data = { + "str": "some string", + "int": 123456789, + "float": 0.12345, + "bool": True, + } + json_str = dumps(data, default=pessimistic_json_iso_dttm_ser) + reloaded_data = json.loads(json_str) + assert reloaded_data["str"] == "some string" + assert reloaded_data["int"] == 123456789 + assert reloaded_data["float"] == 0.12345 + assert reloaded_data["bool"] is True + + +def test_json_dumps_encoding(): + data = { + "utf8": b"Hello World", + "utf16": b"\xff\xfeH\x00e\x00l\x00l\x00o\x00 \x00W\x00o\x00r\x00l\x00d\x00", + "bytes": b"\xff", + } + json_str = dumps(data, default=pessimistic_json_iso_dttm_ser) + reloaded_data = json.loads(json_str) + assert reloaded_data["utf8"] == "Hello World" + assert reloaded_data["utf16"] == "Hello World" + assert reloaded_data["bytes"] == "[bytes]" + + +def test_json_iso_dttm_ser(): + data = { + "datetime": datetime.datetime(2021, 1, 1, 0, 0, 0), + "date": datetime.date(2021, 1, 1), + } + json_str = json.dumps(data, default=json_iso_dttm_ser) + reloaded_data = json.loads(json_str) + assert reloaded_data["datetime"] == "2021-01-01T00:00:00" + assert reloaded_data["date"] == "2021-01-01" + + +def test_pessimistic_json_iso_dttm_ser(): + data = { + "datetime": datetime.datetime(2021, 1, 1, 0, 0, 0), + "date": datetime.date(2021, 1, 1), + "UNSERIALIZABLE": MagicMock(), + } + json_str = json.dumps(data, default=pessimistic_json_iso_dttm_ser) + reloaded_data = json.loads(json_str) + assert reloaded_data["datetime"] == "2021-01-01T00:00:00" + assert reloaded_data["date"] == "2021-01-01" + assert ( + reloaded_data["UNSERIALIZABLE"] + == "Unserializable []" + ) + + +def test_pessimistic_json_iso_dttm_ser_nonutf8(): + data = { + "INVALID_UTF8_BYTES": b"\xff", + } + assert isinstance(data["INVALID_UTF8_BYTES"], bytes) + json_str = json.dumps(data, default=pessimistic_json_iso_dttm_ser) + reloaded_data = json.loads(json_str) + assert reloaded_data["INVALID_UTF8_BYTES"] == "[bytes]" + + +def test_pessimistic_json_iso_dttm_ser_utf16(): + data = { + "VALID_UTF16_BYTES": b"\xff\xfeS0\x930k0a0o0\x16NLu", + } + assert isinstance(data["VALID_UTF16_BYTES"], bytes) + json_str = json.dumps(data, default=pessimistic_json_iso_dttm_ser) + reloaded_data = json.loads(json_str) + assert reloaded_data["VALID_UTF16_BYTES"] == "こんにちは世界" + + +def test_validate_json(): + valid = '{"a": 5, "b": [1, 5, ["g", "h"]]}' + assert validate_json(valid) is None + + invalid = '{"a": 5, "b": [1, 5, ["g", "h]]}' + with pytest.raises(SupersetException) as excinfo: + validate_json(invalid) + assert str(excinfo.value) == "JSON is not valid" diff --git a/tests/unit_tests/utils/test_core.py b/tests/unit_tests/utils/test_core.py index c9ace0d3f6820..2ebec87c2a9c2 100644 --- a/tests/unit_tests/utils/test_core.py +++ b/tests/unit_tests/utils/test_core.py @@ -14,8 +14,6 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -import datetime -import json import os from dataclasses import dataclass from typing import Any, Optional @@ -33,10 +31,8 @@ generic_find_fk_constraint_name, get_datasource_full_name, is_test, - json_iso_dttm_ser, normalize_dttm_col, parse_boolean_string, - pessimistic_json_iso_dttm_ser, QueryObjectFilterClause, remove_extra_adhoc_filters, ) @@ -400,50 +396,3 @@ def test_get_datasource_full_name(): get_datasource_full_name("db", "table", "catalog", None) == "[db].[catalog].[table]" ) - - -def test_json_iso_dttm_ser(): - data = { - "datetime": datetime.datetime(2021, 1, 1, 0, 0, 0), - "date": datetime.date(2021, 1, 1), - } - json_str = json.dumps(data, default=json_iso_dttm_ser) - reloaded_data = json.loads(json_str) - assert reloaded_data["datetime"] == "2021-01-01T00:00:00" - assert reloaded_data["date"] == "2021-01-01" - - -def test_pessimistic_json_iso_dttm_ser(): - data = { - "datetime": datetime.datetime(2021, 1, 1, 0, 0, 0), - "date": datetime.date(2021, 1, 1), - "UNSERIALIZABLE": MagicMock(), - } - json_str = json.dumps(data, default=pessimistic_json_iso_dttm_ser) - reloaded_data = json.loads(json_str) - assert reloaded_data["datetime"] == "2021-01-01T00:00:00" - assert reloaded_data["date"] == "2021-01-01" - assert ( - reloaded_data["UNSERIALIZABLE"] - == "Unserializable []" - ) - - -def test_pessimistic_json_iso_dttm_ser_nonutf8(): - data = { - "INVALID_UTF8_BYTES": b"\xff", - } - assert isinstance(data["INVALID_UTF8_BYTES"], bytes) - json_str = json.dumps(data, default=pessimistic_json_iso_dttm_ser) - reloaded_data = json.loads(json_str) - assert reloaded_data["INVALID_UTF8_BYTES"] == "[bytes]" - - -def test_pessimistic_json_iso_dttm_ser_utf16(): - data = { - "VALID_UTF16_BYTES": b"\xff\xfeS0\x930k0a0o0\x16NLu", - } - assert isinstance(data["VALID_UTF16_BYTES"], bytes) - json_str = json.dumps(data, default=pessimistic_json_iso_dttm_ser) - reloaded_data = json.loads(json_str) - assert reloaded_data["VALID_UTF16_BYTES"] == "こんにちは世界" From 1c48fe05fd291517350ad57bb175ce8155b23580 Mon Sep 17 00:00:00 2001 From: John Bodley <4567245+john-bodley@users.noreply.github.com> Date: Mon, 20 May 2024 11:30:56 -0700 Subject: [PATCH 02/59] fix: Update migration logic in #27119 (#28482) --- UPDATING.md | 1 + ...6750b67e8_change_mediumtext_to_longtext.py | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 superset/migrations/versions/2024-05-09_19-19_f7b6750b67e8_change_mediumtext_to_longtext.py diff --git a/UPDATING.md b/UPDATING.md index 909cd9f6d9bed..67be28ba913b6 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -84,6 +84,7 @@ assists people when migrating to a new version. ### Potential Downtime - [26416](https://github.com/apache/superset/pull/26416): Adds two database indexes to the `report_execution_log` table and one database index to the `report_recipient` to improve performance. Scheduled downtime may be required for large deployments. +- [28482](https://github.com/apache/superset/pull/28482): Potentially augments the `query.executed_sql` and `query.select_sql` columns for MySQL from `MEDIUMTEXT` to `LONGTEXT`. Potential downtime may be required for large deployments which previously ran [27119](https://github.com/apache/superset/pull/27119). ## 3.1.0 diff --git a/superset/migrations/versions/2024-05-09_19-19_f7b6750b67e8_change_mediumtext_to_longtext.py b/superset/migrations/versions/2024-05-09_19-19_f7b6750b67e8_change_mediumtext_to_longtext.py new file mode 100644 index 0000000000000..77235cca9eff9 --- /dev/null +++ b/superset/migrations/versions/2024-05-09_19-19_f7b6750b67e8_change_mediumtext_to_longtext.py @@ -0,0 +1,54 @@ +# 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. +"""change_mediumtext_to_longtext +Revision ID: f7b6750b67e8 +Revises: f84fde59123a +Create Date: 2024-05-09 19:19:46.630140 +""" + +# revision identifiers, used by Alembic. +revision = "f7b6750b67e8" +down_revision = "f84fde59123a" + +from alembic import op # noqa: E402 +from sqlalchemy.dialects.mysql import MEDIUMTEXT # noqa: E402 +from sqlalchemy.dialects.mysql.base import MySQLDialect # noqa: E402 + +from superset.migrations.shared.utils import get_table_column # noqa: E402 +from superset.utils.core import LongText, MediumText # noqa: E402 + + +def upgrade(): + if isinstance(op.get_bind().dialect, MySQLDialect): + for item in ["query.executed_sql", "query.select_sql"]: + table_name, column_name = item.split(".") + + if (column := get_table_column(table_name, column_name)) and isinstance( + column["type"], + MEDIUMTEXT, + ): + with op.batch_alter_table(table_name) as batch_op: + batch_op.alter_column( + column_name, + existing_type=MediumText(), + type_=LongText(), + existing_nullable=True, + ) + + +def downgrade(): + pass From 0d5aec12d452531b8705252983d0d4f991c9ab5c Mon Sep 17 00:00:00 2001 From: Ross Mabbett <92495987+rtexelm@users.noreply.github.com> Date: Tue, 21 May 2024 12:21:47 -0400 Subject: [PATCH 03/59] refactor(superset-ui-core): Migrate ChartFrame to RTL (#28563) --- .../chart-composition/ChartFrame.test.tsx | 188 +++++++++--------- 1 file changed, 96 insertions(+), 92 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/test/chart-composition/ChartFrame.test.tsx b/superset-frontend/packages/superset-ui-core/test/chart-composition/ChartFrame.test.tsx index 777a7b2a2a521..a2573c4208b63 100644 --- a/superset-frontend/packages/superset-ui-core/test/chart-composition/ChartFrame.test.tsx +++ b/superset-frontend/packages/superset-ui-core/test/chart-composition/ChartFrame.test.tsx @@ -18,110 +18,114 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom'; import { ChartFrame } from '@superset-ui/core'; -describe('TooltipFrame', () => { - it('renders content that requires smaller space than frame', () => { - const wrapper = shallow( - ( -
- {width}/{height} -
- )} - />, - ); - expect(wrapper.find('div').text()).toEqual('400/400'); - }); +type Props = { + contentWidth?: number; + contentHeight?: number; + height: number; + renderContent: ({ + height, + width, + }: { + height: number; + width: number; + }) => React.ReactNode; + width: number; +}; + +const renderChartFrame = (props: Props) => render(); - it('renders content without specifying content size', () => { - const wrapper = shallow( - ( -
- {width}/{height} -
- )} - />, - ); - expect(wrapper.find('div').text()).toEqual('400/400'); +it('renders content that requires smaller space than frame', () => { + const { getByText } = renderChartFrame({ + width: 400, + height: 400, + contentWidth: 300, + contentHeight: 300, + renderContent: ({ width, height }) => ( +
+ {width}/{height} +
+ ), }); + expect(getByText('400/400')).toBeInTheDocument(); +}); - it('renders content that requires same size with frame', () => { - const wrapper = shallow( - ( -
- {width}/{height} -
- )} - />, - ); - expect(wrapper.find('div').text()).toEqual('400/400'); +it('renders content without specifying content size', () => { + const { getByText } = renderChartFrame({ + width: 400, + height: 400, + renderContent: ({ width, height }) => ( +
+ {width}/{height} +
+ ), }); + expect(getByText('400/400')).toBeInTheDocument(); +}); - it('renders content that requires space larger than frame', () => { - const wrapper = shallow( - ( -
- {width}/{height} -
- )} - />, - ); - expect(wrapper.find('div.chart').text()).toEqual('500/500'); +it('renders content that requires equivalent size to frame', () => { + const { getByText } = renderChartFrame({ + width: 400, + height: 400, + contentWidth: 400, + contentHeight: 400, + renderContent: ({ width, height }) => ( +
+ {width}/{height} +
+ ), }); + expect(getByText('400/400')).toBeInTheDocument(); +}); - it('renders content that width is larger than frame', () => { - const wrapper = shallow( - ( -
- {width}/{height} -
- )} - />, - ); - expect(wrapper.find('div.chart').text()).toEqual('500/400'); +it('renders content that requires space larger than frame', () => { + const { getByText, container } = renderChartFrame({ + width: 400, + height: 400, + contentWidth: 500, + contentHeight: 500, + renderContent: ({ width, height }) => ( +
+ {width}/{height} +
+ ), }); + expect(getByText('500/500')).toBeInTheDocument(); + const containerDiv = container.firstChild as HTMLElement; + expect(containerDiv).toHaveStyle({ overflowX: 'auto', overflowY: 'auto' }); +}); - it('renders content that height is larger than frame', () => { - const wrapper = shallow( - ( -
- {width}/{height} -
- )} - />, - ); - expect(wrapper.find('div.chart').text()).toEqual('400/600'); +it('renders content when width is larger than frame', () => { + const { getByText, container } = renderChartFrame({ + width: 400, + height: 400, + contentWidth: 500, + renderContent: ({ width, height }) => ( +
+ {width}/{height} +
+ ), }); + expect(getByText('500/400')).toBeInTheDocument(); + const containerDiv = container.firstChild as HTMLElement; + expect(containerDiv).toHaveStyle({ overflowX: 'auto', overflowY: 'hidden' }); +}); - it('renders an empty frame when renderContent is not given', () => { - const wrapper = shallow(); - expect(wrapper.find('div')).toHaveLength(0); +it('renders content when height is larger than frame', () => { + const { getByText, container } = renderChartFrame({ + width: 400, + height: 400, + contentHeight: 600, + renderContent: ({ width, height }) => ( +
+ {width}/{height} +
+ ), }); + expect(getByText('400/600')).toBeInTheDocument(); + const containerDiv = container.firstChild as HTMLElement; + expect(containerDiv).toHaveStyle({ overflowX: 'hidden', overflowY: 'auto' }); }); From 62a03364254a4b59dff59e08c2e4abf77ee0f075 Mon Sep 17 00:00:00 2001 From: Elizabeth Thompson Date: Tue, 21 May 2024 09:52:32 -0700 Subject: [PATCH 04/59] fix: add listener to repaint on visibility change for canvas (#28568) --- superset-frontend/package-lock.json | 65 +++++++++ superset-frontend/package.json | 1 + .../src/dashboard/components/Dashboard.jsx | 23 +++ .../dashboard/components/Dashboard.test.jsx | 133 ++++++++++++++++++ 4 files changed, 222 insertions(+) diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index deaa3748f5983..7ac405a55a216 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -250,6 +250,7 @@ "ignore-styles": "^5.0.1", "imports-loader": "^3.1.1", "jest": "^26.6.3", + "jest-canvas-mock": "^2.5.2", "jest-environment-enzyme": "^7.1.2", "jest-enzyme": "^7.1.2", "jest-websocket-mock": "^2.2.0", @@ -31171,6 +31172,12 @@ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=" }, + "node_modules/cssfontparser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", + "integrity": "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==", + "dev": true + }, "node_modules/cssnano": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.3.tgz", @@ -41175,6 +41182,16 @@ "node": ">= 10.14.2" } }, + "node_modules/jest-canvas-mock": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.5.2.tgz", + "integrity": "sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==", + "dev": true, + "dependencies": { + "cssfontparser": "^1.2.1", + "moo-color": "^1.0.2" + } + }, "node_modules/jest-changed-files": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", @@ -52539,6 +52556,21 @@ "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz", "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==" }, + "node_modules/moo-color": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz", + "integrity": "sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==", + "dev": true, + "dependencies": { + "color-name": "^1.1.4" + } + }, + "node_modules/moo-color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/mousetrap": { "version": "1.6.5", "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz", @@ -96422,6 +96454,12 @@ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=" }, + "cssfontparser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", + "integrity": "sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==", + "dev": true + }, "cssnano": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.0.3.tgz", @@ -104272,6 +104310,16 @@ } } }, + "jest-canvas-mock": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.5.2.tgz", + "integrity": "sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==", + "dev": true, + "requires": { + "cssfontparser": "^1.2.1", + "moo-color": "^1.0.2" + } + }, "jest-changed-files": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", @@ -112794,6 +112842,23 @@ "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz", "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==" }, + "moo-color": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.3.tgz", + "integrity": "sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==", + "dev": true, + "requires": { + "color-name": "^1.1.4" + }, + "dependencies": { + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "mousetrap": { "version": "1.6.5", "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index c526a9b067d2f..70ca63de7e7a4 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -316,6 +316,7 @@ "ignore-styles": "^5.0.1", "imports-loader": "^3.1.1", "jest": "^26.6.3", + "jest-canvas-mock": "^2.5.2", "jest-environment-enzyme": "^7.1.2", "jest-enzyme": "^7.1.2", "jest-websocket-mock": "^2.2.0", diff --git a/superset-frontend/src/dashboard/components/Dashboard.jsx b/superset-frontend/src/dashboard/components/Dashboard.jsx index 038ab148b9bc5..9e48075132848 100644 --- a/superset-frontend/src/dashboard/components/Dashboard.jsx +++ b/superset-frontend/src/dashboard/components/Dashboard.jsx @@ -90,6 +90,8 @@ class Dashboard extends React.PureComponent { this.appliedFilters = props.activeFilters ?? {}; this.appliedOwnDataCharts = props.ownDataCharts ?? {}; this.onVisibilityChange = this.onVisibilityChange.bind(this); + this.handleVisibilityChange = this.handleVisibilityChange.bind(this); + this.repaintCanvas = this.repaintCanvas.bind(this); } componentDidMount() { @@ -192,6 +194,24 @@ class Dashboard extends React.PureComponent { this.props.actions.clearDataMaskState(); } + repaintCanvas(canvas, ctx, imageBitmap) { + // Clear the canvas + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Draw the copied content + ctx.drawImage(imageBitmap, 0, 0); + } + + handleVisibilityChange() { + this.canvases.forEach(canvas => { + const ctx = canvas.getContext('2d'); + createImageBitmap(canvas).then(imageBitmap => { + // Call the repaintCanvas function with canvas, ctx, and imageBitmap + this.repaintCanvas(canvas, ctx, imageBitmap); + }); + }); + } + onVisibilityChange() { if (document.visibilityState === 'hidden') { // from visible to hidden @@ -199,6 +219,7 @@ class Dashboard extends React.PureComponent { start_offset: Logger.getTimestamp(), ts: new Date().getTime(), }; + this.canvases = document.querySelectorAll('canvas'); } else if (document.visibilityState === 'visible') { // from hidden to visible const logStart = this.visibilityEventData.start_offset; @@ -206,6 +227,8 @@ class Dashboard extends React.PureComponent { ...this.visibilityEventData, duration: Logger.getTimestamp() - logStart, }); + // for chrome to ensure that the canvas doesn't disappear + this.handleVisibilityChange(); } } diff --git a/superset-frontend/src/dashboard/components/Dashboard.test.jsx b/superset-frontend/src/dashboard/components/Dashboard.test.jsx index d75bda27dc2e0..bfb8eec077b42 100644 --- a/superset-frontend/src/dashboard/components/Dashboard.test.jsx +++ b/superset-frontend/src/dashboard/components/Dashboard.test.jsx @@ -19,6 +19,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import sinon from 'sinon'; +import 'jest-canvas-mock'; import Dashboard from 'src/dashboard/components/Dashboard'; import { CHART_TYPE } from 'src/dashboard/util/componentTypes'; @@ -38,6 +39,7 @@ import { dashboardLayout } from 'spec/fixtures/mockDashboardLayout'; import dashboardState from 'spec/fixtures/mockDashboardState'; import { sliceEntitiesForChart as sliceEntities } from 'spec/fixtures/mockSliceEntities'; import { getAllActiveFilters } from 'src/dashboard/util/activeAllDashboardFilters'; +import { Logger, LOG_ACTIONS_HIDE_BROWSER_TAB } from '../../logger/LogUtils'; describe('Dashboard', () => { const props = { @@ -245,5 +247,136 @@ describe('Dashboard', () => { expect(refreshSpy.callCount).toBe(1); expect(refreshSpy.getCall(0).args[0]).toEqual([]); }); + + // The canvas is cleared using the clearRect method. + it('should clear the canvas using clearRect method', () => { + // Arrange + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + const imageBitmap = new ImageBitmap(100, 100); + + // Act + wrapper.instance().repaintCanvas(canvas, ctx, imageBitmap); + + // Assert + expect(ctx.clearRect).toHaveBeenCalledWith( + 0, + 0, + canvas.width, + canvas.height, + ); + }); + + // The canvas width and height are 0. + it('should recreate the canvas with the same dimensions', () => { + // Arrange + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + const imageBitmap = new ImageBitmap(100, 100); + + // Act + wrapper.instance().repaintCanvas(canvas, ctx, imageBitmap); + + // Assert + const { width, height } = ctx.canvas; + expect(canvas.width).toBe(width); + expect(canvas.height).toBe(height); + }); + + // When the document visibility state changes to 'hidden', the method sets the 'visibilityEventData' object with a 'start_offset' and 'ts' properties, and queries all canvas elements on the page and stores them in the 'canvases' property. + it('should set visibilityEventData and canvases when document visibility state changes to "hidden"', () => { + // Initialize the class object with props + const props = { + activeFilters: {}, + ownDataCharts: {}, + actions: { + logEvent: jest.fn(), + }, + layout: {}, + dashboardInfo: {}, + dashboardState: { + editMode: false, + isPublished: false, + hasUnsavedChanges: false, + }, + chartConfiguration: {}, + }; + + const DATE_TO_USE = new Date('2020'); + const OrigDate = Date; + global.Date = jest.fn(() => DATE_TO_USE); + global.Date.UTC = OrigDate.UTC; + global.Date.parse = OrigDate.parse; + global.Date.now = OrigDate.now; + + // Your test code here + + const dashboard = new Dashboard(props); + + // Mock the return value of document.visibilityState + jest.spyOn(document, 'visibilityState', 'get').mockReturnValue('hidden'); + // mock Logger.getTimestamp() to return a fixed value + jest.spyOn(Logger, 'getTimestamp').mockReturnValue(1234567890); + + // Invoke the method + dashboard.onVisibilityChange(); + + // Assert that visibilityEventData is set correctly + expect(dashboard.visibilityEventData).toEqual({ + start_offset: 1234567890, + ts: DATE_TO_USE.getTime(), + }); + + // Assert that canvases are queried correctly + expect(dashboard.canvases).toEqual(expect.any(NodeList)); + + // Restore the original implementation of document.visibilityState + jest.restoreAllMocks(); + // After your test + global.Date = OrigDate; + }); + + // When the document visibility state changes to 'visible', the method logs an event and calls the 'handleVisibilityChange' method. + it('should log an event and call handleVisibilityChange when document visibility state changes to "visible"', () => { + // Initialize the class object + const dashboard = new Dashboard({ activeFilters: {} }); + + // Mock the props and actions + dashboard.props = { + actions: { + logEvent: jest.fn(), + }, + }; + + // Mock the visibilityEventData + dashboard.visibilityEventData = { + start_offset: 123, + ts: 456, + }; + + // Mock the handleVisibilityChange method + dashboard.handleVisibilityChange = jest.fn(); + + // Mock the document.visibilityState property + jest.spyOn(document, 'visibilityState', 'get').mockReturnValue('visible'); + + // Invoke the method + dashboard.onVisibilityChange(); + + // Assert that logEvent is called with the correct arguments + expect(dashboard.props.actions.logEvent).toHaveBeenCalledWith( + LOG_ACTIONS_HIDE_BROWSER_TAB, + { + ...dashboard.visibilityEventData, + duration: expect.any(Number), + }, + ); + + // Assert that handleVisibilityChange is called + expect(dashboard.handleVisibilityChange).toHaveBeenCalled(); + + // Restore the original implementation of document.visibilityState + jest.restoreAllMocks(); + }); }); }); From 4fa7619b1f9a16f01c87bc26e8606bc5b0b435ec Mon Sep 17 00:00:00 2001 From: John Bodley <4567245+john-bodley@users.noreply.github.com> Date: Tue, 21 May 2024 11:23:32 -0700 Subject: [PATCH 05/59] fix: revert fix(presto preview): re-enable schema previsualization for Trino/Presto table/schemas" (#28613) --- superset/db_engine_specs/presto.py | 5 +---- tests/unit_tests/db_engine_specs/test_presto.py | 8 ++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/superset/db_engine_specs/presto.py b/superset/db_engine_specs/presto.py index ba542b8f6add6..d97264e002ad5 100644 --- a/superset/db_engine_specs/presto.py +++ b/superset/db_engine_specs/presto.py @@ -536,10 +536,7 @@ def where_latest_partition( } for col_name, value in zip(col_names, values): - col_type = None - if col_type_name := column_type_by_name.get(col_name): - if col_type_class := getattr(types, col_type_name, None): - col_type = col_type_class() + col_type = column_type_by_name.get(col_name) if isinstance(col_type, types.DATE): col_type = Date() diff --git a/tests/unit_tests/db_engine_specs/test_presto.py b/tests/unit_tests/db_engine_specs/test_presto.py index f9680006d69ab..e002102224404 100644 --- a/tests/unit_tests/db_engine_specs/test_presto.py +++ b/tests/unit_tests/db_engine_specs/test_presto.py @@ -116,10 +116,10 @@ def test_get_schema_from_engine_params() -> None: @pytest.mark.parametrize( ["column_type", "column_value", "expected_value"], [ - ("DATE", "2023-05-01", "DATE '2023-05-01'"), - ("TIMESTAMP", "2023-05-01", "TIMESTAMP '2023-05-01'"), - ("VARCHAR", "2023-05-01", "'2023-05-01'"), - ("INT", 1234, "1234"), + (types.DATE(), "2023-05-01", "DATE '2023-05-01'"), + (types.TIMESTAMP(), "2023-05-01", "TIMESTAMP '2023-05-01'"), + (types.VARCHAR(), "2023-05-01", "'2023-05-01'"), + (types.INT(), 1234, "1234"), ], ) def test_where_latest_partition( From ac53f7fea9d820fbc651be7d8e31d054d09f0787 Mon Sep 17 00:00:00 2001 From: "Hugh A. Miles II" Date: Wed, 22 May 2024 13:17:49 -0400 Subject: [PATCH 06/59] feat: Data Zoom scrolls using the mouse (mark II) (#28629) --- .../src/Timeseries/transformProps.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts index e63d67f8b610f..3ef468bc20a83 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts @@ -633,6 +633,18 @@ export default function transformProps( bottom: TIMESERIES_CONSTANTS.zoomBottom, yAxisIndex: isHorizontal ? 0 : undefined, }, + { + type: 'inside', + yAxisIndex: 0, + zoomOnMouseWheel: false, + moveOnMouseWheel: true, + }, + { + type: 'inside', + xAxisIndex: 0, + zoomOnMouseWheel: false, + moveOnMouseWheel: true, + }, ] : [], }; From 1573c101a71d6ade1f24ac89f99c4c0998b429c7 Mon Sep 17 00:00:00 2001 From: Daniel Vaz Gaspar Date: Wed, 22 May 2024 22:31:54 +0100 Subject: [PATCH 07/59] fix(ci): restrict issue comments to members or owners (#28633) --- .github/workflows/update-monorepo-lockfiles.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-monorepo-lockfiles.yml b/.github/workflows/update-monorepo-lockfiles.yml index b0b63a912e29a..820384d8df8bd 100644 --- a/.github/workflows/update-monorepo-lockfiles.yml +++ b/.github/workflows/update-monorepo-lockfiles.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest if: > (github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]') || - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@supersetbot trigger-dependabot-lockfile') && github.event.issue.pull_request) + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@supersetbot trigger-dependabot-lockfile') && github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER')) defaults: run: working-directory: superset-frontend From 35284589cbd180a3ec5ba92509c271c8ab4da55e Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Thu, 23 May 2024 10:40:15 -0700 Subject: [PATCH 08/59] feat: unpack payload into log function (#28521) --- superset/config.py | 2 + superset/utils/log.py | 44 +++++++++++++++ tests/integration_tests/event_logger_tests.py | 54 +++++++++++-------- 3 files changed, 78 insertions(+), 22 deletions(-) diff --git a/superset/config.py b/superset/config.py index 10f075bb5fb2c..3c92354322a1a 100644 --- a/superset/config.py +++ b/superset/config.py @@ -79,6 +79,8 @@ # By default will log events to the metadata database with `DBEventLogger` # Note that you can use `StdOutEventLogger` for debugging +# Note that you can write your own event logger by extending `AbstractEventLogger` +# https://github.com/apache/superset/blob/master/superset/utils/log.py EVENT_LOGGER = DBEventLogger() SUPERSET_LOG_VIEW = True diff --git a/superset/utils/log.py b/superset/utils/log.py index 65355c175ba73..cdf68437e9c36 100644 --- a/superset/utils/log.py +++ b/superset/utils/log.py @@ -91,6 +91,31 @@ def get_logger_from_status( class AbstractEventLogger(ABC): + # Parameters that are passed under the `curated_payload` arg to the log method + curated_payload_params = { + "force", + "standalone", + "runAsync", + "json", + "csv", + "queryLimit", + "select_as_cta", + } + # Similarly, parameters that are passed under the `curated_form_data` arg + curated_form_data_params = { + "dashboardId", + "sliceId", + "viz_type", + "force", + "compare_lag", + "forecastPeriods", + "granularity_sqla", + "legendType", + "legendOrientation", + "show_legend", + "time_grain_sqla", + } + def __call__( self, action: str, @@ -120,6 +145,16 @@ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: **self.payload_override, ) + @classmethod + def curate_payload(cls, payload: dict[str, Any]) -> dict[str, Any]: + """Curate payload to only include relevant keys/safe keys""" + return {k: v for k, v in payload.items() if k in cls.curated_payload_params} + + @classmethod + def curate_form_data(cls, payload: dict[str, Any]) -> dict[str, Any]: + """Curate form_data to only include relevant keys/safe keys""" + return {k: v for k, v in payload.items() if k in cls.curated_form_data_params} + @abstractmethod def log( # pylint: disable=too-many-arguments self, @@ -129,6 +164,8 @@ def log( # pylint: disable=too-many-arguments duration_ms: int | None, slice_id: int | None, referrer: str | None, + curated_payload: dict[str, Any] | None, + curated_form_data: dict[str, Any] | None, *args: Any, **kwargs: Any, ) -> None: @@ -180,6 +217,7 @@ def log_with_context( # pylint: disable=too-many-locals,too-many-arguments "database_driver": database.driver, } + form_data: dict[str, Any] = {} if "form_data" in payload: form_data, _ = get_form_data() payload["form_data"] = form_data @@ -207,6 +245,8 @@ def log_with_context( # pylint: disable=too-many-locals,too-many-arguments slice_id=slice_id, duration_ms=duration_ms, referrer=referrer, + curated_payload=self.curate_payload(payload), + curated_form_data=self.curate_form_data(form_data), **database_params, ) @@ -380,6 +420,8 @@ def log( # pylint: disable=too-many-arguments duration_ms: int | None, slice_id: int | None, referrer: str | None, + curated_payload: dict[str, Any] | None, + curated_form_data: dict[str, Any] | None, *args: Any, **kwargs: Any, ) -> None: @@ -390,6 +432,8 @@ def log( # pylint: disable=too-many-arguments duration_ms=duration_ms, slice_id=slice_id, referrer=referrer, + curated_payload=curated_payload, + curated_form_data=curated_form_data, **kwargs, ) print("StdOutEventLogger: ", data) diff --git a/tests/integration_tests/event_logger_tests.py b/tests/integration_tests/event_logger_tests.py index 62a5759dad736..d3cd2a4ff3fed 100644 --- a/tests/integration_tests/event_logger_tests.py +++ b/tests/integration_tests/event_logger_tests.py @@ -141,14 +141,19 @@ def log( with logger(action="foo", engine="bar"): pass - assert logger.records == [ - { - "records": [{"path": "/", "engine": "bar"}], - "database_id": None, - "user_id": 2, - "duration": 15000, - } - ] + self.assertEquals( + logger.records, + [ + { + "records": [{"path": "/", "engine": "bar"}], + "database_id": None, + "user_id": 2, + "duration": 15000, + "curated_payload": {}, + "curated_form_data": {}, + } + ], + ) @patch("superset.utils.core.g", spec={}) def test_context_manager_log_with_context(self, mock_g): @@ -183,20 +188,25 @@ def log( payload_override={"engine": "sqlite"}, ) - assert logger.records == [ - { - "records": [ - { - "path": "/", - "object_ref": {"baz": "food"}, - "payload_override": {"engine": "sqlite"}, - } - ], - "database_id": None, - "user_id": 2, - "duration": 5558756000, - } - ] + self.assertEquals( + logger.records, + [ + { + "records": [ + { + "path": "/", + "object_ref": {"baz": "food"}, + "payload_override": {"engine": "sqlite"}, + } + ], + "database_id": None, + "user_id": 2, + "duration": 5558756000, + "curated_payload": {}, + "curated_form_data": {}, + } + ], + ) @patch("superset.utils.core.g", spec={}) def test_log_with_context_user_null(self, mock_g): From ac2e0e227a98b790ade7a6743f669e82eb0a5d39 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 16:14:18 -0700 Subject: [PATCH 09/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20db?= =?UTF-8?q?-dtypes=201.1.1=20->=201.2.0=20(#28541)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action Co-authored-by: Maxime Beauchemin --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 289af32ec4b72..0a44d45fc67df 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -44,7 +44,7 @@ cycler==0.11.0 # via matplotlib dataflows-tabulator==1.54.3 # via tableschema -db-dtypes==1.1.1 +db-dtypes==1.2.0 # via pandas-gbq decorator==5.1.1 # via ipython From 575c54f48e64c216a4f0f13b3ed66b0dd1b9ae9b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 16:14:31 -0700 Subject: [PATCH 10/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20to?= =?UTF-8?q?mlkit=200.11.8=20->=200.12.5=20(#28540)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action Co-authored-by: Maxime Beauchemin --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 0a44d45fc67df..e46523c732ad3 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -273,7 +273,7 @@ tomli==2.0.1 # pyproject-hooks # pytest # tox -tomlkit==0.11.8 +tomlkit==0.12.5 # via pylint toposort==1.10 # via pip-compile-multi From 8ae2974a8e63869242018960e62ad740fe2262b2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 17:59:50 -0700 Subject: [PATCH 11/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ru?= =?UTF-8?q?ff=200.4.0=20->=200.4.4=20(#28584)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/requirements/development.txt b/requirements/development.txt index e46523c732ad3..3d07a9378f5cb 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -14,8 +14,6 @@ astroid==3.1.0 # via pylint asttokens==2.2.1 # via stack-data -async-timeout==4.0.3 - # via redis backcall==0.2.0 # via ipython boto3==1.26.130 @@ -56,10 +54,6 @@ docker==7.0.0 # via apache-superset et-xmlfile==1.1.0 # via openpyxl -exceptiongroup==1.2.1 - # via - # cattrs - # pytest executing==1.2.0 # via stack-data filelock==3.12.2 @@ -205,7 +199,9 @@ ptyprocess==0.7.0 pure-eval==0.2.2 # via stack-data pure-sasl==0.6.2 - # via thrift-sasl + # via + # pyhive + # thrift-sasl pydata-google-auth==1.7.0 # via pandas-gbq pydruid==0.6.6 @@ -214,7 +210,7 @@ pyee==11.0.1 # via playwright pyfakefs==5.3.5 # via apache-superset -pyhive[presto]==0.7.0 +pyhive[hive_pure_sasl]==0.7.0 # via apache-superset pyinstrument==4.4.0 # via apache-superset @@ -243,7 +239,7 @@ rfc3339-validator==0.1.4 # via openapi-schema-validator rfc3986==2.0.0 # via tableschema -ruff==0.4.0 +ruff==0.4.4 # via apache-superset s3transfer==0.6.1 # via boto3 @@ -260,9 +256,12 @@ tableschema==1.20.10 thrift==0.16.0 # via # apache-superset + # pyhive # thrift-sasl thrift-sasl==0.4.3 - # via apache-superset + # via + # apache-superset + # pyhive tomli==2.0.1 # via # build From d6a90b0abd1f5ab82e5a3cb04681316974ef9383 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:00:51 -0700 Subject: [PATCH 12/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20pi?= =?UTF-8?q?p-tools=207.3.0=20->=207.4.1=20(#28593)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/requirements/development.txt b/requirements/development.txt index 3d07a9378f5cb..b19cb654b3e59 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -22,7 +22,7 @@ botocore==1.29.130 # via # boto3 # s3transfer -build==0.10.0 +build==1.2.1 # via pip-tools cached-property==1.5.2 # via tableschema @@ -169,7 +169,7 @@ pillow==10.3.0 # matplotlib pip-compile-multi==2.6.3 # via apache-superset -pip-tools==7.3.0 +pip-tools==7.4.1 # via pip-compile-multi playwright==1.42.0 # via apache-superset @@ -219,7 +219,9 @@ pylint==3.1.0 pyproject-api==1.5.2 # via tox pyproject-hooks==1.0.0 - # via build + # via + # build + # pip-tools pytest==7.4.4 # via # apache-superset From 057656f8bedcc48f5ef6b6bcaff4c2c08008bd71 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:01:22 -0700 Subject: [PATCH 13/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20zi?= =?UTF-8?q?pp=203.18.1=20->=203.18.2=20(#28591)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 36dc02127cfbe..7d1d53885f149 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -403,7 +403,7 @@ wtforms-json==0.3.5 # via apache-superset xlsxwriter==3.0.9 # via apache-superset -zipp==3.18.1 +zipp==3.18.2 # via importlib-metadata zstandard==0.22.0 # via flask-compress From 7a55ea717da1d906918b5c72972ab94f84f64f83 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:02:32 -0700 Subject: [PATCH 14/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20go?= =?UTF-8?q?ogle-resumable-media=202.5.0=20->=202.7.0=20(#28588)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index b19cb654b3e59..5a921f04fd1c3 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -94,7 +94,7 @@ google-cloud-core==2.3.2 # via google-cloud-bigquery google-crc32c==1.5.0 # via google-resumable-media -google-resumable-media==2.5.0 +google-resumable-media==2.7.0 # via google-cloud-bigquery googleapis-common-protos==1.59.0 # via From c42f674089028cb372a0e9ec2d748e10c09fa65c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:02:50 -0700 Subject: [PATCH 15/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20sq?= =?UTF-8?q?lalchemy-bigquery=201.10.0=20->=201.11.0=20(#28587)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 5a921f04fd1c3..4814ed35c0b08 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -245,7 +245,7 @@ ruff==0.4.4 # via apache-superset s3transfer==0.6.1 # via boto3 -sqlalchemy-bigquery==1.10.0 +sqlalchemy-bigquery==1.11.0 # via apache-superset sqloxide==0.1.43 # via apache-superset From 51f49c4df437dab9c2ce9c43b7be80ea4f364b86 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:03:12 -0700 Subject: [PATCH 16/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20pr?= =?UTF-8?q?e-commit=203.7.0=20->=203.7.1=20(#28586)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 4814ed35c0b08..7c86896f827a2 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -177,7 +177,7 @@ pluggy==1.4.0 # via # pytest # tox -pre-commit==3.7.0 +pre-commit==3.7.1 # via apache-superset progress==1.6 # via apache-superset From 262a2fc5fb29cd90dfd3cfcfc93a5de4c518e526 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:03:50 -0700 Subject: [PATCH 17/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20id?= =?UTF-8?q?na=203.2=20->=203.7=20(#28589)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 7d1d53885f149..30786e22147e3 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -159,7 +159,7 @@ holidays==0.25 # via apache-superset humanize==4.9.0 # via apache-superset -idna==3.2 +idna==3.7 # via # email-validator # requests From 948c0d08f33d036a84dff1fa13ad895817d19506 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:04:09 -0700 Subject: [PATCH 18/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20py?= =?UTF-8?q?cparser=202.20=20->=202.22=20(#28585)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 30786e22147e3..26a78089cc9e2 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -266,7 +266,7 @@ pyasn1==0.5.1 # rsa pyasn1-modules==0.4.0 # via google-auth -pycparser==2.20 +pycparser==2.22 # via cffi pygments==2.15.0 # via rich From d625a5b99cd8b34a11beaecd3a4cb6071c200273 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:04:38 -0700 Subject: [PATCH 19/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20di?= =?UTF-8?q?stlib=200.3.6=20->=200.3.8=20(#28583)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 7c86896f827a2..a646ba0d96862 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -48,7 +48,7 @@ decorator==5.1.1 # via ipython dill==0.3.8 # via pylint -distlib==0.3.6 +distlib==0.3.8 # via virtualenv docker==7.0.0 # via apache-superset From 071df0263c6329c63627beb2557e2a97ce34a08b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:04:59 -0700 Subject: [PATCH 20/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ap?= =?UTF-8?q?sw=203.42.0.1=20->=203.45.3.0=20(#28582)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 26a78089cc9e2..a6b747e0c1f70 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -13,7 +13,7 @@ amqp==5.2.0 # via kombu apispec[yaml]==6.3.0 # via flask-appbuilder -apsw==3.42.0.1 +apsw==3.45.3.0 # via shillelagh async-timeout==4.0.3 # via redis From bf7946e31c25b9f6e02e3d5cdcb0e999ce6f126f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:05:25 -0700 Subject: [PATCH 21/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ij?= =?UTF-8?q?son=203.2.0.post0=20->=203.2.3=20(#28581)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index a646ba0d96862..30f3938ae165d 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -109,7 +109,7 @@ grpcio-status==1.60.1 # via google-api-core identify==2.5.24 # via pre-commit -ijson==3.2.0.post0 +ijson==3.2.3 # via dataflows-tabulator iniconfig==2.0.0 # via pytest From d5886dc180f72a71f268707e0b534fc60c16a118 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:06:11 -0700 Subject: [PATCH 22/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20tz?= =?UTF-8?q?data=202023.3=20->=202024.1=20(#28579)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index a6b747e0c1f70..8890066331bc5 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -365,7 +365,7 @@ typing-extensions==4.11.0 # flask-limiter # limits # shillelagh -tzdata==2023.3 +tzdata==2024.1 # via # celery # pandas From 245a369d9ff009eb2d8faeb1fc4f909c8bae8afd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:06:36 -0700 Subject: [PATCH 23/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20pa?= =?UTF-8?q?rso=200.8.3=20->=200.8.4=20(#28578)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 30f3938ae165d..8859fa26b1013 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -155,7 +155,7 @@ pandas-gbq==0.19.1 # via apache-superset parameterized==0.9.0 # via apache-superset -parso==0.8.3 +parso==0.8.4 # via jedi pathable==0.4.3 # via jsonschema-spec From 5d52aaf49d1669aad63c9e5cfca172c22bd5c2ba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:07:08 -0700 Subject: [PATCH 24/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20tq?= =?UTF-8?q?dm=204.65.0=20->=204.66.4=20(#28577)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 8859fa26b1013..296852b307358 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -280,7 +280,7 @@ toposort==1.10 # via pip-compile-multi tox==4.6.4 # via apache-superset -tqdm==4.65.0 +tqdm==4.66.4 # via # cmdstanpy # prophet From 2b73b6a80cc26b7b64e3d59ce666fdd1facf19e5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:10:06 -0700 Subject: [PATCH 25/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20fl?= =?UTF-8?q?ask-babel=201.0.0=20->=202.0.0=20(#28576)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 8890066331bc5..e25b30db1c2a9 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -111,7 +111,7 @@ flask==2.3.3 # flask-wtf flask-appbuilder==4.4.1 # via apache-superset -flask-babel==1.0.0 +flask-babel==2.0.0 # via flask-appbuilder flask-caching==2.3.0 # via apache-superset From 5eb9002868076a3fe2df166b75529f70ee198239 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 18:14:40 -0700 Subject: [PATCH 26/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20js?= =?UTF-8?q?onlines=203.1.0=20->=204.0.0=20(#28527)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action Co-authored-by: Maxime Beauchemin --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 296852b307358..f6938d5319314 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -123,7 +123,7 @@ jmespath==1.0.1 # via # boto3 # botocore -jsonlines==3.1.0 +jsonlines==4.0.0 # via dataflows-tabulator jsonschema-spec==0.1.6 # via openapi-spec-validator From 876074cc50d0499e93e3c0ae1a4f83c7f6c99337 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 10:30:20 -0700 Subject: [PATCH 27/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20la?= =?UTF-8?q?zy-object-proxy=201.9.0=20->=201.10.0=20(#28533)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action Co-authored-by: Maxime Beauchemin --- requirements/development.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index f6938d5319314..f40a90d5aea04 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -14,6 +14,8 @@ astroid==3.1.0 # via pylint asttokens==2.2.1 # via stack-data +async-timeout==4.0.3 + # via redis backcall==0.2.0 # via ipython boto3==1.26.130 @@ -54,6 +56,10 @@ docker==7.0.0 # via apache-superset et-xmlfile==1.1.0 # via openpyxl +exceptiongroup==1.2.1 + # via + # cattrs + # pytest executing==1.2.0 # via stack-data filelock==3.12.2 @@ -129,7 +135,7 @@ jsonschema-spec==0.1.6 # via openapi-spec-validator kiwisolver==1.4.4 # via matplotlib -lazy-object-proxy==1.9.0 +lazy-object-proxy==1.10.0 # via openapi-spec-validator linear-tsv==1.1.0 # via dataflows-tabulator From a4ed34ceaf2a93a1d6800481d9baa1934f34d8d7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 10:30:32 -0700 Subject: [PATCH 28/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20tz?= =?UTF-8?q?local=204.3=20->=205.2=20(#28526)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action Co-authored-by: Maxime Beauchemin --- requirements/development.txt | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/requirements/development.txt b/requirements/development.txt index f40a90d5aea04..38a8b27f07d79 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -205,9 +205,7 @@ ptyprocess==0.7.0 pure-eval==0.2.2 # via stack-data pure-sasl==0.6.2 - # via - # pyhive - # thrift-sasl + # via thrift-sasl pydata-google-auth==1.7.0 # via pandas-gbq pydruid==0.6.6 @@ -216,7 +214,7 @@ pyee==11.0.1 # via playwright pyfakefs==5.3.5 # via apache-superset -pyhive[hive_pure_sasl]==0.7.0 +pyhive[presto]==0.7.0 # via apache-superset pyinstrument==4.4.0 # via apache-superset @@ -239,8 +237,6 @@ pytest-mock==3.10.0 # via apache-superset python-ldap==3.4.4 # via apache-superset -pytz-deprecation-shim==0.1.0.post0 - # via tzlocal requests-oauthlib==1.3.1 # via google-auth-oauthlib rfc3339-validator==0.1.4 @@ -264,18 +260,17 @@ tableschema==1.20.10 thrift==0.16.0 # via # apache-superset - # pyhive # thrift-sasl thrift-sasl==0.4.3 - # via - # apache-superset - # pyhive + # via apache-superset tomli==2.0.1 # via + # apache-superset # build # coverage # pip-tools # pylint + # pyhive # pyproject-api # pyproject-hooks # pytest @@ -296,7 +291,7 @@ traitlets==5.9.0 # matplotlib-inline trino==0.328.0 # via apache-superset -tzlocal==4.3 +tzlocal==5.2 # via trino unicodecsv==0.14.1 # via From 6ecbaf3468eead565960232ba3d36a309705c27a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 10:31:23 -0700 Subject: [PATCH 29/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20de?= =?UTF-8?q?precated=201.2.13=20->=201.2.14=20(#28580)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- requirements/development.txt | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index e25b30db1c2a9..16ec3778fed4d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -85,7 +85,7 @@ cryptography==42.0.7 # apache-superset # paramiko # pyopenssl -deprecated==1.2.13 +deprecated==1.2.14 # via limits deprecation==2.1.0 # via apache-superset diff --git a/requirements/development.txt b/requirements/development.txt index 38a8b27f07d79..2f5c6ef442e17 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -14,8 +14,6 @@ astroid==3.1.0 # via pylint asttokens==2.2.1 # via stack-data -async-timeout==4.0.3 - # via redis backcall==0.2.0 # via ipython boto3==1.26.130 @@ -56,10 +54,6 @@ docker==7.0.0 # via apache-superset et-xmlfile==1.1.0 # via openpyxl -exceptiongroup==1.2.1 - # via - # cattrs - # pytest executing==1.2.0 # via stack-data filelock==3.12.2 From d07cebc22031b0c558c7cff9bbcd8b9b14779073 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 10:33:08 -0700 Subject: [PATCH 30/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20py?= =?UTF-8?q?gments=202.15.0=20->=202.18.0=20(#28535)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 16ec3778fed4d..8a7d0ae8019c1 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -268,7 +268,7 @@ pyasn1-modules==0.4.0 # via google-auth pycparser==2.22 # via cffi -pygments==2.15.0 +pygments==2.18.0 # via rich pyjwt==2.8.0 # via From bf80273577c2527288d24e45f588cfc5a8128df5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 10:34:19 -0700 Subject: [PATCH 31/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ri?= =?UTF-8?q?ch=2013.3.4=20->=2013.7.1=20(#28573)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 8a7d0ae8019c1..e0869c3eb69b2 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -314,7 +314,7 @@ requests==2.31.0 # shillelagh requests-cache==1.2.0 # via shillelagh -rich==13.3.4 +rich==13.7.1 # via flask-limiter rsa==4.9 # via google-auth From 296f67197edc7290d89c1cab165cc41b6bfcf776 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 10:34:58 -0700 Subject: [PATCH 32/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20dn?= =?UTF-8?q?spython=202.1.0=20->=202.6.1=20(#28574)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index e0869c3eb69b2..457db364d19ff 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -89,7 +89,7 @@ deprecated==1.2.14 # via limits deprecation==2.1.0 # via apache-superset -dnspython==2.1.0 +dnspython==2.6.1 # via email-validator email-validator==2.1.1 # via flask-appbuilder From 344d359ba9c459b233bd2daa96f2473da742ce5a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:12:42 -0700 Subject: [PATCH 33/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20re?= =?UTF-8?q?quests=202.31.0=20->=202.32.2=20(#28684)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 457db364d19ff..9132ff278c186 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -308,7 +308,7 @@ pyyaml==6.0.1 # apispec redis==4.6.0 # via apache-superset -requests==2.31.0 +requests==2.32.2 # via # requests-cache # shillelagh From 61d44488ddfd564b9c3d8b267a076f7bbc6e72f4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:13:01 -0700 Subject: [PATCH 34/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ki?= =?UTF-8?q?wisolver=201.4.4=20->=201.4.5=20(#28683)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 2f5c6ef442e17..9aab2b318d0cd 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -127,7 +127,7 @@ jsonlines==4.0.0 # via dataflows-tabulator jsonschema-spec==0.1.6 # via openapi-spec-validator -kiwisolver==1.4.4 +kiwisolver==1.4.5 # via matplotlib lazy-object-proxy==1.10.0 # via openapi-spec-validator From b99a815da5811026503dd309657d72ff8e01ebc4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:13:21 -0700 Subject: [PATCH 35/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20py?= =?UTF-8?q?druid=200.6.6=20->=200.6.9=20(#28682)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 9aab2b318d0cd..594cdb24d66b2 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -202,7 +202,7 @@ pure-sasl==0.6.2 # via thrift-sasl pydata-google-auth==1.7.0 # via pandas-gbq -pydruid==0.6.6 +pydruid==0.6.9 # via apache-superset pyee==11.0.1 # via playwright From 796f279aecd949b3464114299e2eb0a7bdea2f57 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:14:32 -0700 Subject: [PATCH 36/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20id?= =?UTF-8?q?entify=202.5.24=20->=202.5.36=20(#28680)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 594cdb24d66b2..100394f776a19 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -107,7 +107,7 @@ grpcio==1.62.1 # grpcio-status grpcio-status==1.60.1 # via google-api-core -identify==2.5.24 +identify==2.5.36 # via pre-commit ijson==3.2.3 # via dataflows-tabulator From d8b69c2656c456c28255c6d15062684e7cf15ec7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:14:48 -0700 Subject: [PATCH 37/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20cl?= =?UTF-8?q?ick-didyoumean=200.3.0=20->=200.3.1=20(#28677)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 9132ff278c186..23ae68e928a46 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -64,7 +64,7 @@ click==8.1.7 # click-repl # flask # flask-appbuilder -click-didyoumean==0.3.0 +click-didyoumean==0.3.1 # via celery click-option-group==0.5.6 # via apache-superset From e5341d28e4a2a9ad087810d495d4929b08e0c0a4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:15:05 -0700 Subject: [PATCH 38/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20cf?= =?UTF-8?q?fi=201.15.1=20->=201.16.0=20(#28676)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 23ae68e928a46..5ff6b7df75579 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -48,7 +48,7 @@ celery==5.3.6 # via apache-superset certifi==2023.7.22 # via requests -cffi==1.15.1 +cffi==1.16.0 # via # cryptography # pynacl From 8953a822a42419852ce7ef9c3d680e7f95a6d572 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:15:24 -0700 Subject: [PATCH 39/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ko?= =?UTF-8?q?mbu=205.3.4=20->=205.3.7=20(#28675)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 5ff6b7df75579..6f73735822cdb 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -179,7 +179,7 @@ jinja2==3.1.4 # flask-babel jsonschema==4.17.3 # via flask-appbuilder -kombu==5.3.4 +kombu==5.3.7 # via celery korean-lunar-calendar==0.3.1 # via holidays From 94f1c4d2a9a6b4b3af3e94fa208e8984233fe6b0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:15:50 -0700 Subject: [PATCH 40/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20cl?= =?UTF-8?q?ick-repl=200.2.0=20->=200.3.0=20(#28671)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 6f73735822cdb..9b171f1d75a2e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -70,7 +70,7 @@ click-option-group==0.5.6 # via apache-superset click-plugins==1.1.1 # via celery -click-repl==0.2.0 +click-repl==0.3.0 # via celery colorama==0.4.6 # via @@ -328,7 +328,6 @@ simplejson==3.19.2 # via apache-superset six==1.16.0 # via - # click-repl # isodate # prison # python-dateutil From 1fd2f2c80e320b440de917c09d18bf128bbf2b86 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:16:01 -0700 Subject: [PATCH 41/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20py?= =?UTF-8?q?project-api=201.5.2=20->=201.6.1=20(#28672)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 100394f776a19..d9956d377ed9c 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -214,7 +214,7 @@ pyinstrument==4.4.0 # via apache-superset pylint==3.1.0 # via apache-superset -pyproject-api==1.5.2 +pyproject-api==1.6.1 # via tox pyproject-hooks==1.0.0 # via From dfc2ee2a88c23caa26b01ce107d6162a601baee6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:16:59 -0700 Subject: [PATCH 42/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ba?= =?UTF-8?q?bel=202.9.1=20->=202.15.0=20(#28668)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 9b171f1d75a2e..b6912d76e7253 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -22,7 +22,7 @@ attrs==23.1.0 # cattrs # jsonschema # requests-cache -babel==2.9.1 +babel==2.15.0 # via flask-babel backoff==2.2.1 # via apache-superset @@ -298,7 +298,6 @@ python-geohash==0.8.5 # via apache-superset pytz==2021.3 # via - # babel # croniter # flask-babel # pandas From 207a5f054b23fbaa9c9bb0127d2ba41ca37cd27a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:17:39 -0700 Subject: [PATCH 43/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20fr?= =?UTF-8?q?eezegun=201.4.0=20->=201.5.1=20(#28666)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index d9956d377ed9c..cb487eeadccd6 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -66,7 +66,7 @@ flask-testing==0.8.1 # via apache-superset fonttools==4.51.0 # via matplotlib -freezegun==1.4.0 +freezegun==1.5.1 # via apache-superset future==0.18.3 # via pyhive From a48128d31706e2537d6580aef9ba55da110e5e92 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:17:47 -0700 Subject: [PATCH 44/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20tr?= =?UTF-8?q?aitlets=205.9.0=20->=205.14.3=20(#28665)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index cb487eeadccd6..fb84607e13917 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -279,7 +279,7 @@ tqdm==4.66.4 # via # cmdstanpy # prophet -traitlets==5.9.0 +traitlets==5.14.3 # via # ipython # matplotlib-inline From 65937947a9c99b71edba74319d545f9b8e6c544f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:17:56 -0700 Subject: [PATCH 45/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20pe?= =?UTF-8?q?xpect=204.8.0=20->=204.9.0=20(#28662)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index fb84607e13917..f80cf06504377 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -159,7 +159,7 @@ parso==0.8.4 # via jedi pathable==0.4.3 # via jsonschema-spec -pexpect==4.8.0 +pexpect==4.9.0 # via ipython pickleshare==0.7.5 # via ipython From 6044f643ed7442d8ae26d367f3919d9e864f0bdb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:18:19 -0700 Subject: [PATCH 46/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20wh?= =?UTF-8?q?eel=200.40.0=20->=200.43.0=20(#28660)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index f80cf06504377..4ed764abca22e 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -295,7 +295,7 @@ virtualenv==20.23.1 # via # pre-commit # tox -wheel==0.40.0 +wheel==0.43.0 # via pip-tools xlrd==2.0.1 # via dataflows-tabulator From 63e98286e9b03841d77d272b7ceb54d8a0f76dfd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:18:57 -0700 Subject: [PATCH 47/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ty?= =?UTF-8?q?ping-extensions=204.11.0=20->=204.12.0=20(#28659)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index b6912d76e7253..ec475abf3f82d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -355,7 +355,7 @@ sshtunnel==0.4.0 # via apache-superset tabulate==0.8.10 # via apache-superset -typing-extensions==4.11.0 +typing-extensions==4.12.0 # via # alembic # apache-superset From a5df955a39b493bca6363450c3784516c88ef5fa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:19:08 -0700 Subject: [PATCH 48/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ca?= =?UTF-8?q?ttrs=2023.2.1=20->=2023.2.3=20(#28658)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index ec475abf3f82d..1b60eba767857 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -42,7 +42,7 @@ cachelib==0.9.0 # flask-session cachetools==5.3.2 # via google-auth -cattrs==23.2.1 +cattrs==23.2.3 # via requests-cache celery==5.3.6 # via apache-superset From f39eda150f8c3449241ae0c4dba1d82fca4714e9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:19:16 -0700 Subject: [PATCH 49/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20bo?= =?UTF-8?q?ttleneck=201.3.7=20->=201.3.8=20(#28657)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 1b60eba767857..19d310baba78e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -32,7 +32,7 @@ billiard==4.2.0 # via celery blinker==1.8.2 # via flask -bottleneck==1.3.7 +bottleneck==1.3.8 # via pandas brotli==1.1.0 # via flask-compress From 820fecb48181ad8f2c49f7e711d92950eefc1064 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:22:13 -0700 Subject: [PATCH 50/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20bc?= =?UTF-8?q?rypt=204.0.1=20->=204.1.3=20(#28590)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 19d310baba78e..3df3afc08d284 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -26,7 +26,7 @@ babel==2.15.0 # via flask-babel backoff==2.2.1 # via apache-superset -bcrypt==4.0.1 +bcrypt==4.1.3 # via paramiko billiard==4.2.0 # via celery From 21486da2b436c3cc1989eb271475dbe64e90cdb2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:22:20 -0700 Subject: [PATCH 51/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ma?= =?UTF-8?q?rshmallow=203.19.0=20->=203.21.2=20(#28655)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 3df3afc08d284..67a9175dbf76d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -201,7 +201,7 @@ markupsafe==2.1.5 # mako # werkzeug # wtforms -marshmallow==3.19.0 +marshmallow==3.21.2 # via # flask-appbuilder # marshmallow-sqlalchemy From 3d67346f77d91625ecdc97e21155407bf9064cf3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:27:50 -0700 Subject: [PATCH 52/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20fl?= =?UTF-8?q?ask-limiter=203.3.1=20->=203.7.0=20(#28670)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 67a9175dbf76d..dc8557db4a4d2 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -119,7 +119,7 @@ flask-compress==1.15 # via apache-superset flask-jwt-extended==4.6.0 # via flask-appbuilder -flask-limiter==3.3.1 +flask-limiter==3.7.0 # via flask-appbuilder flask-login==0.6.3 # via From 278570bc38b00db939b61b694ce5386b64a3f70b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 11:40:38 -0700 Subject: [PATCH 53/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20re?= =?UTF-8?q?quests-oauthlib=201.3.1=20->=202.0.0=20(#28681)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action Co-authored-by: Maxime Beauchemin --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 4ed764abca22e..313bb514d9e4e 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -231,7 +231,7 @@ pytest-mock==3.10.0 # via apache-superset python-ldap==3.4.4 # via apache-superset -requests-oauthlib==1.3.1 +requests-oauthlib==2.0.0 # via google-auth-oauthlib rfc3339-validator==0.1.4 # via openapi-schema-validator From 2c982cf5b382f8831868d53eedd558865312e89c Mon Sep 17 00:00:00 2001 From: Sam Firke Date: Fri, 24 May 2024 15:36:39 -0400 Subject: [PATCH 54/59] chore(docs): address common docker compose error message in Quickstart (#28696) --- docs/docs/quickstart.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/quickstart.mdx b/docs/docs/quickstart.mdx index b36051a4b9534..e1e621a85a93e 100644 --- a/docs/docs/quickstart.mdx +++ b/docs/docs/quickstart.mdx @@ -38,6 +38,8 @@ This may take a moment as Docker Compose will fetch the underlying container images and will load up some examples. Once all containers are downloaded and the output settles, you're ready to log in. +⚠️ If you get an error message like `validating superset\docker-compose-image-tag.yml: services.superset-worker-beat.env_file.0 must be a string`, you need to update your version of `docker-compose`. + ### 3. Log into Superset Now head over to [http://localhost:8088](http://localhost:8088) and log in with the default created account: ```bash From c82c4b6877d71f82e17818651081323e6ecb7e1f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 22:38:51 -0700 Subject: [PATCH 55/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ma?= =?UTF-8?q?tplotlib=203.7.1=20->=203.9.0=20(#28674)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 313bb514d9e4e..39e1108b09a42 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -133,7 +133,7 @@ lazy-object-proxy==1.10.0 # via openapi-spec-validator linear-tsv==1.1.0 # via dataflows-tabulator -matplotlib==3.7.1 +matplotlib==3.9.0 # via prophet matplotlib-inline==0.1.6 # via ipython From eadf84d8cfd5bdbaa7f2953ff6ef8ff7433c7a30 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 22:39:08 -0700 Subject: [PATCH 56/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20ru?= =?UTF-8?q?ff=200.4.4=20->=200.4.5=20(#28669)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 39e1108b09a42..22d4c4f2efcd5 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -237,7 +237,7 @@ rfc3339-validator==0.1.4 # via openapi-schema-validator rfc3986==2.0.0 # via tableschema -ruff==0.4.4 +ruff==0.4.5 # via apache-superset s3transfer==0.6.1 # via boto3 From c1a7bb4f62f59f2737ec4df1a89de07d41f96765 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 22:39:23 -0700 Subject: [PATCH 57/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20go?= =?UTF-8?q?ogleapis-common-protos=201.59.0=20->=201.63.0=20(#28663)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 22d4c4f2efcd5..4b8531017ffc2 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -96,7 +96,7 @@ google-crc32c==1.5.0 # via google-resumable-media google-resumable-media==2.7.0 # via google-cloud-bigquery -googleapis-common-protos==1.59.0 +googleapis-common-protos==1.63.0 # via # google-api-core # grpcio-status From 5f553601f771768455aa6094cc1e082f25b5bf65 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 May 2024 22:39:43 -0700 Subject: [PATCH 58/59] =?UTF-8?q?chore(=F0=9F=A6=BE):=20bump=20python=20st?= =?UTF-8?q?ack-data=200.6.2=20->=200.6.3=20(#28661)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GitHub Action --- requirements/development.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/development.txt b/requirements/development.txt index 4b8531017ffc2..257af5f46ddb0 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -245,7 +245,7 @@ sqlalchemy-bigquery==1.11.0 # via apache-superset sqloxide==0.1.43 # via apache-superset -stack-data==0.6.2 +stack-data==0.6.3 # via ipython statsd==4.0.1 # via apache-superset From 9ac0cf7d1466f71fb41a2c30dcb54d94c733206e Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Sat, 25 May 2024 01:25:12 -0700 Subject: [PATCH 59/59] chore: remove ipython from development dependencies (#28703) --- pyproject.toml | 1 - requirements/base.txt | 4 +--- requirements/development.txt | 29 ----------------------------- 3 files changed, 1 insertion(+), 33 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ed2f78a6f55bc..af6a15dbac9fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -178,7 +178,6 @@ development = [ "freezegun", "greenlet>=2.0.2", "grpcio>=1.55.3", - "ipython", "openapi-spec-validator", "parameterized", "pip-compile-multi", diff --git a/requirements/base.txt b/requirements/base.txt index dc8557db4a4d2..5865cbd6f9096 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -148,9 +148,7 @@ geopy==2.4.1 google-auth==2.27.0 # via shillelagh greenlet==3.0.3 - # via - # shillelagh - # sqlalchemy + # via shillelagh gunicorn==22.0.0 # via apache-superset hashids==1.3.1 diff --git a/requirements/development.txt b/requirements/development.txt index 257af5f46ddb0..8dfdb5e34cd5f 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -12,10 +12,6 @@ # -r requirements/development.in astroid==3.1.0 # via pylint -asttokens==2.2.1 - # via stack-data -backcall==0.2.0 - # via ipython boto3==1.26.130 # via dataflows-tabulator botocore==1.29.130 @@ -44,8 +40,6 @@ dataflows-tabulator==1.54.3 # via tableschema db-dtypes==1.2.0 # via pandas-gbq -decorator==5.1.1 - # via ipython dill==0.3.8 # via pylint distlib==0.3.8 @@ -54,8 +48,6 @@ docker==7.0.0 # via apache-superset et-xmlfile==1.1.0 # via openpyxl -executing==1.2.0 - # via stack-data filelock==3.12.2 # via # tox @@ -113,12 +105,8 @@ ijson==3.2.3 # via dataflows-tabulator iniconfig==2.0.0 # via pytest -ipython==8.12.2 - # via apache-superset isort==5.12.0 # via pylint -jedi==0.18.2 - # via ipython jmespath==1.0.1 # via # boto3 @@ -135,8 +123,6 @@ linear-tsv==1.1.0 # via dataflows-tabulator matplotlib==3.9.0 # via prophet -matplotlib-inline==0.1.6 - # via ipython mccabe==0.7.0 # via pylint mysqlclient==2.2.4 @@ -155,14 +141,8 @@ pandas-gbq==0.19.1 # via apache-superset parameterized==0.9.0 # via apache-superset -parso==0.8.4 - # via jedi pathable==0.4.3 # via jsonschema-spec -pexpect==4.9.0 - # via ipython -pickleshare==0.7.5 - # via ipython pillow==10.3.0 # via # apache-superset @@ -194,10 +174,6 @@ protobuf==4.23.0 # proto-plus psycopg2-binary==2.9.6 # via apache-superset -ptyprocess==0.7.0 - # via pexpect -pure-eval==0.2.2 - # via stack-data pure-sasl==0.6.2 # via thrift-sasl pydata-google-auth==1.7.0 @@ -245,8 +221,6 @@ sqlalchemy-bigquery==1.11.0 # via apache-superset sqloxide==0.1.43 # via apache-superset -stack-data==0.6.3 - # via ipython statsd==4.0.1 # via apache-superset tableschema==1.20.10 @@ -259,12 +233,10 @@ thrift-sasl==0.4.3 # via apache-superset tomli==2.0.1 # via - # apache-superset # build # coverage # pip-tools # pylint - # pyhive # pyproject-api # pyproject-hooks # pytest @@ -281,7 +253,6 @@ tqdm==4.66.4 # prophet traitlets==5.14.3 # via - # ipython # matplotlib-inline trino==0.328.0 # via apache-superset