Skip to content

List

List class for “text:list” tag and ListItem for “text:list-item” tag .

Classes:

Name Description
List

A list of elements, “text:list”.

ListHeader

An header of a list, “text:list-header”.

ListItem

An item of a list, “text:list-item”.

List

Bases: MDList, Element

A list of elements, “text:list”.

Methods:

Name Description
__init__

Initialize the List.

append_item

Append a new item to the end of the list.

get_formatted_text

Return a formatted string representation of the list.

get_item

Get a single list item from the list.

get_items

Get all list items (ListItem) within the list.

insert_item

Insert a new item into the list.

set_list_header

Set the header of the list.

Attributes:

Name Type Description
list_header ListHeader | None

Get or set the list header.

style
Source code in odfdo/list.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
class List(MDList, Element):
    """A list of elements, "text:list"."""

    _tag = "text:list"
    _properties = (PropDef("style", "text:style-name"),)

    def __init__(
        self,
        list_content: str | Element | Iterable[str | Element] | None = None,
        style: str | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize the List.

        A `List` can be initialized with content, which can be a single item
        (string or element) or an iterable of items.

        Args:
            list_content: The initial content of the list. Each item is wrapped in a
                `ListItem`.
            style: The name of the style to apply to the list.
            **kwargs: Additional keyword arguments for the parent `Element` class.
        """
        super().__init__(**kwargs)
        if self._do_init:
            if list_content:
                if isinstance(list_content, (Element, str)):
                    self.append(ListItem(list_content))
                elif hasattr(list_content, "__iter__"):
                    for item in list_content:
                        self.append(ListItem(item))
            if style is not None:
                self.style = style

    def get_items(self, content: str | None = None) -> list[Element]:
        """Get all list items (`ListItem`) within the list.

        Optionally filters items by their textual content.

        Args:
            content: A regular expression to match against
                the text content of the items.

        Returns:
            list[Element]: A list of `ListItem` elements that match the criteria.
        """
        return self._filtered_elements("text:list-item", content=content)

    def get_item(
        self,
        position: int = 0,
        content: str | None = None,
    ) -> Element | None:
        """Get a single list item from the list.

        Can retrieve an item by its position or by matching its text content.
        In nested lists, it returns the `ListItem` that directly contains
        the matched content.

        Args:
            position: The index of the item to retrieve. Defaults to 0.
            content: A regular expression to match against
                the text content of the items. If provided, `position` is
                ignored.

        Returns:
            Element | None: The matching `ListItem` element, or `None` if not found.
        """
        # Custom implementation because of nested lists
        if content:
            # Don't search recursively but on the very own paragraph(s) of
            # each list item
            for paragraph in self.get_elements("descendant::text:p"):
                if paragraph.match(content):
                    return paragraph.get_element("parent::text:list-item")
            return None
        return self._filtered_element("text:list-item", position)

    @property
    def list_header(self) -> ListHeader | None:
        """Get or set the list header."""
        return cast(None | ListHeader, self.get_element("text:list-header"))

    @list_header.setter
    def list_header(
        self, text_or_element: str | Element | Iterable[str | Element] | None = None
    ) -> None:
        current = cast(None | ListHeader, self.get_element("text:list-header"))
        if current:
            current.delete()
        new_header = ListHeader(text_or_element)
        self.insert(new_header, FIRST_CHILD)

    def set_list_header(
        self,
        text_or_element: str | Element | Iterable[str | Element],
    ) -> None:
        """Set the header of the list.

        This method replaces any existing header paragraphs (`text:p`) with
        the provided content.

        You may consider using the property List.list_header.

        Args:
            text_or_element: The content for the list header. Can be a single
                string or element, or an iterable of strings and/or elements.
        """
        self.list_header = text_or_element

    def insert_item(
        self,
        item: ListItem | str | Element | None,
        position: int | None = None,
        before: Element | None = None,
        after: Element | None = None,
    ) -> None:
        """Insert a new item into the list.

        The item can be inserted at a specific `position`, or `before` or
        `after` an existing element.

        Args:
            item: The item to insert.
                If not a `ListItem`, it will be wrapped in one.
            position: The index at which to insert the item.
            before: An existing element to insert the item
                before.
            after: An existing element to insert the item
                after.

        Raises:
            ValueError: If no position (`position`, `before`, or `after`) is specified.
        """
        if not isinstance(item, ListItem):
            item = ListItem(item)
        if before is not None:
            before.insert(item, xmlposition=PREV_SIBLING)
        elif after is not None:
            after.insert(item, xmlposition=NEXT_SIBLING)
        elif position is not None:
            self.insert(item, position=position)
        else:
            raise ValueError("Position must be defined")

    def append_item(
        self,
        item: ListItem | str | Element | None,
    ) -> None:
        """Append a new item to the end of the list.

        Args:
            item: The item to append.
                If not a `ListItem`, it will be wrapped in one.
        """
        if not isinstance(item, ListItem):
            item = ListItem(item)
        self.append(item)

    def get_formatted_text(self, context: dict | None = None) -> str:
        """Return a formatted string representation of the list.

        Each list item is prefixed with "- " and indented.

        Args:
            context: A dictionary providing context for formatting. If
                `rst_mode` is True in the context, additional
                newlines are added for reStructuredText compatibility.

        Returns:
            str: The formatted text content of the list.
        """
        if context is None:
            context = {
                "document": None,
                "footnotes": [],
                "endnotes": [],
                "annotations": [],
                "rst_mode": False,
                "img_counter": 0,
                "images": [],
                "no_img_level": 0,
            }
        rst_mode = bool(context.get("rst_mode"))
        result = []
        if rst_mode:
            result.append("\n")
        list_header = self.list_header
        if list_header:
            text_header_buf = []
            for child in list_header.children:
                text = child.get_formatted_text(context)
                text_header_buf.append(text)
                text_sum = "".join(text_header_buf)
                text_sum = text_sum.strip("\n")
                # Indent the text
                text_sum = text_sum.replace("\n", "\n  ")
                text_sum = f"  {text_sum}\n"
                result.append(text_sum)
        for list_item in self.get_elements("text:list-item"):
            textbuf = []
            for child in list_item.children:
                text = child.get_formatted_text(context)
                tag = child.tag
                if tag == "text:h":
                    # A title in a list is a bug
                    return text
                if tag == "text:list" and not text.lstrip().startswith("-"):
                    # If the list didn't indent, don't either
                    # (inner title)
                    return text  # pragma: nocover
                textbuf.append(text)
            text_sum = "".join(textbuf)
            text_sum = text_sum.strip("\n")
            # Indent the text
            text_sum = text_sum.replace("\n", "\n  ")
            text_sum = f"- {text_sum}\n"
            result.append(text_sum)
        if rst_mode:
            result.append("\n")
        return "".join(result)

_properties class-attribute instance-attribute

_properties = (PropDef('style', 'text:style-name'),)

_tag class-attribute instance-attribute

_tag = 'text:list'

list_header property writable

list_header: ListHeader | None

Get or set the list header.

style instance-attribute

style = style

__init__

__init__(
    list_content: str
    | Element
    | Iterable[str | Element]
    | None = None,
    style: str | None = None,
    **kwargs: Any,
) -> None

Initialize the List.

A List can be initialized with content, which can be a single item (string or element) or an iterable of items.

Parameters:

Name Type Description Default
list_content str | Element | Iterable[str | Element] | None

The initial content of the list. Each item is wrapped in a ListItem.

None
style str | None

The name of the style to apply to the list.

None
**kwargs Any

Additional keyword arguments for the parent Element class.

{}
Source code in odfdo/list.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
def __init__(
    self,
    list_content: str | Element | Iterable[str | Element] | None = None,
    style: str | None = None,
    **kwargs: Any,
) -> None:
    """Initialize the List.

    A `List` can be initialized with content, which can be a single item
    (string or element) or an iterable of items.

    Args:
        list_content: The initial content of the list. Each item is wrapped in a
            `ListItem`.
        style: The name of the style to apply to the list.
        **kwargs: Additional keyword arguments for the parent `Element` class.
    """
    super().__init__(**kwargs)
    if self._do_init:
        if list_content:
            if isinstance(list_content, (Element, str)):
                self.append(ListItem(list_content))
            elif hasattr(list_content, "__iter__"):
                for item in list_content:
                    self.append(ListItem(item))
        if style is not None:
            self.style = style

append_item

append_item(item: ListItem | str | Element | None) -> None

Append a new item to the end of the list.

Parameters:

Name Type Description Default
item ListItem | str | Element | None

The item to append. If not a ListItem, it will be wrapped in one.

required
Source code in odfdo/list.py
260
261
262
263
264
265
266
267
268
269
270
271
272
def append_item(
    self,
    item: ListItem | str | Element | None,
) -> None:
    """Append a new item to the end of the list.

    Args:
        item: The item to append.
            If not a `ListItem`, it will be wrapped in one.
    """
    if not isinstance(item, ListItem):
        item = ListItem(item)
    self.append(item)

get_formatted_text

get_formatted_text(context: dict | None = None) -> str

Return a formatted string representation of the list.

Each list item is prefixed with “- ” and indented.

Parameters:

Name Type Description Default
context dict | None

A dictionary providing context for formatting. If rst_mode is True in the context, additional newlines are added for reStructuredText compatibility.

None

Returns:

Name Type Description
str str

The formatted text content of the list.

Source code in odfdo/list.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
def get_formatted_text(self, context: dict | None = None) -> str:
    """Return a formatted string representation of the list.

    Each list item is prefixed with "- " and indented.

    Args:
        context: A dictionary providing context for formatting. If
            `rst_mode` is True in the context, additional
            newlines are added for reStructuredText compatibility.

    Returns:
        str: The formatted text content of the list.
    """
    if context is None:
        context = {
            "document": None,
            "footnotes": [],
            "endnotes": [],
            "annotations": [],
            "rst_mode": False,
            "img_counter": 0,
            "images": [],
            "no_img_level": 0,
        }
    rst_mode = bool(context.get("rst_mode"))
    result = []
    if rst_mode:
        result.append("\n")
    list_header = self.list_header
    if list_header:
        text_header_buf = []
        for child in list_header.children:
            text = child.get_formatted_text(context)
            text_header_buf.append(text)
            text_sum = "".join(text_header_buf)
            text_sum = text_sum.strip("\n")
            # Indent the text
            text_sum = text_sum.replace("\n", "\n  ")
            text_sum = f"  {text_sum}\n"
            result.append(text_sum)
    for list_item in self.get_elements("text:list-item"):
        textbuf = []
        for child in list_item.children:
            text = child.get_formatted_text(context)
            tag = child.tag
            if tag == "text:h":
                # A title in a list is a bug
                return text
            if tag == "text:list" and not text.lstrip().startswith("-"):
                # If the list didn't indent, don't either
                # (inner title)
                return text  # pragma: nocover
            textbuf.append(text)
        text_sum = "".join(textbuf)
        text_sum = text_sum.strip("\n")
        # Indent the text
        text_sum = text_sum.replace("\n", "\n  ")
        text_sum = f"- {text_sum}\n"
        result.append(text_sum)
    if rst_mode:
        result.append("\n")
    return "".join(result)

get_item

get_item(
    position: int = 0, content: str | None = None
) -> Element | None

Get a single list item from the list.

Can retrieve an item by its position or by matching its text content. In nested lists, it returns the ListItem that directly contains the matched content.

Parameters:

Name Type Description Default
position int

The index of the item to retrieve. Defaults to 0.

0
content str | None

A regular expression to match against the text content of the items. If provided, position is ignored.

None

Returns:

Type Description
Element | None

Element | None: The matching ListItem element, or None if not found.

Source code in odfdo/list.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def get_item(
    self,
    position: int = 0,
    content: str | None = None,
) -> Element | None:
    """Get a single list item from the list.

    Can retrieve an item by its position or by matching its text content.
    In nested lists, it returns the `ListItem` that directly contains
    the matched content.

    Args:
        position: The index of the item to retrieve. Defaults to 0.
        content: A regular expression to match against
            the text content of the items. If provided, `position` is
            ignored.

    Returns:
        Element | None: The matching `ListItem` element, or `None` if not found.
    """
    # Custom implementation because of nested lists
    if content:
        # Don't search recursively but on the very own paragraph(s) of
        # each list item
        for paragraph in self.get_elements("descendant::text:p"):
            if paragraph.match(content):
                return paragraph.get_element("parent::text:list-item")
        return None
    return self._filtered_element("text:list-item", position)

get_items

get_items(content: str | None = None) -> list[Element]

Get all list items (ListItem) within the list.

Optionally filters items by their textual content.

Parameters:

Name Type Description Default
content str | None

A regular expression to match against the text content of the items.

None

Returns:

Type Description
list[Element]

list[Element]: A list of ListItem elements that match the criteria.

Source code in odfdo/list.py
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_items(self, content: str | None = None) -> list[Element]:
    """Get all list items (`ListItem`) within the list.

    Optionally filters items by their textual content.

    Args:
        content: A regular expression to match against
            the text content of the items.

    Returns:
        list[Element]: A list of `ListItem` elements that match the criteria.
    """
    return self._filtered_elements("text:list-item", content=content)

insert_item

insert_item(
    item: ListItem | str | Element | None,
    position: int | None = None,
    before: Element | None = None,
    after: Element | None = None,
) -> None

Insert a new item into the list.

The item can be inserted at a specific position, or before or after an existing element.

Parameters:

Name Type Description Default
item ListItem | str | Element | None

The item to insert. If not a ListItem, it will be wrapped in one.

required
position int | None

The index at which to insert the item.

None
before Element | None

An existing element to insert the item before.

None
after Element | None

An existing element to insert the item after.

None

Raises:

Type Description
ValueError

If no position (position, before, or after) is specified.

Source code in odfdo/list.py
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
def insert_item(
    self,
    item: ListItem | str | Element | None,
    position: int | None = None,
    before: Element | None = None,
    after: Element | None = None,
) -> None:
    """Insert a new item into the list.

    The item can be inserted at a specific `position`, or `before` or
    `after` an existing element.

    Args:
        item: The item to insert.
            If not a `ListItem`, it will be wrapped in one.
        position: The index at which to insert the item.
        before: An existing element to insert the item
            before.
        after: An existing element to insert the item
            after.

    Raises:
        ValueError: If no position (`position`, `before`, or `after`) is specified.
    """
    if not isinstance(item, ListItem):
        item = ListItem(item)
    if before is not None:
        before.insert(item, xmlposition=PREV_SIBLING)
    elif after is not None:
        after.insert(item, xmlposition=NEXT_SIBLING)
    elif position is not None:
        self.insert(item, position=position)
    else:
        raise ValueError("Position must be defined")

set_list_header

set_list_header(
    text_or_element: str
    | Element
    | Iterable[str | Element],
) -> None

Set the header of the list.

This method replaces any existing header paragraphs (text:p) with the provided content.

You may consider using the property List.list_header.

Parameters:

Name Type Description Default
text_or_element str | Element | Iterable[str | Element]

The content for the list header. Can be a single string or element, or an iterable of strings and/or elements.

required
Source code in odfdo/list.py
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
def set_list_header(
    self,
    text_or_element: str | Element | Iterable[str | Element],
) -> None:
    """Set the header of the list.

    This method replaces any existing header paragraphs (`text:p`) with
    the provided content.

    You may consider using the property List.list_header.

    Args:
        text_or_element: The content for the list header. Can be a single
            string or element, or an iterable of strings and/or elements.
    """
    self.list_header = text_or_element

ListHeader

Bases: ListMixin, Element

An header of a list, “text:list-header”.

Methods:

Name Description
__init__

Initialize the ListHeader, “text:list-header”.

Source code in odfdo/list.py
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
class ListHeader(ListMixin, Element):
    """An header of a list, "text:list-header"."""

    _tag = "text:list-header"

    def __init__(
        self,
        text_or_element: str | Element | Iterable[str | Element] | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize the ListHeader, "text:list-header".

        A "ListItem" can be initialized with text content or another element.

        Args:
            text_or_element: The initial content of the list item. If a
                string, a paragraph containing the text is created. If an
                element, it is appended as a child.
            **kwargs: Additional keyword arguments for the parent `Element` class.
        """
        super().__init__(**kwargs)
        if self._do_init:
            actual_list: list[str | Element] | tuple = []
            if text_or_element is None:
                pass
            elif isinstance(text_or_element, (str, Element)):
                actual_list = [text_or_element]
            elif isinstance(text_or_element, (list, tuple)):
                actual_list = text_or_element
            else:
                raise TypeError(f"Expected str or Element, not {text_or_element!r}")
            for item in reversed(actual_list):
                if isinstance(item, str):
                    paragraph = Paragraph(item)
                else:
                    paragraph = item
                self.insert(paragraph, FIRST_CHILD)

_tag class-attribute instance-attribute

_tag = 'text:list-header'

__init__

__init__(
    text_or_element: str
    | Element
    | Iterable[str | Element]
    | None = None,
    **kwargs: Any,
) -> None

Initialize the ListHeader, “text:list-header”.

A “ListItem” can be initialized with text content or another element.

Parameters:

Name Type Description Default
text_or_element str | Element | Iterable[str | Element] | None

The initial content of the list item. If a string, a paragraph containing the text is created. If an element, it is appended as a child.

None
**kwargs Any

Additional keyword arguments for the parent Element class.

{}
Source code in odfdo/list.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def __init__(
    self,
    text_or_element: str | Element | Iterable[str | Element] | None = None,
    **kwargs: Any,
) -> None:
    """Initialize the ListHeader, "text:list-header".

    A "ListItem" can be initialized with text content or another element.

    Args:
        text_or_element: The initial content of the list item. If a
            string, a paragraph containing the text is created. If an
            element, it is appended as a child.
        **kwargs: Additional keyword arguments for the parent `Element` class.
    """
    super().__init__(**kwargs)
    if self._do_init:
        actual_list: list[str | Element] | tuple = []
        if text_or_element is None:
            pass
        elif isinstance(text_or_element, (str, Element)):
            actual_list = [text_or_element]
        elif isinstance(text_or_element, (list, tuple)):
            actual_list = text_or_element
        else:
            raise TypeError(f"Expected str or Element, not {text_or_element!r}")
        for item in reversed(actual_list):
            if isinstance(item, str):
                paragraph = Paragraph(item)
            else:
                paragraph = item
            self.insert(paragraph, FIRST_CHILD)

ListItem

Bases: MDListItem, ListMixin, Element

An item of a list, “text:list-item”.

Methods:

Name Description
__init__

Initialize the ListItem, “text:list-item”.

__str__

Attributes:

Name Type Description
text_content
Source code in odfdo/list.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class ListItem(MDListItem, ListMixin, Element):
    """An item of a list, "text:list-item"."""

    _tag = "text:list-item"

    def __init__(
        self,
        text_or_element: str | Element | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize the ListItem, "text:list-item".

        A "ListItem" can be initialized with text content or another element.

        Args:
            text_or_element: The initial content of the list item. If a
                string, a paragraph containing the text is created. If an
                element, it is appended as a child.
            **kwargs: Additional keyword arguments for the parent `Element` class.
        """
        super().__init__(**kwargs)
        if self._do_init:
            if isinstance(text_or_element, str):
                self.text_content = text_or_element
            elif isinstance(text_or_element, Element):
                self.append(text_or_element)
            elif text_or_element is not None:
                raise TypeError(f"Expected str or Element, not {type(text_or_element)}")

    def __str__(self) -> str:
        self._md_initialize_level()
        return "\n".join(self._md_collect())

_tag class-attribute instance-attribute

_tag = 'text:list-item'

text_content instance-attribute

text_content = text_or_element

__init__

__init__(
    text_or_element: str | Element | None = None,
    **kwargs: Any,
) -> None

Initialize the ListItem, “text:list-item”.

A “ListItem” can be initialized with text content or another element.

Parameters:

Name Type Description Default
text_or_element str | Element | None

The initial content of the list item. If a string, a paragraph containing the text is created. If an element, it is appended as a child.

None
**kwargs Any

Additional keyword arguments for the parent Element class.

{}
Source code in odfdo/list.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def __init__(
    self,
    text_or_element: str | Element | None = None,
    **kwargs: Any,
) -> None:
    """Initialize the ListItem, "text:list-item".

    A "ListItem" can be initialized with text content or another element.

    Args:
        text_or_element: The initial content of the list item. If a
            string, a paragraph containing the text is created. If an
            element, it is appended as a child.
        **kwargs: Additional keyword arguments for the parent `Element` class.
    """
    super().__init__(**kwargs)
    if self._do_init:
        if isinstance(text_or_element, str):
            self.text_content = text_or_element
        elif isinstance(text_or_element, Element):
            self.append(text_or_element)
        elif text_or_element is not None:
            raise TypeError(f"Expected str or Element, not {type(text_or_element)}")

__str__

__str__() -> str
Source code in odfdo/list.py
71
72
73
def __str__(self) -> str:
    self._md_initialize_level()
    return "\n".join(self._md_collect())