5454 from typing_extensions import deprecated # Python 3.12
5555
5656
57+ from urllib .parse import urlparse
58+
5759import pyarrow as pa
5860
5961from datafusion .catalog import (
@@ -611,6 +613,34 @@ def deregister_object_store(self, schema: str, host: str | None = None) -> None:
611613 """
612614 self .ctx .deregister_object_store (schema , host )
613615
616+ def _register_object_store_for_path (
617+ self , path : str | pathlib .Path , store : Any
618+ ) -> None :
619+ """Parse a URL path and register the given object store for its scheme and host.
620+
621+ This is a convenience helper used by methods like
622+ :py:meth:`register_parquet` and :py:meth:`read_parquet` to
623+ automatically register an object store when an ``object_store``
624+ parameter is provided.
625+
626+ Args:
627+ path: A URL-style path (e.g. ``"s3://bucket/key.parquet"``).
628+ store: An object store instance to register.
629+
630+ Raises:
631+ ValueError: If the path does not contain a recognized URL scheme.
632+ """
633+ parsed = urlparse (str (path ))
634+ if not parsed .scheme or not parsed .netloc :
635+ msg = (
636+ f"Cannot determine object store URL from path { path !r} . "
637+ "The path must use a URL scheme (e.g. 's3://bucket/key')."
638+ )
639+ raise ValueError (msg )
640+ scheme = f"{ parsed .scheme } ://"
641+ host = parsed .netloc
642+ self .register_object_store (scheme , store , host = host )
643+
614644 def register_listing_table (
615645 self ,
616646 name : str ,
@@ -1028,6 +1058,7 @@ def register_parquet(
10281058 skip_metadata : bool = True ,
10291059 schema : pa .Schema | None = None ,
10301060 file_sort_order : Sequence [Sequence [SortKey ]] | None = None ,
1061+ object_store : Any | None = None ,
10311062 ) -> None :
10321063 """Register a Parquet file as a table.
10331064
@@ -1049,7 +1080,41 @@ def register_parquet(
10491080 file_sort_order: Sort order for the file. Each sort key can be
10501081 specified as a column name (``str``), an expression
10511082 (``Expr``), or a ``SortExpr``.
1052- """
1083+ object_store: A pre-configured object store instance (e.g.
1084+ :py:class:`~datafusion.object_store.AmazonS3`,
1085+ :py:class:`~datafusion.object_store.GoogleCloud`,
1086+ :py:class:`~datafusion.object_store.MicrosoftAzure`) to use
1087+ for accessing the file. When provided, the store is
1088+ automatically registered for the URL scheme and host parsed
1089+ from ``path``, removing the need to call
1090+ :py:meth:`register_object_store` separately. This is
1091+ especially useful in multi-threaded environments where
1092+ setting credentials via ``os.environ`` is not thread-safe.
1093+
1094+ Examples:
1095+ Register a local Parquet file:
1096+
1097+ >>> import datafusion
1098+ >>> ctx = datafusion.SessionContext()
1099+ >>> ctx.register_parquet("my_table", "data.parquet")
1100+
1101+ Register from S3 with inline credentials (thread-safe):
1102+
1103+ >>> from datafusion.object_store import AmazonS3 # doctest: +SKIP
1104+ >>> store = AmazonS3(
1105+ ... bucket_name="my-bucket",
1106+ ... region="us-east-1",
1107+ ... access_key_id="...",
1108+ ... secret_access_key="...",
1109+ ... ) # doctest: +SKIP
1110+ >>> ctx.register_parquet(
1111+ ... "my_table",
1112+ ... "s3://my-bucket/data.parquet",
1113+ ... object_store=store,
1114+ ... ) # doctest: +SKIP
1115+ """
1116+ if object_store is not None :
1117+ self ._register_object_store_for_path (path , object_store )
10531118 if table_partition_cols is None :
10541119 table_partition_cols = []
10551120 table_partition_cols = _convert_table_partition_cols (table_partition_cols )
@@ -1075,6 +1140,7 @@ def register_csv(
10751140 file_extension : str = ".csv" ,
10761141 file_compression_type : str | None = None ,
10771142 options : CsvReadOptions | None = None ,
1143+ object_store : Any | None = None ,
10781144 ) -> None :
10791145 """Register a CSV file as a table.
10801146
@@ -1096,7 +1162,14 @@ def register_csv(
10961162 file_compression_type: File compression type.
10971163 options: Set advanced options for CSV reading. This cannot be
10981164 combined with any of the other options in this method.
1099- """
1165+ object_store: A pre-configured object store instance to use for
1166+ accessing the file. When provided, the store is automatically
1167+ registered for the URL scheme and host parsed from ``path``.
1168+ """
1169+ if object_store is not None :
1170+ # For list paths, register from the first entry
1171+ register_path = path [0 ] if isinstance (path , list ) else path
1172+ self ._register_object_store_for_path (register_path , object_store )
11001173 if options is not None and (
11011174 schema is not None
11021175 or not has_header
@@ -1143,6 +1216,7 @@ def register_json(
11431216 file_extension : str = ".json" ,
11441217 table_partition_cols : list [tuple [str , str | pa .DataType ]] | None = None ,
11451218 file_compression_type : str | None = None ,
1219+ object_store : Any | None = None ,
11461220 ) -> None :
11471221 """Register a JSON file as a table.
11481222
@@ -1159,7 +1233,12 @@ def register_json(
11591233 selected for data input.
11601234 table_partition_cols: Partition columns.
11611235 file_compression_type: File compression type.
1236+ object_store: A pre-configured object store instance to use for
1237+ accessing the file. When provided, the store is automatically
1238+ registered for the URL scheme and host parsed from ``path``.
11621239 """
1240+ if object_store is not None :
1241+ self ._register_object_store_for_path (path , object_store )
11631242 if table_partition_cols is None :
11641243 table_partition_cols = []
11651244 table_partition_cols = _convert_table_partition_cols (table_partition_cols )
@@ -1180,6 +1259,7 @@ def register_avro(
11801259 schema : pa .Schema | None = None ,
11811260 file_extension : str = ".avro" ,
11821261 table_partition_cols : list [tuple [str , str | pa .DataType ]] | None = None ,
1262+ object_store : Any | None = None ,
11831263 ) -> None :
11841264 """Register an Avro file as a table.
11851265
@@ -1192,7 +1272,12 @@ def register_avro(
11921272 schema: The data source schema.
11931273 file_extension: File extension to select.
11941274 table_partition_cols: Partition columns.
1275+ object_store: A pre-configured object store instance to use for
1276+ accessing the file. When provided, the store is automatically
1277+ registered for the URL scheme and host parsed from ``path``.
11951278 """
1279+ if object_store is not None :
1280+ self ._register_object_store_for_path (path , object_store )
11961281 if table_partition_cols is None :
11971282 table_partition_cols = []
11981283 table_partition_cols = _convert_table_partition_cols (table_partition_cols )
@@ -1205,6 +1290,7 @@ def register_arrow(
12051290 schema : pa .Schema | None = None ,
12061291 file_extension : str = ".arrow" ,
12071292 table_partition_cols : list [tuple [str , str | pa .DataType ]] | None = None ,
1293+ object_store : Any | None = None ,
12081294 ) -> None :
12091295 """Register an Arrow IPC file as a table.
12101296
@@ -1217,6 +1303,9 @@ def register_arrow(
12171303 schema: The data source schema.
12181304 file_extension: File extension to select.
12191305 table_partition_cols: Partition columns.
1306+ object_store: A pre-configured object store instance to use for
1307+ accessing the file. When provided, the store is automatically
1308+ registered for the URL scheme and host parsed from ``path``.
12201309
12211310 Examples:
12221311 >>> import tempfile, os
@@ -1271,6 +1360,8 @@ def register_arrow(
12711360 30
12721361 ]
12731362 """
1363+ if object_store is not None :
1364+ self ._register_object_store_for_path (path , object_store )
12741365 if table_partition_cols is None :
12751366 table_partition_cols = []
12761367 table_partition_cols = _convert_table_partition_cols (table_partition_cols )
@@ -1690,6 +1781,7 @@ def read_json(
16901781 file_extension : str = ".json" ,
16911782 table_partition_cols : list [tuple [str , str | pa .DataType ]] | None = None ,
16921783 file_compression_type : str | None = None ,
1784+ object_store : Any | None = None ,
16931785 ) -> DataFrame :
16941786 """Read a line-delimited JSON data source.
16951787
@@ -1702,10 +1794,15 @@ def read_json(
17021794 selected for data input.
17031795 table_partition_cols: Partition columns.
17041796 file_compression_type: File compression type.
1797+ object_store: A pre-configured object store instance to use for
1798+ accessing the file. When provided, the store is automatically
1799+ registered for the URL scheme and host parsed from ``path``.
17051800
17061801 Returns:
17071802 DataFrame representation of the read JSON files.
17081803 """
1804+ if object_store is not None :
1805+ self ._register_object_store_for_path (path , object_store )
17091806 if table_partition_cols is None :
17101807 table_partition_cols = []
17111808 table_partition_cols = _convert_table_partition_cols (table_partition_cols )
@@ -1731,6 +1828,7 @@ def read_csv(
17311828 table_partition_cols : list [tuple [str , str | pa .DataType ]] | None = None ,
17321829 file_compression_type : str | None = None ,
17331830 options : CsvReadOptions | None = None ,
1831+ object_store : Any | None = None ,
17341832 ) -> DataFrame :
17351833 """Read a CSV data source.
17361834
@@ -1750,10 +1848,16 @@ def read_csv(
17501848 file_compression_type: File compression type.
17511849 options: Set advanced options for CSV reading. This cannot be
17521850 combined with any of the other options in this method.
1851+ object_store: A pre-configured object store instance to use for
1852+ accessing the file. When provided, the store is automatically
1853+ registered for the URL scheme and host parsed from ``path``.
17531854
17541855 Returns:
17551856 DataFrame representation of the read CSV files
17561857 """
1858+ if object_store is not None :
1859+ register_path = path [0 ] if isinstance (path , list ) else path
1860+ self ._register_object_store_for_path (register_path , object_store )
17571861 if options is not None and (
17581862 schema is not None
17591863 or not has_header
@@ -1803,6 +1907,7 @@ def read_parquet(
18031907 skip_metadata : bool = True ,
18041908 schema : pa .Schema | None = None ,
18051909 file_sort_order : Sequence [Sequence [SortKey ]] | None = None ,
1910+ object_store : Any | None = None ,
18061911 ) -> DataFrame :
18071912 """Read a Parquet source into a :py:class:`~datafusion.dataframe.Dataframe`.
18081913
@@ -1822,10 +1927,43 @@ def read_parquet(
18221927 file_sort_order: Sort order for the file. Each sort key can be
18231928 specified as a column name (``str``), an expression
18241929 (``Expr``), or a ``SortExpr``.
1930+ object_store: A pre-configured object store instance (e.g.
1931+ :py:class:`~datafusion.object_store.AmazonS3`,
1932+ :py:class:`~datafusion.object_store.GoogleCloud`,
1933+ :py:class:`~datafusion.object_store.MicrosoftAzure`) to use
1934+ for accessing the file. When provided, the store is
1935+ automatically registered for the URL scheme and host parsed
1936+ from ``path``, removing the need to call
1937+ :py:meth:`register_object_store` separately. This is
1938+ especially useful in multi-threaded environments where
1939+ setting credentials via ``os.environ`` is not thread-safe.
18251940
18261941 Returns:
18271942 DataFrame representation of the read Parquet files
1828- """
1943+
1944+ Examples:
1945+ Read a local Parquet file:
1946+
1947+ >>> import datafusion
1948+ >>> ctx = datafusion.SessionContext()
1949+ >>> df = ctx.read_parquet("data.parquet") # doctest: +SKIP
1950+
1951+ Read from S3 with inline credentials (thread-safe):
1952+
1953+ >>> from datafusion.object_store import AmazonS3 # doctest: +SKIP
1954+ >>> store = AmazonS3(
1955+ ... bucket_name="my-bucket",
1956+ ... region="us-east-1",
1957+ ... access_key_id="...",
1958+ ... secret_access_key="...",
1959+ ... ) # doctest: +SKIP
1960+ >>> df = ctx.read_parquet(
1961+ ... "s3://my-bucket/data.parquet",
1962+ ... object_store=store,
1963+ ... ) # doctest: +SKIP
1964+ """
1965+ if object_store is not None :
1966+ self ._register_object_store_for_path (path , object_store )
18291967 if table_partition_cols is None :
18301968 table_partition_cols = []
18311969 table_partition_cols = _convert_table_partition_cols (table_partition_cols )
@@ -1848,6 +1986,7 @@ def read_avro(
18481986 schema : pa .Schema | None = None ,
18491987 file_partition_cols : list [tuple [str , str | pa .DataType ]] | None = None ,
18501988 file_extension : str = ".avro" ,
1989+ object_store : Any | None = None ,
18511990 ) -> DataFrame :
18521991 """Create a :py:class:`DataFrame` for reading Avro data source.
18531992
@@ -1856,10 +1995,15 @@ def read_avro(
18561995 schema: The data source schema.
18571996 file_partition_cols: Partition columns.
18581997 file_extension: File extension to select.
1998+ object_store: A pre-configured object store instance to use for
1999+ accessing the file. When provided, the store is automatically
2000+ registered for the URL scheme and host parsed from ``path``.
18592001
18602002 Returns:
18612003 DataFrame representation of the read Avro file
18622004 """
2005+ if object_store is not None :
2006+ self ._register_object_store_for_path (path , object_store )
18632007 if file_partition_cols is None :
18642008 file_partition_cols = []
18652009 file_partition_cols = _convert_table_partition_cols (file_partition_cols )
@@ -1873,6 +2017,7 @@ def read_arrow(
18732017 schema : pa .Schema | None = None ,
18742018 file_extension : str = ".arrow" ,
18752019 file_partition_cols : list [tuple [str , str | pa .DataType ]] | None = None ,
2020+ object_store : Any | None = None ,
18762021 ) -> DataFrame :
18772022 """Create a :py:class:`DataFrame` for reading an Arrow IPC data source.
18782023
@@ -1881,6 +2026,9 @@ def read_arrow(
18812026 schema: The data source schema.
18822027 file_extension: File extension to select.
18832028 file_partition_cols: Partition columns.
2029+ object_store: A pre-configured object store instance to use for
2030+ accessing the file. When provided, the store is automatically
2031+ registered for the URL scheme and host parsed from ``path``.
18842032
18852033 Returns:
18862034 DataFrame representation of the read Arrow IPC file.
@@ -1932,6 +2080,8 @@ def read_arrow(
19322080 3
19332081 ]
19342082 """
2083+ if object_store is not None :
2084+ self ._register_object_store_for_path (path , object_store )
19352085 if file_partition_cols is None :
19362086 file_partition_cols = []
19372087 file_partition_cols = _convert_table_partition_cols (file_partition_cols )
0 commit comments