rgapi 0.2.1__tar.gz → 0.2.3__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.2.1 → rgapi-0.2.3}/.github/workflows/ci.yml +1 -1
- {rgapi-0.2.1 → rgapi-0.2.3}/Cargo.lock +55 -54
- {rgapi-0.2.1 → rgapi-0.2.3}/Cargo.toml +3 -3
- {rgapi-0.2.1 → rgapi-0.2.3}/DEV.md +10 -5
- {rgapi-0.2.1 → rgapi-0.2.3}/PKG-INFO +33 -18
- {rgapi-0.2.1 → rgapi-0.2.3}/README.md +32 -17
- {rgapi-0.2.1 → rgapi-0.2.3}/py/Cargo.toml +2 -3
- {rgapi-0.2.1 → rgapi-0.2.3}/py/src/lib.rs +56 -217
- {rgapi-0.2.1 → rgapi-0.2.3}/python/rgapi/__init__.py +39 -45
- {rgapi-0.2.1 → rgapi-0.2.3}/python/rgapi/_cli.py +1 -1
- {rgapi-0.2.1 → rgapi-0.2.3}/python/rgapi/nb.py +4 -2
- {rgapi-0.2.1 → rgapi-0.2.3}/python/rgapi/skill.py +1 -1
- rgapi-0.2.3/rustfmt.toml +1 -0
- {rgapi-0.2.1 → rgapi-0.2.3}/src/block.rs +14 -32
- {rgapi-0.2.1 → rgapi-0.2.3}/src/lib.rs +3 -1
- rgapi-0.2.3/src/matcher.rs +174 -0
- {rgapi-0.2.1 → rgapi-0.2.3}/src/nb.rs +22 -66
- {rgapi-0.2.1 → rgapi-0.2.3}/src/search.rs +27 -118
- {rgapi-0.2.1 → rgapi-0.2.3}/src/walk.rs +60 -120
- {rgapi-0.2.1 → rgapi-0.2.3}/tests/test_async.py +20 -9
- {rgapi-0.2.1 → rgapi-0.2.3}/tests/test_rgapi.py +127 -66
- {rgapi-0.2.1 → rgapi-0.2.3}/tools/bench.py +17 -18
- rgapi-0.2.1/rustfmt.toml +0 -4
- {rgapi-0.2.1 → rgapi-0.2.3}/.gitignore +0 -0
- {rgapi-0.2.1 → rgapi-0.2.3}/LICENSE +0 -0
- {rgapi-0.2.1 → rgapi-0.2.3}/_config.yml +0 -0
- {rgapi-0.2.1 → rgapi-0.2.3}/_layouts/default.html +0 -0
- {rgapi-0.2.1 → rgapi-0.2.3}/py/build.rs +0 -0
- {rgapi-0.2.1 → rgapi-0.2.3}/pyproject.toml +0 -0
- {rgapi-0.2.1 → rgapi-0.2.3}/python/rgapi/block.py +0 -0
|
@@ -62,7 +62,7 @@ jobs:
|
|
|
62
62
|
- uses: dtolnay/rust-toolchain@stable
|
|
63
63
|
- id: crates-auth
|
|
64
64
|
uses: rust-lang/crates-io-auth-action@v1
|
|
65
|
-
- run: cargo publish -p rgapi
|
|
65
|
+
- run: cargo publish -p rgapi --no-verify
|
|
66
66
|
env:
|
|
67
67
|
CARGO_REGISTRY_TOKEN: ${{ steps.crates-auth.outputs.token }}
|
|
68
68
|
- uses: actions/download-artifact@v8
|
|
@@ -11,6 +11,21 @@ dependencies = [
|
|
|
11
11
|
"memchr",
|
|
12
12
|
]
|
|
13
13
|
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "bit-set"
|
|
16
|
+
version = "0.8.0"
|
|
17
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
|
+
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
|
19
|
+
dependencies = [
|
|
20
|
+
"bit-vec",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "bit-vec"
|
|
25
|
+
version = "0.8.0"
|
|
26
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
27
|
+
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
|
28
|
+
|
|
14
29
|
[[package]]
|
|
15
30
|
name = "bstr"
|
|
16
31
|
version = "1.13.1"
|
|
@@ -18,7 +33,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
18
33
|
checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f"
|
|
19
34
|
dependencies = [
|
|
20
35
|
"memchr",
|
|
21
|
-
"regex-automata",
|
|
22
36
|
"serde_core",
|
|
23
37
|
]
|
|
24
38
|
|
|
@@ -36,9 +50,9 @@ checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48"
|
|
|
36
50
|
|
|
37
51
|
[[package]]
|
|
38
52
|
name = "crc32fast"
|
|
39
|
-
version = "1.5.
|
|
53
|
+
version = "1.5.3"
|
|
40
54
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
41
|
-
checksum = "
|
|
55
|
+
checksum = "0ba106e844045ddc02c20768ac4b0b7cb45f2cc2ecbd833ac9f5dacb183c2055"
|
|
42
56
|
dependencies = [
|
|
43
57
|
"cfg-if",
|
|
44
58
|
]
|
|
@@ -91,6 +105,17 @@ dependencies = [
|
|
|
91
105
|
"encoding_rs",
|
|
92
106
|
]
|
|
93
107
|
|
|
108
|
+
[[package]]
|
|
109
|
+
name = "fancy-regex"
|
|
110
|
+
version = "0.20.0"
|
|
111
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
112
|
+
checksum = "20ed7f62ce0ae0da1a8b93c3c7104ab7e0760a74491e097c701fff966e945390"
|
|
113
|
+
dependencies = [
|
|
114
|
+
"bit-set",
|
|
115
|
+
"regex-automata",
|
|
116
|
+
"regex-syntax",
|
|
117
|
+
]
|
|
118
|
+
|
|
94
119
|
[[package]]
|
|
95
120
|
name = "globset"
|
|
96
121
|
version = "0.4.20"
|
|
@@ -113,19 +138,6 @@ dependencies = [
|
|
|
113
138
|
"memchr",
|
|
114
139
|
]
|
|
115
140
|
|
|
116
|
-
[[package]]
|
|
117
|
-
name = "grep-regex"
|
|
118
|
-
version = "0.1.14"
|
|
119
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
120
|
-
checksum = "0ce0c256c3ad82bcc07b812c15a45ec1d398122e8e15124f96695234db7112ef"
|
|
121
|
-
dependencies = [
|
|
122
|
-
"bstr",
|
|
123
|
-
"grep-matcher",
|
|
124
|
-
"log",
|
|
125
|
-
"regex-automata",
|
|
126
|
-
"regex-syntax",
|
|
127
|
-
]
|
|
128
|
-
|
|
129
141
|
[[package]]
|
|
130
142
|
name = "grep-searcher"
|
|
131
143
|
version = "0.1.17"
|
|
@@ -171,9 +183,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
|
171
183
|
|
|
172
184
|
[[package]]
|
|
173
185
|
name = "libc"
|
|
174
|
-
version = "0.2.
|
|
186
|
+
version = "0.2.191"
|
|
175
187
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
176
|
-
checksum = "
|
|
188
|
+
checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
|
|
177
189
|
|
|
178
190
|
[[package]]
|
|
179
191
|
name = "log"
|
|
@@ -189,9 +201,9 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
|
189
201
|
|
|
190
202
|
[[package]]
|
|
191
203
|
name = "memmap2"
|
|
192
|
-
version = "0.9.
|
|
204
|
+
version = "0.9.12"
|
|
193
205
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
194
|
-
checksum = "
|
|
206
|
+
checksum = "6b0ca1155a9ecc25aa12e8313a119f32edf0255fea2f44839c4ac0a12b2ac157"
|
|
195
207
|
dependencies = [
|
|
196
208
|
"libc",
|
|
197
209
|
]
|
|
@@ -225,9 +237,9 @@ dependencies = [
|
|
|
225
237
|
|
|
226
238
|
[[package]]
|
|
227
239
|
name = "pyo3"
|
|
228
|
-
version = "0.
|
|
240
|
+
version = "0.30.1"
|
|
229
241
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
230
|
-
checksum = "
|
|
242
|
+
checksum = "229ca13b8a15bb0d1487b81ab975bfcd97513416499a8312011b6036e42e0de4"
|
|
231
243
|
dependencies = [
|
|
232
244
|
"libc",
|
|
233
245
|
"once_cell",
|
|
@@ -239,18 +251,18 @@ dependencies = [
|
|
|
239
251
|
|
|
240
252
|
[[package]]
|
|
241
253
|
name = "pyo3-build-config"
|
|
242
|
-
version = "0.
|
|
254
|
+
version = "0.30.1"
|
|
243
255
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
244
|
-
checksum = "
|
|
256
|
+
checksum = "70130e2b6dd1ee589a8788026a429561ca5c40fa7a2fa7914c2282eddae24700"
|
|
245
257
|
dependencies = [
|
|
246
258
|
"target-lexicon",
|
|
247
259
|
]
|
|
248
260
|
|
|
249
261
|
[[package]]
|
|
250
262
|
name = "pyo3-ffi"
|
|
251
|
-
version = "0.
|
|
263
|
+
version = "0.30.1"
|
|
252
264
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
253
|
-
checksum = "
|
|
265
|
+
checksum = "5e7ff73c555c286d212d4ce7e5fbda845a973c4f61bd5f160f5f2c3c2a04d1c4"
|
|
254
266
|
dependencies = [
|
|
255
267
|
"libc",
|
|
256
268
|
"pyo3-build-config",
|
|
@@ -258,26 +270,26 @@ dependencies = [
|
|
|
258
270
|
|
|
259
271
|
[[package]]
|
|
260
272
|
name = "pyo3-macros"
|
|
261
|
-
version = "0.
|
|
273
|
+
version = "0.30.1"
|
|
262
274
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
263
|
-
checksum = "
|
|
275
|
+
checksum = "c46d2e5862c7bb8207e3b639f713dda3c499933444784154c11d013c5a691b60"
|
|
264
276
|
dependencies = [
|
|
265
277
|
"proc-macro2",
|
|
266
278
|
"pyo3-macros-backend",
|
|
267
279
|
"quote",
|
|
268
|
-
"syn
|
|
280
|
+
"syn",
|
|
269
281
|
]
|
|
270
282
|
|
|
271
283
|
[[package]]
|
|
272
284
|
name = "pyo3-macros-backend"
|
|
273
|
-
version = "0.
|
|
285
|
+
version = "0.30.1"
|
|
274
286
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
275
|
-
checksum = "
|
|
287
|
+
checksum = "38b89f066f2ae72a2aef52d1537ca2fcd104440ebfadee06e97aa90738a25b9b"
|
|
276
288
|
dependencies = [
|
|
277
289
|
"heck",
|
|
278
290
|
"proc-macro2",
|
|
279
291
|
"quote",
|
|
280
|
-
"syn
|
|
292
|
+
"syn",
|
|
281
293
|
]
|
|
282
294
|
|
|
283
295
|
[[package]]
|
|
@@ -308,24 +320,24 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
|
|
308
320
|
|
|
309
321
|
[[package]]
|
|
310
322
|
name = "rgapi"
|
|
311
|
-
version = "0.2.
|
|
323
|
+
version = "0.2.3"
|
|
312
324
|
dependencies = [
|
|
313
325
|
"crc32fast",
|
|
326
|
+
"fancy-regex",
|
|
314
327
|
"globset",
|
|
315
328
|
"grep-matcher",
|
|
316
|
-
"grep-regex",
|
|
317
329
|
"grep-searcher",
|
|
318
330
|
"ignore",
|
|
331
|
+
"regex-syntax",
|
|
319
332
|
"serde",
|
|
320
333
|
"serde_json",
|
|
321
334
|
]
|
|
322
335
|
|
|
323
336
|
[[package]]
|
|
324
337
|
name = "rgapi-py"
|
|
325
|
-
version = "0.2.
|
|
338
|
+
version = "0.2.3"
|
|
326
339
|
dependencies = [
|
|
327
340
|
"grep-matcher",
|
|
328
|
-
"grep-regex",
|
|
329
341
|
"pyo3",
|
|
330
342
|
"pyo3-build-config",
|
|
331
343
|
"rgapi",
|
|
@@ -379,14 +391,14 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|
|
379
391
|
dependencies = [
|
|
380
392
|
"proc-macro2",
|
|
381
393
|
"quote",
|
|
382
|
-
"syn
|
|
394
|
+
"syn",
|
|
383
395
|
]
|
|
384
396
|
|
|
385
397
|
[[package]]
|
|
386
398
|
name = "serde_json"
|
|
387
|
-
version = "1.0.
|
|
399
|
+
version = "1.0.154"
|
|
388
400
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
389
|
-
checksum = "
|
|
401
|
+
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
|
|
390
402
|
dependencies = [
|
|
391
403
|
"itoa",
|
|
392
404
|
"memchr",
|
|
@@ -403,20 +415,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
|
|
403
415
|
|
|
404
416
|
[[package]]
|
|
405
417
|
name = "syn"
|
|
406
|
-
version = "
|
|
407
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
408
|
-
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
409
|
-
dependencies = [
|
|
410
|
-
"proc-macro2",
|
|
411
|
-
"quote",
|
|
412
|
-
"unicode-ident",
|
|
413
|
-
]
|
|
414
|
-
|
|
415
|
-
[[package]]
|
|
416
|
-
name = "syn"
|
|
417
|
-
version = "3.0.6"
|
|
418
|
+
version = "3.0.9"
|
|
418
419
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
419
|
-
checksum = "
|
|
420
|
+
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
|
|
420
421
|
dependencies = [
|
|
421
422
|
"proc-macro2",
|
|
422
423
|
"quote",
|
|
@@ -431,9 +432,9 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
431
432
|
|
|
432
433
|
[[package]]
|
|
433
434
|
name = "unicode-ident"
|
|
434
|
-
version = "1.0.
|
|
435
|
+
version = "1.0.27"
|
|
435
436
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
436
|
-
checksum = "
|
|
437
|
+
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
|
|
437
438
|
|
|
438
439
|
[[package]]
|
|
439
440
|
name = "walkdir"
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
members = ["py"]
|
|
3
3
|
|
|
4
4
|
[workspace.package]
|
|
5
|
-
version = "0.2.
|
|
5
|
+
version = "0.2.3"
|
|
6
6
|
edition = "2024"
|
|
7
7
|
|
|
8
8
|
[workspace.dependencies]
|
|
9
9
|
grep-matcher = ">=0.1.8"
|
|
10
|
-
grep-regex = ">=0.1.14"
|
|
11
10
|
|
|
12
11
|
[package]
|
|
13
12
|
name = "rgapi"
|
|
@@ -25,11 +24,12 @@ name = "rgapi"
|
|
|
25
24
|
|
|
26
25
|
[dependencies]
|
|
27
26
|
crc32fast = "1"
|
|
27
|
+
fancy-regex = ">=0.20"
|
|
28
28
|
globset = ">=0.4.18"
|
|
29
29
|
grep-matcher.workspace = true
|
|
30
|
-
grep-regex.workspace = true
|
|
31
30
|
grep-searcher = ">=0.1.16"
|
|
32
31
|
ignore = ">=0.4.26"
|
|
32
|
+
regex-syntax = "0.8"
|
|
33
33
|
serde = { version = "=1", features = ["derive"] }
|
|
34
34
|
serde_json = "=1"
|
|
35
35
|
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
## Layout
|
|
6
6
|
|
|
7
7
|
```text
|
|
8
|
-
src/
|
|
9
|
-
src/
|
|
8
|
+
src/matcher.rs shared fancy-regex engine, smart-case policy, and grep-matcher adapter
|
|
9
|
+
src/walk.rs ignore/globset-backed path walking and filtering
|
|
10
|
+
src/search.rs grep-searcher-backed searching
|
|
10
11
|
src/block.rs blank-line-delimited block grouping, matching, and block context
|
|
11
12
|
py/src/lib.rs PyO3 classes and private core functions
|
|
12
13
|
python/rgapi/ public Python wrappers over `rgapi._core`, plus the `rgapi-nbrg` CLI
|
|
@@ -22,7 +23,7 @@ cargo develop
|
|
|
22
23
|
pytest -q
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
`cargo develop` and bare `cargo test` use the same Cargo profile and share the core and binding libraries. Unit tests also build a separate `cfg(test)` executable. The published `rgapi` crate has no Python dependency; the unpublished `rgapi-py` crate in `py/` builds the extension. Run `cargo
|
|
26
|
+
`cargo develop` and bare `cargo test` use the same Cargo profile and share the core and binding libraries. Unit tests also build a separate `cfg(test)` executable. The published `rgapi` crate has no Python dependency; the unpublished `rgapi-py` crate in `py/` builds the extension. Run `cargo fastfmt` after Rust edits and `chkstyle` before committing Python edits.
|
|
26
27
|
|
|
27
28
|
## Release
|
|
28
29
|
|
|
@@ -38,6 +39,10 @@ The GitHub workflow builds wheels for Python 3.10-3.13 on Linux and macOS and pu
|
|
|
38
39
|
|
|
39
40
|
## Design notes
|
|
40
41
|
|
|
42
|
+
All user-supplied regexes use the shared `RegexMatcher` in `src/matcher.rs`, backed by `fancy-regex`. There is no separate fallback engine or native C dependency. The adapter implements `grep_matcher::Matcher` so `grep-searcher` continues to handle file scanning, line numbers, context, and binary detection. `compile_regex` returns rgapi's own exported `RegexMatcher` type.
|
|
43
|
+
|
|
44
|
+
The matcher centralizes smart-case analysis and line-ending policy. Line-safe patterns can search a whole buffer; patterns that can consume line endings, inspect neighbouring lines or need the backtracking engine are evaluated on individual lines, so that engine's step budget covers one line, not a whole buffer. Notebook multiline matching instead uses the whole cell. Filename/path regexes match their whole input. Backtracking-limit errors propagate through the existing result channels, including directory-pruning errors; cancellation must not hide a queued error from async consumers.
|
|
45
|
+
|
|
41
46
|
Python discovery and `paths=True` results contain absolute `pathlib.Path` objects. Structured search rows hold base-relative string labels with `/` 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 12 bits of CRC-32 over the line's UTF-8 bytes, encoded as two Base64url characters); `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, filesystem, hidden, and ignore options use `ignore::WalkBuilder` settings. The `ignore` walker follows every root it is given. Discovery checks each root with `symlink_metadata`. The worker sends each unfollowed link root as a result before walking the other roots. This also supports dangling roots, which the underlying walker would reject. `ls` sets `walk_root_links`, which gives a root link to a directory to the walker. The walker then reports paths under the link. Other discovery roots use absolute paths without canonicalizing. Content searches retain canonical root resolution. `rg_iter` exposes the same parallel search stream that `rg` collects by default; `paths=True` and `count=True` consume that stream with different reducers. Text search skips binary files and invalid UTF-8 content.
|
|
42
47
|
|
|
43
48
|
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 = PathBuf`, 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.
|
|
@@ -46,9 +51,9 @@ Streaming engine: `walk.rs` owns the generic machinery. `StreamIter<T>` is the w
|
|
|
46
51
|
|
|
47
52
|
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.
|
|
48
53
|
|
|
49
|
-
Truncation is recorded on collected results: `max_results` sets `stop_reason="max_results"`, and `timeout_ms` on `rg`/`rga`/`nbrg`/`nbrga`/`fd`/`fda`/`
|
|
54
|
+
Truncation is recorded on collected results: `max_results` sets `stop_reason="max_results"`, and `timeout_ms` on `rg`/`rga`/`nbrg`/`nbrga`/`fd`/`fda`/`ls` sets `stop_reason="timeout"`. `SearchResults`, `BlockResults`, `PathResults`, and `NbResults` share this through `_Results`; `complete` means `stop_reason is None`. In block summary mode, `max_results` counts matching blocks and keeps their block context. `count` returns a plain int, or a dict for `count="file"`, so it rejects timeouts and block summary mode.
|
|
50
55
|
|
|
51
|
-
Rust discovery returns base-relative `PathBuf` values. Glob filters operate on native paths; only regex matching uses string labels. PyO3 converts discovery roots and results with its filesystem-path support. Python joins each result to the base from `_core.walk_base` without resolving links. `PathResults` stores that display base and completion status, including across slices. Its `__repr__` uses `Path.lstat()` for an `ls -l`-style listing capped at `MAX_REPR` rows. `str()` returns one base-relative name per line. `ls` is `fd` with shell-style defaults (one level, dirs, ignore rules off) and `walk_root_links`, sorted in place.
|
|
56
|
+
Rust discovery returns base-relative `PathBuf` values. Glob filters operate on native paths; only regex matching uses string labels. PyO3 converts discovery roots and results with its filesystem-path support. Python joins each result to the base from `_core.walk_base` without resolving links. `PathResults` stores that display base and completion status, including across slices. Its `__repr__` uses `Path.lstat()` for an `ls -l`-style listing capped at `MAX_REPR` rows. `str()` returns one base-relative name per line. `ls` is `fd` with shell-style defaults (one level, dirs, ignore rules off) and `walk_root_links`, sorted in place. `fd` takes `pattern` first, as `rg` does. `ls` takes `root` first, as the shell's `ls DIR` does.
|
|
52
57
|
|
|
53
58
|
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.
|
|
54
59
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rgapi
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Requires-Dist: fastcore>=2.2.29
|
|
@@ -23,7 +23,7 @@ Project-URL: Repository, https://github.com/AnswerDotAI/rgapi
|
|
|
23
23
|
|
|
24
24
|
`rgapi` provides `fd`-style file discovery and `rg`-style text search from Python without starting a shell command.
|
|
25
25
|
|
|
26
|
-
It uses
|
|
26
|
+
It uses ripgrep's `ignore` and `grep-searcher` crates for walking and file scanning, with the pure-Rust `fancy-regex` engine for regex matching. Walking and searching run in parallel by default. Most expensive work stays in Rust.
|
|
27
27
|
|
|
28
28
|
## Overview
|
|
29
29
|
|
|
@@ -32,7 +32,7 @@ For common file discovery and search:
|
|
|
32
32
|
```python
|
|
33
33
|
from rgapi import fd, ls, rg, rg_iter
|
|
34
34
|
|
|
35
|
-
fd(
|
|
35
|
+
fd(ext="py", exclude="test_*.py")
|
|
36
36
|
ls("src")
|
|
37
37
|
for row in rg_iter("TODO", ".", include="*.py", context=2): print(row.asdict())
|
|
38
38
|
rg("TODO", ".", ext="py", skip_dir=".venv", paths=True)
|
|
@@ -56,16 +56,15 @@ await rga("TODO", ".", ext="py", timeout_ms=200)
|
|
|
56
56
|
async for row in rga_iter("TODO", "."): print(row)
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Use the lower-level functions to compile a regex, search text or a single file
|
|
59
|
+
Use the lower-level functions to compile a regex, or to search text or a single file:
|
|
60
60
|
|
|
61
61
|
```python
|
|
62
|
-
from rgapi import compile, search_path, search_text
|
|
62
|
+
from rgapi import compile, search_path, search_text
|
|
63
63
|
|
|
64
64
|
matcher = compile("TODO")
|
|
65
65
|
matcher.is_match("TODO")
|
|
66
66
|
matcher.finditer("TODO TODO")
|
|
67
67
|
|
|
68
|
-
walk(".")
|
|
69
68
|
search_text(matcher, "alpha\nTODO\nomega\n", path="memory.txt", context=1)
|
|
70
69
|
search_path(matcher, "src/lib.rs", display_path="src/lib.rs")
|
|
71
70
|
```
|
|
@@ -78,7 +77,7 @@ pip install rgapi
|
|
|
78
77
|
|
|
79
78
|
## File discovery
|
|
80
79
|
|
|
81
|
-
`fd`
|
|
80
|
+
`fd` returns absolute `pathlib.Path` objects. Use them directly with `.read_text()`, `.open()`, `.stat()`, or other filesystem operations. Its collected results display names relative to `root`. Pass `root` as a `str` or `Path`, or as a list of them. The sync and async APIs expand `~` and accept `.`, `./`, and paths containing `..`.
|
|
82
81
|
|
|
83
82
|
A list of roots, such as `rg("TODO", ["src", "tests"])`, works with every walk and search function. The roots are searched as one walk that returns one result list. `timeout_ms` and `max_results` apply to the whole walk. Result paths are relative to the common ancestor of the roots. For example, rows read `src/app.py` and `tests/test_app.py`. `PathResults` displays use the same relative paths. Filters such as `include`, `path_re` and `skip_dir` match these relative paths. A file under more than one root, such as `src/app.py` with roots `[".", "src"]`, appears once.
|
|
84
83
|
|
|
@@ -88,7 +87,7 @@ Hidden files are skipped unless `hidden=True`. Discovery returns symlinks themse
|
|
|
88
87
|
|
|
89
88
|
Traversal runs in parallel without guaranteed result order. Use `sorted(...)` when order matters.
|
|
90
89
|
|
|
91
|
-
`fd`
|
|
90
|
+
`fd` takes `pattern` first and `root` second, as `rg` does. With no pattern, it returns every file under `root`. `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.
|
|
92
91
|
|
|
93
92
|
`include` and `exclude` use case-sensitive glob syntax. `glob=` is an alias for `include=`. Patterns without `/` match names at any depth, so `*.py` matches `src/app.py`. Patterns containing `/` are root-relative. `*` stays within a path component; `**` spans directories: `src/*` matches immediate children, whereas `src/**` also matches deeper descendants. Matching a directory with `exclude` prunes its entire subtree. Includes never prune traversal. Excludes always win, and includes do not override ignore rules.
|
|
94
93
|
|
|
@@ -96,7 +95,7 @@ Filter extensions with `ext="py"` or `ext=["py", "rs"]`. Extension and glob filt
|
|
|
96
95
|
|
|
97
96
|
Set `min_depth` and `max_depth` to bound recursion. `max_filesize` skips files above a byte limit.
|
|
98
97
|
|
|
99
|
-
`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.
|
|
98
|
+
`ls` follows the shell command's listing conventions. It takes `root` first, as the shell's `ls DIR` does. 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.
|
|
100
99
|
|
|
101
100
|
`fd_iter` yields absolute `Path` objects as the walk finds them. It accepts every `fd` filter. Stopping iteration ends the walk. It does not accept `timeout_ms`.
|
|
102
101
|
|
|
@@ -108,6 +107,16 @@ Set `min_depth` and `max_depth` to bound recursion. `max_filesize` skips files a
|
|
|
108
107
|
|
|
109
108
|
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.
|
|
110
109
|
|
|
110
|
+
Patterns support backreferences and lookaround everywhere, including filename and path filters:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
rg(r"\b(\w+)\s+\1\b", ".") # repeated words
|
|
114
|
+
rg(r"(?<=TODO: )\w+", ".") # word following TODO:
|
|
115
|
+
fd(r"^(\w+)_\1\.py$") # names such as utils_utils.py
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
All regex parameters use `fancy-regex`, with no engine-selection flag and no C library. Ordinary patterns, including word boundaries (`\b`, `\B`) outside lookbehinds, use its non-backtracking engine. Patterns requiring backtracking have a step budget that allows lines of millions of characters. A search that exceeds the budget raises `ValueError`. It never silently skips a file or path.
|
|
119
|
+
|
|
111
120
|
Each `SearchLine` has these fields:
|
|
112
121
|
|
|
113
122
|
```text
|
|
@@ -125,16 +134,18 @@ matches list of (start, end) byte offsets for match rows
|
|
|
125
134
|
|
|
126
135
|
Pass `lnhashs=True` to `rg` or `rg_iter` to display hash addresses instead of line numbers. The `line_number` field remains available.
|
|
127
136
|
|
|
128
|
-
For other result forms, use `rg(..., paths=True)` to return unique matched paths
|
|
137
|
+
For other result forms, use `rg(..., paths=True)` to return unique matched paths, `rg(..., count=True)` to count match spans, or `rg(..., count="file")` for a dict from each file's label to its count, sorted by label. `paths` and `count` cannot both be set.
|
|
138
|
+
|
|
139
|
+
`^` and `$` match at the start and end of each line. A line ends at `\n` or `\r\n`. `$` also matches before a `\r` that has no `\n` after it. `rg`, `rgstr`, and `compile` reject patterns containing a literal `\n` or `\r`. `nbrg` allows them with `multiline=True`.
|
|
129
140
|
|
|
130
|
-
|
|
141
|
+
Content searches and compiled content matchers are confined to individual lines: whitespace classes, backreferences and lookaround cannot cross a line ending, and `\A`/`\z` refer to each line. With `nbrg(..., multiline=True)`, `\A`/`\z` refer to the whole cell, while `^`/`$` still anchor lines. Filename and path filters match the full name or path instead.
|
|
131
142
|
|
|
132
143
|
### Path results
|
|
133
144
|
|
|
134
|
-
`fd
|
|
145
|
+
`fd` and `ls` return `PathResults`, a list of absolute `Path` objects. So do `rg` and `nbrg` with `paths=True`, and their async equivalents. Indexing or iterating returns ordinary Paths:
|
|
135
146
|
|
|
136
147
|
```python
|
|
137
|
-
for path in fd("src", ext="py"):
|
|
148
|
+
for path in fd(root="src", ext="py"):
|
|
138
149
|
text = path.read_text()
|
|
139
150
|
size = path.stat().st_size
|
|
140
151
|
```
|
|
@@ -151,13 +162,15 @@ Rust callers can set `FindOptions::special_files` to also discover FIFOs, socket
|
|
|
151
162
|
|
|
152
163
|
### Limits and timeouts
|
|
153
164
|
|
|
154
|
-
Set `timeout_ms` on `rg`, `fd
|
|
165
|
+
Set `timeout_ms` on `rg`, `fd` 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:
|
|
155
166
|
|
|
156
167
|
- `stop_reason=None` means the result is complete.
|
|
157
168
|
- `stop_reason="max_results"` means `max_results` truncated the result.
|
|
158
169
|
- `stop_reason="timeout"` means the deadline was reached.
|
|
159
170
|
|
|
160
|
-
`complete` is true exactly when `stop_reason` is `None`.
|
|
171
|
+
`complete` is true exactly when `stop_reason` is `None`. A count is a plain integer or dict, with no completion flag. Counting can't be combined with `timeout_ms`.
|
|
172
|
+
|
|
173
|
+
Deadlines stop result collection and request worker cancellation. Workers cannot interrupt a regex evaluation or filesystem call already in progress; a timeout is not a hard per-match execution limit.
|
|
161
174
|
|
|
162
175
|
### Context lines
|
|
163
176
|
|
|
@@ -190,7 +203,9 @@ nbrg("read_csv", ".") # cells whose source matches, across all
|
|
|
190
203
|
nbrg("read_csv", ".", cell_context=1) # also include neighbouring cells as context
|
|
191
204
|
```
|
|
192
205
|
|
|
193
|
-
Notebook discovery, parsing, and matching run together in one parallel Rust pass. Matching uses `
|
|
206
|
+
Notebook discovery, parsing, and matching run together in one parallel Rust pass. Matching uses the same `fancy-regex` engine as `rgapi.rg`, including backreferences and lookaround, 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`.
|
|
207
|
+
|
|
208
|
+
Matching is line-oriented by default. Use `nbrg(r"(foo)\n\1", ".", multiline=True)` to match across lines within a cell. Matches never cross cell boundaries.
|
|
194
209
|
|
|
195
210
|
`nbrg` returns `NbResults`, a list of `NbCell`. Each `NbCell` has:
|
|
196
211
|
|
|
@@ -240,7 +255,7 @@ Run `rgapi-nbrg --help` for its discovery, matching, and output options.
|
|
|
240
255
|
```python
|
|
241
256
|
from rgapi import fda, rga, rga_iter
|
|
242
257
|
|
|
243
|
-
await fda(
|
|
258
|
+
await fda(ext="py")
|
|
244
259
|
res = await rga("TODO", ".", timeout_ms=200)
|
|
245
260
|
if not res.complete: print(f"partial results: {res.stop_reason}")
|
|
246
261
|
async for row in rga_iter("TODO", "."): ...
|
|
@@ -248,7 +263,7 @@ async for row in rga_iter("TODO", "."): ...
|
|
|
248
263
|
|
|
249
264
|
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.
|
|
250
265
|
|
|
251
|
-
Cancellation
|
|
266
|
+
Cancellation is checked between rows and files; it does not interrupt a regex evaluation or filesystem call already in progress. This includes timeouts from `asyncio.wait_for` or `asyncio.timeout`. It also includes task cancellation, such as starlette cancelling a disconnected client's request.
|
|
252
267
|
|
|
253
268
|
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:
|
|
254
269
|
|