rgapi 0.1.23__tar.gz → 0.1.25__tar.gz
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.
- {rgapi-0.1.23 → rgapi-0.1.25}/.github/workflows/ci.yml +1 -1
- {rgapi-0.1.23 → rgapi-0.1.25}/Cargo.lock +76 -12
- {rgapi-0.1.23 → rgapi-0.1.25}/Cargo.toml +15 -1
- {rgapi-0.1.23 → rgapi-0.1.25}/DEV.md +4 -3
- rgapi-0.1.25/PKG-INFO +255 -0
- rgapi-0.1.25/README.md +233 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/pyproject.toml +3 -0
- rgapi-0.1.25/python/rgapi/_cli.py +28 -0
- rgapi-0.1.25/python/rgapi/skill.py +35 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/src/walk.rs +40 -3
- rgapi-0.1.23/PKG-INFO +0 -192
- rgapi-0.1.23/README.md +0 -170
- rgapi-0.1.23/python/rgapi/skill.py +0 -51
- {rgapi-0.1.23 → rgapi-0.1.25}/.gitignore +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/LICENSE +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/_config.yml +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/_layouts/default.html +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/python/rgapi/__init__.py +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/python/rgapi/block.py +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/python/rgapi/nb.py +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/rustfmt.toml +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/src/block.rs +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/src/lib.rs +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/src/nb.rs +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/src/python.rs +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/src/search.rs +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/tests/test_async.py +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/tests/test_rgapi.py +0 -0
- {rgapi-0.1.23 → rgapi-0.1.25}/tools/bench.py +0 -0
|
@@ -29,7 +29,7 @@ jobs:
|
|
|
29
29
|
- uses: actions/checkout@v7
|
|
30
30
|
- uses: PyO3/maturin-action@v1
|
|
31
31
|
with:
|
|
32
|
-
args: --
|
|
32
|
+
args: --profile dist --out dist -i python3.10 -i python3.11 -i python3.12 -i python3.13
|
|
33
33
|
manylinux: auto
|
|
34
34
|
- uses: actions/upload-artifact@v7
|
|
35
35
|
with:
|
|
@@ -28,6 +28,12 @@ version = "1.0.4"
|
|
|
28
28
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
29
29
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
30
30
|
|
|
31
|
+
[[package]]
|
|
32
|
+
name = "core_detect"
|
|
33
|
+
version = "1.0.0"
|
|
34
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
35
|
+
checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48"
|
|
36
|
+
|
|
31
37
|
[[package]]
|
|
32
38
|
name = "crc32fast"
|
|
33
39
|
version = "1.5.1"
|
|
@@ -39,9 +45,9 @@ dependencies = [
|
|
|
39
45
|
|
|
40
46
|
[[package]]
|
|
41
47
|
name = "crossbeam-deque"
|
|
42
|
-
version = "0.8.
|
|
48
|
+
version = "0.8.8"
|
|
43
49
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
44
|
-
checksum = "
|
|
50
|
+
checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a"
|
|
45
51
|
dependencies = [
|
|
46
52
|
"crossbeam-epoch",
|
|
47
53
|
"crossbeam-utils",
|
|
@@ -49,26 +55,32 @@ dependencies = [
|
|
|
49
55
|
|
|
50
56
|
[[package]]
|
|
51
57
|
name = "crossbeam-epoch"
|
|
52
|
-
version = "0.9.
|
|
58
|
+
version = "0.9.21"
|
|
53
59
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
54
|
-
checksum = "
|
|
60
|
+
checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d"
|
|
55
61
|
dependencies = [
|
|
56
62
|
"crossbeam-utils",
|
|
57
63
|
]
|
|
58
64
|
|
|
59
65
|
[[package]]
|
|
60
66
|
name = "crossbeam-utils"
|
|
61
|
-
version = "0.8.
|
|
67
|
+
version = "0.8.23"
|
|
62
68
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
63
|
-
checksum = "
|
|
69
|
+
checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
|
|
64
70
|
|
|
65
71
|
[[package]]
|
|
66
72
|
name = "encoding_rs"
|
|
67
|
-
version = "0.8.
|
|
73
|
+
version = "0.8.40"
|
|
68
74
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
69
|
-
checksum = "
|
|
75
|
+
checksum = "2a7a45518d2863d18aa47f4a0cf9faec2aa4304cc09df5e41299f276b3ad135e"
|
|
70
76
|
dependencies = [
|
|
71
77
|
"cfg-if",
|
|
78
|
+
"core_detect",
|
|
79
|
+
"multiversion",
|
|
80
|
+
"multiversion_no_op",
|
|
81
|
+
"rustversion",
|
|
82
|
+
"scopeguard",
|
|
83
|
+
"simdutf8",
|
|
72
84
|
]
|
|
73
85
|
|
|
74
86
|
[[package]]
|
|
@@ -185,6 +197,34 @@ dependencies = [
|
|
|
185
197
|
"libc",
|
|
186
198
|
]
|
|
187
199
|
|
|
200
|
+
[[package]]
|
|
201
|
+
name = "multiversion"
|
|
202
|
+
version = "0.8.0"
|
|
203
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
204
|
+
checksum = "7edb7f0ff51249dfda9ab96b5823695e15a052dc15074c9dbf3d118afaf2c201"
|
|
205
|
+
dependencies = [
|
|
206
|
+
"multiversion-macros",
|
|
207
|
+
"target-features",
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
[[package]]
|
|
211
|
+
name = "multiversion-macros"
|
|
212
|
+
version = "0.8.0"
|
|
213
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
214
|
+
checksum = "b093064383341eb3271f42e381cb8f10a01459478446953953c75d24bd339fc0"
|
|
215
|
+
dependencies = [
|
|
216
|
+
"proc-macro2",
|
|
217
|
+
"quote",
|
|
218
|
+
"syn 2.0.119",
|
|
219
|
+
"target-features",
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
[[package]]
|
|
223
|
+
name = "multiversion_no_op"
|
|
224
|
+
version = "1.0.0"
|
|
225
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
226
|
+
checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d"
|
|
227
|
+
|
|
188
228
|
[[package]]
|
|
189
229
|
name = "once_cell"
|
|
190
230
|
version = "1.21.4"
|
|
@@ -291,7 +331,7 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
|
|
291
331
|
|
|
292
332
|
[[package]]
|
|
293
333
|
name = "rgapi"
|
|
294
|
-
version = "0.1.
|
|
334
|
+
version = "0.1.25"
|
|
295
335
|
dependencies = [
|
|
296
336
|
"crc32fast",
|
|
297
337
|
"globset",
|
|
@@ -304,6 +344,12 @@ dependencies = [
|
|
|
304
344
|
"serde_json",
|
|
305
345
|
]
|
|
306
346
|
|
|
347
|
+
[[package]]
|
|
348
|
+
name = "rustversion"
|
|
349
|
+
version = "1.0.23"
|
|
350
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
351
|
+
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
|
352
|
+
|
|
307
353
|
[[package]]
|
|
308
354
|
name = "same-file"
|
|
309
355
|
version = "1.0.6"
|
|
@@ -313,6 +359,12 @@ dependencies = [
|
|
|
313
359
|
"winapi-util",
|
|
314
360
|
]
|
|
315
361
|
|
|
362
|
+
[[package]]
|
|
363
|
+
name = "scopeguard"
|
|
364
|
+
version = "1.2.0"
|
|
365
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
366
|
+
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
367
|
+
|
|
316
368
|
[[package]]
|
|
317
369
|
name = "serde"
|
|
318
370
|
version = "1.0.229"
|
|
@@ -340,7 +392,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|
|
340
392
|
dependencies = [
|
|
341
393
|
"proc-macro2",
|
|
342
394
|
"quote",
|
|
343
|
-
"syn 3.0.
|
|
395
|
+
"syn 3.0.5",
|
|
344
396
|
]
|
|
345
397
|
|
|
346
398
|
[[package]]
|
|
@@ -356,6 +408,12 @@ dependencies = [
|
|
|
356
408
|
"zmij",
|
|
357
409
|
]
|
|
358
410
|
|
|
411
|
+
[[package]]
|
|
412
|
+
name = "simdutf8"
|
|
413
|
+
version = "0.1.5"
|
|
414
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
415
|
+
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
|
416
|
+
|
|
359
417
|
[[package]]
|
|
360
418
|
name = "syn"
|
|
361
419
|
version = "2.0.119"
|
|
@@ -369,15 +427,21 @@ dependencies = [
|
|
|
369
427
|
|
|
370
428
|
[[package]]
|
|
371
429
|
name = "syn"
|
|
372
|
-
version = "3.0.
|
|
430
|
+
version = "3.0.5"
|
|
373
431
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
374
|
-
checksum = "
|
|
432
|
+
checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
|
|
375
433
|
dependencies = [
|
|
376
434
|
"proc-macro2",
|
|
377
435
|
"quote",
|
|
378
436
|
"unicode-ident",
|
|
379
437
|
]
|
|
380
438
|
|
|
439
|
+
[[package]]
|
|
440
|
+
name = "target-features"
|
|
441
|
+
version = "0.1.6"
|
|
442
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
443
|
+
checksum = "c1bbb9f3c5c463a01705937a24fdabc5047929ac764b2d5b9cf681c1f5041ed5"
|
|
444
|
+
|
|
381
445
|
[[package]]
|
|
382
446
|
name = "target-lexicon"
|
|
383
447
|
version = "0.13.5"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rgapi"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.25"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
rust-version = "1.91"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -33,4 +33,18 @@ extension-module = ["python", "pyo3/extension-module"]
|
|
|
33
33
|
too_many_arguments = "allow"
|
|
34
34
|
|
|
35
35
|
[profile.release]
|
|
36
|
+
lto = false
|
|
37
|
+
codegen-units = 16
|
|
38
|
+
|
|
39
|
+
[profile.release.package.rgapi]
|
|
40
|
+
incremental = true
|
|
41
|
+
|
|
42
|
+
[profile.dist]
|
|
43
|
+
inherits = "release"
|
|
44
|
+
lto = true
|
|
45
|
+
incremental = false
|
|
46
|
+
codegen-units = 1
|
|
36
47
|
strip = true
|
|
48
|
+
|
|
49
|
+
[profile.dist.package.rgapi]
|
|
50
|
+
incremental = false
|
|
@@ -9,7 +9,7 @@ src/walk.rs ignore/globset/grep-regex-backed path walking and filtering
|
|
|
9
9
|
src/search.rs grep-regex/grep-searcher-backed searching
|
|
10
10
|
src/block.rs blank-line-delimited block grouping, matching, and block context
|
|
11
11
|
src/python.rs PyO3 classes and private core functions
|
|
12
|
-
python/rgapi/ public Python wrappers over `rgapi._core`
|
|
12
|
+
python/rgapi/ public Python wrappers over `rgapi._core`, plus the `rgapi-nbrg` CLI
|
|
13
13
|
tests/ pytest coverage for the Python API
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -40,8 +40,7 @@ The GitHub workflow builds wheels for Python 3.10-3.13 on Linux and macOS and pu
|
|
|
40
40
|
|
|
41
41
|
Paths in `fd`, `walk`, `rg`, and `rg_iter` results are relative to the requested root and use `/` separators. Traversal uses `ignore::WalkParallel`, so result order is not part of the API contract. Search results are structured rows; collected result lists use rg-style `str()` and notebook display. `SearchLine.lnhash` is computed with the same CRC-32-based line-content hash format as exhash (`lineno|hash|`, low 16 bits of CRC-32 over the line's UTF-8 bytes); `lnhashs=True` only changes row display, not `line_number` or matching behavior. Path regexes filter returned/searched paths; `skip_dir` and `skip_dir_re` prune traversal through `ignore::WalkBuilder::filter_entry`. Depth, size, symlink, filesystem, hidden, and ignore options are direct `ignore::WalkBuilder` settings. `rg_iter` exposes the same parallel search stream that `rg` collects by default; `paths=True` and `count=True` consume that stream with different reducers. Binary files and invalid UTF-8 are skipped for now.
|
|
42
42
|
|
|
43
|
-
Streaming engine: `walk.rs` owns the generic machinery. `StreamIter<T>` is the worker-thread-plus-bounded-channel iterator (`sync_channel(8192)`, so producers block rather than buffer without limit when a consumer lags), and `spawn_walk` owns the shared scaffold: walker config, panic catching, cancel flag, and worker thread. `rg_iter` (`T = SearchLine`), `block_iter` (`T = SearchBlock`), `nb_iter` (`T = NbCell`), and `find_iter` (`T = String`, the path walk) plug entry closures into that engine. Block search reads each file once, searches it once, groups nonblank lines into blocks, maps matching lines to their blocks, and expands context by block index.
|
|
44
|
-
Each `SearchBlock` carries numeric boundaries plus hashes for its first and last source lines. Python keeps both and chooses the displayed address without another file read.
|
|
43
|
+
Streaming engine: `walk.rs` owns the generic machinery. `StreamIter<T>` is the worker-thread-plus-bounded-channel iterator (`sync_channel(8192)`, so producers block rather than buffer without limit when a consumer lags), and `spawn_walk` owns the shared scaffold: walker config, panic catching, cancel flag, and worker thread. `rg_iter` (`T = SearchLine`), `block_iter` (`T = SearchBlock`), `nb_iter` (`T = NbCell`), and `find_iter` (`T = String`, the path walk) plug entry closures into that engine. Block search reads each file once, searches it once, groups nonblank lines into blocks, maps matching lines to their blocks, and expands context by block index. Each `SearchBlock` carries numeric boundaries plus hashes for its first and last source lines. Python keeps both and chooses the displayed address without another file read.
|
|
45
44
|
|
|
46
45
|
Async API: `fda`, `fda_iter`, `rga`, `rga_iter`, `nbrga`, and `nbrga_iter` wrap the corresponding private core operations. `rga(summary=True)` uses `_core.block_search_async`; ordinary `rga` uses `_core.rg_async`. Each collected core function takes a Python callback, runs on Rust threads through the generic `stream_async` helper, and delivers with one GIL attach at the end. Iterator forms use `stream_iter_async` and attach once per batch. The Python side settles an `asyncio.Future` or feeds an `asyncio.Queue` via `loop.call_soon_threadsafe`; no Python thread blocks and `asyncio.to_thread` is not involved. `AsyncHandle.cancel()` sets the same atomic flag used by the Rust iterators.
|
|
47
46
|
|
|
@@ -49,4 +48,6 @@ Truncation is recorded on collected results: `max_results` sets `stop_reason="ma
|
|
|
49
48
|
|
|
50
49
|
Path results are `FileEntry` rows: a `str` subclass carrying the walk root, so paths stay plain strings for compatibility while stat info loads lazily (one cached `os.lstat` per entry, read only on attribute access). The wrapping happens at result construction on the Python side; Rust still streams plain strings. `PathResults.__repr__` shows an `ls -l`-style listing capped at `MAX_REPR` rows, so a huge result never stats everything, while `str()` stays one plain path per line. `ls` is `fd` with shell-style defaults (one level, dirs, ignore rules off), re-sorted with `stop_reason` preserved.
|
|
51
50
|
|
|
51
|
+
Rust callers can consume a `StreamIter` with `cancel_and_join()` to cancel, drain queued results, and wait for the walk's workers to finish. `Drop` remains nonblocking. Since filesystem calls already in progress must return before joining completes, keep `cancel_and_join()` off async executors.
|
|
52
|
+
|
|
52
53
|
This package intentionally has no CLI. Python is the interface.
|
rgapi-0.1.25/PKG-INFO
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rgapi
|
|
3
|
+
Version: 0.1.25
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Requires-Dist: fastcore>=1.14.6
|
|
7
|
+
Requires-Dist: fastship>=0.0.12 ; extra == 'dev'
|
|
8
|
+
Requires-Dist: maturin>=1.0,<2.0 ; extra == 'dev'
|
|
9
|
+
Requires-Dist: pytest ; extra == 'dev'
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Summary: Python API for ripgrep-style file walking and searching
|
|
13
|
+
Home-Page: https://github.com/AnswerDotAI/rgapi
|
|
14
|
+
Author-email: Jeremy Howard <j@fast.ai>
|
|
15
|
+
License: Apache-2.0
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
18
|
+
Project-URL: Homepage, https://github.com/AnswerDotAI/rgapi
|
|
19
|
+
Project-URL: Issues, https://github.com/AnswerDotAI/rgapi/issues
|
|
20
|
+
Project-URL: Repository, https://github.com/AnswerDotAI/rgapi
|
|
21
|
+
|
|
22
|
+
# rgapi
|
|
23
|
+
|
|
24
|
+
`rgapi` provides `fd`-style file discovery and `rg`-style text search from Python without starting a shell command.
|
|
25
|
+
|
|
26
|
+
It uses the same `ignore`, `grep-regex`, and `grep-searcher` crates that ripgrep uses for walking, regex matching, and file scanning. Walking and searching run in parallel by default. Most expensive work stays in Rust.
|
|
27
|
+
|
|
28
|
+
## Overview
|
|
29
|
+
|
|
30
|
+
For common file discovery and search:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from rgapi import fd, ls, rg, rg_iter
|
|
34
|
+
|
|
35
|
+
fd(".", ext="py", exclude="test_*.py")
|
|
36
|
+
ls("src")
|
|
37
|
+
for row in rg_iter("TODO", ".", include="*.py", context=2): print(row.asdict())
|
|
38
|
+
rg("TODO", ".", ext="py", skip_dir=".venv", paths=True)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
For cell-aware search of Jupyter notebooks (see [Notebooks](#notebooks)):
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from rgapi import nbrg
|
|
45
|
+
|
|
46
|
+
nbrg("read_csv", ".", cell_context=1)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Walk and search functions have async versions. Streaming versions yield results as the search finds them. See [Async](#async):
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from rgapi import fda, rga, rga_iter, nbrga, nbrga_iter
|
|
53
|
+
|
|
54
|
+
await rga("TODO", ".", ext="py", timeout_ms=200)
|
|
55
|
+
async for row in rga_iter("TODO", "."): print(row)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use the lower-level functions to compile a regex, search text or a single file, or walk a directory:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from rgapi import compile, search_path, search_text, walk
|
|
62
|
+
|
|
63
|
+
matcher = compile("TODO")
|
|
64
|
+
matcher.is_match("TODO")
|
|
65
|
+
matcher.finditer("TODO TODO")
|
|
66
|
+
|
|
67
|
+
walk(".")
|
|
68
|
+
search_text(matcher, "alpha\nTODO\nomega\n", path="memory.txt", context=1)
|
|
69
|
+
search_path(matcher, "src/lib.rs", display_path="src/lib.rs")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Install
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install rgapi
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## File discovery
|
|
79
|
+
|
|
80
|
+
`fd` and `walk` return slash-separated paths relative to `root`. Pass `root` as a `str` or `pathlib.Path`. The sync and async APIs expand `~` and accept `.`, `./`, and paths containing `..`.
|
|
81
|
+
|
|
82
|
+
Discovery uses the `ignore` crate with ripgrep's default filters. It reads `.gitignore`, `.ignore`, and `.rgignore` files. `.rgignore` takes precedence over `.gitignore`. Pass `ignore=False` to disable all ignore-file filtering, including `.rgignore`.
|
|
83
|
+
|
|
84
|
+
Hidden files are skipped unless `hidden=True`. Symlinks are followed only with `follow_links=True`. Use `same_file_system=True` to avoid crossing filesystem boundaries.
|
|
85
|
+
|
|
86
|
+
Traversal runs in parallel without guaranteed result order. Use `sorted(...)` when order matters.
|
|
87
|
+
|
|
88
|
+
`fd` adds filename filters to `walk`. Its `pattern` is a smart-case regex matched against each basename. Lowercase patterns match case-insensitively. A pattern containing uppercase letters is case-sensitive. Use `path_re` to match the slash-separated relative path instead.
|
|
89
|
+
|
|
90
|
+
`include` and `exclude` use glob syntax. `glob=` is an alias for `include=`. A basename glob such as `*.py` also matches nested paths such as `src/app.py`.
|
|
91
|
+
|
|
92
|
+
Filter extensions with `ext="py"` or `ext=["py", "rs"]`. Extension and glob filters must both match. For example, `include="src/*", ext="py"` requires `src/*` and `*.py`, like combining `rg -g` with `-t`.
|
|
93
|
+
|
|
94
|
+
Set `min_depth` and `max_depth` to bound recursion. `max_filesize` skips files above a byte limit.
|
|
95
|
+
|
|
96
|
+
`ls` follows the shell command's listing conventions. It uses `fd` with `max_depth=1`, includes directories, disables ignore rules, and sorts by name. Set `hidden=True` for `ls -a` behaviour. All `fd` filters remain available.
|
|
97
|
+
|
|
98
|
+
`fd_iter` yields `FileEntry` paths as the walk finds them. It accepts every `fd` filter. Stopping iteration ends the walk. It does not accept `timeout_ms`.
|
|
99
|
+
|
|
100
|
+
`path_re` and `skip_path_re` filter slash-separated relative paths using regexes. They select returned paths or searched files without changing traversal. To skip entire subtrees, use `skip_dir` with a glob or `skip_dir_re` with a regex.
|
|
101
|
+
|
|
102
|
+
## Text search
|
|
103
|
+
|
|
104
|
+
`rg` and `rg_iter` return structured `SearchLine` rows. They accept the same filters as `fd`: `include`, `exclude`, `glob`, `ext`, `path_re`, `skip_path_re`, `skip_dir`, `skip_dir_re`, `min_depth`, `max_depth`, `max_filesize`, `follow_links`, and `same_file_system`.
|
|
105
|
+
|
|
106
|
+
Search is case-sensitive by default, matching `rg`. Use `smart_case=True` for `rg --smart-case` behaviour. Use `case_sensitive=False` to force case-insensitive matching.
|
|
107
|
+
|
|
108
|
+
Each `SearchLine` has these fields:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
kind 'match', 'before', 'after', or 'context'
|
|
112
|
+
path path relative to root
|
|
113
|
+
line_number 1-based line number
|
|
114
|
+
lnhash exhash-style `lineno|hash|` address for the line
|
|
115
|
+
line line text without the trailing newline
|
|
116
|
+
matches list of (start, end) byte offsets for match rows
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`rg`, `search_text`, and `search_path` return `SearchResults` by default. This list subclass displays as rg-style multiline text in `str()` and notebook pretty output. `rg_iter` yields rows lazily.
|
|
120
|
+
|
|
121
|
+
`SearchLine` has a structured `repr` and an rg-style `str`. The string display truncates `line` to 180 characters with a trailing `…`. `repr` and `asdict()` retain the full line. `SearchLine.asdict()` returns the fields as a plain Python dictionary.
|
|
122
|
+
|
|
123
|
+
Pass `lnhashs=True` to `rg` or `rg_iter` to display hash addresses instead of line numbers. The `line_number` field remains available.
|
|
124
|
+
|
|
125
|
+
For other result forms, use `rg(..., paths=True)` to return unique matched paths or `rg(..., count=True)` to count match spans. `paths` and `count` cannot both be set.
|
|
126
|
+
|
|
127
|
+
### Path results
|
|
128
|
+
|
|
129
|
+
`fd`, `walk`, and `ls` return `PathResults`. So do `rg` and `nbrg` with `paths=True`. This is a list of `FileEntry` rows.
|
|
130
|
+
|
|
131
|
+
A `FileEntry` is a `str` subclass containing a relative path. Its `stat` property calls `os.lstat` on first access and caches the result. It returns `None` if the path has vanished. `size`, `mtime`, and `is_dir` use the cached stat result. The object also supports ordinary string operations.
|
|
132
|
+
|
|
133
|
+
`PathResults` displays as an `ls -l`-style listing of at most `rgapi.MAX_REPR` rows. A final `… N more` line reports omitted rows. Only displayed rows need stat calls. `str()` returns one plain path per line. `list(res)` also displays plain paths.
|
|
134
|
+
|
|
135
|
+
### Limits and timeouts
|
|
136
|
+
|
|
137
|
+
Set `timeout_ms` on `rg`, `fd`, `walk`, or `ls` to stop at a deadline and return the results collected so far. Their async versions accept it too. Results report why the operation stopped:
|
|
138
|
+
|
|
139
|
+
- `stop_reason=None` means the result is complete.
|
|
140
|
+
- `stop_reason="max_results"` means `max_results` truncated the result.
|
|
141
|
+
- `stop_reason="timeout"` means the deadline was reached.
|
|
142
|
+
|
|
143
|
+
`complete` is true exactly when `stop_reason` is `None`. `count=True` returns a plain integer without a completion flag. It cannot be combined with `timeout_ms`.
|
|
144
|
+
|
|
145
|
+
### Context lines
|
|
146
|
+
|
|
147
|
+
`before_context`, `after_context`, and `context` correspond to `rg -B`, `rg -A`, and `rg -C`. Files containing NUL bytes or invalid UTF-8 are skipped.
|
|
148
|
+
|
|
149
|
+
### Block summaries
|
|
150
|
+
|
|
151
|
+
`rg(..., summary=True)` returns one row per blank-line-delimited block instead of one row per matching line. Empty and whitespace-only lines delimit blocks. A block containing several matching lines appears once and keeps every matching `SearchLine` in `matches`.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
rg("TODO", ".", summary=True, context=1, maxlen=120)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The result is `BlockResults`, a list of `SearchBlock` objects. Each block has `path`, `block_index`, `start_line`, `end_line`, `start_lnhash`, `end_lnhash`, `kind`, full `source`, and `matches`.
|
|
158
|
+
|
|
159
|
+
Matches display as `path:start-end:source`. Context displays as `path:start-end-source`. With `lnhashs=True`, the range uses copyable boundary addresses such as `path:4|a3f2|,6|b1c3|:source`. Newline runs display as `¶`. `maxlen` limits the displayed text without changing `source` or `asdict()`.
|
|
160
|
+
|
|
161
|
+
In summary mode, `before_context`, `after_context`, and `context` count neighbouring blocks. `max_results` counts matching blocks and retains their context. `summary=True` cannot be combined with `paths` or `count`. It can be combined with `lnhash` for copyable block boundaries.
|
|
162
|
+
|
|
163
|
+
## Notebooks
|
|
164
|
+
|
|
165
|
+
`nbrg` searches cell source in Jupyter `.ipynb` files and returns matching cells. Each result identifies the cell by its nbformat cell/message id, which stays stable across edits.
|
|
166
|
+
|
|
167
|
+
Plain `rg` searches escaped notebook JSON, including outputs and metadata. Its line numbers refer to that JSON file. `nbrg` searches the reconstructed cell source and identifies the cell you would edit.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from rgapi import nbrg
|
|
171
|
+
|
|
172
|
+
nbrg("read_csv", ".") # cells whose source matches, across all notebooks under "."
|
|
173
|
+
nbrg("read_csv", ".", cell_context=1) # also include neighbouring cells as context
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Notebook discovery, parsing, and matching run together in one parallel Rust pass. Matching uses `rg`'s regex engine with the same `case_sensitive` and `smart_case` behaviour. `nbrg` accepts the discovery filters from `fd` and `rg`, including `include`, `exclude`, `glob`, `hidden`, `max_depth`, and `skip_dir`.
|
|
177
|
+
|
|
178
|
+
`nbrg` returns `NbResults`, a list of `NbCell`. Each `NbCell` has:
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
path notebook path relative to root
|
|
182
|
+
cell_index 0-based position of the cell in the notebook
|
|
183
|
+
cell_id nbformat cell id (falls back to the cell index for notebooks without ids)
|
|
184
|
+
cell_type 'code', 'markdown', or 'raw'
|
|
185
|
+
kind 'match' or 'context'
|
|
186
|
+
source full cell source
|
|
187
|
+
matches list of SearchLine rows for the matched lines within the cell
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`NbCell.asdict()` returns these fields as a plain dictionary. Its `matches` field contains `SearchLine` dictionaries.
|
|
191
|
+
|
|
192
|
+
`str()` and pretty display show one line per cell. Matches use `path:cell_id:source`. Context uses `path:cell_id-source`. Newline runs display as `¶`.
|
|
193
|
+
|
|
194
|
+
A matching cell's display starts at its first matched line. Earlier lines are replaced by `…[Ln]`, where `n` is the matched line's one-based number within the cell. A leading `#|` directive is retained, as in `#| export…[L4]needle here`.
|
|
195
|
+
|
|
196
|
+
`maxlen` limits displayed source and defaults to 120. The full source remains in `source` and `asdict()`. Each cell appears once even when it has multiple matches. All hits remain in `matches`.
|
|
197
|
+
|
|
198
|
+
`cell_context=N` includes the `N` cells before and after each match as `kind="context"` rows. Context cells are deduplicated within each notebook.
|
|
199
|
+
|
|
200
|
+
`nbrg_iter` yields `NbCell` rows as notebooks are parsed. For collected results, `nbrg` accepts these limits and result options:
|
|
201
|
+
|
|
202
|
+
- `max_results` returns at most that many cells after sorting by path and cell index.
|
|
203
|
+
- `count=True` returns the number of matching cells.
|
|
204
|
+
- `timeout_ms` applies a deadline with the same `stop_reason` values as `rg`.
|
|
205
|
+
|
|
206
|
+
The parser reads only each cell's `id`, `cell_type`, and `source`. It skips outputs and metadata without loading embedded images or plots. `search_nb(pattern, path, ...)` searches a single notebook file in the same way.
|
|
207
|
+
|
|
208
|
+
`rgapi-nbrg` exposes notebook search without requiring a Python kernel:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
rgapi-nbrg 'read_csv' .
|
|
212
|
+
rgapi-nbrg 'read_csv' . --cell-context 1
|
|
213
|
+
rgapi-nbrg 'read_csv' nbs --glob '*.ipynb' --max-results 20
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Run `rgapi-nbrg --help` for its discovery, matching, and output options.
|
|
217
|
+
|
|
218
|
+
## Async
|
|
219
|
+
|
|
220
|
+
`fda`, `rga`, and `nbrga` are async versions of `fd`, `rg`, and `nbrg`. The async generators `fda_iter`, `rga_iter`, and `nbrga_iter` yield rows as the search finds them. Async functions accept the same arguments and return the same types as their synchronous equivalents.
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from rgapi import fda, rga, rga_iter
|
|
224
|
+
|
|
225
|
+
await fda(".", ext="py")
|
|
226
|
+
res = await rga("TODO", ".", timeout_ms=200)
|
|
227
|
+
if not res.complete: print(f"partial results: {res.stop_reason}")
|
|
228
|
+
async for row in rga_iter("TODO", "."): ...
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Walking and searching use Rust threads, without `asyncio.to_thread` or the event loop's executor. A callback uses `loop.call_soon_threadsafe` to complete the awaited future or supply results to the generator's queue. The event loop remains unblocked, with normal `contextvars` behaviour.
|
|
232
|
+
|
|
233
|
+
Cancellation stops the Rust workers within about one row. This includes timeouts from `asyncio.wait_for` or `asyncio.timeout`. It also includes task cancellation, such as starlette cancelling a disconnected client's request.
|
|
234
|
+
|
|
235
|
+
Wrap an async iterator in `contextlib.aclosing` when leaving its loop early. `break` alone delays generator finalization until garbage collection. The context manager provides prompt cleanup:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
async with aclosing(rga_iter("TODO", ".")) as it:
|
|
239
|
+
async for row in it:
|
|
240
|
+
if enough(row): break
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Use streaming results for incremental display, such as sending batches to a browser as they arrive. Collected results with `timeout_ms` return what was found before the deadline. Use `asyncio.wait_for` when a timeout should raise instead.
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
## Benchmarks
|
|
247
|
+
|
|
248
|
+
`tools/bench.py` compares the `rg` CLI with in-process `rgapi`. Run it against a release build. One run on this machine, using best time from seven repeats:
|
|
249
|
+
|
|
250
|
+
| fixture | rg | rgapi |
|
|
251
|
+
| --- | ---: | ---: |
|
|
252
|
+
| 6 x 2 MB files, 2 matches | 6.54 ms | 1.44 ms |
|
|
253
|
+
| 800 x 1.5 KB files, 2 matches | 13.90 ms | 10.94 ms |
|
|
254
|
+
| tiny dir, repeated 30x | 5.92 ms | 2.14 ms |
|
|
255
|
+
|