PyPcre 0.6.0__tar.gz → 0.6.2__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.
Files changed (84) hide show
  1. {pypcre-0.6.0 → pypcre-0.6.2}/PKG-INFO +89 -30
  2. {pypcre-0.6.0 → pypcre-0.6.2}/PyPcre.egg-info/PKG-INFO +89 -30
  3. {pypcre-0.6.0 → pypcre-0.6.2}/PyPcre.egg-info/SOURCES.txt +15 -0
  4. {pypcre-0.6.0 → pypcre-0.6.2}/README.md +86 -27
  5. {pypcre-0.6.0 → pypcre-0.6.2}/pcre/__init__.py +1 -7
  6. pypcre-0.6.2/pcre/_stdlib_re.py +37 -0
  7. {pypcre-0.6.0 → pypcre-0.6.2}/pcre/cache.py +111 -25
  8. pypcre-0.6.2/pcre/pcre.py +1935 -0
  9. {pypcre-0.6.0 → pypcre-0.6.2}/pcre/re_compat.py +154 -8
  10. pypcre-0.6.2/pcre/threads.py +292 -0
  11. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/atomic_compat.h +36 -0
  12. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/cache.c +52 -8
  13. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/error.c +21 -0
  14. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/memory.c +14 -20
  15. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/pattern_cache.c +102 -86
  16. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/pcre2.c +2387 -264
  17. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/pcre2_module.h +6 -0
  18. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/string_helpers.c +192 -3
  19. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/util.c +13 -8
  20. {pypcre-0.6.0 → pypcre-0.6.2}/pyproject.toml +4 -4
  21. {pypcre-0.6.0 → pypcre-0.6.2}/setup.py +7 -1
  22. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_api_parity.py +85 -0
  23. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_c_api_audit.py +91 -0
  24. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_cache.py +82 -23
  25. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_cache_global.py +32 -3
  26. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_cache_global_inproc.py +58 -5
  27. pypcre-0.6.2/tests/test_cache_scope_safety.py +276 -0
  28. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_clobber.py +4 -1
  29. pypcre-0.6.2/tests/test_clobber_verify.py +876 -0
  30. pypcre-0.6.2/tests/test_compile_utf_no_check_safety.py +150 -0
  31. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_core.py +2 -0
  32. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_coverage_gaps.py +18 -4
  33. pypcre-0.6.2/tests/test_escape_fastpath.py +83 -0
  34. pypcre-0.6.2/tests/test_expand_fastpath.py +302 -0
  35. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_gil_zero_safety.py +118 -1
  36. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_jit.py +7 -2
  37. pypcre-0.6.2/tests/test_jit_start_optimize_workaround.py +82 -0
  38. pypcre-0.6.2/tests/test_lastindex_ovector_fastpath.py +78 -0
  39. pypcre-0.6.2/tests/test_literal_capture_findall_fastpath.py +123 -0
  40. pypcre-0.6.2/tests/test_literal_capture_split_fastpath.py +94 -0
  41. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_module.py +5 -0
  42. pypcre-0.6.2/tests/test_multi_literal_capture_findall_fastpath.py +144 -0
  43. pypcre-0.6.2/tests/test_multi_literal_capture_split_fastpath.py +114 -0
  44. pypcre-0.6.2/tests/test_python_coverage_audit.py +826 -0
  45. pypcre-0.6.2/tests/test_stdlib_flag_fastpath.py +111 -0
  46. pypcre-0.6.2/tests/test_sub_count_one_fastpath.py +154 -0
  47. pypcre-0.6.2/tests/test_sub_reference_fastpath.py +140 -0
  48. pypcre-0.6.2/tests/test_template_dispatch_fastpath.py +74 -0
  49. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_threaded_backend.py +27 -4
  50. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_threads.py +134 -6
  51. pypcre-0.6.0/pcre/_stdlib_re.py +0 -28
  52. pypcre-0.6.0/pcre/pcre.py +0 -924
  53. pypcre-0.6.0/pcre/threads.py +0 -181
  54. pypcre-0.6.0/tests/test_python_coverage_audit.py +0 -348
  55. {pypcre-0.6.0 → pypcre-0.6.2}/LICENSE +0 -0
  56. {pypcre-0.6.0 → pypcre-0.6.2}/MANIFEST.in +0 -0
  57. {pypcre-0.6.0 → pypcre-0.6.2}/PyPcre.egg-info/dependency_links.txt +0 -0
  58. {pypcre-0.6.0 → pypcre-0.6.2}/PyPcre.egg-info/top_level.txt +0 -0
  59. {pypcre-0.6.0 → pypcre-0.6.2}/pcre/error.py +0 -0
  60. {pypcre-0.6.0 → pypcre-0.6.2}/pcre/flags.py +0 -0
  61. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/flag.c +0 -0
  62. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/jit_support.c +0 -0
  63. {pypcre-0.6.0 → pypcre-0.6.2}/pcre_ext/pcre2.h +0 -0
  64. {pypcre-0.6.0 → pypcre-0.6.2}/setup.cfg +0 -0
  65. {pypcre-0.6.0 → pypcre-0.6.2}/setup_utils.py +0 -0
  66. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_accuracy.py +0 -0
  67. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_basic.py +0 -0
  68. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_bench_string.py +0 -0
  69. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_benchmark.py +0 -0
  70. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_bytes.py +0 -0
  71. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_clobber_thread.py +0 -0
  72. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_errors.py +0 -0
  73. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_flags.py +0 -0
  74. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_jit_anchoring.py +0 -0
  75. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_lastindex_semantics.py +0 -0
  76. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_memory.py +0 -0
  77. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_mmap.py +0 -0
  78. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_pattern.py +0 -0
  79. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_re_compat.py +0 -0
  80. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_setup_utils_multiarch.py +0 -0
  81. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_simd.py +0 -0
  82. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_transformers_regex.py +0 -0
  83. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_utf8.py +0 -0
  84. {pypcre-0.6.0 → pypcre-0.6.2}/tests/test_version.py +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPcre
3
- Version: 0.6.0
3
+ Version: 0.6.2
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
7
- Project-URL: Homepage, https://github.com/ModelCloud/pcre
7
+ Project-URL: Homepage, https://github.com/ModelCloud/PyPcre
8
8
  Keywords: regex,pcre2,bindings
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.14
16
16
  Classifier: Programming Language :: C
17
17
  Classifier: Topic :: Software Development :: Libraries
18
18
  Classifier: Topic :: Text Processing :: Linguistic
19
- Requires-Python: >=3.9
19
+ Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Dynamic: license-file
@@ -47,6 +47,7 @@ Fast, free-threaded Python bindings for `PCRE2` with a stable `stdlib.re`-compat
47
47
 
48
48
 
49
49
  ## Latest News 🚀
50
+ * 08/09–08/10/2026 **API performance and safety update**: common regex workloads and ordered `parallel_map(search)` workloads were benchmarked across Python 3.10 and free-threaded Python 3.14t/GIL=0. Compatibility fallbacks preserve complex patterns, subclasses, buffers, and callables. Unsafe UTF bytes compilation is also blocked, with differential, randomized, concurrency, subprocess, and memory-safety coverage. 🧵⚡🛡️
50
51
  * 08/08/2026 **0.6.0**: `findall`, `finditer`, `sub`/`subn`, `split`, and `match`/`search`/`fullmatch` are now up to **46x faster** than `stdlib.re` and **48x faster** than `regex` on `finditer`/`findall` workloads, **13x** on `split`, and **2–9x** on `sub`/`subn` backref workloads, with full `re` semantics. Free-threaded `findall` reaches **13.8x** vs `re` on 8 threads. 🚀⚡
51
52
  * 07/27/2026 [0.5.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.5.0): Zero-copy buffer-protocol subject support (`mmap.mmap`, `bytearray`, `array.array`) with UTF-8 validation and GIL=0-safe memory pinning. 🗂️⚡
52
53
  * 07/24/2026 [0.4.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.4.0): C extension hardening (memory/pointer safety, bounds checks, atomic allocator init), GIL=0 safety verified, vectorized UTF-8 index/offset conversion, GIL-release threshold for small calls, C `findall` implementation, and README competitor benchmarks. 🛡️⚡
@@ -68,10 +69,10 @@ PyPcre pairs Python's familiar `re`-compatible API with the real `PCRE2` engine.
68
69
 
69
70
  - 🧬 **Full power of PCRE2**: PyPcre uses the real `PCRE2` engine, so you get native compile options, semantics, JIT, and upstream tuning.
70
71
  - 🔥 **More expressive regex syntax**: `PCRE2` supports constructs beyond stdlib `re`, including atomic groups `(?>...)`, possessive quantifiers `++`, branch-reset groups `(?|...)`, richer lookarounds, and backtracking control verbs like `(*SKIP)(*FAIL)`.
71
- - 🧵 **Thread-safe into `nogil`**: PyPcre is built for `PYTHON_GIL=0`, with CI coverage, lock-aware caches, reusable match/JIT resources, and `parallel_map()` for multi-subject fan-out.
72
+ - 🧵 **Thread-safe into `nogil`**: PyPcre is built for `PYTHON_GIL=0`, with CI coverage and `parallel_map()` for multi-subject fan-out.
72
73
  - ⚡ **Fast on real workloads**: `PCRE2` JIT plus cached compiled patterns lets PyPcre match or beat `re` and `regex` on many common scans, especially multiline searches, lookaround-heavy patterns, and free-threaded execution.
73
74
  - 🛡️ **Safer operational story**: PyPcre prefers the system `libpcre2-8` shared library so normal OS package updates can bring security and bug-fix benefits without a bundled fork.
74
- - ✅ **Validated thoroughly**: the project runs API tests, fuzz tests, memory-safety checks, local `valgrind` leak checks, and `massif` heap profiles. Recent local profiling found `0` definite leaks and `0` possible leaks in both the public API and raw binding paths.
75
+ - ✅ **Validated thoroughly**: the project runs API, fuzz, concurrency, and memory-safety tests.
75
76
 
76
77
  ### Quick Comparison 🥊
77
78
 
@@ -88,17 +89,69 @@ PyPcre pairs Python's familiar `re`-compatible API with the real `PCRE2` engine.
88
89
 
89
90
  ### Benchmark Highlights 🏁
90
91
 
91
- Measured on a `Python 3.14.6` free-threaded build on x86_64 Linux with compiled-pattern reuse and JIT enabled. Times are the best of several runs; lower is better. Only workloads where PyPcre is decisively faster than both `stdlib.re` and `regex` are shown.
92
-
93
- A reproducible version of this benchmark lives in [`benchmarks/competitor_bench.py`](benchmarks/competitor_bench.py).
92
+ The tables below summarize representative public workloads. Lower is better.
93
+
94
+ #### Fan-out and API speedups
95
+
96
+ Both interpreter runs used the same scheduler policy. The host reports 12
97
+ performance logical CPUs and 4 efficiency logical CPUs; macOS does not provide
98
+ an unprivileged hard per-process CPU mask, so these measurements record the
99
+ topology rather than claiming hard CPU affinity.
100
+
101
+ | Workload | Python 3.10 | Python 3.14t/GIL=0 |
102
+ | --- | ---: | ---: |
103
+ | `parallel_map(search)`, 16 × 1 MiB subjects, 12 workers | **8.57x** | **7.85x** |
104
+ | `parallel_map(findall)`, 48 × 1 MiB subjects, 12 workers | **11.51x** | **11.25x** |
105
+ | No-op `escape("literal")` | **5.0x** | **3.6x** |
106
+ | No-op `escape(b"literal")` | **6.9x** | **6.5x** |
107
+ | Bound literal `sub(..., count=1)` | **4.7x** | **4.3x** |
108
+ | Bound numeric-reference `sub(..., count=1)` | **4.3x** | **4.1x** |
109
+ | Bound explicit-reference `sub(..., count=1)` | **4.6x** | **4.3x** |
110
+ | Bound named-reference `sub(..., count=1)` | **4.2x** | **4.3x** |
111
+ | Bound numeric-reference `sub(..., count=2)` | **4.6x** | **4.2x** |
112
+ | Bound numeric-reference `sub(..., count=4)` | **6.0x** | **5.1x** |
113
+ | Bound numeric-reference `sub(..., count=8)` | **8.6x** | **6.1x** |
114
+ | Cached compile with `re.I` | **6.7x** | **6.9x** |
115
+ | Cached compile with `re.I|re.M|re.S|re.X` | **6.2x** | **6.4x** |
116
+ | First-read `lastindex` cost, sole capture | **11.8x** | **8.2x** |
117
+ | Deprecated `template()` compatibility call | **6.2x** | **1.1x** |
118
+ | Literal-capture `findall`, 100 matches | **5.0x** | **6.2x** |
119
+ | Literal-capture `findall`, 500 matches | **7.0x** | **8.4x** |
120
+ | Two literal captures `findall`, 100 matches | **7.5x** | **10.8x** |
121
+ | Two literal captures `findall`, 500 matches | **13.5x** | **18.3x** |
122
+ | Eight literal captures `findall`, 500 matches | **24.5x** | **29.4x** |
123
+ | Three literal captures `split`, 500 captures | **4.5x** | **6.5x** |
124
+ | Eight literal captures `split`, 500 captures | **6.6x** | **9.0x** |
125
+ | Literal-capture `split`, 100 captures | **2.6x** | **3.5x** |
126
+ | Literal-capture `split`, 2,000 captures | **3.1x** | **3.8x** |
127
+ | Bound one-character literal `Pattern.split` | **2.1x** | **1.7x** |
128
+ | Bound backreference `sub` hot path | **1.38 μs** | **1.14 μs** |
129
+ | One-match numeric-reference `Pattern.sub` | **0.45 μs** | **0.34 μs** |
130
+ | One-match explicit-reference `Pattern.sub` | **0.45 μs** | **0.31 μs** |
131
+ | One-match named-reference `Pattern.sub` | **0.46 μs** | **0.33 μs** |
132
+ | Repeated call-local `Match.groups()` | **~0.05 μs** | **~0.05 μs** |
133
+ | Call-local `Match.expand(r"[\\1]")` | **0.07 μs** | **0.07 μs** |
134
+ | Call-local `Match.expand(r"[\\g<1>]")` | **0.11 μs** | **0.08 μs** |
135
+ | Call-local `Match.expand(r"[\\g<word>]")` | **0.13 μs** | **0.11 μs** |
136
+ | Call-local two-name `Match.expand` | **0.18 μs** | **0.15 μs** |
137
+ | Call-local three-name `Match.expand` | **0.23 μs** | **0.20 μs** |
138
+ | Call-local eight-name `Match.expand` | **0.44 μs** | **0.39 μs** |
139
+ | Literal-backslash + named `Match.expand` | **0.12 μs** | **0.10 μs** |
140
+ | Named + backslash-suffix `Match.expand` | **0.16 μs** | **0.13 μs** |
141
+ | Repeated default `compile("(x)")` | **0.49 μs** | **0.38 μs** |
142
+ | Repeated integer-flagged `compile("x", CASELESS)` | **1.16 μs** | **0.81 μs** |
143
+
144
+ The parallel figures are serial-to-parallel speedups and preserve input order
145
+ and exception behavior. They were measured with the same scheduler policy for
146
+ both interpreters on the Apple host.
94
147
 
95
148
  #### `findall` — large multiline and lookaround workloads
96
149
 
97
150
  | Workload | PyPcre (ms) | `re` (ms) | `regex` (ms) | PyPcre edge |
98
151
  | --- | ---: | ---: | ---: | --- |
99
- | Extract `WARN` / `ERROR` lines (multiline) | `0.664` | `27.159` | `30.772` | **40.9x** vs `re`, **46.3x** vs `regex` |
100
- | Per-line full-name extraction (multiline) | `0.914` | `25.685` | `14.482` | **28.1x** vs `re`, **15.8x** vs `regex` |
101
- | Lookbehind + negative-lookahead tokens | `1.874` | `12.353` | `10.386` | **6.6x** vs `re`, **5.5x** vs `regex` |
152
+ | Extract `WARN` / `ERROR` lines (multiline) | `0.471` | `15.098` | `16.281` | **32.1x** vs `re`, **34.6x** vs `regex` |
153
+ | Per-line full-name extraction (multiline) | `0.588` | `14.305` | `8.000` | **24.3x** vs `re`, **13.6x** vs `regex` |
154
+ | Lookbehind + negative-lookahead tokens | `1.013` | `6.129` | `5.267` | **6.1x** vs `re`, **5.2x** vs `regex` |
102
155
 
103
156
  Patterns used:
104
157
 
@@ -113,51 +166,51 @@ Patterns used:
113
166
 
114
167
  #### `finditer` — same workloads
115
168
 
116
- Measured on a Python 3.10 x86_64 Linux build with compiled-pattern reuse and JIT enabled. A reproducible version lives in [`benchmarks/finditer_bench.py`](benchmarks/finditer_bench.py).
169
+ Measured on Python 3.10.11 arm64 with compiled-pattern reuse and JIT enabled. A reproducible version lives in [`benchmarks/finditer_bench.py`](benchmarks/finditer_bench.py).
117
170
 
118
171
  | Workload | PyPcre (ms) | `re` (ms) | `regex` (ms) | PyPcre edge |
119
172
  | --- | ---: | ---: | ---: | --- |
120
- | Extract `WARN` / `ERROR` lines | `0.663` | `30.747` | `31.862` | **46.4x** vs `re`, **48.0x** vs `regex` |
121
- | Per-line full-name extraction | `0.919` | `29.127` | `14.103` | **31.7x** vs `re`, **15.3x** vs `regex` |
122
- | Lookbehind + negative lookahead | `3.755` | `15.828` | `11.896` | **4.2x** vs `re`, **3.2x** vs `regex` |
173
+ | Extract `WARN` / `ERROR` lines | `0.449` | `17.145` | `16.175` | **38.2x** vs `re`, **36.0x** vs `regex` |
174
+ | Per-line full-name extraction | `0.564` | `16.258` | `7.874` | **28.8x** vs `re`, **14.0x** vs `regex` |
175
+ | Lookbehind + negative lookahead | `1.942` | `8.548` | `6.037` | **4.4x** vs `re`, **3.1x** vs `regex` |
123
176
 
124
177
  #### `sub` / `subn` — high-volume replacement workloads
125
178
 
126
- Measured on a Python 3.10 x86_64 Linux build with compiled-pattern reuse and JIT enabled. Times are the best of several runs; lower is better. The benchmark replaces 100,000 space-separated tokens.
179
+ Measured on Python 3.10.11 arm64 with compiled-pattern reuse and JIT enabled. Values are medians of three outer runs; lower is better. The benchmark replaces 100,000 space-separated tokens.
127
180
 
128
181
  A reproducible version lives in [`benchmarks/sub_bench.py`](benchmarks/sub_bench.py).
129
182
 
130
183
  | Workload | PyPcre (ms) | `re` (ms) | `regex` (ms) | PyPcre edge |
131
184
  | --- | ---: | ---: | ---: | --- |
132
- | Literal replacement (`\w+` → `[X]`) | `5.933` | `13.367` | `19.107` | **2.3x** vs `re`, **3.2x** vs `regex` |
133
- | Single numeric backref (`(w)\d+` → `[\1]`) | `7.221` | `64.715` | `25.482` | **9.0x** vs `re`, **3.5x** vs `regex` |
134
- | Two numeric backrefs (`(w)(\d+)` → `\2-\1`) | `17.600` | `76.222` | `32.048` | **4.3x** vs `re`, **1.8x** vs `regex` |
135
- | Named backref (`(?P<g>\w+)` → `<\g<g>>`) | `14.684` | `71.053` | `30.222` | **4.8x** vs `re`, **2.1x** vs `regex` |
185
+ | Literal replacement (`\w+` → `[X]`) | `3.127` | `6.489` | `9.992` | **2.1x** vs `re`, **3.2x** vs `regex` |
186
+ | Single numeric backref (`(w)\d+` → `[\1]`) | `4.003` | `30.098` | `12.481` | **7.5x** vs `re`, **3.1x** vs `regex` |
187
+ | Two numeric backrefs (`(w)(\d+)` → `\2-\1`) | `9.940` | `35.052` | `16.201` | **3.5x** vs `re`, **1.6x** vs `regex` |
188
+ | Named backref (`(?P<g>\w+)` → `<\g<g>>`) | `9.765` | `32.434` | `15.216` | **3.3x** vs `re`, **1.6x** vs `regex` |
136
189
 
137
190
  #### `split` — high-volume delimiter workloads
138
191
 
139
- Measured on a Python 3.10 x86_64 Linux build with compiled-pattern reuse and JIT enabled. Times are the best of several runs; lower is better. The benchmark splits 100,000 space-separated tokens.
192
+ Measured on Python 3.10.11 arm64 with compiled-pattern reuse and JIT enabled. Values are medians of three outer runs; lower is better. The benchmark splits 100,000 space-separated tokens.
140
193
 
141
194
  A reproducible version lives in [`benchmarks/split_bench.py`](benchmarks/split_bench.py).
142
195
 
143
196
  | Workload | PyPcre (ms) | `re` (ms) | `regex` (ms) | PyPcre edge |
144
197
  | --- | ---: | ---: | ---: | --- |
145
- | Delimiter no group (`\s+`) | `7.315` | `17.394` | `19.690` | **2.4x** vs `re`, **2.7x** vs `regex` |
146
- | Delimiter with group (`(\s+)`) | `12.360` | `21.099` | `25.145` | **1.7x** vs `re`, **2.0x** vs `regex` |
147
- | Single char (` `) | `4.583` | `3.990` | `14.577` | parity vs `re`, **3.2x** vs `regex` |
148
- | Single char with group (`( )`) | `8.913` | `11.091` | `18.595` | **1.2x** vs `re`, **2.1x** vs `regex` |
149
- | Empty pattern (`''`) | `5.228` | `45.552` | `69.913` | **8.7x** vs `re`, **13.4x** vs `regex` |
198
+ | Delimiter no group (`\s+`) | `3.349` | `8.013` | `9.913` | **2.4x** vs `re`, **3.0x** vs `regex` |
199
+ | Delimiter with group (`(\s+)`) | `5.080` | `9.142` | `12.374` | **1.8x** vs `re`, **2.4x** vs `regex` |
200
+ | Single char (` `) | `1.484` | `1.666` | `6.963` | **1.1x** vs `re`, **4.7x** vs `regex` |
201
+ | Single char with group (`( )`) | `1.772` | `4.203` | `8.875` | **2.4x** vs `re`, **5.0x** vs `regex` |
202
+ | Empty pattern (`''`) | `35.736` | `18.386` | `35.801` | **0.5x** vs `re`, parity vs `regex` |
150
203
 
151
204
  ### Free-Threaded Benchmark Highlights 🧵
152
205
 
153
- Measured on the same `Python 3.14.6` free-threaded build with `8` threads fanning out over split copies of each workload. Times are the best of several runs; lower is better.
206
+ Measured on the same Apple arm64 `Python 3.14.0rc2` free-threaded build with `8` threads fanning out over split copies of each workload. Values are medians of three outer runs; lower is better.
154
207
 
155
208
  A reproducible version lives in [`benchmarks/free_threaded_bench.py`](benchmarks/free_threaded_bench.py).
156
209
 
157
210
  | Workload | PyPcre (ms) | `re` (ms) | `regex` (ms) | PyPcre edge |
158
211
  | --- | ---: | ---: | ---: | --- |
159
- | Extract `WARN` / `ERROR` lines (`findall`) | `0.672` | `9.063` | `9.297` | **13.5x** vs `re`, **13.8x** vs `regex` |
160
- | Per-line full-name extraction (`findall`) | `0.913` | `8.611` | `4.575` | **9.4x** vs `re`, **5.0x** vs `regex` |
212
+ | Extract `WARN` / `ERROR` lines (`findall`) | `0.401` | `2.430` | `2.626` | **6.1x** vs `re`, **6.6x** vs `regex` |
213
+ | Per-line full-name extraction (`findall`) | `0.462` | `2.351` | `1.477` | **5.1x** vs `re`, **3.2x** vs `regex` |
161
214
 
162
215
  PyPcre is the stronger all-around choice when you want more than the baseline: full `PCRE2` features, more expressive syntax, JIT, explicit free-threaded support, and a stable `re`-compatible API surface. It keeps Python ergonomics while giving you a substantially more capable engine. 🚀
163
216
 
@@ -303,7 +356,13 @@ the conversion without repeating the flag.
303
356
  - `pcre.configure(jit=False)` disables JIT globally. `Flag.JIT` and
304
357
  `Flag.NO_JIT` let you override that per pattern.
305
358
  - `pcre.set_cache_limit()`, `pcre.get_cache_limit()`, and `pcre.clear_cache()`
306
- control the high-level compile cache.
359
+ control every high-level compile/template helper cache in the active context.
360
+ A zero limit disables them, and `None` uses a 256-entry hard safety ceiling
361
+ rather than permitting unbounded growth. High-level cache entries never cross
362
+ thread scope in the default thread-local strategy; a clear invalidates live
363
+ workers' high-level helper caches on their next cache-backed call. Backend
364
+ scratch buffers remain thread-scoped and are released when that thread exits.
365
+ Oversized patterns and templates are not retained.
307
366
  - `pcre.configure_threads()`, `pcre.configure_thread_pool()`,
308
367
  `shutdown_thread_pool()`, `Flag.THREADS`, and `Flag.NO_THREADS` are available
309
368
  if you want to opt into or restrict threaded execution.
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPcre
3
- Version: 0.6.0
3
+ Version: 0.6.2
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
7
- Project-URL: Homepage, https://github.com/ModelCloud/pcre
7
+ Project-URL: Homepage, https://github.com/ModelCloud/PyPcre
8
8
  Keywords: regex,pcre2,bindings
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.14
16
16
  Classifier: Programming Language :: C
17
17
  Classifier: Topic :: Software Development :: Libraries
18
18
  Classifier: Topic :: Text Processing :: Linguistic
19
- Requires-Python: >=3.9
19
+ Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Dynamic: license-file
@@ -47,6 +47,7 @@ Fast, free-threaded Python bindings for `PCRE2` with a stable `stdlib.re`-compat
47
47
 
48
48
 
49
49
  ## Latest News 🚀
50
+ * 08/09–08/10/2026 **API performance and safety update**: common regex workloads and ordered `parallel_map(search)` workloads were benchmarked across Python 3.10 and free-threaded Python 3.14t/GIL=0. Compatibility fallbacks preserve complex patterns, subclasses, buffers, and callables. Unsafe UTF bytes compilation is also blocked, with differential, randomized, concurrency, subprocess, and memory-safety coverage. 🧵⚡🛡️
50
51
  * 08/08/2026 **0.6.0**: `findall`, `finditer`, `sub`/`subn`, `split`, and `match`/`search`/`fullmatch` are now up to **46x faster** than `stdlib.re` and **48x faster** than `regex` on `finditer`/`findall` workloads, **13x** on `split`, and **2–9x** on `sub`/`subn` backref workloads, with full `re` semantics. Free-threaded `findall` reaches **13.8x** vs `re` on 8 threads. 🚀⚡
51
52
  * 07/27/2026 [0.5.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.5.0): Zero-copy buffer-protocol subject support (`mmap.mmap`, `bytearray`, `array.array`) with UTF-8 validation and GIL=0-safe memory pinning. 🗂️⚡
52
53
  * 07/24/2026 [0.4.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.4.0): C extension hardening (memory/pointer safety, bounds checks, atomic allocator init), GIL=0 safety verified, vectorized UTF-8 index/offset conversion, GIL-release threshold for small calls, C `findall` implementation, and README competitor benchmarks. 🛡️⚡
@@ -68,10 +69,10 @@ PyPcre pairs Python's familiar `re`-compatible API with the real `PCRE2` engine.
68
69
 
69
70
  - 🧬 **Full power of PCRE2**: PyPcre uses the real `PCRE2` engine, so you get native compile options, semantics, JIT, and upstream tuning.
70
71
  - 🔥 **More expressive regex syntax**: `PCRE2` supports constructs beyond stdlib `re`, including atomic groups `(?>...)`, possessive quantifiers `++`, branch-reset groups `(?|...)`, richer lookarounds, and backtracking control verbs like `(*SKIP)(*FAIL)`.
71
- - 🧵 **Thread-safe into `nogil`**: PyPcre is built for `PYTHON_GIL=0`, with CI coverage, lock-aware caches, reusable match/JIT resources, and `parallel_map()` for multi-subject fan-out.
72
+ - 🧵 **Thread-safe into `nogil`**: PyPcre is built for `PYTHON_GIL=0`, with CI coverage and `parallel_map()` for multi-subject fan-out.
72
73
  - ⚡ **Fast on real workloads**: `PCRE2` JIT plus cached compiled patterns lets PyPcre match or beat `re` and `regex` on many common scans, especially multiline searches, lookaround-heavy patterns, and free-threaded execution.
73
74
  - 🛡️ **Safer operational story**: PyPcre prefers the system `libpcre2-8` shared library so normal OS package updates can bring security and bug-fix benefits without a bundled fork.
74
- - ✅ **Validated thoroughly**: the project runs API tests, fuzz tests, memory-safety checks, local `valgrind` leak checks, and `massif` heap profiles. Recent local profiling found `0` definite leaks and `0` possible leaks in both the public API and raw binding paths.
75
+ - ✅ **Validated thoroughly**: the project runs API, fuzz, concurrency, and memory-safety tests.
75
76
 
76
77
  ### Quick Comparison 🥊
77
78
 
@@ -88,17 +89,69 @@ PyPcre pairs Python's familiar `re`-compatible API with the real `PCRE2` engine.
88
89
 
89
90
  ### Benchmark Highlights 🏁
90
91
 
91
- Measured on a `Python 3.14.6` free-threaded build on x86_64 Linux with compiled-pattern reuse and JIT enabled. Times are the best of several runs; lower is better. Only workloads where PyPcre is decisively faster than both `stdlib.re` and `regex` are shown.
92
-
93
- A reproducible version of this benchmark lives in [`benchmarks/competitor_bench.py`](benchmarks/competitor_bench.py).
92
+ The tables below summarize representative public workloads. Lower is better.
93
+
94
+ #### Fan-out and API speedups
95
+
96
+ Both interpreter runs used the same scheduler policy. The host reports 12
97
+ performance logical CPUs and 4 efficiency logical CPUs; macOS does not provide
98
+ an unprivileged hard per-process CPU mask, so these measurements record the
99
+ topology rather than claiming hard CPU affinity.
100
+
101
+ | Workload | Python 3.10 | Python 3.14t/GIL=0 |
102
+ | --- | ---: | ---: |
103
+ | `parallel_map(search)`, 16 × 1 MiB subjects, 12 workers | **8.57x** | **7.85x** |
104
+ | `parallel_map(findall)`, 48 × 1 MiB subjects, 12 workers | **11.51x** | **11.25x** |
105
+ | No-op `escape("literal")` | **5.0x** | **3.6x** |
106
+ | No-op `escape(b"literal")` | **6.9x** | **6.5x** |
107
+ | Bound literal `sub(..., count=1)` | **4.7x** | **4.3x** |
108
+ | Bound numeric-reference `sub(..., count=1)` | **4.3x** | **4.1x** |
109
+ | Bound explicit-reference `sub(..., count=1)` | **4.6x** | **4.3x** |
110
+ | Bound named-reference `sub(..., count=1)` | **4.2x** | **4.3x** |
111
+ | Bound numeric-reference `sub(..., count=2)` | **4.6x** | **4.2x** |
112
+ | Bound numeric-reference `sub(..., count=4)` | **6.0x** | **5.1x** |
113
+ | Bound numeric-reference `sub(..., count=8)` | **8.6x** | **6.1x** |
114
+ | Cached compile with `re.I` | **6.7x** | **6.9x** |
115
+ | Cached compile with `re.I|re.M|re.S|re.X` | **6.2x** | **6.4x** |
116
+ | First-read `lastindex` cost, sole capture | **11.8x** | **8.2x** |
117
+ | Deprecated `template()` compatibility call | **6.2x** | **1.1x** |
118
+ | Literal-capture `findall`, 100 matches | **5.0x** | **6.2x** |
119
+ | Literal-capture `findall`, 500 matches | **7.0x** | **8.4x** |
120
+ | Two literal captures `findall`, 100 matches | **7.5x** | **10.8x** |
121
+ | Two literal captures `findall`, 500 matches | **13.5x** | **18.3x** |
122
+ | Eight literal captures `findall`, 500 matches | **24.5x** | **29.4x** |
123
+ | Three literal captures `split`, 500 captures | **4.5x** | **6.5x** |
124
+ | Eight literal captures `split`, 500 captures | **6.6x** | **9.0x** |
125
+ | Literal-capture `split`, 100 captures | **2.6x** | **3.5x** |
126
+ | Literal-capture `split`, 2,000 captures | **3.1x** | **3.8x** |
127
+ | Bound one-character literal `Pattern.split` | **2.1x** | **1.7x** |
128
+ | Bound backreference `sub` hot path | **1.38 μs** | **1.14 μs** |
129
+ | One-match numeric-reference `Pattern.sub` | **0.45 μs** | **0.34 μs** |
130
+ | One-match explicit-reference `Pattern.sub` | **0.45 μs** | **0.31 μs** |
131
+ | One-match named-reference `Pattern.sub` | **0.46 μs** | **0.33 μs** |
132
+ | Repeated call-local `Match.groups()` | **~0.05 μs** | **~0.05 μs** |
133
+ | Call-local `Match.expand(r"[\\1]")` | **0.07 μs** | **0.07 μs** |
134
+ | Call-local `Match.expand(r"[\\g<1>]")` | **0.11 μs** | **0.08 μs** |
135
+ | Call-local `Match.expand(r"[\\g<word>]")` | **0.13 μs** | **0.11 μs** |
136
+ | Call-local two-name `Match.expand` | **0.18 μs** | **0.15 μs** |
137
+ | Call-local three-name `Match.expand` | **0.23 μs** | **0.20 μs** |
138
+ | Call-local eight-name `Match.expand` | **0.44 μs** | **0.39 μs** |
139
+ | Literal-backslash + named `Match.expand` | **0.12 μs** | **0.10 μs** |
140
+ | Named + backslash-suffix `Match.expand` | **0.16 μs** | **0.13 μs** |
141
+ | Repeated default `compile("(x)")` | **0.49 μs** | **0.38 μs** |
142
+ | Repeated integer-flagged `compile("x", CASELESS)` | **1.16 μs** | **0.81 μs** |
143
+
144
+ The parallel figures are serial-to-parallel speedups and preserve input order
145
+ and exception behavior. They were measured with the same scheduler policy for
146
+ both interpreters on the Apple host.
94
147
 
95
148
  #### `findall` — large multiline and lookaround workloads
96
149
 
97
150
  | Workload | PyPcre (ms) | `re` (ms) | `regex` (ms) | PyPcre edge |
98
151
  | --- | ---: | ---: | ---: | --- |
99
- | Extract `WARN` / `ERROR` lines (multiline) | `0.664` | `27.159` | `30.772` | **40.9x** vs `re`, **46.3x** vs `regex` |
100
- | Per-line full-name extraction (multiline) | `0.914` | `25.685` | `14.482` | **28.1x** vs `re`, **15.8x** vs `regex` |
101
- | Lookbehind + negative-lookahead tokens | `1.874` | `12.353` | `10.386` | **6.6x** vs `re`, **5.5x** vs `regex` |
152
+ | Extract `WARN` / `ERROR` lines (multiline) | `0.471` | `15.098` | `16.281` | **32.1x** vs `re`, **34.6x** vs `regex` |
153
+ | Per-line full-name extraction (multiline) | `0.588` | `14.305` | `8.000` | **24.3x** vs `re`, **13.6x** vs `regex` |
154
+ | Lookbehind + negative-lookahead tokens | `1.013` | `6.129` | `5.267` | **6.1x** vs `re`, **5.2x** vs `regex` |
102
155
 
103
156
  Patterns used:
104
157
 
@@ -113,51 +166,51 @@ Patterns used:
113
166
 
114
167
  #### `finditer` — same workloads
115
168
 
116
- Measured on a Python 3.10 x86_64 Linux build with compiled-pattern reuse and JIT enabled. A reproducible version lives in [`benchmarks/finditer_bench.py`](benchmarks/finditer_bench.py).
169
+ Measured on Python 3.10.11 arm64 with compiled-pattern reuse and JIT enabled. A reproducible version lives in [`benchmarks/finditer_bench.py`](benchmarks/finditer_bench.py).
117
170
 
118
171
  | Workload | PyPcre (ms) | `re` (ms) | `regex` (ms) | PyPcre edge |
119
172
  | --- | ---: | ---: | ---: | --- |
120
- | Extract `WARN` / `ERROR` lines | `0.663` | `30.747` | `31.862` | **46.4x** vs `re`, **48.0x** vs `regex` |
121
- | Per-line full-name extraction | `0.919` | `29.127` | `14.103` | **31.7x** vs `re`, **15.3x** vs `regex` |
122
- | Lookbehind + negative lookahead | `3.755` | `15.828` | `11.896` | **4.2x** vs `re`, **3.2x** vs `regex` |
173
+ | Extract `WARN` / `ERROR` lines | `0.449` | `17.145` | `16.175` | **38.2x** vs `re`, **36.0x** vs `regex` |
174
+ | Per-line full-name extraction | `0.564` | `16.258` | `7.874` | **28.8x** vs `re`, **14.0x** vs `regex` |
175
+ | Lookbehind + negative lookahead | `1.942` | `8.548` | `6.037` | **4.4x** vs `re`, **3.1x** vs `regex` |
123
176
 
124
177
  #### `sub` / `subn` — high-volume replacement workloads
125
178
 
126
- Measured on a Python 3.10 x86_64 Linux build with compiled-pattern reuse and JIT enabled. Times are the best of several runs; lower is better. The benchmark replaces 100,000 space-separated tokens.
179
+ Measured on Python 3.10.11 arm64 with compiled-pattern reuse and JIT enabled. Values are medians of three outer runs; lower is better. The benchmark replaces 100,000 space-separated tokens.
127
180
 
128
181
  A reproducible version lives in [`benchmarks/sub_bench.py`](benchmarks/sub_bench.py).
129
182
 
130
183
  | Workload | PyPcre (ms) | `re` (ms) | `regex` (ms) | PyPcre edge |
131
184
  | --- | ---: | ---: | ---: | --- |
132
- | Literal replacement (`\w+` → `[X]`) | `5.933` | `13.367` | `19.107` | **2.3x** vs `re`, **3.2x** vs `regex` |
133
- | Single numeric backref (`(w)\d+` → `[\1]`) | `7.221` | `64.715` | `25.482` | **9.0x** vs `re`, **3.5x** vs `regex` |
134
- | Two numeric backrefs (`(w)(\d+)` → `\2-\1`) | `17.600` | `76.222` | `32.048` | **4.3x** vs `re`, **1.8x** vs `regex` |
135
- | Named backref (`(?P<g>\w+)` → `<\g<g>>`) | `14.684` | `71.053` | `30.222` | **4.8x** vs `re`, **2.1x** vs `regex` |
185
+ | Literal replacement (`\w+` → `[X]`) | `3.127` | `6.489` | `9.992` | **2.1x** vs `re`, **3.2x** vs `regex` |
186
+ | Single numeric backref (`(w)\d+` → `[\1]`) | `4.003` | `30.098` | `12.481` | **7.5x** vs `re`, **3.1x** vs `regex` |
187
+ | Two numeric backrefs (`(w)(\d+)` → `\2-\1`) | `9.940` | `35.052` | `16.201` | **3.5x** vs `re`, **1.6x** vs `regex` |
188
+ | Named backref (`(?P<g>\w+)` → `<\g<g>>`) | `9.765` | `32.434` | `15.216` | **3.3x** vs `re`, **1.6x** vs `regex` |
136
189
 
137
190
  #### `split` — high-volume delimiter workloads
138
191
 
139
- Measured on a Python 3.10 x86_64 Linux build with compiled-pattern reuse and JIT enabled. Times are the best of several runs; lower is better. The benchmark splits 100,000 space-separated tokens.
192
+ Measured on Python 3.10.11 arm64 with compiled-pattern reuse and JIT enabled. Values are medians of three outer runs; lower is better. The benchmark splits 100,000 space-separated tokens.
140
193
 
141
194
  A reproducible version lives in [`benchmarks/split_bench.py`](benchmarks/split_bench.py).
142
195
 
143
196
  | Workload | PyPcre (ms) | `re` (ms) | `regex` (ms) | PyPcre edge |
144
197
  | --- | ---: | ---: | ---: | --- |
145
- | Delimiter no group (`\s+`) | `7.315` | `17.394` | `19.690` | **2.4x** vs `re`, **2.7x** vs `regex` |
146
- | Delimiter with group (`(\s+)`) | `12.360` | `21.099` | `25.145` | **1.7x** vs `re`, **2.0x** vs `regex` |
147
- | Single char (` `) | `4.583` | `3.990` | `14.577` | parity vs `re`, **3.2x** vs `regex` |
148
- | Single char with group (`( )`) | `8.913` | `11.091` | `18.595` | **1.2x** vs `re`, **2.1x** vs `regex` |
149
- | Empty pattern (`''`) | `5.228` | `45.552` | `69.913` | **8.7x** vs `re`, **13.4x** vs `regex` |
198
+ | Delimiter no group (`\s+`) | `3.349` | `8.013` | `9.913` | **2.4x** vs `re`, **3.0x** vs `regex` |
199
+ | Delimiter with group (`(\s+)`) | `5.080` | `9.142` | `12.374` | **1.8x** vs `re`, **2.4x** vs `regex` |
200
+ | Single char (` `) | `1.484` | `1.666` | `6.963` | **1.1x** vs `re`, **4.7x** vs `regex` |
201
+ | Single char with group (`( )`) | `1.772` | `4.203` | `8.875` | **2.4x** vs `re`, **5.0x** vs `regex` |
202
+ | Empty pattern (`''`) | `35.736` | `18.386` | `35.801` | **0.5x** vs `re`, parity vs `regex` |
150
203
 
151
204
  ### Free-Threaded Benchmark Highlights 🧵
152
205
 
153
- Measured on the same `Python 3.14.6` free-threaded build with `8` threads fanning out over split copies of each workload. Times are the best of several runs; lower is better.
206
+ Measured on the same Apple arm64 `Python 3.14.0rc2` free-threaded build with `8` threads fanning out over split copies of each workload. Values are medians of three outer runs; lower is better.
154
207
 
155
208
  A reproducible version lives in [`benchmarks/free_threaded_bench.py`](benchmarks/free_threaded_bench.py).
156
209
 
157
210
  | Workload | PyPcre (ms) | `re` (ms) | `regex` (ms) | PyPcre edge |
158
211
  | --- | ---: | ---: | ---: | --- |
159
- | Extract `WARN` / `ERROR` lines (`findall`) | `0.672` | `9.063` | `9.297` | **13.5x** vs `re`, **13.8x** vs `regex` |
160
- | Per-line full-name extraction (`findall`) | `0.913` | `8.611` | `4.575` | **9.4x** vs `re`, **5.0x** vs `regex` |
212
+ | Extract `WARN` / `ERROR` lines (`findall`) | `0.401` | `2.430` | `2.626` | **6.1x** vs `re`, **6.6x** vs `regex` |
213
+ | Per-line full-name extraction (`findall`) | `0.462` | `2.351` | `1.477` | **5.1x** vs `re`, **3.2x** vs `regex` |
161
214
 
162
215
  PyPcre is the stronger all-around choice when you want more than the baseline: full `PCRE2` features, more expressive syntax, JIT, explicit free-threaded support, and a stable `re`-compatible API surface. It keeps Python ergonomics while giving you a substantially more capable engine. 🚀
163
216
 
@@ -303,7 +356,13 @@ the conversion without repeating the flag.
303
356
  - `pcre.configure(jit=False)` disables JIT globally. `Flag.JIT` and
304
357
  `Flag.NO_JIT` let you override that per pattern.
305
358
  - `pcre.set_cache_limit()`, `pcre.get_cache_limit()`, and `pcre.clear_cache()`
306
- control the high-level compile cache.
359
+ control every high-level compile/template helper cache in the active context.
360
+ A zero limit disables them, and `None` uses a 256-entry hard safety ceiling
361
+ rather than permitting unbounded growth. High-level cache entries never cross
362
+ thread scope in the default thread-local strategy; a clear invalidates live
363
+ workers' high-level helper caches on their next cache-backed call. Backend
364
+ scratch buffers remain thread-scoped and are released when that thread exits.
365
+ Oversized patterns and templates are not retained.
307
366
  - `pcre.configure_threads()`, `pcre.configure_thread_pool()`,
308
367
  `shutdown_thread_pool()`, `Flag.THREADS`, and `Flag.NO_THREADS` are available
309
368
  if you want to opt into or restrict threaded execution.
@@ -38,24 +38,39 @@ tests/test_c_api_audit.py
38
38
  tests/test_cache.py
39
39
  tests/test_cache_global.py
40
40
  tests/test_cache_global_inproc.py
41
+ tests/test_cache_scope_safety.py
41
42
  tests/test_clobber.py
42
43
  tests/test_clobber_thread.py
44
+ tests/test_clobber_verify.py
45
+ tests/test_compile_utf_no_check_safety.py
43
46
  tests/test_core.py
44
47
  tests/test_coverage_gaps.py
45
48
  tests/test_errors.py
49
+ tests/test_escape_fastpath.py
50
+ tests/test_expand_fastpath.py
46
51
  tests/test_flags.py
47
52
  tests/test_gil_zero_safety.py
48
53
  tests/test_jit.py
49
54
  tests/test_jit_anchoring.py
55
+ tests/test_jit_start_optimize_workaround.py
56
+ tests/test_lastindex_ovector_fastpath.py
50
57
  tests/test_lastindex_semantics.py
58
+ tests/test_literal_capture_findall_fastpath.py
59
+ tests/test_literal_capture_split_fastpath.py
51
60
  tests/test_memory.py
52
61
  tests/test_mmap.py
53
62
  tests/test_module.py
63
+ tests/test_multi_literal_capture_findall_fastpath.py
64
+ tests/test_multi_literal_capture_split_fastpath.py
54
65
  tests/test_pattern.py
55
66
  tests/test_python_coverage_audit.py
56
67
  tests/test_re_compat.py
57
68
  tests/test_setup_utils_multiarch.py
58
69
  tests/test_simd.py
70
+ tests/test_stdlib_flag_fastpath.py
71
+ tests/test_sub_count_one_fastpath.py
72
+ tests/test_sub_reference_fastpath.py
73
+ tests/test_template_dispatch_fastpath.py
59
74
  tests/test_threaded_backend.py
60
75
  tests/test_threads.py
61
76
  tests/test_transformers_regex.py