From 8ec06c528fc75798ba1558c1896421ddf3bf5145 Mon Sep 17 00:00:00 2001 From: Apoorv Darshan Date: Thu, 9 Jul 2026 22:07:26 +0530 Subject: [PATCH] Exclude annotation text from Node.__str__/__unicode__ (fixes #146) Node.__str__ and Node.__unicode__ collect the string value of an element by recursing into every child, including office:annotation (cell comment) subtrees. As a result str(cell) on a spreadsheet cell that carries a comment returned the comment's date and body concatenated with the cell value, e.g. "2025-05-12T00:00:00On Day2, did thing.-CommenterCell Value" instead of "Cell Value". Skip office:annotation and office:annotation-end children when building the string value so the returned text reflects only the cell's own content. The annotation elements are untouched and still serialize to XML via toXml(); only text extraction changes. --- odf/element.py | 15 +++++++++- tests/testannotation.py | 61 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/testannotation.py diff --git a/odf/element.py b/odf/element.py index e547073..f9b8266 100644 --- a/odf/element.py +++ b/odf/element.py @@ -27,7 +27,7 @@ import re import xml.dom from xml.dom.minicompat import * -from odf.namespaces import nsdict +from odf.namespaces import nsdict, OFFICENS import odf.grammar as grammar from odf.attrconverters import AttrConverters @@ -159,6 +159,15 @@ class IllegalText(Exception): """ Complains if you add text or cdata to an element where it is not allowed """ +# Qualified names of annotation (cell comment) elements. Their text content is +# metadata about the containing element rather than part of its textual value, +# so it is skipped when collecting the string value of a node (see issue #146). +_annotation_qnames = frozenset(( + (OFFICENS, u'annotation'), + (OFFICENS, u'annotation-end'), +)) + + class Node(xml.dom.Node): """ super class for more specific nodes """ parentNode = None @@ -250,12 +259,16 @@ def removeChild(self, oldChild): def __str__(self): val = [] for c in self.childNodes: + if getattr(c, "qname", None) in _annotation_qnames: + continue val.append(str(c)) return ''.join(val) def __unicode__(self): val = [] for c in self.childNodes: + if getattr(c, "qname", None) in _annotation_qnames: + continue val.append(unicode(c)) return u''.join(val) diff --git a/tests/testannotation.py b/tests/testannotation.py new file mode 100644 index 0000000..2526b34 --- /dev/null +++ b/tests/testannotation.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2007 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# + +import unittest +import sys +from odf import office, table, text, dc + +if sys.version_info[0] == 3: + unicode = str + + +class TestAnnotation(unittest.TestCase): + + def _cell_with_annotation(self): + """ Build a table cell containing an annotation (cell comment) + followed by the actual cell value, as in issue #146. """ + cell = table.TableCell(valuetype="string") + + annotation = office.Annotation() + annotation.addElement(dc.Date(text=u"2025-05-12T00:00:00")) + p1 = text.P() + p1.addElement(text.Span(text=u"On Day2, did thing.")) + annotation.addElement(p1) + p2 = text.P() + p2.addElement(text.Span(text=u"-Commenter")) + annotation.addElement(p2) + cell.addElement(annotation) + + cell.addElement(text.P(text=u"Cell Value")) + return cell + + def test_str_ignores_annotation(self): + """ str(cell) must return only the cell value, not the comment text """ + cell = self._cell_with_annotation() + self.assertEqual("Cell Value", str(cell)) + + def test_unicode_ignores_annotation(self): + """ unicode(cell) must return only the cell value, not the comment """ + cell = self._cell_with_annotation() + self.assertEqual(u"Cell Value", unicode(cell)) + + +if __name__ == '__main__': + unittest.main()