diff --git a/src/tablib/formats/_dbf.py b/src/tablib/formats/_dbf.py index 16737037..b51ef940 100644 --- a/src/tablib/formats/_dbf.py +++ b/src/tablib/formats/_dbf.py @@ -14,6 +14,7 @@ from .._vendor.dbfpy import dbf, dbfnew from .._vendor.dbfpy import record as dbfrecord +from ..exceptions import UnsupportedFormat class DBFFormat: @@ -58,10 +59,22 @@ def import_set(cls, dset, in_stream): """Returns a dataset from a DBF stream.""" dset.wipe() - _dbf = dbf.Dbf(in_stream) - dset.headers = _dbf.fieldNames - for record in range(_dbf.recordCount): - row = [_dbf[record][f] for f in _dbf.fieldNames] + # A malformed DBF makes the vendored parser raise a variety of low-level + # errors (struct.error, IndexError, UnicodeDecodeError, ...). Report + # them as UnsupportedFormat, consistent with detect() treating any + # parsing error as "not a valid DBF". + try: + _dbf = dbf.Dbf(in_stream) + headers = _dbf.fieldNames + rows = [[_dbf[record][f] for f in headers] + for record in range(_dbf.recordCount)] + except Exception as e: + raise UnsupportedFormat( + 'Error parsing DBF: the stream is not a valid DBF file.' + ) from e + + dset.headers = headers + for row in rows: dset.append(row) @classmethod diff --git a/tests/test_tablib.py b/tests/test_tablib.py index ca8ac05e..019fec88 100755 --- a/tests/test_tablib.py +++ b/tests/test_tablib.py @@ -1910,6 +1910,19 @@ def test_dbf_import_set(self): ) index += 1 + def test_dbf_import_malformed(self): + """A malformed DBF stream raises UnsupportedFormat rather than a raw + struct.error / IndexError from the vendored parser.""" + data.append(self.john) + data.headers = self.headers + good = data.dbf + # truncate the DBF inside its header + truncated = good[:16] + with self.assertRaises(UnsupportedFormat): + tablib.Dataset().load(truncated, format='dbf') + with self.assertRaises(UnsupportedFormat): + tablib.Dataset().load(b'not a dbf file', format='dbf') + def test_dbf_export_set(self): """Test DBF import.""" data.append(self.john)