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

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new b93b577753 [python] Parse overrides in ConfigResponse and fix merge 
priority (#8989)
b93b577753 is described below

commit b93b577753760a036805a8aedffd4f2374138b6d
Author: wenhaozhao011-cmd <[email protected]>
AuthorDate: Mon Aug 3 18:19:52 2026 +0800

    [python] Parse overrides in ConfigResponse and fix merge priority (#8989)
---
 paimon-python/pypaimon/api/api_response.py    | 15 +++++--
 paimon-python/pypaimon/tests/rest/api_test.py | 62 +++++++++++++++++++++++++++
 2 files changed, 73 insertions(+), 4 deletions(-)

diff --git a/paimon-python/pypaimon/api/api_response.py 
b/paimon-python/pypaimon/api/api_response.py
index 0d392b9eef..a2168e301d 100644
--- a/paimon-python/pypaimon/api/api_response.py
+++ b/paimon-python/pypaimon/api/api_response.py
@@ -21,7 +21,7 @@ from typing import Dict, Generic, List, Optional
 
 from pypaimon.api.api_request import RESTRequest
 from pypaimon.common.identifier import Identifier
-from pypaimon.common.json_util import T, json_field
+from pypaimon.common.json_util import T, json_field, optional_json_field
 from pypaimon.common.options import Options
 from pypaimon.schema.data_types import DataField
 from pypaimon.schema.schema import Schema
@@ -336,13 +336,20 @@ class GetDatabaseResponse(AuditRESTResponse):
 @dataclass
 class ConfigResponse(RESTResponse):
     FILED_DEFAULTS = "defaults"
+    FIELD_OVERRIDES = "overrides"
 
     defaults: Dict[str, str] = json_field(FILED_DEFAULTS)
+    overrides: Optional[Dict[str, str]] = optional_json_field(FIELD_OVERRIDES, 
"non_null")
 
     def merge(self, options: Options) -> Options:
-        merged = options.copy()
-        merged.data.update(self.defaults)
-        return merged
+        # Priority from low to high: server defaults, client options, server 
overrides.
+        # Server defaults can be overridden by the client, while server 
overrides are
+        # enforced and win over any client value (e.g. "data-token.enabled").
+        merged = dict(self.defaults or {})
+        merged.update(options.data)
+        if self.overrides:
+            merged.update(self.overrides)
+        return Options({key: value for key, value in merged.items() if value 
is not None})
 
 
 @dataclass
diff --git a/paimon-python/pypaimon/tests/rest/api_test.py 
b/paimon-python/pypaimon/tests/rest/api_test.py
index da0220e7e0..94ab827cbf 100644
--- a/paimon-python/pypaimon/tests/rest/api_test.py
+++ b/paimon-python/pypaimon/tests/rest/api_test.py
@@ -472,3 +472,65 @@ class ApiTest(unittest.TestCase):
             self.assertEqual("normal_table_2", second_page.next_page_token)
         finally:
             server.shutdown()
+
+
+class ConfigResponseTest(unittest.TestCase):
+
+    def test_deserialize_overrides(self):
+        response = JSON.from_json(
+            '{"defaults": {"a": "1"}, "overrides": {"data-token.enabled": 
"true"}}',
+            ConfigResponse,
+        )
+        self.assertEqual({"a": "1"}, response.defaults)
+        self.assertEqual({"data-token.enabled": "true"}, response.overrides)
+
+    def test_deserialize_without_overrides(self):
+        response = JSON.from_json('{"defaults": {"a": "1"}}', ConfigResponse)
+        self.assertIsNone(response.overrides)
+        self.assertEqual({"a": "1"}, response.merge(Options({})).to_map())
+
+    def test_serialize_skips_absent_overrides(self):
+        self.assertEqual(
+            '{"defaults": {"a": "1"}}',
+            JSON.to_json(ConfigResponse(defaults={"a": "1"})),
+        )
+
+    def test_merge_priority(self):
+        response = ConfigResponse(
+            defaults={"only-default": "d", "shared": "from-defaults", 
"forced": "from-defaults"},
+            overrides={"forced": "from-overrides", "only-override": "o"},
+        )
+        merged = response.merge(Options({"shared": "from-client", "forced": 
"from-client"}))
+        self.assertEqual(
+            {
+                "only-default": "d",
+                # client options win over server defaults
+                "shared": "from-client",
+                # server overrides win over client options
+                "forced": "from-overrides",
+                "only-override": "o",
+            },
+            merged.to_map(),
+        )
+
+    def test_merge_does_not_mutate_client_options(self):
+        options = Options({"shared": "from-client"})
+        response = ConfigResponse(defaults={"a": "1"}, overrides={"shared": 
"from-overrides"})
+        response.merge(options)
+        self.assertEqual({"shared": "from-client"}, options.to_map())
+
+    def test_merge_filters_none_values(self):
+        response = ConfigResponse(
+            defaults={"from-defaults": None, "reset-by-override": "1"},
+            overrides={"from-overrides": None, "reset-by-override": None},
+        )
+        merged = response.merge(Options({"from-client": None, "kept": "v"}))
+        self.assertEqual({"kept": "v"}, merged.to_map())
+
+    def test_merge_enables_data_token_from_overrides(self):
+        response = ConfigResponse(
+            defaults={},
+            overrides={CatalogOptions.DATA_TOKEN_ENABLED.key(): "true"},
+        )
+        merged = 
response.merge(Options({CatalogOptions.DATA_TOKEN_ENABLED.key(): "false"}))
+        self.assertTrue(merged.get(CatalogOptions.DATA_TOKEN_ENABLED))

Reply via email to