Skip to main content

tree-sitter-xml-django

Django-XML template language grammar for tree-sitter.

Supports .xml and .rml files using Django template syntax embedded in XML.

Requirements

  • Node.js (for grammar generation)
  • Python 3.x (for Python bindings)
  • tree-sitter CLI

Usage

Python

import tree_sitter_xml_django as ts_xml_django
from tree_sitter import Language, Parser

language = Language(ts_xml_django.language())
parser = Parser(language)

tree = parser.parse(b"<root>{% if condition %}<child/>{% endif %}</root>")

Contributing

First-time setup

Requires Node.js, uv, and Python 3.10+.

# Install Node.js and Python dependencies
make dev

# Install pre-commit hooks
uvx pre-commit install --install-hooks

make dev runs npm install (tree-sitter CLI) and uv sync (Python dev dependencies) in one step.

Common tasks

Command Description
make generate Re-generate src/parser.c from grammar.js
make test Run tree-sitter corpus tests and Python tests
make package Build the Python distribution package

Releasing a new version

To publish a new version of the package to PyPI:

  1. Bump the version field in pyproject.toml (follows semver)
  2. Merge the change to main

CI will detect that the version has no corresponding git tag, publish the package to PyPI, and then push a vX.Y.Z tag to GitHub automatically. No manual tagging or release steps are needed.

If a merge to main does not include a version bump, the publish step is skipped silently.

The external scanner at src/scanner.c

Using the parser requires a binding in your target language that calls the generated file src/parser.c. For more complex "lookahead" rules, src/scanner.c provides a collection of methods that expose tokens referenced in parser.c. It is worth noting that parser.c cannot be edited directly without breaking the synchronisation between grammar.js and parser.c, but scanner.c can be edited so long as the method names don't change. The CI will only detect differences between the generated parser.c file and its grammar rules in grammar.js, and from the perspective of the parser, a change to the method logic doesn't change what it sees - only the method name that it calls.

Paired vs unpaired Django statements

Django template tags fall into two categories, and the grammar treats them very differently.

Unpaired statements (e.g. {% load %}, {% url %}, {% csrf_token %}) stand alone — no body, no closing tag. Because they require no context about what came before or after, the grammar handles them with a single catch-all rule (dj_unpaired_statement) that matches any {% identifier %} not claimed by a more specific rule. The tag name is deliberately not enumerated; knowing it adds nothing to the parse.

Paired statements with unique rules block, if-elif-else, for are unique Django tags with specific sequences that are handled separately to regular paired statements.

For all other paired tags (e.g. {% editable %}...{% endeditable %}), the external scanner maintains a stack of open Django tag names. When it encounters {% endXXX %}, it checks whether XXX is on the stack and, if so, emits a specialised close token that closes the corresponding dj_paired_statement. This means arbitrary third-party and project-specific paired tags are handled automatically without needing to enumerate them in the grammar.

N.B: The {% [tag] %} ... {% end[tag] %} convention is just that — a convention, not a rule of Django syntax. The scanner is deliberately lenient: a tag that has no matching {% endXXX %} in the remaining input is still treated as an unpaired statement rather than an error.

Not (yet) implemented

Query files (typically found at /queries/*.scm) are not implemented in this grammar. The only use case for this library at the time of writing is programmatic tree manipulation, not editor integration. Implement these if the grammar is ever adopted for editor use (syntax highlighting, language injection, symbol navigation).

Limitations

Whitespace or newlines before branch/close tags are not preserved as char_data

Leading whitespace (newlines, spaces) immediately before closing paired tags such as {% elif %}, or {% endXXX %} is consumed by the external scanner with skip=true as part of establishing the correct GLR parse path. When the whitespace immediately precedes one of these tags with nothing else on the same line, it does not appear as a char_data node in the parse tree:

{% if a %}
{% elif b %}...{% endif %}
{# the \n between if-body and {% elif %} is silently dropped #}

{% mytag %}
{% endmytag %}
{# the \n between open and close is silently dropped #}

<para>This is well{% if use_whitespace %} {% elif use_hyphen %}-{% endif %}known fact</para>
{# Resolves to "This is wellknown fact" when use_whitespace=True #}

When there is any other content on the same line before the branch/close tag, normal char_data nodes are produced for the surrounding whitespace:

{% if a %}
  <x/>
{% elif b %}...{% endif %}
{# \n after {% if a %} and \n after <x/> both become char_data nodes #}

This is a known limitation of the GLR path-resolution mechanism in the scanner. Removing the skip=true whitespace advance causes GLR to commit to the wrong parse path for certain body content (e.g. <!DOCTYPE> nodes), so the current approach is retained.

Generic paired tag whose close tag appears only inside a comment body

If the only occurrence of {% endXXX %} in the remaining input sits inside a {% comment %}…{% endcomment %} block, the scanner will treat it as a valid close tag, emit _generic_open_tag for XXX, and then never emit the matching _generic_close_tag (because the comment body is opaque and consumes the {% endXXX %}). This produces an ERROR node:

{# BUG: produces ERROR — {% endmytag %} inside comment is unreachable #}
{% mytag %}{% comment %}{% endmytag %}{% endcomment %}

The correct fix is to make the lookahead scanner skip {% comment %} blocks, but this is an uncommon pattern in practice and the added scanner complexity is not currently justified.

XML tags spanning Django conditional branches

Django templates sometimes use conditional blocks to select between variants of an XML structure, relying on the template engine to produce valid XML at render time even though the static source is not well-formed XML. Any XML tag that opens or closes across a Django branch boundary — in either direction — will produce incorrect parse trees:

{# open before block, close inside branch — ERROR nodes #}
<keepTogether>
    {% if necf_state %}
        </keepTogether>
    {% elif vic_state %}
        </keepTogether>
    {% endif %}

{# open inside branches, close after block — wrong tree, no ERROR nodes #}
{% if wide %}
    <keepTogether>
{% else %}
    <keepTogether>
{% endif %}
</keepTogether>

The second case appears to parse without ERROR nodes when the pattern sits at the document root, but in practice these templates have a wrapping parent element. Inside XML element content (_node), unpaired end_tag is not allowed, so the closing tag is silently consumed as the close of the parent element instead, producing a structurally wrong tree.

Adding unpaired start_tag/end_tag alternatives to regular XML content causes GLR ambiguity: the parser begins treating every ordinary opening tag as an unpaired node and orphans its close tag, breaking large amounts of valid XML.

The one exception is when both the mismatched open and close tags land inside Django statement bodies (e.g. inside a {% for %} or {% block %} body), because that context already allows unpaired tags:

{% for item in items %}
    {% if wide %}<keepTogether>{% else %}<keepTogether>{% endif %}
    <para>{{ item }}</para>
    </keepTogether>    {# inside the for body — parses without error #}
{% endfor %}

If a fix becomes necessary: the principled path is an external scanner (C code in src/scanner.c) that maintains a stack of open XML element names. When it encounters a </tag> whose name is not on the stack it emits a distinct _orphan_end_tag token rather than a normal end_tag, allowing the grammar to admit it in _node context without competing with legitimate close tags.

License

BSD 3-Clause

Metadata

Release files for tree-sitter-xml-django 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tree-sitter-xml-django 0.6.0
File Size Uploaded Provenance
tree_sitter_xml_django-0.6.0.tar.gz 66.4 kB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for tree-sitter-xml-django 0.6.0
File
tree_sitter_xml_django-0.6.0-cp310-abi3-win_arm64.whl CPython 3.10 abi3 Windows ARM64 Details
tree_sitter_xml_django-0.6.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
tree_sitter_xml_django-0.6.0-cp310-abi3-musllinux_1_2_x86_64.whl CPython 3.10 abi3 Linux musl 1.2+ x86-64 Details
tree_sitter_xml_django-0.6.0-cp310-abi3-musllinux_1_2_aarch64.whl CPython 3.10 abi3 Linux musl 1.2+ ARM64 Details
tree_sitter_xml_django-0.6.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 abi3 Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 Details
tree_sitter_xml_django-0.6.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl CPython 3.10 abi3 Linux glibc 2.17+ ARM64, Linux glibc 2.28+ ARM64 Details
tree_sitter_xml_django-0.6.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
tree_sitter_xml_django-0.6.0-cp310-abi3-macosx_10_9_x86_64.whl CPython 3.10 abi3 macOS 10.9+ x86-64 Details

Total release size: 481.2 kB

Release files / tree_sitter_xml_django-0.6.0.tar.gz

Download URL tree_sitter_xml_django-0.6.0.tar.gz
Size 66.4 kB
Tags Source
SHA-256 checksum
How to use checksums
26718d9f11df0ce0cb31c4627f7f96b749e90144ade91e107ac323fad1943cb6
BLAKE2b-256 checksum
How to use checksums
1f90eb3a3e76476a6178c84e3931fea365f1b380e45c17ec3b95b29678e18e77
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / tree_sitter_xml_django-0.6.0-cp310-abi3-win_arm64.whl

Download URL tree_sitter_xml_django-0.6.0-cp310-abi3-win_arm64.whl
Size 37.7 kB
Tags CPython 3.10 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
cc37945a30ca3eb3d54b5dc627b6a9530d3d32136992001f615ae66161a6f30a
BLAKE2b-256 checksum
How to use checksums
b7e42a31a18f65965d848077ed1925767434733a64f11f130a241f797ec383a9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / tree_sitter_xml_django-0.6.0-cp310-abi3-win_amd64.whl

Download URL tree_sitter_xml_django-0.6.0-cp310-abi3-win_amd64.whl
Size 37.7 kB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
5cd46718e9bb1a0888288ffa9d61fa1a5f5d8f0bdbd359eb9ea37a426c311231
BLAKE2b-256 checksum
How to use checksums
f7a431446dc7e33da0d9d45af06826ba45799e2f40ce6fe5c3e71e434130d6f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / tree_sitter_xml_django-0.6.0-cp310-abi3-musllinux_1_2_x86_64.whl

Download URL tree_sitter_xml_django-0.6.0-cp310-abi3-musllinux_1_2_x86_64.whl
Size 65.0 kB
Tags CPython 3.10 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
2067ebb52bf37ff78c6ece1eb7655061680e18ebd53c3903c76182b29970902e
BLAKE2b-256 checksum
How to use checksums
ff7116b913fbbe505af464eb6a2598c398732982459a0dbb25420cdbe4e2d18c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / tree_sitter_xml_django-0.6.0-cp310-abi3-musllinux_1_2_aarch64.whl

Download URL tree_sitter_xml_django-0.6.0-cp310-abi3-musllinux_1_2_aarch64.whl
Size 67.3 kB
Tags CPython 3.10 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
2ed6eec15572e4d1e620ce993809ff223971ec01bf7ef249d246895cef620fa6
BLAKE2b-256 checksum
How to use checksums
6f5c398f07710d5b8a1381f9b9c292830abed316e8ec2170b23ffc7270ecd610
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / tree_sitter_xml_django-0.6.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

Download URL tree_sitter_xml_django-0.6.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 65.0 kB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
331474ab167c650646c2b320d04c2860d9968efc54595bf1fdb5b6ca42cb80f6
BLAKE2b-256 checksum
How to use checksums
289662571ed4d651f2a0ba4e9c46137f79d7ec8b03a07cb8d7c37bc53d025f35
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / tree_sitter_xml_django-0.6.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

Download URL tree_sitter_xml_django-0.6.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
Size 68.1 kB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
dc84a12569472d594333c04d840ef5f0df165e15157be0a5fd7f2692cb137b3e
BLAKE2b-256 checksum
How to use checksums
1a1d4f3fa0bb3f42cb7433282f4f052ea2e252a34c5712769099b6527ef81d3b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / tree_sitter_xml_django-0.6.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL tree_sitter_xml_django-0.6.0-cp310-abi3-macosx_11_0_arm64.whl
Size 37.7 kB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
cacb34f4565c93c282f43e543ab805f5623039e0547c373c40a169aae07d333c
BLAKE2b-256 checksum
How to use checksums
9aa062a40a59f362c6388d1432ce81028cf5c3e32609a6ddf5286cf066a5612a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / tree_sitter_xml_django-0.6.0-cp310-abi3-macosx_10_9_x86_64.whl

Download URL tree_sitter_xml_django-0.6.0-cp310-abi3-macosx_10_9_x86_64.whl
Size 36.4 kB
Tags CPython 3.10 abi3 macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
dabc5a2e4af15274d4c993832525a73315d7a68e89e5f43803a3b3a5a221805d
BLAKE2b-256 checksum
How to use checksums
cd81be93590b8a902e5cc9624fd6c431d2c857ba07216c063a9cbb96250b0838
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Security for tree-sitter-xml-django 0.6.0

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.

Full provenance

All 9 files in this release have been uploaded with provenance metadata.

Select from 9 files

Attestation for tree_sitter_xml_django-0.6.0.tar.gz

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 Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page