diff --git a/AUTHORS b/AUTHORS index d645dfc9..01f47885 100644 --- a/AUTHORS +++ b/AUTHORS @@ -20,6 +20,7 @@ Here is a list of past and present much-appreciated contributors: Joel Friedly Josh Ourisman Kenneth Reitz + kigland Luca Beltrame Luke Lee Marc Abramowitz diff --git a/src/tablib/formats/_jira.py b/src/tablib/formats/_jira.py index 9bb62e79..36fb6773 100644 --- a/src/tablib/formats/_jira.py +++ b/src/tablib/formats/_jira.py @@ -2,6 +2,7 @@ Generates a Jira table from the dataset. """ +from tablib.utils import is_empty_cell class JIRAFormat: @@ -35,6 +36,6 @@ def _get_header(cls, headers): def _serialize_row(cls, row, delimiter='|'): return '{}{}{}'.format( delimiter, - delimiter.join([str(item) if item else ' ' for item in row]), + delimiter.join([' ' if is_empty_cell(item) else str(item) for item in row]), delimiter ) diff --git a/src/tablib/formats/_latex.py b/src/tablib/formats/_latex.py index 67b5b004..c57fb12d 100644 --- a/src/tablib/formats/_latex.py +++ b/src/tablib/formats/_latex.py @@ -4,6 +4,8 @@ """ import re +from tablib.utils import is_empty_cell + class LATEXFormat: title = 'latex' @@ -117,7 +119,8 @@ def _serialize_row(cls, row): :param row: single dataset row """ - new_row = [cls._escape_tex_reserved_symbols(str(item)) if item else '' + new_row = ['' if is_empty_cell(item) + else cls._escape_tex_reserved_symbols(str(item)) for item in row] return 6 * ' ' + ' & '.join(new_row) + ' \\\\' diff --git a/src/tablib/utils.py b/src/tablib/utils.py index 97bae72e..2084723b 100644 --- a/src/tablib/utils.py +++ b/src/tablib/utils.py @@ -1,6 +1,16 @@ from io import BytesIO, StringIO +def is_empty_cell(value): + return ( + value is None + or ( + isinstance(value, (str, bytes, bytearray, list, tuple, dict, set, frozenset)) + and len(value) == 0 + ) + ) + + def normalize_input(stream): """ Accept either a str/bytes stream or a file-like object and always return a diff --git a/tests/test_tablib.py b/tests/test_tablib.py index 2bc397ba..5a9cb6c4 100755 --- a/tests/test_tablib.py +++ b/tests/test_tablib.py @@ -1772,6 +1772,18 @@ def test_latex_export_none_values(self): self.assertIn('foo', output) self.assertNotIn('None', output) + def test_latex_export_falsy_values(self): + # 0 and False are real values and must be rendered, not dropped. + output = tablib.Dataset([0, False, 90]).latex + self.assertIn('0 & False & 90', output) + + def test_latex_export_empty_containers_and_bytes(self): + output = tablib.Dataset([[], {}, b'']).latex + self.assertIn(' & & ', output) + self.assertNotIn('[]', output) + self.assertNotIn('{}', output) + self.assertNotIn("b''", output) + def test_latex_escaping(self): d = tablib.Dataset(['~', '^']) output = d.latex @@ -1902,6 +1914,14 @@ def test_jira_export_no_headers(self): def test_jira_export_none_and_empty_values(self): self.assertEqual('| | |c|', tablib.Dataset(['', None, 'c']).jira) + def test_jira_export_falsy_values(self): + # 0 and False are real values, not empty cells. + self.assertEqual('|0|False|c|', tablib.Dataset([0, False, 'c']).jira) + + def test_jira_export_empty_containers_and_bytes(self): + self.assertEqual('| | |c|', tablib.Dataset([[], {}, 'c']).jira) + self.assertEqual('| |c|', tablib.Dataset([b'', 'c']).jira) + def test_jira_export_empty_dataset(self): self.assertIsNotNone(tablib.Dataset().jira)