@@ -92,8 +92,6 @@ class Endpoint(IcebergBaseModel):
9292 @field_validator ("path" , mode = "before" )
9393 @classmethod
9494 def _validate_path (cls , raw_path : str ) -> str :
95- if not raw_path :
96- raise ValueError ("Invalid path: empty" )
9795 raw_path = raw_path .strip ()
9896 if not raw_path :
9997 raise ValueError ("Invalid path: empty" )
@@ -104,10 +102,8 @@ def __str__(self) -> str:
104102 return f"{ self .http_method .value } { self .path } "
105103
106104 @classmethod
107- def from_string (cls , endpoint : str | None ) -> "Endpoint" :
108- if endpoint is None :
109- raise ValueError ("Invalid endpoint (must consist of 'METHOD /path'): None" )
110- elements = endpoint .split (None , 1 )
105+ def from_string (cls , endpoint : str ) -> "Endpoint" :
106+ elements = endpoint .strip ().split (None , 1 )
111107 if len (elements ) != 2 :
112108 raise ValueError (f"Invalid endpoint (must consist of two elements separated by a single space): { endpoint } " )
113109 return cls (http_method = HttpMethod (elements [0 ].upper ()), path = elements [1 ])
@@ -137,36 +133,31 @@ class Endpoints:
137133 fetch_scan_tasks : str = "namespaces/{namespace}/tables/{table}/tasks"
138134
139135
136+ API_PREFIX = "/v1/{prefix}"
137+
138+
140139class Capability :
141- V1_LIST_NAMESPACES = Endpoint (http_method = HttpMethod .GET , path = "/v1/{prefix}/namespaces" )
142- V1_LOAD_NAMESPACE = Endpoint (http_method = HttpMethod .GET , path = "/v1/{prefix}/namespaces/{namespace}" )
143- V1_NAMESPACE_EXISTS = Endpoint (http_method = HttpMethod .HEAD , path = "/v1/{prefix}/namespaces/{namespace}" )
144- V1_UPDATE_NAMESPACE = Endpoint (http_method = HttpMethod .POST , path = "/v1/{prefix}/namespaces/{namespace}/properties" )
145- V1_CREATE_NAMESPACE = Endpoint (http_method = HttpMethod .POST , path = "/v1/{prefix}/namespaces" )
146- V1_DELETE_NAMESPACE = Endpoint (http_method = HttpMethod .DELETE , path = "/v1/{prefix}/namespaces/{namespace}" )
147-
148- V1_LIST_TABLES = Endpoint (http_method = HttpMethod .GET , path = "/v1/{prefix}/namespaces/{namespace}/tables" )
149- V1_LOAD_TABLE = Endpoint (http_method = HttpMethod .GET , path = "/v1/{prefix}/namespaces/{namespace}/tables/{table}" )
150- V1_TABLE_EXISTS = Endpoint (http_method = HttpMethod .HEAD , path = "/v1/{prefix}/namespaces/{namespace}/tables/{table}" )
151- V1_CREATE_TABLE = Endpoint (http_method = HttpMethod .POST , path = "/v1/{prefix}/namespaces/{namespace}/tables" )
152- V1_UPDATE_TABLE = Endpoint (http_method = HttpMethod .POST , path = "/v1/{prefix}/namespaces/{namespace}/tables/{table}" )
153- V1_DELETE_TABLE = Endpoint (http_method = HttpMethod .DELETE , path = "/v1/{prefix}/namespaces/{namespace}/tables/{table}" )
154- V1_RENAME_TABLE = Endpoint (http_method = HttpMethod .POST , path = "/v1/{prefix}/tables/rename" )
155- V1_REGISTER_TABLE = Endpoint (http_method = HttpMethod .POST , path = "/v1/{prefix}/namespaces/{namespace}/register" )
156-
157- V1_LIST_VIEWS = Endpoint (http_method = HttpMethod .GET , path = "/v1/{prefix}/namespaces/{namespace}/views" )
158- V1_LOAD_VIEW = Endpoint (http_method = HttpMethod .GET , path = "/v1/{prefix}/namespaces/{namespace}/views/{view}" )
159- V1_VIEW_EXISTS = Endpoint (http_method = HttpMethod .HEAD , path = "/v1/{prefix}/namespaces/{namespace}/views/{view}" )
160- V1_CREATE_VIEW = Endpoint (http_method = HttpMethod .POST , path = "/v1/{prefix}/namespaces/{namespace}/views" )
161- V1_UPDATE_VIEW = Endpoint (http_method = HttpMethod .POST , path = "/v1/{prefix}/namespaces/{namespace}/views/{view}" )
162- V1_DELETE_VIEW = Endpoint (http_method = HttpMethod .DELETE , path = "/v1/{prefix}/namespaces/{namespace}/views/{view}" )
163- V1_RENAME_VIEW = Endpoint (http_method = HttpMethod .POST , path = "/v1/{prefix}/views/rename" )
164- V1_SUBMIT_TABLE_SCAN_PLAN = Endpoint (
165- http_method = HttpMethod .POST , path = "/v1/{prefix}/namespaces/{namespace}/tables/{table}/plan"
166- )
167- V1_TABLE_SCAN_PLAN_TASKS = Endpoint (
168- http_method = HttpMethod .POST , path = "/v1/{prefix}/namespaces/{namespace}/tables/{table}/tasks"
169- )
140+ V1_LIST_NAMESPACES = Endpoint (http_method = HttpMethod .GET , path = f"{ API_PREFIX } /{ Endpoints .list_namespaces } " )
141+ V1_LOAD_NAMESPACE = Endpoint (http_method = HttpMethod .GET , path = f"{ API_PREFIX } /{ Endpoints .load_namespace_metadata } " )
142+ V1_NAMESPACE_EXISTS = Endpoint (http_method = HttpMethod .HEAD , path = f"{ API_PREFIX } /{ Endpoints .namespace_exists } " )
143+ V1_UPDATE_NAMESPACE = Endpoint (http_method = HttpMethod .POST , path = f"{ API_PREFIX } /{ Endpoints .update_namespace_properties } " )
144+ V1_CREATE_NAMESPACE = Endpoint (http_method = HttpMethod .POST , path = f"{ API_PREFIX } /{ Endpoints .create_namespace } " )
145+ V1_DELETE_NAMESPACE = Endpoint (http_method = HttpMethod .DELETE , path = f"{ API_PREFIX } /{ Endpoints .drop_namespace } " )
146+
147+ V1_LIST_TABLES = Endpoint (http_method = HttpMethod .GET , path = f"{ API_PREFIX } /{ Endpoints .list_tables } " )
148+ V1_LOAD_TABLE = Endpoint (http_method = HttpMethod .GET , path = f"{ API_PREFIX } /{ Endpoints .load_table } " )
149+ V1_TABLE_EXISTS = Endpoint (http_method = HttpMethod .HEAD , path = f"{ API_PREFIX } /{ Endpoints .table_exists } " )
150+ V1_CREATE_TABLE = Endpoint (http_method = HttpMethod .POST , path = f"{ API_PREFIX } /{ Endpoints .create_table } " )
151+ V1_UPDATE_TABLE = Endpoint (http_method = HttpMethod .POST , path = f"{ API_PREFIX } /{ Endpoints .update_table } " )
152+ V1_DELETE_TABLE = Endpoint (http_method = HttpMethod .DELETE , path = f"{ API_PREFIX } /{ Endpoints .drop_table } " )
153+ V1_RENAME_TABLE = Endpoint (http_method = HttpMethod .POST , path = f"{ API_PREFIX } /{ Endpoints .rename_table } " )
154+ V1_REGISTER_TABLE = Endpoint (http_method = HttpMethod .POST , path = f"{ API_PREFIX } /{ Endpoints .register_table } " )
155+
156+ V1_LIST_VIEWS = Endpoint (http_method = HttpMethod .GET , path = f"{ API_PREFIX } /{ Endpoints .list_views } " )
157+ V1_VIEW_EXISTS = Endpoint (http_method = HttpMethod .HEAD , path = f"{ API_PREFIX } /{ Endpoints .view_exists } " )
158+ V1_DELETE_VIEW = Endpoint (http_method = HttpMethod .DELETE , path = f"{ API_PREFIX } /{ Endpoints .drop_view } " )
159+ V1_SUBMIT_TABLE_SCAN_PLAN = Endpoint (http_method = HttpMethod .POST , path = f"{ API_PREFIX } /{ Endpoints .plan_table_scan } " )
160+ V1_TABLE_SCAN_PLAN_TASKS = Endpoint (http_method = HttpMethod .POST , path = f"{ API_PREFIX } /{ Endpoints .fetch_scan_tasks } " )
170161
171162
172163# Default endpoints for backwards compatibility with legacy servers that don't return endpoints
@@ -231,6 +222,8 @@ class IdentifierKind(Enum):
231222CUSTOM = "custom"
232223REST_SCAN_PLANNING_ENABLED = "rest-scan-planning-enabled"
233224REST_SCAN_PLANNING_ENABLED_DEFAULT = False
225+ # for backwards compatibility with older REST servers where it can be assumed that a particular
226+ # server supports view endpoints but doesn't send the "endpoints" field in the ConfigResponse
234227VIEW_ENDPOINTS_SUPPORTED = "view-endpoints-supported"
235228VIEW_ENDPOINTS_SUPPORTED_DEFAULT = False
236229
@@ -966,7 +959,7 @@ def update_namespace_properties(
966959 def namespace_exists (self , namespace : str | Identifier ) -> bool :
967960 namespace_tuple = self ._check_valid_namespace_identifier (namespace )
968961 namespace = NAMESPACE_SEPARATOR .join (namespace_tuple )
969-
962+ # fallback in order to work with older rest catalog implementations
970963 if Capability .V1_NAMESPACE_EXISTS not in self ._supported_endpoints :
971964 try :
972965 self .load_namespace_properties (namespace_tuple )
@@ -998,6 +991,7 @@ def table_exists(self, identifier: str | Identifier) -> bool:
998991 Returns:
999992 bool: True if the table exists, False otherwise.
1000993 """
994+ # fallback in order to work with older rest catalog implementations
1001995 if Capability .V1_TABLE_EXISTS not in self ._supported_endpoints :
1002996 try :
1003997 self .load_table (identifier )
0 commit comments