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

lzljs3620320 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 8ee17fc3bd [python] Change Schema to TableSchema in Class 
GetTableResponse.  (#5990)
8ee17fc3bd is described below

commit 8ee17fc3bdb78ebd98e41fa6887cb27bc6057ea2
Author: HeavenZH <[email protected]>
AuthorDate: Thu Jul 31 10:29:22 2025 +0800

    [python] Change Schema to TableSchema in Class GetTableResponse.  (#5990)
---
 paimon-python/pypaimon/api/__init__.py            |  3 +-
 paimon-python/pypaimon/api/api_response.py        | 24 +++---
 paimon-python/pypaimon/api/api_resquest.py        |  2 +-
 paimon-python/pypaimon/common/rest_json.py        |  2 +-
 paimon-python/pypaimon/rest/rest_catalog.py       |  3 +-
 paimon-python/pypaimon/schema/table_schema.py     | 26 +++++++
 paimon-python/pypaimon/tests/api_test.py          | 59 ---------------
 paimon-python/pypaimon/tests/rest_catalog_test.py | 90 +++++++++++++++++++++++
 paimon-python/pypaimon/tests/rest_server.py       | 11 +--
 9 files changed, 139 insertions(+), 81 deletions(-)

diff --git a/paimon-python/pypaimon/api/__init__.py 
b/paimon-python/pypaimon/api/__init__.py
index 2b35cd12df..8e05df6c01 100644
--- a/paimon-python/pypaimon/api/__init__.py
+++ b/paimon-python/pypaimon/api/__init__.py
@@ -27,7 +27,7 @@ from pypaimon.api.api_response import (
     GetDatabaseResponse,
     ConfigResponse,
     PagedResponse,
-    GetTableTokenResponse, Schema,
+    GetTableTokenResponse,
 )
 from pypaimon.api.api_resquest import CreateDatabaseRequest, 
AlterDatabaseRequest, RenameTableRequest, \
     CreateTableRequest
@@ -35,6 +35,7 @@ from pypaimon.api.config import CatalogOptions
 from pypaimon.api.client import HttpClient
 from pypaimon.api.identifier import Identifier
 from pypaimon.api.typedef import T
+from pypaimon.schema.schema import Schema
 
 
 class RESTException(Exception):
diff --git a/paimon-python/pypaimon/api/api_response.py 
b/paimon-python/pypaimon/api/api_response.py
index b50754b3cf..9709c4f05c 100644
--- a/paimon-python/pypaimon/api/api_response.py
+++ b/paimon-python/pypaimon/api/api_response.py
@@ -22,7 +22,7 @@ from dataclasses import dataclass
 
 from pypaimon.common.rest_json import json_field
 from .typedef import T
-from .. import Schema
+from ..schema.table_schema import TableSchema
 
 
 @dataclass
@@ -151,15 +151,15 @@ class GetTableResponse(AuditRESTResponse):
     FIELD_NAME = "name"
     FIELD_PATH = "path"
     FIELD_IS_EXTERNAL = "isExternal"
-    FIELD_SCHEMA_ID = "schemaId"
-    FIELD_SCHEMA = "schema"
+    FIELD_TABLE_SCHEMA_ID = "tableSchemaId"
+    FIELD_TABLE_SCHEMA = "tableSchema"
 
     id: Optional[str] = json_field(FIELD_ID, default=None)
     name: Optional[str] = json_field(FIELD_NAME, default=None)
     path: Optional[str] = json_field(FIELD_PATH, default=None)
     is_external: Optional[bool] = json_field(FIELD_IS_EXTERNAL, default=None)
-    schema_id: Optional[int] = json_field(FIELD_SCHEMA_ID, default=None)
-    schema: Optional[Schema] = json_field(FIELD_SCHEMA, default=None)
+    table_schema_id: Optional[int] = json_field(FIELD_TABLE_SCHEMA_ID, 
default=None)
+    table_schema: Optional[TableSchema] = json_field(FIELD_TABLE_SCHEMA, 
default=None)
 
     def __init__(
             self,
@@ -167,8 +167,8 @@ class GetTableResponse(AuditRESTResponse):
             name: str,
             path: str,
             is_external: bool,
-            schema_id: int,
-            schema: Schema,
+            table_schema_id: int,
+            table_schema: TableSchema,
             owner: Optional[str] = None,
             created_at: Optional[int] = None,
             created_by: Optional[str] = None,
@@ -180,8 +180,8 @@ class GetTableResponse(AuditRESTResponse):
         self.name = name
         self.path = path
         self.is_external = is_external
-        self.schema_id = schema_id
-        self.schema = schema
+        self.table_schema_id = table_schema_id
+        self.table_schema = table_schema
 
     def get_id(self) -> str:
         return self.id
@@ -196,10 +196,10 @@ class GetTableResponse(AuditRESTResponse):
         return self.is_external
 
     def get_schema_id(self) -> int:
-        return self.schema_id
+        return self.table_schema_id
 
-    def get_schema(self) -> Schema:
-        return self.schema
+    def get_schema(self) -> TableSchema:
+        return self.table_schema
 
 
 @dataclass
diff --git a/paimon-python/pypaimon/api/api_resquest.py 
b/paimon-python/pypaimon/api/api_resquest.py
index 1ddc3ebe5a..dfc517ae89 100644
--- a/paimon-python/pypaimon/api/api_resquest.py
+++ b/paimon-python/pypaimon/api/api_resquest.py
@@ -20,9 +20,9 @@ from abc import ABC
 from dataclasses import dataclass
 from typing import Dict, List
 
-from .api_response import Schema
 from .identifier import Identifier
 from pypaimon.common.rest_json import json_field
+from ..schema.schema import Schema
 
 
 class RESTRequest(ABC):
diff --git a/paimon-python/pypaimon/common/rest_json.py 
b/paimon-python/pypaimon/common/rest_json.py
index b944bb625a..b6db836fc1 100644
--- a/paimon-python/pypaimon/common/rest_json.py
+++ b/paimon-python/pypaimon/common/rest_json.py
@@ -87,7 +87,7 @@ class JSON:
         for json_name, value in data.items():
             if json_name in field_mapping:
                 field_name = field_mapping[json_name]
-                if field_name in type_mapping:
+                if json_name in type_mapping:
                     kwargs[field_name] = JSON.__from_dict(value, 
type_mapping[json_name])
                 else:
                     kwargs[field_name] = value
diff --git a/paimon-python/pypaimon/rest/rest_catalog.py 
b/paimon-python/pypaimon/rest/rest_catalog.py
index 2b66ea9981..32b5c5c7ad 100644
--- a/paimon-python/pypaimon/rest/rest_catalog.py
+++ b/paimon-python/pypaimon/rest/rest_catalog.py
@@ -25,7 +25,6 @@ from pypaimon.api.core_options import CoreOptions
 from pypaimon.api.identifier import Identifier
 from pypaimon.api.options import Options
 
-from pypaimon.schema.table_schema import TableSchema
 
 from pypaimon.catalog.catalog_context import CatalogContext
 from pypaimon.catalog.catalog_utils import CatalogUtils
@@ -102,7 +101,7 @@ class RESTCatalog(Catalog):
         return self.to_table_metadata(identifier.get_database_name(), response)
 
     def to_table_metadata(self, db: str, response: GetTableResponse) -> 
TableMetadata:
-        schema = TableSchema.from_schema(response.get_schema_id(), 
response.get_schema())
+        schema = response.get_schema()
         options: Dict[str, str] = dict(schema.options)
         options[CoreOptions.PATH] = response.get_path()
         response.put_audit_options_to(options)
diff --git a/paimon-python/pypaimon/schema/table_schema.py 
b/paimon-python/pypaimon/schema/table_schema.py
index 7d3f6aea64..635cb3464d 100644
--- a/paimon-python/pypaimon/schema/table_schema.py
+++ b/paimon-python/pypaimon/schema/table_schema.py
@@ -15,25 +15,51 @@ 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.
 """
+
 import json
 import time
+from dataclasses import dataclass
 from pathlib import Path
 from typing import List, Dict, Optional
 
 import pyarrow
 
 from pypaimon import Schema
+from pypaimon.common.rest_json import json_field
 from pypaimon.schema import data_types
 from pypaimon.api.core_options import CoreOptions
 from pypaimon.common.file_io import FileIO
 from pypaimon.schema.data_types import DataField
 
 
+@dataclass
 class TableSchema:
     PAIMON_07_VERSION = 1
     PAIMON_08_VERSION = 2
     CURRENT_VERSION = 3
 
+    FIELD_VERSION = "version"
+    FIELD_ID = "id"
+    FIELD_FIELDS = "fields"
+    FIELD_HEIGHEST_FIELD_ID = "highestFieldId"
+    FIELD_PARTITION_KEYS = "partitionKeys"
+    FIELD_PRIMARY_KEYS = "primaryKeys"
+    FIELD_OPTIONS = "options"
+    FIELD_COMMENT = "comment"
+    FIELD_TIME_MILLIS = "timeMillis"
+
+    version: int = json_field(FIELD_VERSION, default=CURRENT_VERSION)
+    id: int = json_field(FIELD_ID, default=0)
+    fields: List[DataField] = json_field(FIELD_FIELDS, default_factory=list)
+    highest_field_id: int = json_field("highestFieldId", default=0)
+    partition_keys: List[str] = json_field(
+        FIELD_PARTITION_KEYS, default_factory=list)
+    primary_keys: List[str] = json_field(
+        FIELD_PRIMARY_KEYS, default_factory=list)
+    options: Dict[str, str] = json_field(FIELD_OPTIONS, default_factory=dict)
+    comment: Optional[str] = json_field(FIELD_COMMENT, default=None)
+    time_millis: int = json_field("timeMillis", default_factory=lambda: 
int(time.time() * 1000))
+
     def __init__(self, version: int, id: int, fields: List[DataField], 
highest_field_id: int,
                  partition_keys: List[str], primary_keys: List[str], options: 
Dict[str, str],
                  comment: Optional[str] = None, time_millis: Optional[int] = 
None):
diff --git a/paimon-python/pypaimon/tests/api_test.py 
b/paimon-python/pypaimon/tests/api_test.py
index 9a604dad60..b84bc405b5 100644
--- a/paimon-python/pypaimon/tests/api_test.py
+++ b/paimon-python/pypaimon/tests/api_test.py
@@ -25,15 +25,12 @@ from ..api.api_response import (ConfigResponse)
 from ..api import RESTApi
 from ..api.auth import BearTokenAuthProvider
 from ..api.identifier import Identifier
-from ..api.options import Options
 from pypaimon.common.rest_json import JSON
 from pypaimon.schema.table_schema import TableSchema
 from ..api.token_loader import DLFTokenLoaderFactory, DLFToken
 
 from pypaimon.schema.data_types import AtomicInteger, DataTypeParser, 
AtomicType, ArrayType, MapType, RowType, DataField
-from ..catalog.catalog_context import CatalogContext
 from ..catalog.table_metadata import TableMetadata
-from ..rest.rest_catalog import RESTCatalog
 
 
 class ApiTestCase(unittest.TestCase):
@@ -182,62 +179,6 @@ class ApiTestCase(unittest.TestCase):
             server.shutdown()
             print("Server stopped")
 
-    def test_rest_catalog(self):
-        """Example usage of RESTCatalogServer"""
-        # Setup logging
-        logging.basicConfig(level=logging.INFO)
-
-        # Create config
-        config = ConfigResponse(defaults={"prefix": "mock-test"})
-        token = str(uuid.uuid4())
-        # Create server
-        server = RESTCatalogServer(
-            data_path="/tmp/test_warehouse",
-            auth_provider=BearTokenAuthProvider(token),
-            config=config,
-            warehouse="test_warehouse"
-        )
-        try:
-            # Start server
-            server.start()
-            print(f"Server started at: {server.get_url()}")
-            test_databases = {
-                "default": server.mock_database("default", {"env": "test"}),
-                "test_db1": server.mock_database("test_db1", {"env": "test"}),
-                "test_db2": server.mock_database("test_db2", {"env": "test"}),
-                "prod_db": server.mock_database("prod_db", {"env": "prod"})
-            }
-            data_fields = [
-                DataField(0, "name", AtomicType('INT'), 'desc  name'),
-                DataField(1, "arr11", ArrayType(True, AtomicType('INT')), 
'desc  arr11'),
-                DataField(2, "map11", MapType(False, AtomicType('INT'),
-                                              MapType(False, 
AtomicType('INT'), AtomicType('INT'))),
-                          'desc  arr11'),
-            ]
-            schema = TableSchema(TableSchema.CURRENT_VERSION, 
len(data_fields), data_fields, len(data_fields),
-                                 [], [], {}, "")
-            test_tables = {
-                "default.user": TableMetadata(uuid=str(uuid.uuid4()), 
is_external=True, schema=schema),
-            }
-            server.table_metadata_store.update(test_tables)
-            server.database_store.update(test_databases)
-            options = {
-                'uri': f"http://localhost:{server.port}";,
-                'warehouse': 'test_warehouse',
-                'dlf.region': 'cn-hangzhou',
-                "token.provider": "bear",
-                'token': token
-            }
-            rest_catalog = 
RESTCatalog(CatalogContext.create_from_options(Options(options)))
-            self.assertSetEqual(set(rest_catalog.list_databases()), 
{*test_databases})
-            self.assertEqual(rest_catalog.get_database('default').name, 
test_databases.get('default').name)
-            table = 
rest_catalog.get_table(Identifier.from_string('default.user'))
-            self.assertEqual(table.identifier.get_full_name(), 'default.user')
-        finally:
-            # Shutdown server
-            server.shutdown()
-            print("Server stopped")
-
     def test_ecs_loader_token(self):
         token = DLFToken(
             access_key_id='AccessKeyId',
diff --git a/paimon-python/pypaimon/tests/rest_catalog_test.py 
b/paimon-python/pypaimon/tests/rest_catalog_test.py
new file mode 100644
index 0000000000..be5989a171
--- /dev/null
+++ b/paimon-python/pypaimon/tests/rest_catalog_test.py
@@ -0,0 +1,90 @@
+"""
+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.
+"""
+import logging
+import unittest
+import uuid
+
+from pypaimon.api import ConfigResponse, Identifier
+from pypaimon.api.auth import BearTokenAuthProvider
+
+from pypaimon.api.options import Options
+from pypaimon.catalog.catalog_context import CatalogContext
+from pypaimon.catalog.table_metadata import TableMetadata
+from pypaimon.rest.rest_catalog import RESTCatalog
+from pypaimon.schema.data_types import DataField, ArrayType, AtomicType, 
MapType
+from pypaimon.schema.table_schema import TableSchema
+from pypaimon.tests.rest_server import RESTCatalogServer
+
+
+class RESTCatalogTestCase(unittest.TestCase):
+
+    def test_rest_catalog(self):
+        """Example usage of RESTCatalogServer"""
+        # Setup logging
+        logging.basicConfig(level=logging.INFO)
+
+        # Create config
+        config = ConfigResponse(defaults={"prefix": "mock-test"})
+        token = str(uuid.uuid4())
+        # Create server
+        server = RESTCatalogServer(
+            data_path="/tmp/test_warehouse",
+            auth_provider=BearTokenAuthProvider(token),
+            config=config,
+            warehouse="test_warehouse"
+        )
+        try:
+            # Start server
+            server.start()
+            print(f"Server started at: {server.get_url()}")
+            test_databases = {
+                "default": server.mock_database("default", {"env": "test"}),
+                "test_db1": server.mock_database("test_db1", {"env": "test"}),
+                "test_db2": server.mock_database("test_db2", {"env": "test"}),
+                "prod_db": server.mock_database("prod_db", {"env": "prod"})
+            }
+            data_fields = [
+                DataField(0, "name", AtomicType('INT'), 'desc  name'),
+                DataField(1, "arr11", ArrayType(True, AtomicType('INT')), 
'desc  arr11'),
+                DataField(2, "map11", MapType(False, AtomicType('INT'),
+                                              MapType(False, 
AtomicType('INT'), AtomicType('INT'))),
+                          'desc  arr11'),
+            ]
+            schema = TableSchema(TableSchema.CURRENT_VERSION, 
len(data_fields), data_fields, len(data_fields),
+                                 [], [], {}, "")
+            test_tables = {
+                "default.user": TableMetadata(uuid=str(uuid.uuid4()), 
is_external=True, schema=schema),
+            }
+            server.table_metadata_store.update(test_tables)
+            server.database_store.update(test_databases)
+            options = {
+                'uri': f"http://localhost:{server.port}";,
+                'warehouse': 'test_warehouse',
+                'dlf.region': 'cn-hangzhou',
+                "token.provider": "bear",
+                'token': token
+            }
+            rest_catalog = 
RESTCatalog(CatalogContext.create_from_options(Options(options)))
+            self.assertSetEqual(set(rest_catalog.list_databases()), 
{*test_databases})
+            self.assertEqual(rest_catalog.get_database('default').name, 
test_databases.get('default').name)
+            table = 
rest_catalog.get_table(Identifier.from_string('default.user'))
+            self.assertEqual(table.identifier.get_full_name(), 'default.user')
+        finally:
+            # Shutdown server
+            server.shutdown()
+            print("Server stopped")
diff --git a/paimon-python/pypaimon/tests/rest_server.py 
b/paimon-python/pypaimon/tests/rest_server.py
index 44be0e9e42..2070acd626 100644
--- a/paimon-python/pypaimon/tests/rest_server.py
+++ b/paimon-python/pypaimon/tests/rest_server.py
@@ -30,13 +30,14 @@ from urllib.parse import urlparse
 import pypaimon.api as api
 from ..api import RenameTableRequest, CreateTableRequest, 
CreateDatabaseRequest, Identifier
 from ..api.api_response import (ConfigResponse, ListDatabasesResponse, 
GetDatabaseResponse,
-                                Schema, GetTableResponse, ListTablesResponse,
+                                GetTableResponse, ListTablesResponse,
                                 RESTResponse, PagedList)
 from pypaimon.common.rest_json import JSON
 from pypaimon.schema.table_schema import TableSchema
 from ..catalog.catalog_exception import DatabaseNoPermissionException, 
TableNotExistException, \
     DatabaseNotExistException, TableNoPermissionException
 from ..catalog.table_metadata import TableMetadata
+from ..schema.schema import Schema
 
 
 @dataclass
@@ -394,7 +395,7 @@ class RESTCatalogServer:
                 raise TableNotExistException(identifier)
             table_metadata = 
self.table_metadata_store[identifier.get_full_name()]
             table_path = 
f'file://{self.data_path}/{self.warehouse}/{identifier.database_name}/{identifier.object_name}'
-            schema = table_metadata.schema.to_schema()
+            schema = table_metadata.schema
             response = self.mock_table(identifier, table_metadata, table_path, 
schema)
             return self._mock_response(response, 200)
         #
@@ -593,14 +594,14 @@ class RESTCatalogServer:
         )
 
     def mock_table(self, identifier: Identifier, table_metadata: 
TableMetadata, path: str,
-                   schema: Schema) -> GetTableResponse:
+                   schema: TableSchema) -> GetTableResponse:
         return GetTableResponse(
             id=str(table_metadata.uuid),
             name=identifier.get_object_name(),
             path=path,
             is_external=table_metadata.is_external,
-            schema_id=table_metadata.schema.id,
-            schema=schema,
+            table_schema_id=table_metadata.schema.id,
+            table_schema=schema,
             owner="owner",
             created_at=1,
             created_by="created",

Reply via email to