{"id":13578960,"url":"https://github.com/tree-sitter/py-tree-sitter","last_synced_at":"2025-05-13T17:14:10.357Z","repository":{"id":37773910,"uuid":"176665172","full_name":"tree-sitter/py-tree-sitter","owner":"tree-sitter","description":"Python bindings to the Tree-sitter parsing library","archived":false,"fork":false,"pushed_at":"2025-03-30T10:03:23.000Z","size":483,"stargazers_count":1041,"open_issues_count":10,"forks_count":126,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-04-25T14:51:00.057Z","etag":null,"topics":["binding","python","tree-sitter"],"latest_commit_sha":null,"homepage":"https://tree-sitter.github.io/py-tree-sitter/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tree-sitter.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-03-20T06:00:07.000Z","updated_at":"2025-04-25T07:17:26.000Z","dependencies_parsed_at":"2022-07-07T11:14:55.191Z","dependency_job_id":"c7c19bb0-cc04-44a0-a4cb-d80b16160a2d","html_url":"https://github.com/tree-sitter/py-tree-sitter","commit_stats":{"total_commits":266,"total_committers":31,"mean_commits":8.580645161290322,"dds":0.7706766917293233,"last_synced_commit":"3aa49bfdc95d60a5b46f7e68bab19b6642643997"},"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tree-sitter%2Fpy-tree-sitter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tree-sitter%2Fpy-tree-sitter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tree-sitter%2Fpy-tree-sitter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tree-sitter%2Fpy-tree-sitter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tree-sitter","download_url":"https://codeload.github.com/tree-sitter/py-tree-sitter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253990499,"owners_count":21995776,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["binding","python","tree-sitter"],"created_at":"2024-08-01T15:01:35.352Z","updated_at":"2025-05-13T17:14:05.344Z","avatar_url":"https://github.com/tree-sitter.png","language":"C","funding_links":[],"categories":["C","Language bindings"],"sub_categories":["Others"],"readme":"# Python Tree-sitter\n\n[![CI][ci]](https://github.com/tree-sitter/py-tree-sitter/actions/workflows/ci.yml)\n[![pypi][pypi]](https://pypi.org/project/tree-sitter/)\n[![docs][docs]](https://tree-sitter.github.io/py-tree-sitter/)\n\nThis module provides Python bindings to the [tree-sitter] parsing library.\n\n## Installation\n\nThe package has no library dependencies and provides pre-compiled wheels for all major platforms.\n\n\u003e [!NOTE]\n\u003e If your platform is not currently supported, please submit an [issue] on GitHub.\n\n```sh\npip install tree-sitter\n```\n\n## Usage\n\n### Setup\n\n#### Install languages\n\nTree-sitter language implementations also provide pre-compiled binary wheels.\nLet's take [Python][tree-sitter-python] as an example.\n\n```sh\npip install tree-sitter-python\n```\n\nThen, you can load it as a `Language` object:\n\n```python\nimport tree_sitter_python as tspython\nfrom tree_sitter import Language, Parser\n\nPY_LANGUAGE = Language(tspython.language())\n```\n\n### Basic parsing\n\nCreate a `Parser` and configure it to use a language:\n\n```python\nparser = Parser(PY_LANGUAGE)\n```\n\nParse some source code:\n\n```python\ntree = parser.parse(\n    bytes(\n        \"\"\"\ndef foo():\n    if bar:\n        baz()\n\"\"\",\n        \"utf8\"\n    )\n)\n```\n\nIf you have your source code in some data structure other than a bytes object,\nyou can pass a \"read\" callable to the parse function.\n\nThe read callable can use either the byte offset or point tuple to read from\nbuffer and return source code as bytes object. An empty bytes object or None\nterminates parsing for that line. The bytes must be encoded as UTF-8 or UTF-16.\n\nFor example, to use the byte offset with UTF-8 encoding:\n\n```python\nsrc = bytes(\n    \"\"\"\ndef foo():\n    if bar:\n        baz()\n\"\"\",\n    \"utf8\",\n)\n\n\ndef read_callable_byte_offset(byte_offset, point):\n    return src[byte_offset : byte_offset + 1]\n\n\ntree = parser.parse(read_callable_byte_offset, encoding=\"utf8\")\n```\n\nAnd to use the point:\n\n```python\nsrc_lines = [\"\\n\", \"def foo():\\n\", \"    if bar:\\n\", \"        baz()\\n\"]\n\n\ndef read_callable_point(byte_offset, point):\n    row, column = point\n    if row \u003e= len(src_lines) or column \u003e= len(src_lines[row]):\n        return None\n    return src_lines[row][column:].encode(\"utf8\")\n\n\ntree = parser.parse(read_callable_point, encoding=\"utf8\")\n```\n\nInspect the resulting `Tree`:\n\n```python\nroot_node = tree.root_node\nassert root_node.type == 'module'\nassert root_node.start_point == (1, 0)\nassert root_node.end_point == (4, 0)\n\nfunction_node = root_node.children[0]\nassert function_node.type == 'function_definition'\nassert function_node.child_by_field_name('name').type == 'identifier'\n\nfunction_name_node = function_node.children[1]\nassert function_name_node.type == 'identifier'\nassert function_name_node.start_point == (1, 4)\nassert function_name_node.end_point == (1, 7)\n\nfunction_body_node = function_node.child_by_field_name(\"body\")\n\nif_statement_node = function_body_node.child(0)\nassert if_statement_node.type == \"if_statement\"\n\nfunction_call_node = if_statement_node.child_by_field_name(\"consequence\").child(0).child(0)\nassert function_call_node.type == \"call\"\n\nfunction_call_name_node = function_call_node.child_by_field_name(\"function\")\nassert function_call_name_node.type == \"identifier\"\n\nfunction_call_args_node = function_call_node.child_by_field_name(\"arguments\")\nassert function_call_args_node.type == \"argument_list\"\n\n\nassert str(root_node) == (\n    \"(module \"\n        \"(function_definition \"\n            \"name: (identifier) \"\n            \"parameters: (parameters) \"\n            \"body: (block \"\n                \"(if_statement \"\n                    \"condition: (identifier) \"\n                    \"consequence: (block \"\n                        \"(expression_statement (call \"\n                            \"function: (identifier) \"\n                            \"arguments: (argument_list))))))))\"\n)\n```\n\nOr, to use the byte offset with UTF-16 encoding:\n\n```python\nparser.language = JAVASCRIPT\nsource_code = bytes(\"'😎' \u0026\u0026 '🐍'\", \"utf16\")\n\ndef read(byte_position, _):\n    return source_code[byte_position: byte_position + 2]\n\ntree = parser.parse(read, encoding=\"utf16\")\nroot_node = tree.root_node\nstatement_node = root_node.children[0]\nbinary_node = statement_node.children[0]\nsnake_node = binary_node.children[2]\nsnake = source_code[snake_node.start_byte:snake_node.end_byte]\n\nassert binary_node.type == \"binary_expression\"\nassert snake_node.type == \"string\"\nassert snake.decode(\"utf16\") == \"'🐍'\"\n```\n\n### Walking syntax trees\n\nIf you need to traverse a large number of nodes efficiently, you can use\na `TreeCursor`:\n\n```python\ncursor = tree.walk()\n\nassert cursor.node.type == \"module\"\n\nassert cursor.goto_first_child()\nassert cursor.node.type == \"function_definition\"\n\nassert cursor.goto_first_child()\nassert cursor.node.type == \"def\"\n\n# Returns `False` because the `def` node has no children\nassert not cursor.goto_first_child()\n\nassert cursor.goto_next_sibling()\nassert cursor.node.type == \"identifier\"\n\nassert cursor.goto_next_sibling()\nassert cursor.node.type == \"parameters\"\n\nassert cursor.goto_parent()\nassert cursor.node.type == \"function_definition\"\n```\n\n\u003e [!IMPORTANT]\n\u003e Keep in mind that the cursor can only walk into children of the node that it started from.\n\nSee [examples/walk_tree.py] for a complete example of iterating over every node in a tree.\n\n### Editing\n\nWhen a source file is edited, you can edit the syntax tree to keep it in sync with\nthe source:\n\n```python\nnew_src = src[:5] + src[5 : 5 + 2].upper() + src[5 + 2 :]\n\ntree.edit(\n    start_byte=5,\n    old_end_byte=5,\n    new_end_byte=5 + 2,\n    start_point=(0, 5),\n    old_end_point=(0, 5),\n    new_end_point=(0, 5 + 2),\n)\n```\n\nThen, when you're ready to incorporate the changes into a new syntax tree,\nyou can call `Parser.parse` again, but pass in the old tree:\n\n```python\nnew_tree = parser.parse(new_src, tree)\n```\n\nThis will run much faster than if you were parsing from scratch.\n\nThe `Tree.changed_ranges` method can be called on the _old_ tree to return\nthe list of ranges whose syntactic structure has been changed:\n\n```python\nfor changed_range in tree.changed_ranges(new_tree):\n    print(\"Changed range:\")\n    print(f\"  Start point {changed_range.start_point}\")\n    print(f\"  Start byte {changed_range.start_byte}\")\n    print(f\"  End point {changed_range.end_point}\")\n    print(f\"  End byte {changed_range.end_byte}\")\n```\n\n### Pattern-matching\n\nYou can search for patterns in a syntax tree using a [tree query]:\n\n```python\nquery = PY_LANGUAGE.query(\n    \"\"\"\n(function_definition\n  name: (identifier) @function.def\n  body: (block) @function.block)\n\n(call\n  function: (identifier) @function.call\n  arguments: (argument_list) @function.args)\n\"\"\"\n)\n```\n\n#### Captures\n\n```python\ncaptures = query.captures(tree.root_node)\nassert len(captures) == 4\nassert captures[\"function.def\"][0] == function_name_node\nassert captures[\"function.block\"][0] == function_body_node\nassert captures[\"function.call\"][0] == function_call_name_node\nassert captures[\"function.args\"][0] == function_call_args_node\n```\n\n#### Matches\n\n```python\nmatches = query.matches(tree.root_node)\nassert len(matches) == 2\n\n# first match\nassert matches[0][1][\"function.def\"] == [function_name_node]\nassert matches[0][1][\"function.block\"] == [function_body_node]\n\n# second match\nassert matches[1][1][\"function.call\"] == [function_call_name_node]\nassert matches[1][1][\"function.args\"] == [function_call_args_node]\n```\n\nThe difference between the two methods is that `Query.matches()` groups captures into matches,\nwhich is much more useful when your captures within a query relate to each other.\n\nTo try out and explore the code referenced in this README, check out [examples/usage.py].\n\n[tree-sitter]: https://tree-sitter.github.io/tree-sitter/\n[issue]: https://github.com/tree-sitter/py-tree-sitter/issues/new\n[tree-sitter-python]: https://github.com/tree-sitter/tree-sitter-python\n[tree query]: https://tree-sitter.github.io/tree-sitter/using-parsers/queries\n[ci]: https://img.shields.io/github/actions/workflow/status/tree-sitter/py-tree-sitter/ci.yml?logo=github\u0026label=CI\n[pypi]: https://img.shields.io/pypi/v/tree-sitter?logo=pypi\u0026logoColor=ffd242\u0026label=PyPI\n[docs]: https://img.shields.io/github/deployments/tree-sitter/py-tree-sitter/github-pages?logo=sphinx\u0026label=Docs\n[examples/walk_tree.py]: https://github.com/tree-sitter/py-tree-sitter/blob/master/examples/walk_tree.py\n[examples/usage.py]: https://github.com/tree-sitter/py-tree-sitter/blob/master/examples/usage.py\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftree-sitter%2Fpy-tree-sitter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftree-sitter%2Fpy-tree-sitter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftree-sitter%2Fpy-tree-sitter/lists"}