77from typing import TYPE_CHECKING
88
99from sift_client ._internal .low_level_wrappers .data_imports import DataImportsLowLevelClient
10+ from sift_client ._internal .util .executor import run_sync_function
1011from sift_client .resources ._base import ResourceBase
1112from sift_client .sift_types .data_import import (
1213 EXTENSION_TO_DATA_TYPE_KEY ,
2526_DETECT_CONFIG_SAMPLE_SIZE = 65_536 # 64 KiB
2627
2728
29+ def _validate_config (config : ImportConfig ) -> None :
30+ """Validate an import config before sending it to the server."""
31+ if isinstance (config , CsvImportConfig ):
32+ tc = config .time_column
33+ if tc .format .name .startswith ("RELATIVE_" ) and tc .relative_start_time is None :
34+ raise ValueError (
35+ f"'relative_start_time' is required when using a relative time format ({ tc .format .name } )."
36+ )
37+
38+
2839class DataImportAPIAsync (ResourceBase ):
2940 """High-level API for importing data into Sift.
3041
@@ -55,9 +66,8 @@ async def import_from_path(
5566 ) -> DataImport :
5667 """Import data from a local file.
5768
58- Creates a data import on the server and uploads the file to the
59- returned presigned URL. Returns a :class:`DataImport` that can be
60- polled for status via ``data_import.refresh()``.
69+ Creates a data import on the server, uploads the file, and waits
70+ for the import to complete. Returns the completed :class:`DataImport`.
6171
6272 When ``config`` is omitted the file format is auto-detected via
6373 :meth:`detect_config` and a :class:`CsvImportConfig` is built using
@@ -88,9 +98,7 @@ async def import_from_path(
8898
8999 if config is None :
90100 if asset_name is None :
91- raise ValueError (
92- "Either 'config' or 'asset_name' must be provided."
93- )
101+ raise ValueError ("Either 'config' or 'asset_name' must be provided." )
94102 detected = await self .detect_config (file_path )
95103 config = detected .model_copy (
96104 update = {
@@ -100,14 +108,14 @@ async def import_from_path(
100108 }
101109 )
102110
111+ _validate_config (config )
103112 data_import_id , upload_url = await self ._low_level_client .create_from_upload (config )
104113 logger .info ("Created data import %s" , data_import_id )
105114
106115 await self ._low_level_client .upload_file (upload_url , path )
107116 logger .info ("Uploaded file to presigned URL for import %s" , data_import_id )
108117
109- data_import = await self ._low_level_client .get (data_import_id )
110- return self ._apply_client_to_instance (data_import )
118+ return await self .wait_until_complete (data_import_id )
111119
112120 async def import_from_url (
113121 self ,
@@ -128,11 +136,11 @@ async def import_from_url(
128136 Returns:
129137 A :class:`DataImport` representing the import operation.
130138 """
139+ _validate_config (config )
131140 data_import_id = await self ._low_level_client .create_from_url (url , config )
132141 logger .info ("Created URL-based data import %s" , data_import_id )
133142
134- data_import = await self ._low_level_client .get (data_import_id )
135- return self ._apply_client_to_instance (data_import )
143+ return await self .wait_until_complete (data_import_id )
136144
137145 async def get (self , data_import_id : str ) -> DataImport :
138146 """Get a data import by ID.
@@ -229,8 +237,11 @@ async def detect_config(self, file_path: str | Path) -> ImportConfig:
229237 f"Supported: { ', ' .join (sorted (EXTENSION_TO_DATA_TYPE_KEY ))} "
230238 )
231239
232- with open (path , "rb" ) as f :
233- sample = f .read (_DETECT_CONFIG_SAMPLE_SIZE )
240+ def _read_sample () -> bytes :
241+ with open (path , "rb" ) as f :
242+ return f .read (_DETECT_CONFIG_SAMPLE_SIZE )
243+
244+ sample = await run_sync_function (_read_sample )
234245
235246 response = await self ._low_level_client .detect_config (sample , data_type_key .value )
236247
0 commit comments