mirror of
https://github.com/kennethreitz/tablib.git
synced 2026-06-05 15:00:19 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 985c3d98b0 | |||
| 6d097c0214 | |||
| 16b5565354 | |||
| c25fe54b6f | |||
| b39aefb8d8 | |||
| a442758729 | |||
| 21479001a7 |
+18
@@ -1,5 +1,23 @@
|
||||
# History
|
||||
|
||||
## 2.0.0 (2020-05-16)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- The `Row.lpush/rpush` logic was reversed. `lpush` was appending while `rpush`
|
||||
and `append` were prepending. This was fixed (reversed behavior). If you
|
||||
counted on the broken behavior, please update your code (#453).
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- Fixed minimal openpyxl dependency version to 2.6.0 (#457).
|
||||
- Dates from xls files are now read as Python datetime objects (#373).
|
||||
- Allow import of "ragged" xlsx files (#465).
|
||||
|
||||
### Improvements
|
||||
|
||||
- When importing an xlsx file, Tablib will now read cell values instead of formulas (#462).
|
||||
|
||||
## 1.1.0 (2020-02-13)
|
||||
|
||||
### Deprecations
|
||||
|
||||
@@ -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::
|
||||
|
||||
@@ -38,13 +38,13 @@ setup(
|
||||
],
|
||||
python_requires='>=3.5',
|
||||
extras_require={
|
||||
'all': ['markuppy', 'odfpy', 'openpyxl>=2.4.0', 'pandas', 'pyyaml', 'tabulate', 'xlrd', 'xlwt'],
|
||||
'all': ['markuppy', 'odfpy', 'openpyxl>=2.6.0', 'pandas', 'pyyaml', 'tabulate', 'xlrd', 'xlwt'],
|
||||
'cli': ['tabulate'],
|
||||
'html': ['markuppy'],
|
||||
'ods': ['odfpy'],
|
||||
'pandas': ['pandas'],
|
||||
'xls': ['xlrd', 'xlwt'],
|
||||
'xlsx': ['openpyxl>=2.4.0'],
|
||||
'xlsx': ['openpyxl>=2.6.0'],
|
||||
'yaml': ['pyyaml'],
|
||||
},
|
||||
)
|
||||
|
||||
+2
-2
@@ -71,10 +71,10 @@ class Row:
|
||||
setattr(self, k, v)
|
||||
|
||||
def rpush(self, value):
|
||||
self.insert(0, value)
|
||||
self.insert(len(self._row), value)
|
||||
|
||||
def lpush(self, value):
|
||||
self.insert(len(value), value)
|
||||
self.insert(0, value)
|
||||
|
||||
def append(self, value):
|
||||
self.rpush(value)
|
||||
|
||||
@@ -6,6 +6,7 @@ from io import BytesIO
|
||||
import tablib
|
||||
import xlrd
|
||||
import xlwt
|
||||
from xlrd.xldate import xldate_as_datetime
|
||||
|
||||
# special styles
|
||||
wrap = xlwt.easyxf("alignment: wrap on")
|
||||
@@ -74,12 +75,19 @@ class XLSFormat:
|
||||
|
||||
dset.title = sheet.name
|
||||
|
||||
def cell_value(value, type_):
|
||||
if type_ == xlrd.XL_CELL_ERROR:
|
||||
return xlrd.error_text_from_code[value]
|
||||
elif type_ == xlrd.XL_CELL_DATE:
|
||||
return xldate_as_datetime(value, xls_book.datemode)
|
||||
return value
|
||||
|
||||
for i in range(sheet.nrows):
|
||||
if i == 0 and headers:
|
||||
dset.headers = sheet.row_values(0)
|
||||
else:
|
||||
dset.append([
|
||||
val if typ != xlrd.XL_CELL_ERROR else xlrd.error_text_from_code[val]
|
||||
cell_value(val, typ)
|
||||
for val, typ in zip(sheet.row_values(i), sheet.row_types(i))
|
||||
])
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -92,6 +92,8 @@ class XLSXFormat:
|
||||
if (i == 0) and (headers):
|
||||
data.headers = row_vals
|
||||
else:
|
||||
if i > 0 and len(row_vals) < data.width:
|
||||
row_vals += [''] * (data.width - len(row_vals))
|
||||
data.append(row_vals)
|
||||
|
||||
dbook.add_sheet(data)
|
||||
|
||||
@@ -60,7 +60,7 @@ class DbfRecord:
|
||||
|
||||
Arguments:
|
||||
dbf:
|
||||
A `Dbf.Dbf` instance this record belonogs to.
|
||||
A `Dbf.Dbf` instance this record belongs to.
|
||||
index:
|
||||
An integer record index or None. If this value is
|
||||
None, record will be appended to the DBF.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,7 +2,7 @@ pytest
|
||||
pytest-cov
|
||||
MarkupPy
|
||||
odfpy
|
||||
openpyxl>=2.4.0
|
||||
openpyxl>=2.6.0
|
||||
pandas
|
||||
pyyaml
|
||||
tabulate
|
||||
|
||||
+24
-16
@@ -556,27 +556,15 @@ class TablibTestCase(BaseTestCase):
|
||||
|
||||
def test_row_lpush(self):
|
||||
"""Row lpush."""
|
||||
# Arrange
|
||||
john = Row(self.john)
|
||||
george = Row(self.george)
|
||||
|
||||
# Act
|
||||
john.lpush(george)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(john[-1], george)
|
||||
john.lpush(53)
|
||||
self.assertEqual(john.list, [53, 'John', 'Adams', 90])
|
||||
|
||||
def test_row_append(self):
|
||||
"""Row append."""
|
||||
# Arrange
|
||||
john = Row(self.john)
|
||||
george = Row(self.george)
|
||||
|
||||
# Act
|
||||
john.append(george)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(john[0], george)
|
||||
john.append('stuff')
|
||||
self.assertEqual(john.list, ['John', 'Adams', 90, 'stuff'])
|
||||
|
||||
def test_row_contains(self):
|
||||
"""Row __contains__."""
|
||||
@@ -987,6 +975,12 @@ class XLSTests(BaseTestCase):
|
||||
in_stream = self.founders.xls
|
||||
self.assertEqual(detect_format(in_stream), 'xls')
|
||||
|
||||
def test_xls_date_import(self):
|
||||
xls_source = Path(__file__).parent / 'files' / 'dates.xls'
|
||||
with open(str(xls_source), mode='rb') as fh:
|
||||
dset = tablib.Dataset().load(fh, 'xls')
|
||||
self.assertEqual(dset.dict[0]['birth_date'], datetime.datetime(2015, 4, 12, 0, 0))
|
||||
|
||||
def test_xls_import_with_errors(self):
|
||||
"""Errors from imported files are kept as errors."""
|
||||
xls_source = Path(__file__).parent / 'files' / 'errors.xls'
|
||||
@@ -1021,6 +1015,13 @@ class XLSXTests(BaseTestCase):
|
||||
self.assertEqual(data.dict[0]['float'], 21.55)
|
||||
self.assertEqual(data.dict[0]['date/time'], date_time)
|
||||
|
||||
def test_xlsx_import_set_ragged(self):
|
||||
"""Import XLSX file when not all rows have the same length."""
|
||||
xlsx_source = Path(__file__).parent / 'files' / 'ragged.xlsx'
|
||||
with open(str(xlsx_source), mode='rb') as fh:
|
||||
book = tablib.Databook().load(fh, 'xlsx')
|
||||
self.assertEqual(book.sheets()[0].pop(), (1.0, ''))
|
||||
|
||||
def test_xlsx_wrong_char(self):
|
||||
"""Bad characters are not silently ignored. We let the exception bubble up."""
|
||||
from openpyxl.utils.exceptions import IllegalCharacterError
|
||||
@@ -1029,6 +1030,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):
|
||||
|
||||
Reference in New Issue
Block a user