@@ -42,6 +42,7 @@ def __init__(
4242 created_timestamp_column : Optional [str ] = "" ,
4343 field_mapping : Optional [Dict [str , str ]] = None ,
4444 s3_endpoint_override : Optional [str ] = None ,
45+ s3_url_style : Optional [str ] = None ,
4546 description : Optional [str ] = "" ,
4647 tags : Optional [Dict [str , str ]] = None ,
4748 owner : Optional [str ] = "" ,
@@ -61,6 +62,8 @@ def __init__(
6162 field_mapping: A dictionary mapping of column names in this data source to feature names in a feature table
6263 or view. Only used for feature columns, not entities or timestamp columns.
6364 s3_endpoint_override (optional): Overrides AWS S3 enpoint with custom S3 storage
65+ s3_url_style (optional): S3 URL style for path-based access. Valid values: "path" or "vhost".
66+ Required for S3-compatible storage like MinIO or LocalStack.
6467 description (optional): A human-readable description.
6568 tags (optional): A dictionary of key-value pairs to store arbitrary metadata.
6669 owner (optional): The owner of the file source, typically the email of the primary
@@ -76,6 +79,7 @@ def __init__(
7679 file_format = file_format ,
7780 uri = path ,
7881 s3_endpoint_override = s3_endpoint_override ,
82+ s3_url_style = s3_url_style ,
7983 )
8084
8185 super ().__init__ (
@@ -102,6 +106,7 @@ def __eq__(self, other):
102106 and self .file_options .file_format == other .file_options .file_format
103107 and self .file_options .s3_endpoint_override
104108 == other .file_options .s3_endpoint_override
109+ and self .file_options .s3_url_style == other .file_options .s3_url_style
105110 )
106111
107112 @property
@@ -119,6 +124,11 @@ def s3_endpoint_override(self) -> Optional[str]:
119124 """Returns the s3 endpoint override of this file data source."""
120125 return self .file_options .s3_endpoint_override
121126
127+ @property
128+ def s3_url_style (self ) -> Optional [str ]:
129+ """Returns the s3 URL style of this file data source."""
130+ return self .file_options .s3_url_style
131+
122132 @staticmethod
123133 def from_proto (data_source : DataSourceProto ):
124134 return FileSource (
@@ -129,6 +139,7 @@ def from_proto(data_source: DataSourceProto):
129139 timestamp_field = data_source .timestamp_field ,
130140 created_timestamp_column = data_source .created_timestamp_column ,
131141 s3_endpoint_override = data_source .file_options .s3_endpoint_override ,
142+ s3_url_style = data_source .file_options .s3_url_style or None ,
132143 description = data_source .description ,
133144 tags = dict (data_source .tags ),
134145 owner = data_source .owner ,
@@ -201,6 +212,8 @@ def get_table_column_names_and_types(
201212 storage_options = {
202213 "AWS_ENDPOINT_URL" : str (self .s3_endpoint_override ),
203214 }
215+ if self .s3_url_style :
216+ storage_options ["AWS_S3_URL_STYLE" ] = self .s3_url_style
204217
205218 schema = (
206219 DeltaTable (self .path , storage_options = storage_options )
@@ -235,18 +248,21 @@ class FileOptions:
235248 Attributes:
236249 uri: File source url, e.g. s3:// or local file.
237250 s3_endpoint_override: Custom s3 endpoint (used only with s3 uri).
251+ s3_url_style: S3 URL style for path-based access ("path" or "vhost").
238252 file_format: File source format, e.g. parquet.
239253 """
240254
241255 uri : str
242256 file_format : Optional [FileFormat ]
243257 s3_endpoint_override : str
258+ s3_url_style : str
244259
245260 def __init__ (
246261 self ,
247262 uri : str ,
248263 file_format : Optional [FileFormat ],
249264 s3_endpoint_override : Optional [str ],
265+ s3_url_style : Optional [str ] = None ,
250266 ):
251267 """
252268 Initializes a FileOptions object.
@@ -255,10 +271,12 @@ def __init__(
255271 uri: File source url, e.g. s3:// or local file.
256272 file_format (optional): File source format, e.g. parquet.
257273 s3_endpoint_override (optional): Custom s3 endpoint (used only with s3 uri).
274+ s3_url_style (optional): S3 URL style for path-based access ("path" or "vhost").
258275 """
259276 self .uri = uri
260277 self .file_format = file_format
261278 self .s3_endpoint_override = s3_endpoint_override or ""
279+ self .s3_url_style = s3_url_style or ""
262280
263281 @classmethod
264282 def from_proto (cls , file_options_proto : DataSourceProto .FileOptions ):
@@ -275,6 +293,7 @@ def from_proto(cls, file_options_proto: DataSourceProto.FileOptions):
275293 file_format = FileFormat .from_proto (file_options_proto .file_format ),
276294 uri = file_options_proto .uri ,
277295 s3_endpoint_override = file_options_proto .s3_endpoint_override ,
296+ s3_url_style = file_options_proto .s3_url_style or None ,
278297 )
279298 return file_options
280299
@@ -291,6 +310,7 @@ def to_proto(self) -> DataSourceProto.FileOptions:
291310 ),
292311 uri = self .uri ,
293312 s3_endpoint_override = self .s3_endpoint_override ,
313+ s3_url_style = self .s3_url_style ,
294314 )
295315
296316 return file_options_proto
@@ -306,10 +326,12 @@ def __init__(
306326 path : str ,
307327 file_format : FileFormat = ParquetFormat (),
308328 s3_endpoint_override : Optional [str ] = None ,
329+ s3_url_style : Optional [str ] = None ,
309330 ):
310331 self .file_options = FileOptions (
311332 file_format = file_format ,
312333 s3_endpoint_override = s3_endpoint_override ,
334+ s3_url_style = s3_url_style ,
313335 uri = path ,
314336 )
315337
@@ -320,6 +342,7 @@ def from_proto(storage_proto: SavedDatasetStorageProto) -> SavedDatasetStorage:
320342 path = file_options .uri ,
321343 file_format = file_options .file_format ,
322344 s3_endpoint_override = file_options .s3_endpoint_override ,
345+ s3_url_style = file_options .s3_url_style or None ,
323346 )
324347
325348 def to_proto (self ) -> SavedDatasetStorageProto :
@@ -330,6 +353,7 @@ def to_data_source(self) -> DataSource:
330353 path = self .file_options .uri ,
331354 file_format = self .file_options .file_format ,
332355 s3_endpoint_override = self .file_options .s3_endpoint_override ,
356+ s3_url_style = self .file_options .s3_url_style or None ,
333357 )
334358
335359 @staticmethod
@@ -341,6 +365,7 @@ def from_data_source(data_source: DataSource) -> "SavedDatasetStorage":
341365 if data_source .file_format
342366 else ParquetFormat (),
343367 s3_endpoint_override = data_source .s3_endpoint_override ,
368+ s3_url_style = data_source .s3_url_style ,
344369 )
345370
346371
@@ -349,24 +374,28 @@ class FileLoggingDestination(LoggingDestination):
349374
350375 path : str
351376 s3_endpoint_override : str
377+ s3_url_style : str
352378 partition_by : Optional [List [str ]]
353379
354380 def __init__ (
355381 self ,
356382 * ,
357383 path : str ,
358384 s3_endpoint_override = "" ,
385+ s3_url_style : str = "" ,
359386 partition_by : Optional [List [str ]] = None ,
360387 ):
361388 self .path = path
362389 self .s3_endpoint_override = s3_endpoint_override
390+ self .s3_url_style = s3_url_style
363391 self .partition_by = partition_by
364392
365393 @classmethod
366394 def from_proto (cls , config_proto : LoggingConfigProto ) -> "LoggingDestination" :
367395 return FileLoggingDestination (
368396 path = config_proto .file_destination .path ,
369397 s3_endpoint_override = config_proto .file_destination .s3_endpoint_override ,
398+ s3_url_style = config_proto .file_destination .s3_url_style or "" ,
370399 partition_by = list (config_proto .file_destination .partition_by )
371400 if config_proto .file_destination .partition_by
372401 else None ,
@@ -377,6 +406,7 @@ def to_proto(self) -> LoggingConfigProto:
377406 file_destination = LoggingConfigProto .FileDestination (
378407 path = self .path ,
379408 s3_endpoint_override = self .s3_endpoint_override ,
409+ s3_url_style = self .s3_url_style ,
380410 partition_by = self .partition_by ,
381411 )
382412 )
@@ -386,4 +416,5 @@ def to_data_source(self) -> DataSource:
386416 path = self .path ,
387417 file_format = ParquetFormat (),
388418 s3_endpoint_override = self .s3_endpoint_override ,
419+ s3_url_style = self .s3_url_style or None ,
389420 )
0 commit comments