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))