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.5.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.5.0
File Size Uploaded Provenance
tree_sitter_xml_django-0.5.0.tar.gz 67.2 kB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for tree-sitter-xml-django 0.5.0
File
tree_sitter_xml_django-0.5.0-cp310-abi3-win_arm64.whl CPython 3.10 abi3 Windows ARM64 Details
tree_sitter_xml_django-0.5.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
tree_sitter_xml_django-0.5.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.5.0-cp310-abi3-musllinux_1_2_aarch64.whl CPython 3.10 abi3 Linux musl 1.2+ ARM64 Details
tree_sitter_xml_django-0.5.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64, Linux glibc 2.28+ x86-64 Details
tree_sitter_xml_django-0.5.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.5.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
tree_sitter_xml_django-0.5.0-cp310-abi3-macosx_10_9_x86_64.whl CPython 3.10 abi3 macOS 10.9+ x86-64 Details

Total release size: 481.3 kB

Release files / tree_sitter_xml_django-0.5.0.tar.gz

Download URL tree_sitter_xml_django-0.5.0.tar.gz
Size 67.2 kB
Tags Source
SHA-256 checksum
How to use checksums
94b838e779cc1f593ccf02154315a919469c12c042ffd46be6e018e74f0d7896
BLAKE2b-256 checksum
How to use checksums
70450a04bcfc26bd57763e14b22142646f89044787ce0c010fbfec71bb318203
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 15, 2026.

Transparency log

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

Download URL tree_sitter_xml_django-0.5.0-cp310-abi3-win_arm64.whl
Size 36.5 kB
Tags CPython 3.10 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
b7d5fd113123c65f88f4a4e41e28b5cd72e61f36c5398d3b575cf66d5fbf9421
BLAKE2b-256 checksum
How to use checksums
ab29791602a7600f7a9a5841731d20b8d8f2864d032d7e5be643505b81420d31
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 15, 2026.

Transparency log

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

Download URL tree_sitter_xml_django-0.5.0-cp310-abi3-win_amd64.whl
Size 37.8 kB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
c484f05b8f1072395962fcbebce88760d932558e19ded315bb19a6717f39dd5d
BLAKE2b-256 checksum
How to use checksums
f75c8e4d74f51d3085ea2e7c135d72f5b11ef40583f015dedd6f2a353a16db51
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 15, 2026.

Transparency log

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

Download URL tree_sitter_xml_django-0.5.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
b7dee6a9297e2913bd645f749713dfe4838669c31703e1cd400791e2b23a24f0
BLAKE2b-256 checksum
How to use checksums
cedba2de054a84b11cfbe7590548d6a45b76ad5080e623516df5e95cf17d27d8
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 15, 2026.

Transparency log

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

Download URL tree_sitter_xml_django-0.5.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
7c917bcbb274a8276a1f0a8f38ac811dddb1784bd3a5eeefce1644ab285a16e5
BLAKE2b-256 checksum
How to use checksums
198c6915bedbec4601ade833533a7ecec184051e0c4d6e8eb941d18d974f12e9
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 15, 2026.

Transparency log

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

Download URL tree_sitter_xml_django-0.5.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Size 65.1 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
02df06b11c8878ef7fff6d6f4e4241a0878fe393ba20ef6f1c2a539d19e4f322
BLAKE2b-256 checksum
How to use checksums
eb836c2d6e799eaf861abe9126a97c27d6498e00bc6eba6ddf587dd859230911
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 15, 2026.

Transparency log

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

Download URL tree_sitter_xml_django-0.5.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
912123deed6661ecd268579ea50be0354e6c95d389619608551c418189b61f69
BLAKE2b-256 checksum
How to use checksums
913f579ce6a384241f70d2667245de5ec65e582db9a4dd6a5fe3bb01fd5a8b2d
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 15, 2026.

Transparency log

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

Download URL tree_sitter_xml_django-0.5.0-cp310-abi3-macosx_11_0_arm64.whl
Size 37.8 kB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
1b6abaf8c750818814c070f6dfd1e349af4e1835ef53e37a3acbf5f818434b4c
BLAKE2b-256 checksum
How to use checksums
63faf2e3b8c35efa5e7aa369afee99f5e180579cd090c24ee0303cc2ef428069
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 15, 2026.

Transparency log

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

Download URL tree_sitter_xml_django-0.5.0-cp310-abi3-macosx_10_9_x86_64.whl
Size 36.5 kB
Tags CPython 3.10 abi3 macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
da01bba82a779ebeeb5640ddd50a2e37f0c1b1805fbccb96d67172f74c9c7e40
BLAKE2b-256 checksum
How to use checksums
67f4fb4484244482fec44ae3639e88544877d56fa3c10b8eb6f63784a333bcce
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 15, 2026.

Transparency log

Security for tree-sitter-xml-django 0.5.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.5.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 Sep 15, 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