Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
1. [#720](https://github.com/influxdata/influxdb-client-python/pull/720): Prevent the `api_client` class from raising exceptions while the Python interpreter clears the client state during shutdown.
1. [#723](https://github.com/influxdata/influxdb-client-python/pull/723): Export public names using generated `__all__` array in `__init__.py` as per reference documentation.
1. [#725](https://github.com/influxdata/influxdb-client-python/pull/725): Start the `WritesRetry` `max_retry_time` at a request's first failure, so a strategy passed as `retries` to `InfluxDBClient` keeps retrying after `max_retry_time` has passed since it was created.
1. [#732](https://github.com/influxdata/influxdb-client-python/pull/732): Apply the `#default` annotation in `FluxCsvParser` when it comes before `#datatype`.

## 1.50.0 [2026-01-23]

Expand Down
7 changes: 6 additions & 1 deletion influxdb_client/client/flux_csv_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def __init__(self):
self.start_new_table = False
self.table = None
self.groups = []
self.default_values = None
self.parsing_state_error = False


Expand Down Expand Up @@ -185,6 +186,7 @@ def _parse_flux_response_row(self, metadata, csv):
self._insert_table(metadata.table, metadata.table_index)
metadata.table_index = metadata.table_index + 1
metadata.table_id = -1
metadata.default_values = None
elif metadata.table is None:
raise FluxCsvParserException("Unable to parse CSV response. FluxTable definition was not found.")

Expand All @@ -196,7 +198,8 @@ def _parse_flux_response_row(self, metadata, csv):
metadata.groups = csv

elif ANNOTATION_DEFAULT == token:
self.add_default_empty_values(metadata.table, csv)
# Applied with the column names: "#default" can come before "#datatype", which creates the columns
metadata.default_values = csv

else:
# parse column names
Expand All @@ -207,6 +210,8 @@ def _parse_flux_response_row(self, metadata, csv):
self.add_data_types(metadata.table, list(map(lambda column: 'string', csv)))
metadata.groups = list(map(lambda column: 'false', csv))
self.add_groups(metadata.table, metadata.groups)
if metadata.default_values is not None:
self.add_default_empty_values(metadata.table, metadata.default_values)
self.add_column_names_and_tags(metadata.table, csv)
metadata.start_new_table = False
# Create DataFrame with default values
Expand Down
47 changes: 47 additions & 0 deletions tests/test_FluxCSVParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,53 @@ def test_parse_duplicate_column_names(self):
self.assertEqual(25.3, tables[0].records[0].row[7])


def test_default_annotation_before_datatype(self):
data = "#default,_result,,,,,,,,,42,\n" \
"#group,false,false,true,true,true,true,true,true,false,false,false\n" \
"#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,string,string,string,string,long,long,string\n" \
",result,table,_start,_stop,_field,_measurement,host,region,_value2,value1,value_str\n" \
",,0,1677-09-21T00:12:43.145224192Z,2018-07-16T11:21:02.547596934Z,free,mem,A,west,121,,test\n"

tables = self._parse_to_tables(data=data)
self.assertEqual(1, tables.__len__())
self.assertEqual(11, tables[0].columns.__len__())
self.assertEqual("_result", tables[0].columns[0].default_value)
self.assertEqual("long", tables[0].columns[9].data_type)
self.assertTrue(tables[0].columns[2].group)
record = tables[0].records[0]
self.assertEqual("_result", record.values["result"])
self.assertEqual(42, record.values["value1"])
self.assertEqual(121, record.values["_value2"])

def test_default_annotation_before_datatype_more_tables(self):
data = "#default,first,,,\n" \
"#datatype,string,long,string,long\n" \
"#group,false,false,true,false\n" \
",result,table,host,_value\n" \
",,0,A,1\n" \
"\n" \
"#default,second,,,7\n" \
"#group,false,false,true,false\n" \
"#datatype,string,long,string,long\n" \
",result,table,host,_value\n" \
",,1,B,\n" \
"\n" \
"#datatype,string,long,string,long\n" \
"#group,false,false,true,false\n" \
",result,table,host,_value\n" \
",,2,C,\n"

tables = self._parse_to_tables(data=data)
self.assertEqual(3, tables.__len__())
self.assertEqual("first", tables[0].records[0].values["result"])
self.assertEqual(1, tables[0].records[0].values["_value"])
self.assertEqual("second", tables[1].records[0].values["result"])
self.assertEqual(7, tables[1].records[0].values["_value"])
self.assertTrue(tables[1].columns[2].group)
# defaults of a previous table must not leak into a table without #default
self.assertIsNone(tables[2].records[0].values["result"])
self.assertIsNone(tables[2].records[0].values["_value"])

@staticmethod
def _parse_to_tables(data: str, serialization_mode=FluxSerializationMode.tables,
response_metadata_mode=FluxResponseMetadataMode.full) -> TableList:
Expand Down
Loading