Pandas version checks
Reproducible Example
from pathlib import Path
import pandas as pd
from odf.opendocument import OpenDocumentSpreadsheet
from odf.table import CoveredTableCell, Table, TableCell, TableRow
path = Path("covered_cell_value.ods")
doc = OpenDocumentSpreadsheet()
sheet = Table(name="Sheet1")
doc.spreadsheet.addElement(sheet)
row0 = TableRow()
sheet.addElement(row0)
row0.addElement(TableCell(valuetype="float", value="1"))
row0.addElement(TableCell(valuetype="float", value="100"))
# A covered cell carrying its own explicit office:value/office:value-type —
# this is exactly what the cells covered by an ODF array/matrix formula
# (table:number-matrix-rows-spanned) hold, per the OpenDocument spec.
row1 = TableRow()
sheet.addElement(row1)
row1.addElement(CoveredTableCell(valuetype="float", value="42"))
row1.addElement(TableCell(valuetype="float", value="200"))
doc.save(str(path))
print(pd.read_excel(path, engine="odf", header=None))
print(pd.read_excel(path, engine="calamine", header=None))
Output:
0 1
0 1.0 100
1 NaN 200 # engine="odf": the covered cell's value (42) is silently dropped
0 1
0 1 100
1 42 200 # engine="calamine": reads the same file correctly
(LibreOffice shows correctly the number 42 when opening the file manually.)
Issue Description
read_excel(engine="odf") treats every <table:covered-table-cell> as empty,
without ever inspecting whether it carries its own office:value/
office:value-type. The relevant code, in
pandas/io/excel/_odfreader.py, ODFReader.get_sheet_data:
if sheet_cell.qname == table_cell_name:
value = self._get_cell_value(sheet_cell)
else:
value = self.empty_value
Any cell that isn't literally a <table:table-cell> — i.e. any
<table:covered-table-cell> — takes the else branch and is hard-coded to
self.empty_value, regardless of its actual attributes.
This is usually harmless, since a covered cell is normally just a
merged-away placeholder for an ordinary row/column span with no value of its
own. But it's incorrect for the cells covered by an ODF array/matrix
formula (table:number-matrix-rows-spanned /
table:number-matrix-columns-spanned, OpenDocument v1.2 Part 1, §9.1.5
<table:covered-table-cell>;
the attribute itself is defined in §19.680, cross-referenced e.g. in
MS-OODF3):
per the OpenDocument spec, those covered cells hold their own evaluated
result, and real spreadsheet applications (verified with LibreOffice:
soffice --headless --convert-to csv recomputes and exports the correct
distinct value for each covered cell) read/recompute them accordingly.
I've confirmed the data is genuinely present and correctly parsed at the
odfpy layer (which pandas' odf engine is built on) — reading the same file
with odfpy directly via cell.getAttrNS(office_ns, "value") returns "42"
for the covered cell — so this is specific to pandas' reader wrapper, not to
odfpy or to how the file was written.
Expected Behavior
read_excel(engine="odf") should read a covered cell's own office:value/
office:value-type when present, the same way engine="calamine" already
does, instead of unconditionally treating it as empty. At minimum, a covered
cell that carries an explicit office:value should not silently lose it.
Installed Versions
pandas : 3.0.5 (latest PyPI release; also reproduces on 3.0.3)
odfpy : 1.4.1 (latest PyPI release)
python-calamine : 0.8.2 (latest PyPI release)
python : 3.13.5
python-bits : 64
OS : Linux
OS-release : 6.12.95+deb13-amd64
machine : x86_64
(pd.show_versions() itself reports odfpy: None and python-calamine: None
on this install despite both being present and functional — checked directly
via importlib.metadata.version(...).)
Pandas version checks
I have checked that this issue has not already been reported.
I have confirmed this bug exists on the latest version of pandas.
I have confirmed this bug exists on the main branch of pandas.
Reproducible Example
Issue Description
read_excel(engine="odf")treats every<table:covered-table-cell>as empty,without ever inspecting whether it carries its own
office:value/office:value-type. The relevant code, inpandas/io/excel/_odfreader.py,ODFReader.get_sheet_data:Any cell that isn't literally a
<table:table-cell>— i.e. any<table:covered-table-cell>— takes theelsebranch and is hard-coded toself.empty_value, regardless of its actual attributes.This is usually harmless, since a covered cell is normally just a
merged-away placeholder for an ordinary row/column span with no value of its
own. But it's incorrect for the cells covered by an ODF array/matrix
formula (
table:number-matrix-rows-spanned/table:number-matrix-columns-spanned, OpenDocument v1.2 Part 1, §9.1.5<table:covered-table-cell>;the attribute itself is defined in §19.680, cross-referenced e.g. in
MS-OODF3):
per the OpenDocument spec, those covered cells hold their own evaluated
result, and real spreadsheet applications (verified with LibreOffice:
soffice --headless --convert-to csvrecomputes and exports the correctdistinct value for each covered cell) read/recompute them accordingly.
I've confirmed the data is genuinely present and correctly parsed at the
odfpy layer (which pandas' odf engine is built on) — reading the same file
with
odfpydirectly viacell.getAttrNS(office_ns, "value")returns"42"for the covered cell — so this is specific to pandas' reader wrapper, not to
odfpy or to how the file was written.
Expected Behavior
read_excel(engine="odf")should read a covered cell's ownoffice:value/office:value-typewhen present, the same wayengine="calamine"alreadydoes, instead of unconditionally treating it as empty. At minimum, a covered
cell that carries an explicit
office:valueshould not silently lose it.Installed Versions
(
pd.show_versions()itself reportsodfpy: Noneandpython-calamine: Noneon this install despite both being present and functional — checked directly
via
importlib.metadata.version(...).)