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 f60438742f [python] Remove ExpirationAt from DLF token JSON (#10186)
f60438742f is described below
commit f60438742fd4e8bf1e4013e2e7210192cfd44775
Author: XiaoHongbo <[email protected]>
AuthorDate: Mon Sep 28 21:35:29 2026 +0800
[python] Remove ExpirationAt from DLF token JSON (#10186)
---
paimon-python/pypaimon/api/token_loader.py | 4 ++--
paimon-python/pypaimon/common/json_util.py | 15 +++++++++++++++
.../pypaimon/tests/rest/token_loader_test.py | 19 +++++++++++++++++++
3 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/paimon-python/pypaimon/api/token_loader.py
b/paimon-python/pypaimon/api/token_loader.py
index f76eca4306..15e8035b0d 100644
--- a/paimon-python/pypaimon/api/token_loader.py
+++ b/paimon-python/pypaimon/api/token_loader.py
@@ -29,7 +29,7 @@ from requests.exceptions import RequestException
from pypaimon.api.client import ExponentialRetry
from pypaimon.common.options import Options
from pypaimon.common.options.config import CatalogOptions
-from pypaimon.common.json_util import JSON, json_field
+from pypaimon.common.json_util import JSON, json_field, json_ignore_field
@dataclass
@@ -40,7 +40,7 @@ class DLFToken:
access_key_secret: str = json_field('AccessKeySecret')
security_token: Optional[str] = json_field('SecurityToken')
expiration: Optional[str] = json_field('Expiration')
- expiration_at_millis: Optional[int] = json_field('ExpirationAt',
default=None)
+ expiration_at_millis: Optional[int] = json_ignore_field(default=None)
@staticmethod
def parse_expiration_to_millis(expiration: str) -> int:
diff --git a/paimon-python/pypaimon/common/json_util.py
b/paimon-python/pypaimon/common/json_util.py
index 0ae8c232f9..df9c95427c 100644
--- a/paimon-python/pypaimon/common/json_util.py
+++ b/paimon-python/pypaimon/common/json_util.py
@@ -28,6 +28,12 @@ def json_field(json_name: str, **kwargs):
return field(metadata={"json_name": json_name, **metadata}, **kwargs)
+def json_ignore_field(**kwargs):
+ """Create a field excluded from JSON serialization and deserialization."""
+ metadata = kwargs.pop("metadata", {})
+ return field(metadata={"json_ignore": True, **metadata}, **kwargs)
+
+
def optional_json_field(json_name: str, json_include: str):
"""Create a field with custom JSON name"""
return field(metadata={"json_name": json_name, "json_include":
json_include}, default=None)
@@ -76,6 +82,9 @@ class JSON:
# Otherwise, use dataclass field-by-field serialization
result = {}
for field_info in fields(obj):
+ if field_info.metadata.get("json_ignore", False):
+ continue
+
field_value = getattr(obj, field_info.name)
# Get custom JSON name from metadata
@@ -122,6 +131,9 @@ class JSON:
type_mapping = {}
decoder_mapping = {}
for field_info in fields(target_class):
+ if field_info.metadata.get("json_ignore", False):
+ continue
+
json_name = field_info.metadata.get("json_name", field_info.name)
field_mapping[json_name] = field_info.name
decoder = field_info.metadata.get("decoder")
@@ -168,6 +180,9 @@ class JSON:
kwargs[field_name] = value
for field_info in fields(target_class):
+ if field_info.metadata.get("json_ignore", False):
+ continue
+
json_name = field_info.metadata.get("json_name", field_info.name)
if (
json_name not in data
diff --git a/paimon-python/pypaimon/tests/rest/token_loader_test.py
b/paimon-python/pypaimon/tests/rest/token_loader_test.py
index 1fb1d29f61..23a9d18874 100644
--- a/paimon-python/pypaimon/tests/rest/token_loader_test.py
+++ b/paimon-python/pypaimon/tests/rest/token_loader_test.py
@@ -15,6 +15,7 @@
# specific language governing permissions and limitations
# under the License.
+import json
import tempfile
import unittest
from pathlib import Path
@@ -32,6 +33,24 @@ from pypaimon.common.options.config import CatalogOptions
class DLFLocalFileTokenLoaderTest(unittest.TestCase):
+ def test_expiration_at_millis_is_internal_only(self):
+ token = DLFToken(
+ access_key_id="access-key-id",
+ access_key_secret="access-key-secret",
+ security_token="security-token",
+ expiration="2099-12-01T12:00:00Z",
+ )
+
+ token_json = JSON.to_json(token)
+ self.assertNotIn("ExpirationAt", json.loads(token_json))
+
+ loaded_token = JSON.from_json(
+ '{"AccessKeyId":"ak","AccessKeySecret":"sk",'
+ '"SecurityToken":"sts","ExpirationAt":123}',
+ DLFToken,
+ )
+ self.assertIsNone(loaded_token.expiration_at_millis)
+
def test_load_token_from_configured_path(self):
token = DLFToken(
access_key_id="access-key-id",