11from __future__ import annotations
22
3+ from abc import ABC
34from datetime import datetime # noqa: TC003
45from enum import Enum
56from typing import Union
@@ -82,7 +83,27 @@ class DataTypeKey(Enum):
8283}
8384
8485
85- class CsvTimeColumn (BaseModel ):
86+ class TimeColumnBase (BaseModel , ABC ):
87+ """Base class for time column configurations.
88+
89+ Attributes:
90+ format: The time format used in this column.
91+ relative_start_time: Required when using a relative time format.
92+ """
93+
94+ format : TimeFormat
95+ relative_start_time : datetime | None = None
96+
97+ @model_validator (mode = "after" )
98+ def _check_relative_start_time (self ) -> TimeColumnBase :
99+ if self .format .name .startswith ("RELATIVE_" ) and self .relative_start_time is None :
100+ raise ValueError (
101+ f"'relative_start_time' is required when using a relative time format ({ self .format .name } )."
102+ )
103+ return self
104+
105+
106+ class CsvTimeColumn (TimeColumnBase ):
86107 """Time column configuration for CSV imports.
87108
88109 Attributes:
@@ -92,8 +113,6 @@ class CsvTimeColumn(BaseModel):
92113 """
93114
94115 column : int
95- format : TimeFormat
96- relative_start_time : datetime | None = None
97116
98117 def _to_proto (self ) -> CsvTimeColumnProto :
99118 proto = CsvTimeColumnProto (
@@ -104,14 +123,6 @@ def _to_proto(self) -> CsvTimeColumnProto:
104123 proto .relative_start_time .CopyFrom (to_pb_timestamp (self .relative_start_time ))
105124 return proto
106125
107- @model_validator (mode = "after" )
108- def _check_relative_start_time (self ) -> CsvTimeColumn :
109- if self .format .name .startswith ("RELATIVE_" ) and self .relative_start_time is None :
110- raise ValueError (
111- f"'relative_start_time' is required when using a relative time format ({ self .format .name } )."
112- )
113- return self
114-
115126
116127class CsvDataColumn (BaseModel ):
117128 """A data column definition for CSV imports.
@@ -131,21 +142,29 @@ class CsvDataColumn(BaseModel):
131142 description : str = ""
132143
133144
134- class CsvImportConfig (BaseModel ):
135- """Configuration for importing a CSV file .
145+ class ImportConfigBase (BaseModel , ABC ):
146+ """Base class for all import configurations .
136147
137148 Attributes:
138149 asset_name: Name of the asset to import data into.
139150 run_name: Name for the run. Ignored if ``run_id`` is set.
140151 run_id: ID of an existing run to append data to.
141- first_data_row: The first row containing data (1-indexed). Defaults to 2 to skip a header row.
142- time_column: Time column configuration.
143- data_columns: List of data column definitions.
144152 """
145153
146154 asset_name : str
147155 run_name : str | None = None
148156 run_id : str | None = None
157+
158+
159+ class CsvImportConfig (ImportConfigBase ):
160+ """Configuration for importing a CSV file.
161+
162+ Attributes:
163+ first_data_row: The first row containing data (1-indexed). Defaults to 2 to skip a header row.
164+ time_column: Time column configuration.
165+ data_columns: List of data column definitions.
166+ """
167+
149168 first_data_row : int = 2
150169 time_column : CsvTimeColumn
151170 data_columns : list [CsvDataColumn ]
@@ -229,7 +248,7 @@ class ParquetComplexTypesImportMode(Enum):
229248 BYTES = PARQUET_COMPLEX_TYPES_IMPORT_MODE_BYTES
230249
231250
232- class ParquetTimeColumn (BaseModel ):
251+ class ParquetTimeColumn (TimeColumnBase ):
233252 """Time column configuration for Parquet imports.
234253
235254 Attributes:
@@ -240,7 +259,6 @@ class ParquetTimeColumn(BaseModel):
240259
241260 path : str
242261 format : TimeFormat = TimeFormat .ABSOLUTE_UNIX_NANOSECONDS
243- relative_start_time : datetime | None = None
244262
245263 def _to_proto (self ) -> ParquetTimeColumnProto :
246264 if not self .path :
@@ -268,14 +286,6 @@ def _from_proto(cls, proto: ParquetTimeColumnProto) -> ParquetTimeColumn:
268286 relative_start_time = relative_start_time ,
269287 )
270288
271- @model_validator (mode = "after" )
272- def _check_relative_start_time (self ) -> ParquetTimeColumn :
273- if self .format .name .startswith ("RELATIVE_" ) and self .relative_start_time is None :
274- raise ValueError (
275- f"'relative_start_time' is required when using a relative time format ({ self .format .name } )."
276- )
277- return self
278-
279289
280290class ParquetDataColumn (BaseModel ):
281291 """A data column definition for Parquet flat dataset imports.
@@ -295,15 +305,12 @@ class ParquetDataColumn(BaseModel):
295305 description : str = ""
296306
297307
298- class ParquetFlatDatasetImportConfig (BaseModel ):
308+ class ParquetFlatDatasetImportConfig (ImportConfigBase ):
299309 """Configuration for importing a Parquet file with a flat dataset layout.
300310
301311 Each column in the file maps to a separate channel.
302312
303313 Attributes:
304- asset_name: Name of the asset to import data into.
305- run_name: Name for the run. Ignored if ``run_id`` is set.
306- run_id: ID of an existing run to append data to.
307314 time_column: Time column configuration.
308315 data_columns: List of data column definitions.
309316 footer_offset: Byte offset where the Parquet footer begins. Populated
@@ -313,9 +320,6 @@ class ParquetFlatDatasetImportConfig(BaseModel):
313320 complex_types_import_mode: How to handle complex Parquet types.
314321 """
315322
316- asset_name : str
317- run_name : str | None = None
318- run_id : str | None = None
319323 time_column : ParquetTimeColumn
320324 data_columns : list [ParquetDataColumn ]
321325 footer_offset : int = 0
@@ -430,16 +434,13 @@ class ParquetMultiChannelConfig(BaseModel):
430434 data_path : str
431435
432436
433- class ParquetSingleChannelPerRowImportConfig (BaseModel ):
437+ class ParquetSingleChannelPerRowImportConfig (ImportConfigBase ):
434438 """Configuration for importing a Parquet file where each row represents
435439 a single channel's data point.
436440
437441 Exactly one of ``single_channel`` or ``multi_channel`` must be set.
438442
439443 Attributes:
440- asset_name: Name of the asset to import data into.
441- run_name: Name for the run. Ignored if ``run_id`` is set.
442- run_id: ID of an existing run to append data to.
443444 time_column: Time column configuration.
444445 single_channel: Set when the entire file contains data for one channel.
445446 multi_channel: Set when each row identifies its channel via a name column.
@@ -450,9 +451,6 @@ class ParquetSingleChannelPerRowImportConfig(BaseModel):
450451 complex_types_import_mode: How to handle complex Parquet types.
451452 """
452453
453- asset_name : str
454- run_name : str | None = None
455- run_id : str | None = None
456454 time_column : ParquetTimeColumn
457455 single_channel : ParquetSingleChannelConfig | None = None
458456 multi_channel : ParquetMultiChannelConfig | None = None
@@ -540,17 +538,13 @@ def _from_proto(
540538 )
541539
542540
543- class Ch10ImportConfig (BaseModel ):
541+ class Ch10ImportConfig (ImportConfigBase ):
544542 """Configuration for importing a CH10 file.
545543
546544 Attributes:
547- asset_name: Name of the asset to import data into.
548- run_name: Name for the run.
549545 scale_values: Whether to apply EU (engineering unit) scaling to channel values.
550546 """
551547
552- asset_name : str
553- run_name : str | None = None
554548 scale_values : bool = False
555549
556550 def _to_proto (self ) -> Ch10ConfigProto :
@@ -561,21 +555,15 @@ def _to_proto(self) -> Ch10ConfigProto:
561555 )
562556
563557
564- class TdmsImportConfig (BaseModel ):
558+ class TdmsImportConfig (ImportConfigBase ):
565559 """Configuration for importing a TDMS file.
566560
567561 Attributes:
568- asset_name: Name of the asset to import data into.
569- run_name: Name for the run. Ignored if ``run_id`` is set.
570- run_id: ID of an existing run to append data to.
571562 start_time_override: Override the ``wf_start_time`` metadata field for all channels.
572563 Useful when waveform channels have ``wf_increment`` but no ``wf_start_time``.
573564 file_size: The file size in bytes. Required if the file has truncated chunks.
574565 """
575566
576- asset_name : str
577- run_name : str | None = None
578- run_id : str | None = None
579567 start_time_override : datetime | None = None
580568 file_size : int | None = None
581569
@@ -622,21 +610,15 @@ class Hdf5DataColumn(BaseModel):
622610 value_field : str | None = None
623611
624612
625- class Hdf5ImportConfig (BaseModel ):
613+ class Hdf5ImportConfig (ImportConfigBase ):
626614 """Configuration for importing an HDF5 file.
627615
628616 Attributes:
629- asset_name: Name of the asset to import data into.
630- run_name: Name for the run. Ignored if ``run_id`` is set.
631- run_id: ID of an existing run to append data to.
632617 data: List of dataset mappings, each pairing a time and value dataset to a channel.
633618 time_format: The time format used across all time datasets.
634619 relative_start_time: Required when using a relative time format.
635620 """
636621
637- asset_name : str
638- run_name : str | None = None
639- run_id : str | None = None
640622 data : list [Hdf5DataColumn ]
641623 time_format : TimeFormat
642624 relative_start_time : datetime | None = None
@@ -678,9 +660,6 @@ def _to_proto(self) -> Hdf5ConfigProto:
678660 return proto
679661
680662
681- # Note: Using Union instead of | syntax for Python 3.9 compatibility at module level.
682- # While `from __future__ import annotations` allows | in type hints (they're strings),
683- # module-level type aliases are evaluated at runtime and require Union in Python <3.10.
684663ImportConfig = Union [
685664 CsvImportConfig ,
686665 ParquetFlatDatasetImportConfig ,
0 commit comments