From 16b556535445ed2a2347104f0e0604f59f066099 Mon Sep 17 00:00:00 2001 From: dragonworks Date: Wed, 11 Mar 2020 10:05:43 +0200 Subject: [PATCH] Fixes #462 - Update xlsx import to read cell values instead of cell formulas Co-authored-by: Claude Paroz --- HISTORY.md | 4 ++++ docs/formats.rst | 9 +++++++++ src/tablib/formats/_xlsx.py | 4 ++-- tests/files/xlsx_cell_values.xlsx | Bin 0 -> 9227 bytes tests/test_tablib.py | 7 +++++++ 5 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/files/xlsx_cell_values.xlsx diff --git a/HISTORY.md b/HISTORY.md index c02639d..de2ab9d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -13,6 +13,10 @@ - Fixed minimal openpyxl dependency version to 2.6.0 (#457). - Dates from xls files are now read as Python datetime objects (#373). +### Improvements + +- When importing an xlsx file, Tablib will now read cell values instead of formulas (#462). + ## 1.1.0 (2020-02-13) ### Deprecations diff --git a/docs/formats.rst b/docs/formats.rst index 9db94ce..12ea52d 100644 --- a/docs/formats.rst +++ b/docs/formats.rst @@ -206,6 +206,15 @@ Import/export data in Excel 07+ Spreadsheet representation. This format is optional, install Tablib with ``pip install tablib[xlsx]`` to make the format available. +.. note:: + + When reading an ``xlsx`` file containing formulas in its cells, Tablib will + read the cell values, not the cell formulas. + +.. versionchanged:: 2.0.0 + + Reads cell values instead of formulas. + .. admonition:: Binary Warning The ``xlsx`` file format is binary, so make sure to write in binary mode:: diff --git a/src/tablib/formats/_xlsx.py b/src/tablib/formats/_xlsx.py index 2e08a54..dffc192 100644 --- a/src/tablib/formats/_xlsx.py +++ b/src/tablib/formats/_xlsx.py @@ -63,7 +63,7 @@ class XLSXFormat: dset.wipe() - xls_book = load_workbook(in_stream, read_only=True) + xls_book = load_workbook(in_stream, read_only=True, data_only=True) sheet = xls_book.active dset.title = sheet.title @@ -81,7 +81,7 @@ class XLSXFormat: dbook.wipe() - xls_book = load_workbook(in_stream, read_only=True) + xls_book = load_workbook(in_stream, read_only=True, data_only=True) for sheet in xls_book.worksheets: data = tablib.Dataset() diff --git a/tests/files/xlsx_cell_values.xlsx b/tests/files/xlsx_cell_values.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0f72e9caff26a0631038b30a10f0bc7e45231885 GIT binary patch literal 9227 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 literal 0 HcmV?d00001 diff --git a/tests/test_tablib.py b/tests/test_tablib.py index 6a2fbc2..ca57d00 100755 --- a/tests/test_tablib.py +++ b/tests/test_tablib.py @@ -1023,6 +1023,13 @@ class XLSXTests(BaseTestCase): data.append(('string', b'\x0cf')) data.xlsx + def test_xlsx_cell_values(self): + """Test cell values are read and not formulas""" + xls_source = Path(__file__).parent / 'files' / 'xlsx_cell_values.xlsx' + with xls_source.open('rb') as fh: + data = tablib.Dataset().load(fh) + self.assertEqual(data.headers[0], 'Hello World') + class JSONTests(BaseTestCase): def test_json_format_detect(self):