Install & Compatibility
Where this runs
tested against v3.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.920 runs
installs and imports cleanly · install 0.0s · import 0.182s · 30MB
glibcpy 3.10–3.920 runs
installs and imports cleanly · install 2.3s · import 0.166s · 30MB
31MB installed
● package 31MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
main
✓ from xmldiff import main
Provides the primary diffing functions like `diff_files`, `diff_texts`, `diff_trees`, and `patch_file`.
formatting
✓ from xmldiff import formatting
Contains built-in formatters like `XMLFormatter` for controlling output style.
etree
✓ from lxml import etree
Essential for creating and manipulating XML tree objects when using `diff_trees` or processing formatter output.
This quickstart demonstrates how to use `xmldiff` to compare two XML files. It creates two temporary XML files, then calls `main.diff_files()` with `formatting.XMLFormatter` to get a human-readable XML output with differences marked. It also shows how to get the raw 'edit script' (list of actions) by calling `main.diff_trees()` with `lxml` Element objects directly.
import os
from lxml import etree
from xmldiff import main, formatting
# Create dummy XML files
xml1_content = """
<root>
<item id="1">Value A</item>
<item id="2">Value B</item>
</root>
"""
xml2_content = """
<root>
<item id="1">Value A - Changed</item>
<item id="3">Value C</item>
<item id="2" status="new">Value B</item>
</root>
"""
with open('file1.xml', 'w') as f:
f.write(xml1_content)
with open('file2.xml', 'w') as f:
f.write(xml2_content)
# Diff two XML files and format the output as XML with diff tags
diff_output_xml = main.diff_files(
'file1.xml',
'file2.xml',
formatter=formatting.XMLFormatter(pretty_print=True)
)
print("--- XML Diff ---")
print(diff_output_xml)
# Clean up dummy files
os.remove('file1.xml')
os.remove('file2.xml')
# Example using diff_trees with lxml elements directly
tree1 = etree.fromstring(xml1_content)
tree2 = etree.fromstring(xml2_content)
diff_actions = main.diff_trees(tree1, tree2)
print("\n--- Edit Script (List of Actions) ---")
for action in diff_actions:
print(action)
xmldiff --version
Debug
Known issues
breakingxmldiff 2.0 introduced a complete, ground-up rewrite of the library. This change included a new API, different output formats, and was initially significantly slower than previous 0.x/1.x versions. Code written for 0.x/1.x is incompatible with 2.x and later.fixReview the official documentation for xmldiff 2.x and rewrite code to use the new API, particularly `main.diff_files`, `diff_texts`, `diff_trees`, and `formatting` modules.
affects: 2.0.0 and later
gotchaThe `xmldiff` library expects `lxml` ElementTree instances when using functions like `diff_trees()`. Passing standard `xml.etree.ElementTree` objects will result in errors or unexpected behavior, requiring conversion to `lxml` types first.fixEnsure that any XML trees passed to `xmldiff` functions are `lxml.etree._Element` or `lxml.etree._ElementTree` objects. Convert from `xml.etree` if necessary, e.g., by parsing XML strings directly with `lxml.etree.fromstring` or `lxml.etree.parse`.
affects: All 2.x versions
gotchaThe output (edit script or formatted XML) generated by `xmldiff` can change between minor versions. The library explicitly states that there are 'no guarantees' the output will be the same across versions, as it's under 'rapid development'. This means automated tests relying on exact output matches may break.fixWhen writing tests or parsers for `xmldiff` output, focus on the semantic correctness of the changes rather than exact string or action sequence matches. Consider flexible parsing or asserting on the presence/absence of expected changes rather than full output equality.
affects: All 2.x versions
gotchaPrior to version 2.6, `xmldiff` had limited or buggy handling of XML namespaces, potentially leading to 'Unknown namespace prefix' errors. While improved in 2.6, changing the URI of an existing namespace prefix is still not supported and will raise an error.fixUpgrade to `xmldiff` version 2.6.0 or higher for improved namespace handling. Avoid scenarios where a namespace prefix's URI is changed between the two XML documents; instead, ensure prefixes map to consistent URIs or handle such cases manually before diffing.
affects: < 2.6.0 (Namespace bugs), All versions (Changing URI for prefix)
gotchaThe `ratio-mode` (`accurate`, `faster`, `fast`) and `--fast-match` options can significantly impact the diff's accuracy and performance. The `fast` mode, in particular, yields less accurate results, which might be acceptable for speed but could miss subtle changes.fixUnderstand the trade-offs between performance and accuracy when selecting `ratio-mode` or `--fast-match`. For critical diffing where every change must be detected, use `accurate` mode. For large files where an approximate diff is sufficient, faster modes can be considered, but be aware of potential missed details.
affects: All 2.x versions
Errors
Common errors & fixes
ModuleNotFoundError: No module named 'xmldiff'
The 'xmldiff' package has not been installed in the active Python environment.
ImportError: cannot import name 'diff_texts' from 'xmldiff'
The main diffing functions like 'diff_texts' and 'diff_files' are located within the 'xmldiff.main' submodule, not directly under the top-level 'xmldiff' package.
fixfrom xmldiff.main import diff_texts
TypeError: diff_texts() missing 1 required positional argument: 'b'
The 'diff_texts' (or 'diff_files') function requires two XML inputs (the old and new versions) for comparison, but only one or none was provided.
fixdiff_texts(old_xml_string, new_xml_string)
lxml.etree.XMLSyntaxError: Document is empty
The input provided to xmldiff's functions (e.g., diff_texts, diff_files) is not valid or well-formed XML, or is an empty string/file, which lxml cannot parse.
fixEnsure that the input strings or files contain valid, non-empty, and well-formed XML documents.
Upgrade
Version history
3.0latest on PyPI · released Jun 11, 2026
Audit
Dependencies
lxmlrequiredRequired for parsing and manipulating XML trees, central to xmldiff's functionality.
diff-match-patchoptionalOptional dependency for faster text comparisons in certain scenarios.