From d62efb0be894f5e319d453aa3022706b5226b543 Mon Sep 17 00:00:00 2001 From: Alexandre Lavigne Date: Mon, 19 Aug 2024 21:15:23 +0200 Subject: [PATCH] fixup! update `find_table` to return correct behaviour --- gspread/utils.py | 31 ++++++++++++++++------- gspread/worksheet.py | 26 ++++++++++---------- tests/utils_test.py | 58 +++++++++++++++++++++++--------------------- 3 files changed, 65 insertions(+), 50 deletions(-) diff --git a/gspread/utils.py b/gspread/utils.py index 5e389ea5..ab2cb66e 100644 --- a/gspread/utils.py +++ b/gspread/utils.py @@ -1030,10 +1030,12 @@ def find_table( * ``TableDirection.right``: expands right until the first empty cell * ``TableDirection.down``: expands down until the first empty cell - * ``TableDirection.table``: expands right until the first empty cell, then down until the first empty cell + * ``TableDirection.table``: expands right until the first empty cell and down until first empty cell - Regardless of the direction this function always returns a matrix of data, even if it has - only one column. + In case of empty result an empty list is restuned. + + When the given ``start_range`` is outside the given matrix of values the exception + `~gspread.exceptions.InvalidInputValue` is raised. Example:: @@ -1052,7 +1054,7 @@ def find_table( .. note:: - the ``TableDirection.table`` will first look right, then look down. + the ``TableDirection.table`` will look right from starting cell then look down from starting cell. It will not check cells located inside the table. This could lead to potential empty values located in the middle of the table. @@ -1071,20 +1073,31 @@ def find_table( row -= 1 col -= 1 + if row >= len(values): + raise InvalidInputValue( + "given row for start_range is outside given values: start range row ({}) >= rows in values {}".format( + row, len(values) + ) + ) + + if col >= len(values[row]): + raise InvalidInputValue( + "given collumn for start_range is outside given values: start range column ({}) >= columns in values {}".format( + col, len(values[row]) + ) + ) + if direction == TableDirection.down: rightMost = col bottomMost = _expand_bottom(values, row, len(values), col) if direction == TableDirection.right: - if row >= len(values): - rightMost = len(values) - 1 - else: - rightMost = _expand_right(values, col, len(values[row]), row) bottomMost = row + rightMost = _expand_right(values, col, len(values[row]), row) if direction == TableDirection.table: + rightMost = _expand_right(values, col, len(values[row]), row) bottomMost = _expand_bottom(values, row, len(values), col) - rightMost = _expand_right(values, col, len(values[bottomMost]), bottomMost) result = [] diff --git a/gspread/worksheet.py b/gspread/worksheet.py index 17f7c60b..2e05b41a 100644 --- a/gspread/worksheet.py +++ b/gspread/worksheet.py @@ -3350,31 +3350,31 @@ def expand( * ``TableDirection.right``: expands right until the first empty cell * ``TableDirection.down``: expands down until the first empty cell - * ``TableDirection.table``: expands right until the first empty cell, then down until the first empty cell + * ``TableDirection.table``: expands right until the first empty cell and down until the first empty cell - Regardless of the direction this function always returns a matrix of data, even if it has - only one column. + In case of empty result an empty list is restuned. + + When the given ``start_range`` is outside the given matrix of values the exception + `~gspread.exceptions.InvalidInputValue` is raised. Example:: values = [ - ['', '', '', '' , '' , ''], - ['', 'B2', 'C2', 'D2', '' , 'F2'], - ['', 'B3', '' , 'D3', '' , 'F3'], - ['', 'B4', 'C4', 'D4', '' , 'F4'], - ['', '' , '' , '' , '' , 'F5'], + ['', '', '', '', '' ], + ['', 'B2', 'C2', '', 'E2'], + ['', 'B3', 'C3', '', 'E3'], + ['', '' , '' , '', 'E4'], ] - >>> worksheet.expand_table(TableDirection.table, 'B2') + >>> utils.find_table(TableDirection.table, 'B2') [ - ['B2', 'C2', 'D2], - ['B3', '' , 'D3'], - ['B4', 'C4', 'D4'], + ['B2', 'C2'], + ['B3', 'C3'], ] .. note:: - the ``TableDirection.table`` will first look right, then look down. + the ``TableDirection.table`` will look right from starting cell then look down from starting cell. It will not check cells located inside the table. This could lead to potential empty values located in the middle of the table. diff --git a/tests/utils_test.py b/tests/utils_test.py index f986d9bd..bb217c1b 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -528,43 +528,45 @@ def test_find_table_simple(self): "B2", utils.TableDirection.down, ) - single = utils.find_table(values, "C3", utils.TableDirection.table) + single = utils.find_table(values, "D1", utils.TableDirection.table) no_values = utils.find_table(values, "A2", utils.TableDirection.table) table_values = [ - ["B2", "C2", "", "E2"], - ["B3", "C3", "D3", "E3"], + ["B2", "C2"], + ["B3", "C3"], ] - for rowindex, row in enumerate(table): - self.assertListEqual(row, table_values[rowindex]) + for rowindex, row in enumerate(table_values): + self.assertListEqual(row, table[rowindex]) right_values = [ ["B2", "C2"], ] - for rowindex, row in enumerate(right): - self.assertListEqual(row, right_values[rowindex]) + for rowindex, row in enumerate(right_values): + self.assertListEqual(row, right[rowindex]) bottom_values = [ ["B2"], ["B3"], ] - for rowindex, row in enumerate(down): - self.assertListEqual(row, bottom_values[rowindex]) + for rowindex, row in enumerate(bottom_values): + self.assertListEqual(row, down[rowindex]) - self.assertEqual(single[0][0], "C3") + self.assertEqual(len(single), 1) + self.assertEqual(len(single[0]), 1) + self.assertEqual(single[0][0], "D1") self.assertEqual(no_values, []) - def test_find_table_header_gap(self): + def test_find_table_inner_gap(self): """Test find table with gap in header""" values = [ - ["A1", "", "C1", ""], - ["A2", "B2", "C2", ""], + ["A1", "B1", "C1", ""], + ["A2", "", "C2", ""], ["A3", "B3", "C3", ""], ["", "", "", ""], ] expected_table = [ - ["A1", "", "C1"], - ["A2", "B2", "C2"], + ["A1", "B1", "C1"], + ["A2", "", "C2"], ["A3", "B3", "C3"], ] @@ -574,21 +576,21 @@ def test_find_table_header_gap(self): utils.TableDirection.table, ) - for rowindex, row in enumerate(table): - self.assertListEqual(row, expected_table[rowindex]) + for rowindex, row in enumerate(expected_table): + self.assertListEqual(row, table[rowindex]) - def test_find_table_empty_first_cell(self): + def test_find_table_first_row_gap(self): """Test find table with first cell empty""" values = [ - ["", "B1", "C1", ""], + ["A1", "", "C1", ""], ["A2", "B2", "C2", ""], ["A3", "B3", "C3", ""], ["", "", "", ""], ] expected_table = [ - ["", "B1", "C1"], - ["A2", "B2", "C2"], - ["A3", "B3", "C3"], + ["A1"], + ["A2"], + ["A3"], ] table = utils.find_table( @@ -597,8 +599,8 @@ def test_find_table_empty_first_cell(self): utils.TableDirection.table, ) - for rowindex, row in enumerate(table): - self.assertListEqual(row, expected_table[rowindex]) + for rowindex, row in enumerate(expected_table): + self.assertListEqual(row, table[rowindex]) def test_find_table_first_column_gap(self): """Test find table with a gap in first column""" @@ -618,8 +620,8 @@ def test_find_table_first_column_gap(self): utils.TableDirection.table, ) - for rowindex, row in enumerate(table): - self.assertListEqual(row, expected_table[rowindex]) + for rowindex, row in enumerate(expected_table): + self.assertListEqual(row, table[rowindex]) def test_find_table_last_column_gap(self): """Test find table with a gap in last column""" @@ -641,5 +643,5 @@ def test_find_table_last_column_gap(self): utils.TableDirection.table, ) - for rowindex, row in enumerate(table): - self.assertListEqual(row, expected_table[rowindex]) + for rowindex, row in enumerate(expected_table): + self.assertListEqual(row, table[rowindex])