PyPcre 0.6.3__tar.gz → 0.6.6__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.
- {pypcre-0.6.3 → pypcre-0.6.6}/PKG-INFO +26 -3
- {pypcre-0.6.3 → pypcre-0.6.6}/PyPcre.egg-info/PKG-INFO +26 -3
- {pypcre-0.6.3 → pypcre-0.6.6}/PyPcre.egg-info/SOURCES.txt +6 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/README.md +25 -2
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre/__init__.py +2 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre/cache.py +4 -5
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre/pcre.py +167 -96
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre/threads.py +21 -1
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/cache.c +18 -8
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/error.c +8 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/memory.c +3 -1
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/pcre2.c +423 -76
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/pcre2_module.h +11 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pyproject.toml +1 -1
- {pypcre-0.6.3 → pypcre-0.6.6}/setup.py +30 -21
- {pypcre-0.6.3 → pypcre-0.6.6}/setup_utils.py +82 -3
- pypcre-0.6.6/tests/test_build_hardening.py +162 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_cache.py +2 -2
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_cache_scope_safety.py +70 -0
- pypcre-0.6.6/tests/test_capture_limits.py +39 -0
- pypcre-0.6.6/tests/test_lastindex_replay_and_jit_stack_fallback.py +191 -0
- pypcre-0.6.6/tests/test_literal_capture_sub_fastpath.py +64 -0
- pypcre-0.6.6/tests/test_match_limits.py +152 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_python_coverage_audit.py +12 -4
- pypcre-0.6.6/tests/test_setup_msvc_pkg_config.py +100 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/LICENSE +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/MANIFEST.in +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/PyPcre.egg-info/dependency_links.txt +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/PyPcre.egg-info/top_level.txt +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre/_stdlib_re.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre/error.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre/flags.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre/re_compat.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/atomic_compat.h +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/flag.c +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/jit_support.c +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/pattern_cache.c +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/pcre2.h +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/string_helpers.c +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/pcre_ext/util.c +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/setup.cfg +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_accuracy.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_api_parity.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_basic.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_bench_string.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_benchmark.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_bytes.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_c_api_audit.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_cache_global.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_cache_global_inproc.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_clobber.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_clobber_thread.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_clobber_verify.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_compile_utf_no_check_safety.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_core.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_coverage_gaps.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_errors.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_escape_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_expand_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_flags.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_gil_zero_safety.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_jit.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_jit_anchoring.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_jit_start_optimize_workaround.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_lastindex_ovector_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_lastindex_semantics.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_literal_capture_findall_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_literal_capture_split_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_memory.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_mmap.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_module.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_multi_literal_capture_findall_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_multi_literal_capture_split_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_pattern.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_re_compat.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_setup_utils_multiarch.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_simd.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_stdlib_flag_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_sub_count_one_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_sub_reference_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_template_dispatch_fastpath.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_threaded_backend.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_threads.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_transformers_regex.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_utf8.py +0 -0
- {pypcre-0.6.3 → pypcre-0.6.6}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyPcre
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.6
|
|
4
4
|
Summary: Modern, GIL-friendly, Fast Python bindings for PCRE2 with auto caching and JIT of compiled patterns.
|
|
5
5
|
Author-email: ModelCloud <qubitium@modelcloud.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -47,7 +47,8 @@ Fast, free-threaded Python bindings for `PCRE2` with a stable `stdlib.re`-compat
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
## Latest News 🚀
|
|
50
|
-
* 10/
|
|
50
|
+
* 10/21/2026 **[0.6.5](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.5)**: Inlined the generation-validated module hot lookup for faster module-level `match`, `search`, `fullmatch`, `findall`, and `finditer`, while preserving legacy-backend, cache, thread-default, and free-threaded fallbacks. 🚀⚡🧵
|
|
51
|
+
* 10/20/2026 **[0.6.4](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.4)**: Fixed free-threaded races in `Match.lastindex` replay and PCRE2 version initialization, reduced Python dispatch overhead across module-level and compiled-pattern `match`, `search`, `fullmatch`, `findall`, `finditer`, `split`, `sub`, and `subn` hot paths, added generation-validated module caches and exact literal-capture substitution paths, and prevented native Windows MSVC builds from inheriting MinGW `pkg-config` flags. CI now uses interpreter-scoped pip/pytest bootstraps and the matching FreeBSD Python pip package. 🛡️🧵🚀⚡🪟🐧
|
|
51
52
|
* 08/25/2026 **[0.6.2](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.2)**: Hardened packaging metadata and fixed FreeBSD/WSL CI failures on PCRE2 10.42 runtimes, including legacy replacement-template and JIT-fallback compatibility. 🛠️🐧🪟
|
|
52
53
|
* 08/25/2026 **[0.6.1](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.1)**: Workaround for PCRE2 10.46/10.47 JIT start-optimization regressions, new verifying clobber suite, second memory/thread-safety sweep, Python 3.15 `sre_parse` removal compatibility, aligned `Pattern` `pos`/`endpos` signatures, and `setuptools` metadata fixes. 🛡️🧵🐍
|
|
53
54
|
* 08/09/2026 **[0.6.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.0)**: `findall`, `finditer`, `sub`/`subn`, `split`, and `match`/`search`/`fullmatch` hot-path speedups up to **46x** vs `stdlib.re` and **48x** vs `regex`, with free-threaded `findall` reaching **13.8x** on 8 threads. 🚀⚡
|
|
@@ -255,7 +256,8 @@ numbers = pattern.findall(b"line 1\nline 22")
|
|
|
255
256
|
|
|
256
257
|
- Module helpers: `prefixmatch`, `match`, `search`, `fullmatch`, `finditer`,
|
|
257
258
|
`findall`, `split`, `sub`, `subn`, `compile`, `escape`, `purge`,
|
|
258
|
-
`template`, `parallel_map`, `configure`, `
|
|
259
|
+
`template`, `parallel_map`, `configure`, `configure_match_limits`,
|
|
260
|
+
`configure_threads`,
|
|
259
261
|
`configure_thread_pool`, `shutdown_thread_pool`, `set_cache_limit`,
|
|
260
262
|
`get_cache_limit`, and `clear_cache`.
|
|
261
263
|
- `compile()` returns a `Pattern` object with the familiar matching helpers
|
|
@@ -270,6 +272,12 @@ numbers = pattern.findall(b"line 1\nline 22")
|
|
|
270
272
|
`NOFLAG`.
|
|
271
273
|
- Errors are raised as `pcre.PcreError`; `error` and `PatternError` are kept as
|
|
272
274
|
compatibility aliases.
|
|
275
|
+
- Buffer-protocol subjects (`mmap.mmap`, `bytearray`, `array.array`,
|
|
276
|
+
`memoryview`) are accepted anywhere `bytes` is. Each call takes an immutable
|
|
277
|
+
snapshot of the buffer before matching, so concurrent writers (or another
|
|
278
|
+
process writing a shared mapping) cannot corrupt a match in progress. That
|
|
279
|
+
snapshot is a full copy: searching a 200 MB `mmap` allocates 200 MB per call.
|
|
280
|
+
For very large inputs, match over slices or chunks instead of the whole map.
|
|
273
281
|
|
|
274
282
|
### Common examples 🧪
|
|
275
283
|
|
|
@@ -367,10 +375,25 @@ the conversion without repeating the flag.
|
|
|
367
375
|
workers' high-level helper caches on their next cache-backed call. Backend
|
|
368
376
|
scratch buffers remain thread-scoped and are released when that thread exits.
|
|
369
377
|
Oversized patterns and templates are not retained.
|
|
378
|
+
- 🛑 `pcre.configure_match_limits(match_limit=..., depth_limit=..., heap_limit=...)`
|
|
379
|
+
bounds the work any single match may do, process-wide (PCRE2's
|
|
380
|
+
`pcre2_set_match_limit` / `depth_limit` / `heap_limit`; the heap limit is in
|
|
381
|
+
KiB). A match that exceeds a limit raises `PcreError` (`MATCHLIMIT`,
|
|
382
|
+
`DEPTHLIMIT` or `HEAPLIMIT`) instead of running unbounded, which is useful
|
|
383
|
+
for untrusted patterns or subjects. `0` restores the PCRE2 build default.
|
|
384
|
+
PCRE2 counts the match limit per start position, so a search over `n`
|
|
385
|
+
characters can still take up to `n × match_limit` steps.
|
|
370
386
|
- 🧵 `pcre.configure_threads()`, `pcre.configure_thread_pool()`,
|
|
371
387
|
`shutdown_thread_pool()`, `Flag.THREADS`, and `Flag.NO_THREADS` are available
|
|
372
388
|
if you want to opt into or restrict threaded execution.
|
|
373
389
|
|
|
390
|
+
## Documentation 📚
|
|
391
|
+
|
|
392
|
+
- [`lastindex` replay and JIT stack fallback](docs/lastindex-and-jit-stack.md) —
|
|
393
|
+
how `Match.lastindex`/`lastgroup` are reconstructed, why the replay must use
|
|
394
|
+
the original start offset and options, and how an exhausted JIT stack falls
|
|
395
|
+
back to the interpreter instead of raising.
|
|
396
|
+
|
|
374
397
|
## Building 🏗️
|
|
375
398
|
|
|
376
399
|
The extension links against an existing `libpcre2-8` installation. Install the development headers for your platform before building,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyPcre
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.6
|
|
4
4
|
Summary: Modern, GIL-friendly, Fast Python bindings for PCRE2 with auto caching and JIT of compiled patterns.
|
|
5
5
|
Author-email: ModelCloud <qubitium@modelcloud.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -47,7 +47,8 @@ Fast, free-threaded Python bindings for `PCRE2` with a stable `stdlib.re`-compat
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
## Latest News 🚀
|
|
50
|
-
* 10/
|
|
50
|
+
* 10/21/2026 **[0.6.5](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.5)**: Inlined the generation-validated module hot lookup for faster module-level `match`, `search`, `fullmatch`, `findall`, and `finditer`, while preserving legacy-backend, cache, thread-default, and free-threaded fallbacks. 🚀⚡🧵
|
|
51
|
+
* 10/20/2026 **[0.6.4](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.4)**: Fixed free-threaded races in `Match.lastindex` replay and PCRE2 version initialization, reduced Python dispatch overhead across module-level and compiled-pattern `match`, `search`, `fullmatch`, `findall`, `finditer`, `split`, `sub`, and `subn` hot paths, added generation-validated module caches and exact literal-capture substitution paths, and prevented native Windows MSVC builds from inheriting MinGW `pkg-config` flags. CI now uses interpreter-scoped pip/pytest bootstraps and the matching FreeBSD Python pip package. 🛡️🧵🚀⚡🪟🐧
|
|
51
52
|
* 08/25/2026 **[0.6.2](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.2)**: Hardened packaging metadata and fixed FreeBSD/WSL CI failures on PCRE2 10.42 runtimes, including legacy replacement-template and JIT-fallback compatibility. 🛠️🐧🪟
|
|
52
53
|
* 08/25/2026 **[0.6.1](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.1)**: Workaround for PCRE2 10.46/10.47 JIT start-optimization regressions, new verifying clobber suite, second memory/thread-safety sweep, Python 3.15 `sre_parse` removal compatibility, aligned `Pattern` `pos`/`endpos` signatures, and `setuptools` metadata fixes. 🛡️🧵🐍
|
|
53
54
|
* 08/09/2026 **[0.6.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.0)**: `findall`, `finditer`, `sub`/`subn`, `split`, and `match`/`search`/`fullmatch` hot-path speedups up to **46x** vs `stdlib.re` and **48x** vs `regex`, with free-threaded `findall` reaching **13.8x** on 8 threads. 🚀⚡
|
|
@@ -255,7 +256,8 @@ numbers = pattern.findall(b"line 1\nline 22")
|
|
|
255
256
|
|
|
256
257
|
- Module helpers: `prefixmatch`, `match`, `search`, `fullmatch`, `finditer`,
|
|
257
258
|
`findall`, `split`, `sub`, `subn`, `compile`, `escape`, `purge`,
|
|
258
|
-
`template`, `parallel_map`, `configure`, `
|
|
259
|
+
`template`, `parallel_map`, `configure`, `configure_match_limits`,
|
|
260
|
+
`configure_threads`,
|
|
259
261
|
`configure_thread_pool`, `shutdown_thread_pool`, `set_cache_limit`,
|
|
260
262
|
`get_cache_limit`, and `clear_cache`.
|
|
261
263
|
- `compile()` returns a `Pattern` object with the familiar matching helpers
|
|
@@ -270,6 +272,12 @@ numbers = pattern.findall(b"line 1\nline 22")
|
|
|
270
272
|
`NOFLAG`.
|
|
271
273
|
- Errors are raised as `pcre.PcreError`; `error` and `PatternError` are kept as
|
|
272
274
|
compatibility aliases.
|
|
275
|
+
- Buffer-protocol subjects (`mmap.mmap`, `bytearray`, `array.array`,
|
|
276
|
+
`memoryview`) are accepted anywhere `bytes` is. Each call takes an immutable
|
|
277
|
+
snapshot of the buffer before matching, so concurrent writers (or another
|
|
278
|
+
process writing a shared mapping) cannot corrupt a match in progress. That
|
|
279
|
+
snapshot is a full copy: searching a 200 MB `mmap` allocates 200 MB per call.
|
|
280
|
+
For very large inputs, match over slices or chunks instead of the whole map.
|
|
273
281
|
|
|
274
282
|
### Common examples 🧪
|
|
275
283
|
|
|
@@ -367,10 +375,25 @@ the conversion without repeating the flag.
|
|
|
367
375
|
workers' high-level helper caches on their next cache-backed call. Backend
|
|
368
376
|
scratch buffers remain thread-scoped and are released when that thread exits.
|
|
369
377
|
Oversized patterns and templates are not retained.
|
|
378
|
+
- 🛑 `pcre.configure_match_limits(match_limit=..., depth_limit=..., heap_limit=...)`
|
|
379
|
+
bounds the work any single match may do, process-wide (PCRE2's
|
|
380
|
+
`pcre2_set_match_limit` / `depth_limit` / `heap_limit`; the heap limit is in
|
|
381
|
+
KiB). A match that exceeds a limit raises `PcreError` (`MATCHLIMIT`,
|
|
382
|
+
`DEPTHLIMIT` or `HEAPLIMIT`) instead of running unbounded, which is useful
|
|
383
|
+
for untrusted patterns or subjects. `0` restores the PCRE2 build default.
|
|
384
|
+
PCRE2 counts the match limit per start position, so a search over `n`
|
|
385
|
+
characters can still take up to `n × match_limit` steps.
|
|
370
386
|
- 🧵 `pcre.configure_threads()`, `pcre.configure_thread_pool()`,
|
|
371
387
|
`shutdown_thread_pool()`, `Flag.THREADS`, and `Flag.NO_THREADS` are available
|
|
372
388
|
if you want to opt into or restrict threaded execution.
|
|
373
389
|
|
|
390
|
+
## Documentation 📚
|
|
391
|
+
|
|
392
|
+
- [`lastindex` replay and JIT stack fallback](docs/lastindex-and-jit-stack.md) —
|
|
393
|
+
how `Match.lastindex`/`lastgroup` are reconstructed, why the replay must use
|
|
394
|
+
the original start offset and options, and how an exhausted JIT stack falls
|
|
395
|
+
back to the interpreter instead of raising.
|
|
396
|
+
|
|
374
397
|
## Building 🏗️
|
|
375
398
|
|
|
376
399
|
The extension links against an existing `libpcre2-8` installation. Install the development headers for your platform before building,
|
|
@@ -33,12 +33,14 @@ tests/test_api_parity.py
|
|
|
33
33
|
tests/test_basic.py
|
|
34
34
|
tests/test_bench_string.py
|
|
35
35
|
tests/test_benchmark.py
|
|
36
|
+
tests/test_build_hardening.py
|
|
36
37
|
tests/test_bytes.py
|
|
37
38
|
tests/test_c_api_audit.py
|
|
38
39
|
tests/test_cache.py
|
|
39
40
|
tests/test_cache_global.py
|
|
40
41
|
tests/test_cache_global_inproc.py
|
|
41
42
|
tests/test_cache_scope_safety.py
|
|
43
|
+
tests/test_capture_limits.py
|
|
42
44
|
tests/test_clobber.py
|
|
43
45
|
tests/test_clobber_thread.py
|
|
44
46
|
tests/test_clobber_verify.py
|
|
@@ -54,9 +56,12 @@ tests/test_jit.py
|
|
|
54
56
|
tests/test_jit_anchoring.py
|
|
55
57
|
tests/test_jit_start_optimize_workaround.py
|
|
56
58
|
tests/test_lastindex_ovector_fastpath.py
|
|
59
|
+
tests/test_lastindex_replay_and_jit_stack_fallback.py
|
|
57
60
|
tests/test_lastindex_semantics.py
|
|
58
61
|
tests/test_literal_capture_findall_fastpath.py
|
|
59
62
|
tests/test_literal_capture_split_fastpath.py
|
|
63
|
+
tests/test_literal_capture_sub_fastpath.py
|
|
64
|
+
tests/test_match_limits.py
|
|
60
65
|
tests/test_memory.py
|
|
61
66
|
tests/test_mmap.py
|
|
62
67
|
tests/test_module.py
|
|
@@ -65,6 +70,7 @@ tests/test_multi_literal_capture_split_fastpath.py
|
|
|
65
70
|
tests/test_pattern.py
|
|
66
71
|
tests/test_python_coverage_audit.py
|
|
67
72
|
tests/test_re_compat.py
|
|
73
|
+
tests/test_setup_msvc_pkg_config.py
|
|
68
74
|
tests/test_setup_utils_multiarch.py
|
|
69
75
|
tests/test_simd.py
|
|
70
76
|
tests/test_stdlib_flag_fastpath.py
|
|
@@ -24,7 +24,8 @@ Fast, free-threaded Python bindings for `PCRE2` with a stable `stdlib.re`-compat
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
## Latest News 🚀
|
|
27
|
-
* 10/
|
|
27
|
+
* 10/21/2026 **[0.6.5](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.5)**: Inlined the generation-validated module hot lookup for faster module-level `match`, `search`, `fullmatch`, `findall`, and `finditer`, while preserving legacy-backend, cache, thread-default, and free-threaded fallbacks. 🚀⚡🧵
|
|
28
|
+
* 10/20/2026 **[0.6.4](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.4)**: Fixed free-threaded races in `Match.lastindex` replay and PCRE2 version initialization, reduced Python dispatch overhead across module-level and compiled-pattern `match`, `search`, `fullmatch`, `findall`, `finditer`, `split`, `sub`, and `subn` hot paths, added generation-validated module caches and exact literal-capture substitution paths, and prevented native Windows MSVC builds from inheriting MinGW `pkg-config` flags. CI now uses interpreter-scoped pip/pytest bootstraps and the matching FreeBSD Python pip package. 🛡️🧵🚀⚡🪟🐧
|
|
28
29
|
* 08/25/2026 **[0.6.2](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.2)**: Hardened packaging metadata and fixed FreeBSD/WSL CI failures on PCRE2 10.42 runtimes, including legacy replacement-template and JIT-fallback compatibility. 🛠️🐧🪟
|
|
29
30
|
* 08/25/2026 **[0.6.1](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.1)**: Workaround for PCRE2 10.46/10.47 JIT start-optimization regressions, new verifying clobber suite, second memory/thread-safety sweep, Python 3.15 `sre_parse` removal compatibility, aligned `Pattern` `pos`/`endpos` signatures, and `setuptools` metadata fixes. 🛡️🧵🐍
|
|
30
31
|
* 08/09/2026 **[0.6.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.6.0)**: `findall`, `finditer`, `sub`/`subn`, `split`, and `match`/`search`/`fullmatch` hot-path speedups up to **46x** vs `stdlib.re` and **48x** vs `regex`, with free-threaded `findall` reaching **13.8x** on 8 threads. 🚀⚡
|
|
@@ -232,7 +233,8 @@ numbers = pattern.findall(b"line 1\nline 22")
|
|
|
232
233
|
|
|
233
234
|
- Module helpers: `prefixmatch`, `match`, `search`, `fullmatch`, `finditer`,
|
|
234
235
|
`findall`, `split`, `sub`, `subn`, `compile`, `escape`, `purge`,
|
|
235
|
-
`template`, `parallel_map`, `configure`, `
|
|
236
|
+
`template`, `parallel_map`, `configure`, `configure_match_limits`,
|
|
237
|
+
`configure_threads`,
|
|
236
238
|
`configure_thread_pool`, `shutdown_thread_pool`, `set_cache_limit`,
|
|
237
239
|
`get_cache_limit`, and `clear_cache`.
|
|
238
240
|
- `compile()` returns a `Pattern` object with the familiar matching helpers
|
|
@@ -247,6 +249,12 @@ numbers = pattern.findall(b"line 1\nline 22")
|
|
|
247
249
|
`NOFLAG`.
|
|
248
250
|
- Errors are raised as `pcre.PcreError`; `error` and `PatternError` are kept as
|
|
249
251
|
compatibility aliases.
|
|
252
|
+
- Buffer-protocol subjects (`mmap.mmap`, `bytearray`, `array.array`,
|
|
253
|
+
`memoryview`) are accepted anywhere `bytes` is. Each call takes an immutable
|
|
254
|
+
snapshot of the buffer before matching, so concurrent writers (or another
|
|
255
|
+
process writing a shared mapping) cannot corrupt a match in progress. That
|
|
256
|
+
snapshot is a full copy: searching a 200 MB `mmap` allocates 200 MB per call.
|
|
257
|
+
For very large inputs, match over slices or chunks instead of the whole map.
|
|
250
258
|
|
|
251
259
|
### Common examples 🧪
|
|
252
260
|
|
|
@@ -344,10 +352,25 @@ the conversion without repeating the flag.
|
|
|
344
352
|
workers' high-level helper caches on their next cache-backed call. Backend
|
|
345
353
|
scratch buffers remain thread-scoped and are released when that thread exits.
|
|
346
354
|
Oversized patterns and templates are not retained.
|
|
355
|
+
- 🛑 `pcre.configure_match_limits(match_limit=..., depth_limit=..., heap_limit=...)`
|
|
356
|
+
bounds the work any single match may do, process-wide (PCRE2's
|
|
357
|
+
`pcre2_set_match_limit` / `depth_limit` / `heap_limit`; the heap limit is in
|
|
358
|
+
KiB). A match that exceeds a limit raises `PcreError` (`MATCHLIMIT`,
|
|
359
|
+
`DEPTHLIMIT` or `HEAPLIMIT`) instead of running unbounded, which is useful
|
|
360
|
+
for untrusted patterns or subjects. `0` restores the PCRE2 build default.
|
|
361
|
+
PCRE2 counts the match limit per start position, so a search over `n`
|
|
362
|
+
characters can still take up to `n × match_limit` steps.
|
|
347
363
|
- 🧵 `pcre.configure_threads()`, `pcre.configure_thread_pool()`,
|
|
348
364
|
`shutdown_thread_pool()`, `Flag.THREADS`, and `Flag.NO_THREADS` are available
|
|
349
365
|
if you want to opt into or restrict threaded execution.
|
|
350
366
|
|
|
367
|
+
## Documentation 📚
|
|
368
|
+
|
|
369
|
+
- [`lastindex` replay and JIT stack fallback](docs/lastindex-and-jit-stack.md) —
|
|
370
|
+
how `Match.lastindex`/`lastgroup` are reconstructed, why the replay must use
|
|
371
|
+
the original start offset and options, and how an exhausted JIT stack falls
|
|
372
|
+
back to the interpreter instead of raising.
|
|
373
|
+
|
|
351
374
|
## Building 🏗️
|
|
352
375
|
|
|
353
376
|
The extension links against an existing `libpcre2-8` installation. Install the development headers for your platform before building,
|
|
@@ -26,6 +26,7 @@ from .pcre import (
|
|
|
26
26
|
clear_cache,
|
|
27
27
|
compile,
|
|
28
28
|
configure,
|
|
29
|
+
configure_match_limits,
|
|
29
30
|
findall,
|
|
30
31
|
finditer,
|
|
31
32
|
fullmatch,
|
|
@@ -109,6 +110,7 @@ __all__ = [
|
|
|
109
110
|
"clear_cache",
|
|
110
111
|
"purge",
|
|
111
112
|
"configure",
|
|
113
|
+
"configure_match_limits",
|
|
112
114
|
"configure_threads",
|
|
113
115
|
"configure_thread_pool",
|
|
114
116
|
"cache_strategy",
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
import os
|
|
11
10
|
from enum import Enum
|
|
12
11
|
from threading import RLock, local
|
|
13
12
|
from typing import Any, Callable, TypeVar, cast
|
|
@@ -113,10 +112,10 @@ def _env_flag_is_true(value: str | None) -> bool:
|
|
|
113
112
|
return value[0] not in {"0", "f", "F", "n", "N"}
|
|
114
113
|
|
|
115
114
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
# The C extension latches the strategy when it is imported (honouring -E/-I
|
|
116
|
+
# and the PCRE2_CACHE_PATTERN_GLOBAL alias via Py_GETENV); mirror it rather
|
|
117
|
+
# than re-reading the environment so the two layers can never disagree.
|
|
118
|
+
_CACHE_STRATEGY = _CacheStrategy(_pcre2.get_cache_strategy())
|
|
120
119
|
|
|
121
120
|
|
|
122
121
|
def cache_strategy(strategy: str | None = None) -> str:
|