This is an automated email from the ASF dual-hosted git repository.

aminghadersohi 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 9c7c96fe057 fix(logging): register LogRestApi only once (#44732)
9c7c96fe057 is described below

commit 9c7c96fe05741acd43b7a3ac84544cea84d4cc64
Author: Amin Ghadersohi <[email protected]>
AuthorDate: Fri Oct 2 16:28:52 2026 +1000

    fix(logging): register LogRestApi only once (#44732)
---
 superset/initialization/__init__.py                |   1 -
 tests/unit_tests/initialization/init_views_test.py | 113 +++++++++++++++++++++
 2 files changed, 113 insertions(+), 1 deletion(-)

diff --git a/superset/initialization/__init__.py 
b/superset/initialization/__init__.py
index 5074d456cfe..6dcde2b3a58 100644
--- a/superset/initialization/__init__.py
+++ b/superset/initialization/__init__.py
@@ -553,7 +553,6 @@ class SupersetAppInitializer:  # pylint: 
disable=too-many-public-methods
             category_label=_("Manage"),
             menu_cond=lambda: 
feature_flag_manager.is_feature_enabled("SOFT_DELETE"),
         )
-        appbuilder.add_api(LogRestApi)
         appbuilder.add_api(UserRegistrationsRestAPI)
         appbuilder.add_view(
             ActionLogView,
diff --git a/tests/unit_tests/initialization/init_views_test.py 
b/tests/unit_tests/initialization/init_views_test.py
new file mode 100644
index 00000000000..560adbd9982
--- /dev/null
+++ b/tests/unit_tests/initialization/init_views_test.py
@@ -0,0 +1,113 @@
+# 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.
+
+"""Structural checks for API registration without bootstrapping the 
application."""
+
+import ast
+from collections import Counter
+from pathlib import Path
+from textwrap import dedent
+
+import pytest
+
+
+def _count_api_registrations(node: ast.AST) -> Counter[str]:
+    """Count registrations, taking the maximum across exclusive if/else 
arms."""
+    if isinstance(node, ast.If):
+        body = Counter[str]()
+        otherwise = Counter[str]()
+        for statement in node.body:
+            body.update(_count_api_registrations(statement))
+        for statement in node.orelse:
+            otherwise.update(_count_api_registrations(statement))
+        return _count_api_registrations(node.test) + (body | otherwise)
+
+    registrations = Counter[str]()
+    if (
+        isinstance(node, ast.Call)
+        and isinstance(node.func, ast.Attribute)
+        and isinstance(node.func.value, ast.Name)
+        and node.func.value.id == "appbuilder"
+        and node.func.attr == "add_api"
+        and node.args
+    ):
+        registrations[ast.unparse(node.args[0])] += 1
+    for child in ast.iter_child_nodes(node):
+        registrations.update(_count_api_registrations(child))
+    return registrations
+
+
[email protected](
+    ("source", "expected"),
+    [
+        (
+            """
+            appbuilder.add_api(LogRestApi)
+            appbuilder.add_api(LogRestApi)
+            """,
+            2,
+        ),
+        (
+            """
+            if enabled:
+                appbuilder.add_api(LogRestApi)
+            else:
+                appbuilder.add_api(LogRestApi)
+            """,
+            1,
+        ),
+        (
+            """
+            appbuilder.add_api(LogRestApi)
+            if enabled:
+                appbuilder.add_api(LogRestApi)
+            """,
+            2,
+        ),
+    ],
+    ids=["unconditional-duplicate", "exclusive-branches", 
"conditional-duplicate"],
+)
+def test_count_api_registrations(source: str, expected: int) -> None:
+    """Distinguish duplicate registrations from mutually exclusive 
alternatives."""
+    registrations = _count_api_registrations(ast.parse(dedent(source)))
+    assert registrations["LogRestApi"] == expected
+
+
+def test_init_views_registers_each_api_once() -> None:
+    """Keep API registrations unique, including feature-flagged 
registrations."""
+    source = (
+        Path(__file__).resolve().parents[3]
+        / "superset"
+        / "initialization"
+        / "__init__.py"
+    )
+    module = ast.parse(source.read_text(encoding="utf-8"))
+    initializer = next(
+        node
+        for node in module.body
+        if isinstance(node, ast.ClassDef) and node.name == 
"SupersetAppInitializer"
+    )
+    init_views = next(
+        node
+        for node in initializer.body
+        if isinstance(node, ast.FunctionDef) and node.name == "init_views"
+    )
+    registrations = _count_api_registrations(init_views)
+
+    assert registrations["LogRestApi"] == 1
+    duplicates = {api: count for api, count in registrations.items() if count 
> 1}
+    assert not duplicates, f"Duplicate API registrations: {duplicates}"

Reply via email to