You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/09/03 18:48:37 UTC
(superset) branch master updated: fix(charts): exclude string helpers from post_processing operation allowlist (#43345)
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 9828281e7fd fix(charts): exclude string helpers from post_processing operation allowlist (#43345)
9828281e7fd is described below
commit 9828281e7fdface10353841427b9e2b6a0c14202
Author: Evan Rusackas <ev...@preset.io>
AuthorDate: Thu Sep 3 11:48:29 2026 -0700
fix(charts): exclude string helpers from post_processing operation allowlist (#43345)
Co-authored-by: Superset Dev <de...@superset.apache.org>
Co-authored-by: Claude Opus 4.8 <no...@anthropic.com>
---
superset/charts/schemas.py | 10 +++++++++-
superset/common/query_object.py | 8 ++++----
superset/utils/pandas_postprocessing/__init__.py | 10 ++++++++++
tests/unit_tests/charts/test_schemas.py | 11 +++++++++++
tests/unit_tests/queries/query_object_test.py | 17 +++++++++++++++++
5 files changed, 51 insertions(+), 5 deletions(-)
diff --git a/superset/charts/schemas.py b/superset/charts/schemas.py
index 458320aac48..74c42caa862 100644
--- a/superset/charts/schemas.py
+++ b/superset/charts/schemas.py
@@ -1033,7 +1033,15 @@ class ChartDataGeodeticParseOptionsSchema(
class ChartDataPostProcessingOperationSchema(Schema):
- _builtin_ops = pandas_postprocessing.__all__
+ # OPERATIONS excludes escape_separator/unescape_separator: those are
+ # internal str -> str helpers used by flatten, not DataFrame
+ # post-processing operations, so dispatching one against a DataFrame
+ # raises a confusing TypeError instead of the intended clean validation
+ # error. No field-level `validate=` here: it would run before, and thus
+ # reject, any EXTRA_PANDAS_POSTPROCESSING_OPS-registered custom
+ # operation, which `validate_operation` below is responsible for
+ # allowing.
+ _builtin_ops = pandas_postprocessing.OPERATIONS
operation = fields.String(
metadata={
diff --git a/superset/common/query_object.py b/superset/common/query_object.py
index e67a470b069..389bd828678 100644
--- a/superset/common/query_object.py
+++ b/superset/common/query_object.py
@@ -644,10 +644,10 @@ class QueryObject: # pylint: disable=too-many-instance-attributes
raise InvalidPostProcessingError(
_("`operation` property of post processing object undefined")
)
- # ``__all__`` is the authoritative list of built-in operations.
- # ``hasattr`` would also match module internals (helpers, imported
- # submodules, typing aliases), shadowing a like-named custom op.
- if operation in pandas_postprocessing.__all__:
+ # ``OPERATIONS`` is the authoritative list of built-in operations;
+ # excludes escape_separator/unescape_separator (str -> str helpers
+ # used by flatten, not DataFrame post-processing operations).
+ if operation in pandas_postprocessing.OPERATIONS:
func = getattr(pandas_postprocessing, operation)
else:
extra_ops = pandas_postprocessing.build_extra_ops_map(
diff --git a/superset/utils/pandas_postprocessing/__init__.py b/superset/utils/pandas_postprocessing/__init__.py
index d7400d7c236..7da9c6e750d 100644
--- a/superset/utils/pandas_postprocessing/__init__.py
+++ b/superset/utils/pandas_postprocessing/__init__.py
@@ -80,3 +80,13 @@ def build_extra_ops_map(
spec.
"""
return {name: fn for fn in extra if (name := getattr(fn, "__name__", None))}
+
+
+# Operations that can be requested via a chart's `post_processing` spec.
+# Excludes `escape_separator`/`unescape_separator`: those are internal
+# str -> str helpers used by `flatten`, not DataFrame post-processing
+# operations, so dispatching one against a DataFrame raises a confusing
+# TypeError instead of the intended clean validation error.
+OPERATIONS = [
+ name for name in __all__ if name not in ("escape_separator", "unescape_separator")
+]
diff --git a/tests/unit_tests/charts/test_schemas.py b/tests/unit_tests/charts/test_schemas.py
index e22d8cb928c..6610ab18147 100644
--- a/tests/unit_tests/charts/test_schemas.py
+++ b/tests/unit_tests/charts/test_schemas.py
@@ -601,3 +601,14 @@ def test_chart_data_adhoc_metric_schema_accepts_extended_aggregates(
}
)
assert result["aggregate"] == aggregate
+
+
+@pytest.mark.parametrize("operation", ["escape_separator", "unescape_separator"])
+def test_post_processing_operation_schema_rejects_string_helpers(
+ app_context: None, operation: str
+) -> None:
+ """`escape_separator`/`unescape_separator` are internal str -> str helpers,
+ not DataFrame post-processing operations, and shouldn't validate as one."""
+ schema = ChartDataPostProcessingOperationSchema()
+ with pytest.raises(ValidationError):
+ schema.load({"operation": operation, "options": {}})
diff --git a/tests/unit_tests/queries/query_object_test.py b/tests/unit_tests/queries/query_object_test.py
index 9d66f576095..dcbcb95ce62 100644
--- a/tests/unit_tests/queries/query_object_test.py
+++ b/tests/unit_tests/queries/query_object_test.py
@@ -718,3 +718,20 @@ def test_post_processing_keeps_an_entry_without_an_operation():
query_object = QueryObject(row_limit=1, post_processing=post_processing)
assert query_object.post_processing == post_processing
+
+
+@pytest.mark.parametrize("operation", ["escape_separator", "unescape_separator"])
+def test_exec_post_processing_rejects_string_helpers(
+ app_context: None, operation: str
+) -> None:
+ """`escape_separator`/`unescape_separator` are str -> str helpers used by
+ `flatten`, not DataFrame post-processing operations, and must not be
+ reachable as a `post_processing` operation name."""
+ df = pd.DataFrame({"value": [1, 2, 3]})
+ query_object = QueryObject(
+ row_limit=10,
+ post_processing=[{"operation": operation, "options": {}}],
+ )
+
+ with pytest.raises(InvalidPostProcessingError):
+ query_object.exec_post_processing(df)