From 6b698c2d283d1cb16a6ec138d8d0de70d1a5453c Mon Sep 17 00:00:00 2001 From: John McNamara Date: Wed, 1 Jan 2025 20:10:59 +0000 Subject: [PATCH] autofilter: fix range clash with table autofilter Fix issue where a worksheet and table filter could overlap and create a corrupt file. Closes #1102 --- .../test/workbook/test_overlap_exceptions.py | 18 ++++++++++++++ xlsxwriter/worksheet.py | 24 ++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/xlsxwriter/test/workbook/test_overlap_exceptions.py b/xlsxwriter/test/workbook/test_overlap_exceptions.py index 8b81a191..f8ced865 100644 --- a/xlsxwriter/test/workbook/test_overlap_exceptions.py +++ b/xlsxwriter/test/workbook/test_overlap_exceptions.py @@ -96,3 +96,21 @@ def test_overlaps08(self): with self.assertRaises(OverlappingRange): worksheet.merge_range("B3:C3", "") + + def test_overlaps09(self): + """Test Worksheet range overlap exceptions""" + worksheet = self.workbook.add_worksheet() + + worksheet.add_table("A1:C5", {"autofilter": True}) + + with self.assertRaises(OverlappingRange): + worksheet.autofilter("A1:C5") + + def test_overlaps10(self): + """Test Worksheet range overlap exceptions""" + worksheet = self.workbook.add_worksheet() + + worksheet.autofilter("A1:C5") + + with self.assertRaises(OverlappingRange): + worksheet.add_table("A1:C5", {"autofilter": True}) diff --git a/xlsxwriter/worksheet.py b/xlsxwriter/worksheet.py index 2d6b47bd..32f67cc2 100644 --- a/xlsxwriter/worksheet.py +++ b/xlsxwriter/worksheet.py @@ -2390,7 +2390,7 @@ def autofilter(self, first_row, first_col, last_row, last_col): if last_col < first_col: (first_col, last_col) = (last_col, first_col) - # Build up the print area range "Sheet1!$A$1:$C$13". + # Build up the autofilter area range "Sheet1!$A$1:$C$13". area = self._convert_name_area(first_row, first_col, last_row, last_col) ref = xl_range(first_row, first_col, last_row, last_col) @@ -2400,7 +2400,16 @@ def autofilter(self, first_row, first_col, last_row, last_col): # Store the filter cell positions for use in the autofit calculation. for col in range(first_col, last_col + 1): - self.filter_cells[(first_row, col)] = True + # Check that the autofilter doesn't overlap a table filter. + if self.filter_cells.get((first_row, col)): + filter_type, filter_range = self.filter_cells.get((first_row, col)) + if filter_type == "table": + raise OverlappingRange( + f"Worksheet autofilter range '{ref}' overlaps previous " + f"Table autofilter range '{filter_range}'." + ) + + self.filter_cells[(first_row, col)] = ("worksheet", ref) def filter_column(self, col, criteria): """ @@ -3591,7 +3600,16 @@ def add_table(self, first_row, first_col, last_row, last_col, options=None): # Store the filter cell positions for use in the autofit calculation. if options["autofilter"]: for col in range(first_col, last_col + 1): - self.filter_cells[(first_row, col)] = True + # Check that the table autofilter doesn't overlap a worksheet filter. + if self.filter_cells.get((first_row, col)): + filter_type, filter_range = self.filter_cells.get((first_row, col)) + if filter_type == "worksheet": + raise OverlappingRange( + f"Table autofilter range '{cell_range}' overlaps previous " + f"Worksheet autofilter range '{filter_range}'." + ) + + self.filter_cells[(first_row, col)] = ("table", cell_range) return 0