// the find
tree-sitter/py-tree-sitter
Python bindings to the Tree-sitter parsing library
Official Python bindings for tree-sitter, the incremental parsing library used by editors and code analysis tools. If you're building anything that needs to parse or analyze source code in Python — linters, formatters, code search, AST diffing — this is the right starting point. It wraps the C library directly and ships pre-compiled wheels so you don't need to deal with build tooling in most cases.
Pre-compiled wheels for all major platforms mean pip install tree-sitter just works, which is genuinely rare for a C extension. The incremental parsing API (pass the old tree to parser.parse) is properly exposed and fast — reparsing a changed file does much less work than a full parse. The QueryCursor distinguishes between captures() and matches(), which matters when your S-expression query has related captures; most wrappers conflate the two. Test coverage across language, node, parser, query, and tree cursor in separate files suggests the bindings are tracked carefully against the upstream C API.
The binding layer is hand-written C, one file per type — there's no codegen or abstraction, so whenever tree-sitter adds API surface someone has to manually add the C glue and the .pyi stubs. The incremental edit API requires you to pass exact byte offsets and points before and after every change, which is error-prone to compute correctly in practice; the README example is minimal and doesn't show a realistic diff-to-edit-list workflow. Language grammars are separate packages, so you're always managing a version matrix between tree-sitter, py-tree-sitter, and each tree-sitter-{lang} wheel — breakage between releases is a known source of confusion. No async support for the read callable, so parsing very large files with a streaming source blocks the event loop.