rbtr-lang-python 2026.7.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ """Python language plugin package."""
@@ -0,0 +1,120 @@
1
+ """Python language plugin.
2
+
3
+ Provides full support: symbol extraction (functions, classes,
4
+ methods, imports) and structured import metadata.
5
+
6
+ Extracted chunks::
7
+
8
+ def hello(): → function "hello", scope ""
9
+ pass
10
+
11
+ class Config: → class "Config", scope ""
12
+ def load(self): → method "load", scope "Config"
13
+ pass
14
+
15
+ import os → import, metadata {module: "os"}
16
+ from pathlib import Path
17
+ → import, metadata {module: "pathlib", names: "Path"}
18
+ from ..core import engine
19
+ → import, metadata {dots: "2", module: "core",
20
+ names: "engine"}
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from typing import TYPE_CHECKING
26
+
27
+ from rbtr.index.models import ImportMeta
28
+ from rbtr.languages.registration import (
29
+ ImportResolver,
30
+ LanguageRegistration,
31
+ ModuleStyle,
32
+ QueryExtraction,
33
+ load_query,
34
+ )
35
+
36
+ if TYPE_CHECKING:
37
+ from tree_sitter import Node
38
+
39
+ # ── Query ────────────────────────────────────────────────────────────
40
+
41
+ # The `@_docstring` sub-capture marks the first-statement body
42
+ # docstring for doc-range detection (used by `extract_doc_spans`
43
+ # and the eval query sampler). It is optional in the match
44
+ # (`?`), so functions/classes without a docstring still match.
45
+
46
+ # ── Import extractor ─────────────────────────────────────────────────
47
+
48
+
49
+ def extract_import_meta(
50
+ resolver: ImportResolver, node: Node, captures: dict[str, list[Node]]
51
+ ) -> ImportMeta:
52
+ """Extract structured import data from a Python import node.
53
+
54
+ Reads query captures (`@_import_module`, `@_import_dots`)
55
+ and walks the node for multi-valued import names (which
56
+ the query can't capture).
57
+
58
+ Examples:
59
+
60
+ `import os.path`:
61
+ module="os.path"
62
+
63
+ `from pathlib import Path`:
64
+ module="pathlib", names="Path"
65
+
66
+ `from ..core import engine`:
67
+ dots="2", module="core", names="engine"
68
+
69
+ `from .models import Chunk as C`:
70
+ dots="1", module="models", names="Chunk"
71
+
72
+ `from . import utils`:
73
+ dots="1", names="utils"
74
+ """
75
+ meta = resolver(node, captures)
76
+
77
+ # Convert @_import_dots from raw import_prefix (e.g. "..")
78
+ # to a count string.
79
+ dots_nodes = captures.get("_import_dots", [])
80
+ if dots_nodes and dots_nodes[0].text:
81
+ meta.dots = str(dots_nodes[0].text.decode().count("."))
82
+
83
+ if node.type == "import_from_statement":
84
+ # Imported names. The `name` field can be dotted_name
85
+ # or aliased_import; there may be multiple `name` fields.
86
+ names: list[str] = []
87
+ for name_node in node.children_by_field_name("name"):
88
+ match name_node.type:
89
+ case "dotted_name":
90
+ if name_node.text:
91
+ names.append(name_node.text.decode())
92
+ case "aliased_import":
93
+ original = name_node.child_by_field_name("name")
94
+ if original and original.text:
95
+ names.append(original.text.decode())
96
+ if names:
97
+ meta.names = ",".join(names)
98
+
99
+ return meta
100
+
101
+
102
+ # ── Plugin ───────────────────────────────────────────────────────────
103
+
104
+
105
+ python = LanguageRegistration(
106
+ id="python",
107
+ extensions=frozenset({".py", ".pyi"}),
108
+ grammar_module="tree_sitter_python",
109
+ extraction=QueryExtraction(
110
+ query=load_query(__package__, "python"),
111
+ scope_types=frozenset({"class_definition", "function_definition"}),
112
+ class_scope_types=frozenset({"class_definition"}),
113
+ ),
114
+ index_files=frozenset({"__init__.py"}),
115
+ source_roots=("", "src"),
116
+ module_style=ModuleStyle.DOTTED,
117
+ extraction_serial=5,
118
+ )
119
+
120
+ python.import_extractor(extract_import_meta)
File without changes
@@ -0,0 +1,59 @@
1
+ ; Comments and the module docstring. The engine groups comments into
2
+ ; blank-line-delimited blocks and either folds a block into the symbol flush
3
+ ; after it (its docstring, possibly nested), drops it if interior, or emits
4
+ ; it as a standalone COMMENT chunk.
5
+ (comment) @comment
6
+ (module (expression_statement (string) @comment))
7
+
8
+ (function_definition
9
+ name: (identifier) @_fn_name
10
+ body: (block
11
+ . (expression_statement (string) @_docstring)?)) @function
12
+
13
+ (class_definition
14
+ name: (identifier) @_cls_name
15
+ body: (block
16
+ . (expression_statement (string) @_docstring)?)) @class
17
+
18
+ (type_alias_statement
19
+ . (type (identifier) @_cls_name)) @class
20
+
21
+ (module
22
+ (expression_statement
23
+ (assignment
24
+ left: (identifier) @_var_name) @variable))
25
+
26
+ (module
27
+ (expression_statement
28
+ (assignment
29
+ left: (pattern_list (identifier) @_var_name)) @variable))
30
+
31
+ (module
32
+ (expression_statement
33
+ (assignment
34
+ left: (tuple_pattern (identifier) @_var_name)) @variable))
35
+
36
+ (module
37
+ (expression_statement
38
+ (assignment
39
+ left: (list_pattern (identifier) @_var_name)) @variable))
40
+
41
+ (module
42
+ (expression_statement
43
+ (assignment
44
+ left: (pattern_list (list_splat_pattern (identifier) @_var_name))) @variable))
45
+
46
+ (import_statement
47
+ name: (dotted_name) @_import_module) @import
48
+
49
+ (import_from_statement
50
+ module_name: (dotted_name) @_import_module) @import
51
+
52
+ (import_from_statement
53
+ module_name: (relative_import
54
+ (import_prefix) @_import_dots
55
+ (dotted_name) @_import_module)) @import
56
+
57
+ (import_from_statement
58
+ module_name: (relative_import
59
+ (import_prefix) @_import_dots .)) @import
File without changes
@@ -0,0 +1,3 @@
1
+ [
2
+ "python.py::from .config import LOCALE -> config.py::LOCALE [imports]"
3
+ ]
@@ -0,0 +1,470 @@
1
+ [
2
+ {
3
+ "id": "8a4843f093a489a4",
4
+ "blob_sha": "sha1",
5
+ "file_path": "config.py",
6
+ "kind": "comment",
7
+ "name": "<anonymous>",
8
+ "scope": "",
9
+ "language": "python",
10
+ "content": "\"\"\"Configuration values for the greeter.\"\"\"",
11
+ "line_start": 1,
12
+ "line_end": 1,
13
+ "metadata": {
14
+ "module": "",
15
+ "names": "",
16
+ "dots": "",
17
+ "language_hint": ""
18
+ }
19
+ },
20
+ {
21
+ "id": "e03be7d87e70e097",
22
+ "blob_sha": "sha1",
23
+ "file_path": "config.py",
24
+ "kind": "variable",
25
+ "name": "LOCALE",
26
+ "scope": "",
27
+ "language": "python",
28
+ "content": "LOCALE = \"en\"",
29
+ "line_start": 3,
30
+ "line_end": 3,
31
+ "metadata": {
32
+ "module": "",
33
+ "names": "",
34
+ "dots": "",
35
+ "language_hint": ""
36
+ }
37
+ },
38
+ {
39
+ "id": "90a5683217fcbf40",
40
+ "blob_sha": "sha1",
41
+ "file_path": "python.py",
42
+ "kind": "comment",
43
+ "name": "<anonymous>",
44
+ "scope": "",
45
+ "language": "python",
46
+ "content": "# Top-of-file banner comment, attached to nothing.\n# Second banner line, same block.",
47
+ "line_start": 1,
48
+ "line_end": 2,
49
+ "metadata": {
50
+ "module": "",
51
+ "names": "",
52
+ "dots": "",
53
+ "language_hint": ""
54
+ }
55
+ },
56
+ {
57
+ "id": "bcb39bbdc83beb10",
58
+ "blob_sha": "sha1",
59
+ "file_path": "python.py",
60
+ "kind": "comment",
61
+ "name": "<anonymous>",
62
+ "scope": "",
63
+ "language": "python",
64
+ "content": "\"\"\"Greeter — formats greetings for named recipients.\n\nA sample module exercising the constructs the python plugin extracts:\nfunctions (sync, async, decorated), classes, methods (instance, property,\nstatic, class), module-level variables (including tuple unpacking and\nannotated assignments), nested functions (scoped to their parent), PEP 695\n`type` aliases (as classes), the import styles that carry distinct\nmetadata, and standalone / leading comments.\n\"\"\"",
65
+ "line_start": 4,
66
+ "line_end": 12,
67
+ "metadata": {
68
+ "module": "",
69
+ "names": "",
70
+ "dots": "",
71
+ "language_hint": ""
72
+ }
73
+ },
74
+ {
75
+ "id": "86800a35eec519ec",
76
+ "blob_sha": "sha1",
77
+ "file_path": "python.py",
78
+ "kind": "import",
79
+ "name": "import os",
80
+ "scope": "",
81
+ "language": "python",
82
+ "content": "import os",
83
+ "line_start": 16,
84
+ "line_end": 16,
85
+ "metadata": {
86
+ "module": "os",
87
+ "names": "",
88
+ "dots": "",
89
+ "language_hint": ""
90
+ }
91
+ },
92
+ {
93
+ "id": "99f2b46e21fdc9ed",
94
+ "blob_sha": "sha1",
95
+ "file_path": "python.py",
96
+ "kind": "import",
97
+ "name": "from functools import lru_cache",
98
+ "scope": "",
99
+ "language": "python",
100
+ "content": "from functools import lru_cache",
101
+ "line_start": 17,
102
+ "line_end": 17,
103
+ "metadata": {
104
+ "module": "functools",
105
+ "names": "lru_cache",
106
+ "dots": "",
107
+ "language_hint": ""
108
+ }
109
+ },
110
+ {
111
+ "id": "a1111f0d689a9844",
112
+ "blob_sha": "sha1",
113
+ "file_path": "python.py",
114
+ "kind": "import",
115
+ "name": "from pathlib import Path as P",
116
+ "scope": "",
117
+ "language": "python",
118
+ "content": "from pathlib import Path as P",
119
+ "line_start": 18,
120
+ "line_end": 18,
121
+ "metadata": {
122
+ "module": "pathlib",
123
+ "names": "Path",
124
+ "dots": "",
125
+ "language_hint": ""
126
+ }
127
+ },
128
+ {
129
+ "id": "df33b6fad729b242",
130
+ "blob_sha": "sha1",
131
+ "file_path": "python.py",
132
+ "kind": "import",
133
+ "name": "from .config import LOCALE",
134
+ "scope": "",
135
+ "language": "python",
136
+ "content": "from .config import LOCALE",
137
+ "line_start": 20,
138
+ "line_end": 20,
139
+ "metadata": {
140
+ "module": "config",
141
+ "names": "LOCALE",
142
+ "dots": "1",
143
+ "language_hint": ""
144
+ }
145
+ },
146
+ {
147
+ "id": "d81a70db3e82f25a",
148
+ "blob_sha": "sha1",
149
+ "file_path": "python.py",
150
+ "kind": "class",
151
+ "name": "GreetingList",
152
+ "scope": "",
153
+ "language": "python",
154
+ "content": "type GreetingList = list[str]",
155
+ "line_start": 22,
156
+ "line_end": 22,
157
+ "metadata": {
158
+ "module": "",
159
+ "names": "",
160
+ "dots": "",
161
+ "language_hint": ""
162
+ }
163
+ },
164
+ {
165
+ "id": "39b5a904c304c9c9",
166
+ "blob_sha": "sha1",
167
+ "file_path": "python.py",
168
+ "kind": "variable",
169
+ "name": "DEFAULT_GREETING",
170
+ "scope": "",
171
+ "language": "python",
172
+ "content": "DEFAULT_GREETING = \"Hello\"",
173
+ "line_start": 24,
174
+ "line_end": 24,
175
+ "metadata": {
176
+ "module": "",
177
+ "names": "",
178
+ "dots": "",
179
+ "language_hint": ""
180
+ }
181
+ },
182
+ {
183
+ "id": "397202f732684d50",
184
+ "blob_sha": "sha1",
185
+ "file_path": "python.py",
186
+ "kind": "variable",
187
+ "name": "LOCALES",
188
+ "scope": "",
189
+ "language": "python",
190
+ "content": "LOCALES, FALLBACK = (\"en\", \"fr\"), \"en\"",
191
+ "line_start": 25,
192
+ "line_end": 25,
193
+ "metadata": {
194
+ "module": "",
195
+ "names": "",
196
+ "dots": "",
197
+ "language_hint": ""
198
+ }
199
+ },
200
+ {
201
+ "id": "0ed9c20b3fe660be",
202
+ "blob_sha": "sha1",
203
+ "file_path": "python.py",
204
+ "kind": "variable",
205
+ "name": "FALLBACK",
206
+ "scope": "",
207
+ "language": "python",
208
+ "content": "LOCALES, FALLBACK = (\"en\", \"fr\"), \"en\"",
209
+ "line_start": 25,
210
+ "line_end": 25,
211
+ "metadata": {
212
+ "module": "",
213
+ "names": "",
214
+ "dots": "",
215
+ "language_hint": ""
216
+ }
217
+ },
218
+ {
219
+ "id": "9ac255b17801a682",
220
+ "blob_sha": "sha1",
221
+ "file_path": "python.py",
222
+ "kind": "variable",
223
+ "name": "MAX_RECIPIENTS",
224
+ "scope": "",
225
+ "language": "python",
226
+ "content": "MAX_RECIPIENTS: int = 100",
227
+ "line_start": 26,
228
+ "line_end": 26,
229
+ "metadata": {
230
+ "module": "",
231
+ "names": "",
232
+ "dots": "",
233
+ "language_hint": ""
234
+ }
235
+ },
236
+ {
237
+ "id": "7f441b61192c0790",
238
+ "blob_sha": "sha1",
239
+ "file_path": "python.py",
240
+ "kind": "comment",
241
+ "name": "<anonymous>",
242
+ "scope": "",
243
+ "language": "python",
244
+ "content": "# trailing comment: not folded, its own chunk",
245
+ "line_start": 26,
246
+ "line_end": 26,
247
+ "metadata": {
248
+ "module": "",
249
+ "names": "",
250
+ "dots": "",
251
+ "language_hint": ""
252
+ }
253
+ },
254
+ {
255
+ "id": "faa176a42cc8b903",
256
+ "blob_sha": "sha1",
257
+ "file_path": "python.py",
258
+ "kind": "comment",
259
+ "name": "<anonymous>",
260
+ "scope": "",
261
+ "language": "python",
262
+ "content": "# Section: greeting helpers.\n# A standalone block between definitions.",
263
+ "line_start": 28,
264
+ "line_end": 29,
265
+ "metadata": {
266
+ "module": "",
267
+ "names": "",
268
+ "dots": "",
269
+ "language_hint": ""
270
+ }
271
+ },
272
+ {
273
+ "id": "b2c0884b8be95035",
274
+ "blob_sha": "sha1",
275
+ "file_path": "python.py",
276
+ "kind": "function",
277
+ "name": "format_greeting",
278
+ "scope": "",
279
+ "language": "python",
280
+ "content": "# Leading doc comment folded into format_greeting.\ndef format_greeting(name: str) -> str:\n \"\"\"Return a greeting for ``name`` in the configured locale.\"\"\"\n\n def normalise(raw: str) -> str:\n \"\"\"Trim and title-case a raw recipient name.\"\"\"\n return raw.strip().title()\n\n return f\"{DEFAULT_GREETING}, {normalise(name)} ({LOCALE})\"",
281
+ "line_start": 32,
282
+ "line_end": 40,
283
+ "metadata": {
284
+ "module": "",
285
+ "names": "",
286
+ "dots": "",
287
+ "language_hint": ""
288
+ }
289
+ },
290
+ {
291
+ "id": "a7a4d2b076bcecd6",
292
+ "blob_sha": "sha1",
293
+ "file_path": "python.py",
294
+ "kind": "function",
295
+ "name": "normalise",
296
+ "scope": "format_greeting",
297
+ "language": "python",
298
+ "content": "def normalise(raw: str) -> str:\n \"\"\"Trim and title-case a raw recipient name.\"\"\"\n return raw.strip().title()",
299
+ "line_start": 36,
300
+ "line_end": 38,
301
+ "metadata": {
302
+ "module": "",
303
+ "names": "",
304
+ "dots": "",
305
+ "language_hint": ""
306
+ }
307
+ },
308
+ {
309
+ "id": "4781f65b33434b65",
310
+ "blob_sha": "sha1",
311
+ "file_path": "python.py",
312
+ "kind": "function",
313
+ "name": "fetch_remote_greeting",
314
+ "scope": "",
315
+ "language": "python",
316
+ "content": "async def fetch_remote_greeting(url: str) -> str:\n \"\"\"Fetch a greeting template from a remote source.\"\"\"\n return os.environ.get(\"GREETING\", DEFAULT_GREETING)",
317
+ "line_start": 43,
318
+ "line_end": 45,
319
+ "metadata": {
320
+ "module": "",
321
+ "names": "",
322
+ "dots": "",
323
+ "language_hint": ""
324
+ }
325
+ },
326
+ {
327
+ "id": "512e3f71a402f3b7",
328
+ "blob_sha": "sha1",
329
+ "file_path": "python.py",
330
+ "kind": "function",
331
+ "name": "cached_default",
332
+ "scope": "",
333
+ "language": "python",
334
+ "content": "def cached_default() -> str:\n \"\"\"Cache and return the default greeting prefix.\"\"\"\n return DEFAULT_GREETING",
335
+ "line_start": 49,
336
+ "line_end": 51,
337
+ "metadata": {
338
+ "module": "",
339
+ "names": "",
340
+ "dots": "",
341
+ "language_hint": ""
342
+ }
343
+ },
344
+ {
345
+ "id": "c509d883ae06d023",
346
+ "blob_sha": "sha1",
347
+ "file_path": "python.py",
348
+ "kind": "class",
349
+ "name": "Greeter",
350
+ "scope": "",
351
+ "language": "python",
352
+ "content": "class Greeter:\n \"\"\"Stateful greeter holding a prefix and recipient log.\"\"\"\n\n def __init__(self, prefix: str = DEFAULT_GREETING) -> None:\n self.prefix = prefix\n self._seen: list[str] = []\n\n def greet(self, name: str) -> str:\n \"\"\"Greet ``name`` and record the recipient.\"\"\"\n self._seen.append(name)\n return format_greeting(name)\n\n @property\n def seen(self) -> list[str]:\n \"\"\"Recipients greeted so far.\"\"\"\n return list(self._seen)\n\n @staticmethod\n def shout(message: str) -> str:\n \"\"\"Upper-case a message.\"\"\"\n return message.upper()\n\n @classmethod\n def default(cls) -> Greeter:\n \"\"\"Build a greeter with the default prefix.\"\"\"\n return cls(DEFAULT_GREETING)",
353
+ "line_start": 54,
354
+ "line_end": 79,
355
+ "metadata": {
356
+ "module": "",
357
+ "names": "",
358
+ "dots": "",
359
+ "language_hint": ""
360
+ }
361
+ },
362
+ {
363
+ "id": "f5316a1306021dbf",
364
+ "blob_sha": "sha1",
365
+ "file_path": "python.py",
366
+ "kind": "method",
367
+ "name": "__init__",
368
+ "scope": "Greeter",
369
+ "language": "python",
370
+ "content": "def __init__(self, prefix: str = DEFAULT_GREETING) -> None:\n self.prefix = prefix\n self._seen: list[str] = []",
371
+ "line_start": 57,
372
+ "line_end": 59,
373
+ "metadata": {
374
+ "module": "",
375
+ "names": "",
376
+ "dots": "",
377
+ "language_hint": ""
378
+ }
379
+ },
380
+ {
381
+ "id": "04526264daec6180",
382
+ "blob_sha": "sha1",
383
+ "file_path": "python.py",
384
+ "kind": "method",
385
+ "name": "greet",
386
+ "scope": "Greeter",
387
+ "language": "python",
388
+ "content": "def greet(self, name: str) -> str:\n \"\"\"Greet ``name`` and record the recipient.\"\"\"\n self._seen.append(name)\n return format_greeting(name)",
389
+ "line_start": 61,
390
+ "line_end": 64,
391
+ "metadata": {
392
+ "module": "",
393
+ "names": "",
394
+ "dots": "",
395
+ "language_hint": ""
396
+ }
397
+ },
398
+ {
399
+ "id": "404ed4eb32f154f2",
400
+ "blob_sha": "sha1",
401
+ "file_path": "python.py",
402
+ "kind": "method",
403
+ "name": "seen",
404
+ "scope": "Greeter",
405
+ "language": "python",
406
+ "content": "def seen(self) -> list[str]:\n \"\"\"Recipients greeted so far.\"\"\"\n return list(self._seen)",
407
+ "line_start": 67,
408
+ "line_end": 69,
409
+ "metadata": {
410
+ "module": "",
411
+ "names": "",
412
+ "dots": "",
413
+ "language_hint": ""
414
+ }
415
+ },
416
+ {
417
+ "id": "4174241e8ca95cc9",
418
+ "blob_sha": "sha1",
419
+ "file_path": "python.py",
420
+ "kind": "method",
421
+ "name": "shout",
422
+ "scope": "Greeter",
423
+ "language": "python",
424
+ "content": "def shout(message: str) -> str:\n \"\"\"Upper-case a message.\"\"\"\n return message.upper()",
425
+ "line_start": 72,
426
+ "line_end": 74,
427
+ "metadata": {
428
+ "module": "",
429
+ "names": "",
430
+ "dots": "",
431
+ "language_hint": ""
432
+ }
433
+ },
434
+ {
435
+ "id": "b4873c720ebf2b3b",
436
+ "blob_sha": "sha1",
437
+ "file_path": "python.py",
438
+ "kind": "method",
439
+ "name": "default",
440
+ "scope": "Greeter",
441
+ "language": "python",
442
+ "content": "def default(cls) -> Greeter:\n \"\"\"Build a greeter with the default prefix.\"\"\"\n return cls(DEFAULT_GREETING)",
443
+ "line_start": 77,
444
+ "line_end": 79,
445
+ "metadata": {
446
+ "module": "",
447
+ "names": "",
448
+ "dots": "",
449
+ "language_hint": ""
450
+ }
451
+ },
452
+ {
453
+ "id": "290aeaf830254689",
454
+ "blob_sha": "sha1",
455
+ "file_path": "python.py",
456
+ "kind": "function",
457
+ "name": "config_path",
458
+ "scope": "",
459
+ "language": "python",
460
+ "content": "def config_path() -> P:\n \"\"\"Return the path to the greeter config file.\"\"\"\n return P.home() / \".greeter\"",
461
+ "line_start": 82,
462
+ "line_end": 84,
463
+ "metadata": {
464
+ "module": "",
465
+ "names": "",
466
+ "dots": "",
467
+ "language_hint": ""
468
+ }
469
+ }
470
+ ]