From 8a75234b09f959167db47ec7cf88a95e3e48adc3 Mon Sep 17 00:00:00 2001 From: sarankumar1325 <120080287+sarankumar1325@users.noreply.github.com> Date: Fri, 30 Oct 2026 11:01:08 +0000 Subject: [PATCH 1/2] fix: apply #default annotation when it comes before #datatype FluxCsvParser applied the #default row right away, but the columns are only created by the #datatype row. When #default came first, the defaults were dropped and empty cells parsed as None. Keep the #default row like #group and apply it when the column names are parsed; reset it for each new table. Closes #727 --- influxdb_client/client/flux_csv_parser.py | 7 +++- tests/test_FluxCSVParser.py | 47 +++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/influxdb_client/client/flux_csv_parser.py b/influxdb_client/client/flux_csv_parser.py index 6d8fa76c..cdfa5187 100644 --- a/influxdb_client/client/flux_csv_parser.py +++ b/influxdb_client/client/flux_csv_parser.py @@ -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 @@ -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.") @@ -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 @@ -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 diff --git a/tests/test_FluxCSVParser.py b/tests/test_FluxCSVParser.py index ae9adcec..aeae2960 100644 --- a/tests/test_FluxCSVParser.py +++ b/tests/test_FluxCSVParser.py @@ -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: From 7bae87e8c52b407e520cf733514de818cfb28f53 Mon Sep 17 00:00:00 2001 From: sarankumar1325 <120080287+sarankumar1325@users.noreply.github.com> Date: Fri, 30 Oct 2026 11:01:52 +0000 Subject: [PATCH 2/2] docs: add CHANGELOG entry for #732 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca6775c6..e218a067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ 1. [#714](https://github.com/influxdata/influxdb-client-python/pull/714): Implement `WriteApi.flush()` to force pending batch writes without disposing the API (mirrors C#/Java batch writer). 1. [#715](https://github.com/influxdata/influxdb-client-python/pull/715): Accept numeric zero for `RangeThreshold.min`/`max` and greater/lesser `value` (identity checks vs `None`, not truthiness); cover create-check serialization. 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. [#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]