Spans and Trees
A small Python library for converting between XML trees and a span-based structure. This can be useful for extracting sections of text from XML documents and doing special things with some of the tags.
The three functions are tree_to_spans, spans_to_tree and spans_to_passages for converting between an ElementTree element and text with a list of spans. Examples are shown below.
Install
pip install spans-and-trees
tree_to_spans
First create a little example XML tree to convert.
import xml.etree.ElementTree as ET
xmlstring = "<doc><title>Important document</title><contents>Empty</contents></doc>"
root = ET.ElementTree(ET.fromstring(xmlstring)).getroot()
Then use the tree_to_spans function to convert the XML document into the text content with spans.
from spans_and_trees import tree_to_spans
text, spans = tree_to_spans(root)
print(text) # Important documentEmpty
print(spans) # [(0, 18, 'title', {}), (18, 5, 'contents', {})]
The format of the spans is a Span, a namedtuple of length 4 (importable from spans_and_trees). The element contents are:
start- The start location of the spanlength- The length of the spantag- The tag of the spanattrib- A dictionary of the attributes of the span.
spans_to_tree
Now we create a dummy document with a block of text and a span at particular offset.
from spans_and_trees import spans_to_tree
text = 'The quick brown fox jumped over the lazy dog'
spans = [ (10,5,'colour',{'dummy_attrib':'5'}) ] # The span starts at 10, has length of 5, is a 'colour' tag and has a dummy attribute.
root = spans_to_tree(text, spans)
print(type(root)) # <class 'xml.etree.ElementTree.Element'>
We can check the XML tree that has been created:
xmlstr = ET.tostring(root)
print(xmlstr) # b'<tree>The quick <colour dummy_attrib="5">brown</colour> fox jumped over the lazy dog</tree>'
The root element's tag defaults to "tree", since spans don't record a tag for the whole document (tree_to_spans doesn't include one either). Pass root_tag to use something else, e.g. spans_to_tree(text, spans, root_tag="doc").
spans_to_passages
spans_to_passages takes the text/spans output of tree_to_spans and splits it into a list of text passages, one per split_tags element (e.g. p, sec), dropping the content of any ignore_tags element (e.g. table), and attaching any keep_tags spans (e.g. bold) that fall within each passage.
from spans_and_trees import tree_to_spans, spans_to_passages
xmlstring = """
<article>
<sec>
<title>Introduction</title>
<p>This is <bold>important</bold> background text.</p>
<table-wrap>Some table content we want to ignore.</table-wrap>
<p>A second paragraph follows.</p>
</sec>
</article>
"""
root = ET.ElementTree(ET.fromstring(xmlstring)).getroot()
text, spans = tree_to_spans(root)
passages = spans_to_passages(text, spans, ignore_tags={'table-wrap'}, split_tags={'title','p'}, keep_tags={'bold'})
for p in passages:
print(p)
# {'start': 5, 'end': 17, 'text': 'Introduction', 'spans': []}
# {'start': 20, 'end': 54, 'text': 'This is important background text.', 'spans': [(8, 9, 'bold', {})]}
# {'start': 97, 'end': 124, 'text': 'A second paragraph follows.', 'spans': []}
Each passage's start/end are offsets into the original text; each attached span's offsets are relative to the passage's own text.
Metadata
Release files for spans-and-trees 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| spans_and_trees-0.4.0.tar.gz | 7.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| spans_and_trees-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 13.6 kB
Release files / spans_and_trees-0.4.0.tar.gz
| Download URL | spans_and_trees-0.4.0.tar.gz |
|---|---|
| Size | 7.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a46d9613c72896ac433a485e9b44521da3673d772fe21cb1ed5c5e011e0b3cc6
|
|
BLAKE2b-256 checksum How to use checksums |
5f11c4ada3a2fc3ebb836cb46b58b9c800a8569b7ac2f042b762059ac6fe8561
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency logRelease files / spans_and_trees-0.4.0-py3-none-any.whl
| Download URL | spans_and_trees-0.4.0-py3-none-any.whl |
|---|---|
| Size | 6.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6b4b3eafd8114ee261a520d2335c2a0647e3bb9d9fed6cb83425e515cc629769
|
|
BLAKE2b-256 checksum How to use checksums |
5781598bc0d7225bab3a93a484ca4145bd8ae9e0fd865b48947d2b83c44d7112
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency log