PyPcre 0.2.14__tar.gz → 0.3.0__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 (57) hide show
  1. pypcre-0.3.0/PKG-INFO +290 -0
  2. pypcre-0.3.0/PyPcre.egg-info/PKG-INFO +290 -0
  3. pypcre-0.3.0/README.md +267 -0
  4. {pypcre-0.2.14 → pypcre-0.3.0}/pcre/__init__.py +3 -0
  5. {pypcre-0.2.14 → pypcre-0.3.0}/pcre/pcre.py +98 -24
  6. {pypcre-0.2.14 → pypcre-0.3.0}/pcre/re_compat.py +19 -15
  7. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/pcre2.c +262 -19
  8. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/pcre2_module.h +10 -0
  9. {pypcre-0.2.14 → pypcre-0.3.0}/pyproject.toml +1 -1
  10. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_api_parity.py +15 -0
  11. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_clobber_thread.py +1 -0
  12. pypcre-0.2.14/PKG-INFO +0 -313
  13. pypcre-0.2.14/PyPcre.egg-info/PKG-INFO +0 -313
  14. pypcre-0.2.14/README.md +0 -290
  15. {pypcre-0.2.14 → pypcre-0.3.0}/LICENSE +0 -0
  16. {pypcre-0.2.14 → pypcre-0.3.0}/MANIFEST.in +0 -0
  17. {pypcre-0.2.14 → pypcre-0.3.0}/PyPcre.egg-info/SOURCES.txt +0 -0
  18. {pypcre-0.2.14 → pypcre-0.3.0}/PyPcre.egg-info/dependency_links.txt +0 -0
  19. {pypcre-0.2.14 → pypcre-0.3.0}/PyPcre.egg-info/top_level.txt +0 -0
  20. {pypcre-0.2.14 → pypcre-0.3.0}/pcre/_stdlib_re.py +0 -0
  21. {pypcre-0.2.14 → pypcre-0.3.0}/pcre/cache.py +0 -0
  22. {pypcre-0.2.14 → pypcre-0.3.0}/pcre/error.py +0 -0
  23. {pypcre-0.2.14 → pypcre-0.3.0}/pcre/flags.py +0 -0
  24. {pypcre-0.2.14 → pypcre-0.3.0}/pcre/threads.py +0 -0
  25. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/atomic_compat.h +0 -0
  26. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/cache.c +0 -0
  27. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/error.c +0 -0
  28. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/flag.c +0 -0
  29. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/jit_support.c +0 -0
  30. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/memory.c +0 -0
  31. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/pattern_cache.c +0 -0
  32. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/pcre2.h +0 -0
  33. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/string_helpers.c +0 -0
  34. {pypcre-0.2.14 → pypcre-0.3.0}/pcre_ext/util.c +0 -0
  35. {pypcre-0.2.14 → pypcre-0.3.0}/setup.cfg +0 -0
  36. {pypcre-0.2.14 → pypcre-0.3.0}/setup.py +1 -1
  37. {pypcre-0.2.14 → pypcre-0.3.0}/setup_utils.py +0 -0
  38. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_accuracy.py +0 -0
  39. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_basic.py +0 -0
  40. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_bench_string.py +0 -0
  41. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_benchmark.py +0 -0
  42. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_bytes.py +0 -0
  43. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_cache.py +0 -0
  44. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_clobber.py +0 -0
  45. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_core.py +1 -1
  46. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_errors.py +0 -0
  47. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_flags.py +0 -0
  48. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_jit.py +0 -0
  49. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_memory.py +0 -0
  50. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_module.py +0 -0
  51. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_pattern.py +0 -0
  52. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_setup_utils_multiarch.py +0 -0
  53. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_simd.py +0 -0
  54. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_threaded_backend.py +0 -0
  55. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_transformers_regex.py +0 -0
  56. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_utf8.py +0 -0
  57. {pypcre-0.2.14 → pypcre-0.3.0}/tests/test_version.py +0 -0
pypcre-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,290 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyPcre
3
+ Version: 0.3.0
4
+ Summary: Modern, GIL-friendly, Fast Python bindings for PCRE2 with auto caching and JIT of compiled patterns.
5
+ Author-email: ModelCloud <qubitium@modelcloud.ai>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/ModelCloud/pcre
8
+ Keywords: regex,pcre2,bindings
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Programming Language :: C
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Classifier: Topic :: Text Processing :: Linguistic
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: license-file
23
+
24
+ <!--
25
+ # SPDX-FileCopyrightText: 2025 ModelCloud.ai
26
+ # SPDX-FileCopyrightText: 2025 qubitium@modelcloud.ai
27
+ # SPDX-License-Identifier: Apache-2.0
28
+ # Contact: qubitium@modelcloud.ai, x.com/qubitium
29
+ -->
30
+ <div align=center>
31
+ <img width="400" height="1024" alt="image" src="https://github.com/user-attachments/assets/92964c3a-f82e-4949-bd27-278f57c62d9f" />
32
+ </div>
33
+ <h1 align="center">PyPcre (Python PCRE2 Binding) 🧬</h1>
34
+
35
+ <p align=center>
36
+ Fast, free-threaded Python bindings for `PCRE2` with a stable `stdlib.re`-compatible API. ⚡
37
+ </p>
38
+
39
+ <p align="center">
40
+ <a href="https://github.com/ModelCloud/PyPcre/releases" style="text-decoration:none;"><img alt="GitHub release" src="https://img.shields.io/github/release/ModelCloud/Pcre.svg"></a>
41
+ <a href="https://pypi.org/project/PyPcre/" style="text-decoration:none;"><img alt="PyPI - Version" src="https://img.shields.io/pypi/v/PyPcre"></a>
42
+ <a href="https://pepy.tech/projects/PyPcre" style="text-decoration:none;"><img src="https://static.pepy.tech/badge/PyPcre" alt="PyPI Downloads"></a>
43
+ <a href="https://github.com/ModelCloud/PyPcre/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/PyPcre"></a>
44
+ <a href="https://huggingface.co/modelcloud/"><img src="https://img.shields.io/badge/🤗%20Hugging%20Face-ModelCloud-%23ff8811.svg"></a>
45
+ </p>
46
+
47
+
48
+
49
+ ## Latest News 🚀
50
+ * 04/13/2026 [0.3.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.3.0): Lower-overhead public `Match` objects, faster hot-path `match()` / `search()` / `fullmatch()` / `findall()`, and tighter free-threaded execution. ⚡
51
+ * 03/22/2026 [0.2.15](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.15): Python 3.15 `re` compatibility (`prefixmatch`, `NOFLAG`) ✅
52
+ * 03/21/2026 [0.2.14](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.14): Python 3.14 compatibility 🐍
53
+ * 03/02/2026 [0.2.11](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.11): Auto-detect `Visual Studio` in Windows environments during install and compile. 🪟
54
+ * 02/24/2026 [0.2.10](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.10): Allow a `Visual Studio` (VS) compiler version check override via an environment variable. 🧰
55
+ * 12/15/2025 [0.2.8](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.8): Fixed multi-arch Linux OS compatibility when both x86_64 and i386 `pcre2` libraries are installed. 🐧
56
+ * 10/20/2025 [0.2.4](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.4): Removed the dependency on a system `python3-dev` package. `Python.h` will be downloaded optimistically from python.org when needed. 📦
57
+ * 10/12/2025 [0.2.3](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.3): 🤗 Full `GIL=0` compliance for Python >= 3.13T. Reduced cache thread contention. Improved performance across all APIs. Expanded CI test coverage. FreeBSD, Solaris, and Windows compatibility validated.
58
+ * 10/09/2025 [0.1.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.1.0): 🎉 First release. Thread-safe, with auto JIT, auto pattern caching, and optimistic linking to the system library for fast installs.
59
+
60
+ ## Why PyPcre ⚡
61
+
62
+ PyPcre pairs Python's familiar `re`-compatible API with the real `PCRE2` engine. You keep the ergonomics of the standard library while gaining a more capable regex engine, optional JIT, explicit threading support, and a binding designed and tested for free-threaded Python. 🧠⚡
63
+
64
+ ### Big Wins 🏆
65
+
66
+ - 🧬 **Full power of PCRE2**: PyPcre uses the real `PCRE2` engine, so you get native compile options, semantics, JIT, and upstream tuning.
67
+ - 🔥 **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)`.
68
+ - 🧵 **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.
69
+ - ⚡ **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.
70
+ - 🛡️ **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.
71
+ - ✅ **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.
72
+
73
+ ### Quick Comparison 🥊
74
+
75
+ | Area | PyPcre | `stdlib.re` | `regex` |
76
+ | --- | --- | --- | --- |
77
+ | Engine | Full `PCRE2` ✅ | CPython stdlib engine | Separate engine, not `PCRE2` |
78
+ | `PCRE2` syntax and flags | Full access ✅ | No | No |
79
+ | Syntax power | Very rich ✅ | More limited | Rich, but different from `PCRE2` |
80
+ | JIT execution | `PCRE2` JIT ✅ | No | No |
81
+ | `re`-compatible API surface | Stable and familiar ✅ | Native | Similar, but not the main goal |
82
+ | Free-threaded support | Built and tested for `PYTHON_GIL=0` ✅ | No explicit PyPcre-style layer | Not a project focus here |
83
+ | Built-in threaded subject fan-out | `parallel_map()` ✅ | No | No |
84
+ | System library updates | Uses system `libpcre2-8` by default ✅ | N/A | N/A |
85
+
86
+ ### Benchmark Highlights 🏁
87
+
88
+ Measured on a `Python 3.14.3` free-threaded build on x86_64 Linux with compiled-pattern reuse. Times are best-of-5; lower is better.
89
+
90
+ | Workload | Operation | PyPcre | `re` | `regex` | PyPcre edge |
91
+ | --- | --- | ---: | ---: | ---: | --- |
92
+ | First `ERROR` line in a multiline log buffer | `search` | `3.68 ms` | `51.72 ms` | `5.67 ms` | `14.0x` vs `re`, `1.54x` vs `regex` |
93
+ | Extract only `WARN` / `ERROR` lines | `findall` | `6.41 ms` | `91.84 ms` | `91.14 ms` | `14.3x` vs `re`, `14.2x` vs `regex` |
94
+ | Per-line full-name extraction | `findall` | `22.28 ms` | `172.38 ms` | `218.29 ms` | `7.74x` vs `re`, `9.80x` vs `regex` |
95
+ | Lookbehind + negative-lookahead extraction | `findall` | `50.23 ms` | `53.35 ms` | `57.03 ms` | `1.06x` vs `re`, `1.14x` vs `regex` |
96
+ | UUID extraction | `findall` | `77.49 ms` | `83.19 ms` | `134.87 ms` | `1.07x` vs `re`, `1.74x` vs `regex` |
97
+ | Boundary-aware token scan | `findall` | `127.76 ms` | `128.03 ms` | `146.37 ms` | effectively tied with `re`, `1.15x` vs `regex` |
98
+
99
+ ### Free-Threaded Benchmark Highlights 🧵
100
+
101
+ Measured in the same environment with `8` threads sharing one compiled pattern. Times are best-of-3; lower is better.
102
+
103
+ | Workload | Threads | PyPcre | `re` | `regex` | PyPcre edge |
104
+ | --- | ---: | ---: | ---: | ---: | --- |
105
+ | First `ERROR` line in a multiline log buffer | `8` | `25.34 ms` | `38.83 ms` | `40.34 ms` | `1.53x` vs `re`, `1.59x` vs `regex` |
106
+ | Extract only `WARN` / `ERROR` lines | `8` | `28.58 ms` | `65.54 ms` | `73.55 ms` | `2.29x` vs `re`, `2.57x` vs `regex` |
107
+ | Per-line full-name extraction | `8` | `31.68 ms` | `123.44 ms` | `164.80 ms` | `3.90x` vs `re`, `5.20x` vs `regex` |
108
+
109
+ 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. 🚀
110
+
111
+ ## Installation 📦
112
+
113
+ ```bash
114
+ pip install PyPcre
115
+ ```
116
+
117
+ By default, the package links against the system `libpcre2-8` shared library for fast installs and to inherit OS security updates. See [Building](#building) for manual build details.
118
+
119
+ ## Platform Support (Validated) ✅
120
+
121
+ `Linux`, `macOS`, `Windows`, `WSL`, `FreeBSD`
122
+
123
+
124
+ ## Usage 🛠️
125
+
126
+ If you already use the standard library `re`, migration is often just an import swap:
127
+
128
+ ```python
129
+ import pcre as re
130
+ ```
131
+
132
+ The high-level API stays close to the standard library, so most existing `re` code can move over with little or no rewriting.
133
+
134
+ ### Quick start 🚀
135
+
136
+ ```python
137
+ from pcre import compile, findall, match, search, Flag
138
+
139
+ if match(r"(?P<word>\\w+)", "hello world"):
140
+ print("found word")
141
+
142
+ pattern = compile(rb"\d+", flags=Flag.MULTILINE)
143
+ numbers = pattern.findall(b"line 1\nline 22")
144
+ ```
145
+
146
+ ### API Overview 🧭
147
+
148
+ - Module helpers: `prefixmatch`, `match`, `search`, `fullmatch`, `finditer`,
149
+ `findall`, `split`, `sub`, `subn`, `compile`, `escape`, `purge`, and
150
+ `parallel_map`.
151
+ - `compile()` returns a `Pattern` object with the familiar matching helpers
152
+ plus `split()`, `sub()`, and `subn()`.
153
+ - `Pattern` exposes `.pattern`, `.flags`, `.jit`, `.groupindex`, and `.groups`
154
+ for introspection.
155
+ - `Match` objects expose the usual `group()`, `groups()`, `groupdict()`,
156
+ `start()`, `end()`, `span()`, and `expand()` methods, along with `.re`,
157
+ `.string`, `.pos`, `.endpos`, `.lastindex`, `.lastgroup`, and `.regs`.
158
+ - Flags are available through `pcre.Flag` and familiar aliases such as
159
+ `IGNORECASE`, `MULTILINE`, `DOTALL`, `VERBOSE`, `ASCII`, `UNICODE`, and
160
+ `NOFLAG`.
161
+ - Errors are raised as `pcre.PcreError`; `error` and `PatternError` are kept as
162
+ compatibility aliases.
163
+
164
+ ### Common examples 🧪
165
+
166
+ Compiled patterns:
167
+
168
+ ```python
169
+ from pcre import compile, Flag
170
+
171
+ pattern = compile(r"(?P<name>[A-Za-z]+)", flags=Flag.CASELESS)
172
+ match = pattern.search("User: alice")
173
+ print(match.group("name")) # alice
174
+ ```
175
+
176
+ Substitution:
177
+
178
+ ```python
179
+ from pcre import sub
180
+
181
+ result = sub(r"\d+", "#", "room 101")
182
+ print(result) # room #
183
+ ```
184
+
185
+ Bytes:
186
+
187
+ ```python
188
+ from pcre import compile
189
+
190
+ pattern = compile(br"\w+")
191
+ print(pattern.findall(b"ab cd")) # [b'ab', b'cd']
192
+ ```
193
+
194
+ ### Stdlib `re` compatibility 🔁
195
+
196
+ - Module-level helpers and the `Pattern` class follow the same call shapes as
197
+ the standard library `re` module, including `pos`, `endpos`, and `flags`
198
+ behavior.
199
+ - Python 3.15's `prefixmatch()` alias is available at both the module level
200
+ and on compiled `Pattern` objects, and `re.NOFLAG` is re-exported as the
201
+ zero-value compatibility alias.
202
+ - `Pattern` mirrors `re.Pattern` attributes like `.pattern`, `.groupindex`,
203
+ and `.groups`, while `Match` objects surface the familiar `.re`, `.string`,
204
+ `.pos`, `.endpos`, `.lastindex`, `.lastgroup`, `.regs`, and `.expand()` API.
205
+ - Substitution helpers enforce the same type rules as the standard library
206
+ `re` module: string patterns require string replacements, byte patterns
207
+ require bytes-like replacements, and callable replacements receive the
208
+ wrapped `Match`.
209
+ - `compile()` accepts native `Flag` values as well as compatible
210
+ `re.RegexFlag` members from the standard library. Supported stdlib flags
211
+ map 1:1 to PCRE2 options (`IGNORECASE→CASELESS`, `MULTILINE→MULTILINE`,
212
+ `DOTALL→DOTALL`, `VERBOSE→EXTENDED`); passing unsupported stdlib flags
213
+ raises a compatibility `ValueError` to prevent silent divergences.
214
+ - `pcre.escape()` delegates directly to `re.escape` for byte and text
215
+ patterns so escaping semantics remain identical.
216
+ - String patterns enable Unicode behavior by default. Byte patterns do not.
217
+
218
+ ### `regex` package compatibility 🔄
219
+
220
+ The [`regex`](https://pypi.org/project/regex/) package interprets
221
+ `\uXXXX` and `\UXXXXXXXX` escapes as UTF-8 code points, while PCRE2 expects
222
+ hexadecimal escapes to use the `\x{...}` form. Enable `Flag.COMPAT_UNICODE_ESCAPE` to
223
+ translate those escapes automatically when compiling patterns:
224
+
225
+ ```python
226
+ from pcre import compile, Flag
227
+
228
+ pattern = compile(r"\\U0001F600", flags=Flag.COMPAT_UNICODE_ESCAPE)
229
+ assert pattern.pattern == r"\\x{0001F600}"
230
+ ```
231
+
232
+ Set the default behavior globally with `pcre.configure(compat_regex=True)`
233
+ so that subsequent calls to `compile()` and the module-level helpers apply
234
+ the conversion without repeating the flag.
235
+
236
+ ### Common issues ⚠️
237
+
238
+ - Unsupported stdlib flags such as `re.DEBUG`, `re.LOCALE`, and `re.ASCII`
239
+ raise `ValueError`. If you want ASCII-style behavior, use `pcre.ASCII` or
240
+ `Flag.NO_UTF | Flag.NO_UCP`.
241
+ - Replacement types must match the subject type: text patterns use `str`
242
+ replacements, while byte patterns use bytes-like replacements.
243
+ - If you are porting patterns from the third-party `regex` package, check
244
+ `\u` and `\U` escapes first. That is the most common compatibility gap.
245
+ - Most users do not need to tune caching, JIT, or threading. The defaults are
246
+ intended to work well out of the box.
247
+
248
+ ### Optional runtime controls 🎛️
249
+
250
+ - `pcre.configure(jit=False)` disables JIT globally. `Flag.JIT` and
251
+ `Flag.NO_JIT` let you override that per pattern.
252
+ - `pcre.set_cache_limit()`, `pcre.get_cache_limit()`, and `pcre.clear_cache()`
253
+ control the high-level compile cache.
254
+ - `pcre.configure_threads()`, `pcre.configure_thread_pool()`,
255
+ `shutdown_thread_pool()`, `Flag.THREADS`, and `Flag.NO_THREADS` are available
256
+ if you want to opt into or restrict threaded execution.
257
+
258
+ ## Building 🏗️
259
+
260
+ The extension links against an existing `libpcre2-8` installation. Install the development headers for your platform before building,
261
+ for example `apt install libpcre2-dev` on Debian/Ubuntu, `dnf install pcre2-devel`
262
+ on Fedora/RHEL derivatives, or `brew install pcre2` on macOS.
263
+
264
+ If the headers or library live in a non-standard location, you can export one
265
+ or more of the following environment variables prior to invoking the build
266
+ (`pip install .`, `python -m build`, etc.):
267
+
268
+ - `PYPCRE_ROOT`
269
+ - `PYPCRE_INCLUDE_DIR`
270
+ - `PYPCRE_LIBRARY_DIR`
271
+ - `PYPCRE_LIBRARY_PATH` *(pathsep-separated directories or explicit library files to
272
+ prioritize when resolving `libpcre2-8`)*
273
+ - `PYPCRE_LIBRARIES`
274
+ - `PYPCRE_CFLAGS`
275
+ - `PYPCRE_LDFLAGS`
276
+
277
+ If you would rather force a source build, set `PYPCRE_BUILD_FROM_SOURCE=1`
278
+ before installing.
279
+
280
+ When `pkg-config` is available, the build automatically picks up the
281
+ required include and link flags via `pkg-config --cflags/--libs libpcre2-8`.
282
+ Without `pkg-config`, the build script scans common installation prefixes for
283
+ Linux distributions (Debian, Ubuntu, Fedora/RHEL/CentOS, openSUSE, Alpine),
284
+ FreeBSD, and macOS (including Homebrew) to locate the headers and
285
+ libraries.
286
+
287
+ If your system ships `libpcre2-8` under `/usr` but you also maintain a
288
+ manually built copy under `/usr/local`, export `PYPCRE_LIBRARY_PATH` (and, if
289
+ needed, a matching `PYPCRE_INCLUDE_DIR`) so the build links against the desired
290
+ location.
@@ -0,0 +1,290 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyPcre
3
+ Version: 0.3.0
4
+ Summary: Modern, GIL-friendly, Fast Python bindings for PCRE2 with auto caching and JIT of compiled patterns.
5
+ Author-email: ModelCloud <qubitium@modelcloud.ai>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/ModelCloud/pcre
8
+ Keywords: regex,pcre2,bindings
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Programming Language :: C
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Classifier: Topic :: Text Processing :: Linguistic
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Dynamic: license-file
23
+
24
+ <!--
25
+ # SPDX-FileCopyrightText: 2025 ModelCloud.ai
26
+ # SPDX-FileCopyrightText: 2025 qubitium@modelcloud.ai
27
+ # SPDX-License-Identifier: Apache-2.0
28
+ # Contact: qubitium@modelcloud.ai, x.com/qubitium
29
+ -->
30
+ <div align=center>
31
+ <img width="400" height="1024" alt="image" src="https://github.com/user-attachments/assets/92964c3a-f82e-4949-bd27-278f57c62d9f" />
32
+ </div>
33
+ <h1 align="center">PyPcre (Python PCRE2 Binding) 🧬</h1>
34
+
35
+ <p align=center>
36
+ Fast, free-threaded Python bindings for `PCRE2` with a stable `stdlib.re`-compatible API. ⚡
37
+ </p>
38
+
39
+ <p align="center">
40
+ <a href="https://github.com/ModelCloud/PyPcre/releases" style="text-decoration:none;"><img alt="GitHub release" src="https://img.shields.io/github/release/ModelCloud/Pcre.svg"></a>
41
+ <a href="https://pypi.org/project/PyPcre/" style="text-decoration:none;"><img alt="PyPI - Version" src="https://img.shields.io/pypi/v/PyPcre"></a>
42
+ <a href="https://pepy.tech/projects/PyPcre" style="text-decoration:none;"><img src="https://static.pepy.tech/badge/PyPcre" alt="PyPI Downloads"></a>
43
+ <a href="https://github.com/ModelCloud/PyPcre/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/PyPcre"></a>
44
+ <a href="https://huggingface.co/modelcloud/"><img src="https://img.shields.io/badge/🤗%20Hugging%20Face-ModelCloud-%23ff8811.svg"></a>
45
+ </p>
46
+
47
+
48
+
49
+ ## Latest News 🚀
50
+ * 04/13/2026 [0.3.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.3.0): Lower-overhead public `Match` objects, faster hot-path `match()` / `search()` / `fullmatch()` / `findall()`, and tighter free-threaded execution. ⚡
51
+ * 03/22/2026 [0.2.15](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.15): Python 3.15 `re` compatibility (`prefixmatch`, `NOFLAG`) ✅
52
+ * 03/21/2026 [0.2.14](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.14): Python 3.14 compatibility 🐍
53
+ * 03/02/2026 [0.2.11](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.11): Auto-detect `Visual Studio` in Windows environments during install and compile. 🪟
54
+ * 02/24/2026 [0.2.10](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.10): Allow a `Visual Studio` (VS) compiler version check override via an environment variable. 🧰
55
+ * 12/15/2025 [0.2.8](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.8): Fixed multi-arch Linux OS compatibility when both x86_64 and i386 `pcre2` libraries are installed. 🐧
56
+ * 10/20/2025 [0.2.4](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.4): Removed the dependency on a system `python3-dev` package. `Python.h` will be downloaded optimistically from python.org when needed. 📦
57
+ * 10/12/2025 [0.2.3](https://github.com/ModelCloud/PyPcre/releases/tag/v0.2.3): 🤗 Full `GIL=0` compliance for Python >= 3.13T. Reduced cache thread contention. Improved performance across all APIs. Expanded CI test coverage. FreeBSD, Solaris, and Windows compatibility validated.
58
+ * 10/09/2025 [0.1.0](https://github.com/ModelCloud/PyPcre/releases/tag/v0.1.0): 🎉 First release. Thread-safe, with auto JIT, auto pattern caching, and optimistic linking to the system library for fast installs.
59
+
60
+ ## Why PyPcre ⚡
61
+
62
+ PyPcre pairs Python's familiar `re`-compatible API with the real `PCRE2` engine. You keep the ergonomics of the standard library while gaining a more capable regex engine, optional JIT, explicit threading support, and a binding designed and tested for free-threaded Python. 🧠⚡
63
+
64
+ ### Big Wins 🏆
65
+
66
+ - 🧬 **Full power of PCRE2**: PyPcre uses the real `PCRE2` engine, so you get native compile options, semantics, JIT, and upstream tuning.
67
+ - 🔥 **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)`.
68
+ - 🧵 **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.
69
+ - ⚡ **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.
70
+ - 🛡️ **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.
71
+ - ✅ **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.
72
+
73
+ ### Quick Comparison 🥊
74
+
75
+ | Area | PyPcre | `stdlib.re` | `regex` |
76
+ | --- | --- | --- | --- |
77
+ | Engine | Full `PCRE2` ✅ | CPython stdlib engine | Separate engine, not `PCRE2` |
78
+ | `PCRE2` syntax and flags | Full access ✅ | No | No |
79
+ | Syntax power | Very rich ✅ | More limited | Rich, but different from `PCRE2` |
80
+ | JIT execution | `PCRE2` JIT ✅ | No | No |
81
+ | `re`-compatible API surface | Stable and familiar ✅ | Native | Similar, but not the main goal |
82
+ | Free-threaded support | Built and tested for `PYTHON_GIL=0` ✅ | No explicit PyPcre-style layer | Not a project focus here |
83
+ | Built-in threaded subject fan-out | `parallel_map()` ✅ | No | No |
84
+ | System library updates | Uses system `libpcre2-8` by default ✅ | N/A | N/A |
85
+
86
+ ### Benchmark Highlights 🏁
87
+
88
+ Measured on a `Python 3.14.3` free-threaded build on x86_64 Linux with compiled-pattern reuse. Times are best-of-5; lower is better.
89
+
90
+ | Workload | Operation | PyPcre | `re` | `regex` | PyPcre edge |
91
+ | --- | --- | ---: | ---: | ---: | --- |
92
+ | First `ERROR` line in a multiline log buffer | `search` | `3.68 ms` | `51.72 ms` | `5.67 ms` | `14.0x` vs `re`, `1.54x` vs `regex` |
93
+ | Extract only `WARN` / `ERROR` lines | `findall` | `6.41 ms` | `91.84 ms` | `91.14 ms` | `14.3x` vs `re`, `14.2x` vs `regex` |
94
+ | Per-line full-name extraction | `findall` | `22.28 ms` | `172.38 ms` | `218.29 ms` | `7.74x` vs `re`, `9.80x` vs `regex` |
95
+ | Lookbehind + negative-lookahead extraction | `findall` | `50.23 ms` | `53.35 ms` | `57.03 ms` | `1.06x` vs `re`, `1.14x` vs `regex` |
96
+ | UUID extraction | `findall` | `77.49 ms` | `83.19 ms` | `134.87 ms` | `1.07x` vs `re`, `1.74x` vs `regex` |
97
+ | Boundary-aware token scan | `findall` | `127.76 ms` | `128.03 ms` | `146.37 ms` | effectively tied with `re`, `1.15x` vs `regex` |
98
+
99
+ ### Free-Threaded Benchmark Highlights 🧵
100
+
101
+ Measured in the same environment with `8` threads sharing one compiled pattern. Times are best-of-3; lower is better.
102
+
103
+ | Workload | Threads | PyPcre | `re` | `regex` | PyPcre edge |
104
+ | --- | ---: | ---: | ---: | ---: | --- |
105
+ | First `ERROR` line in a multiline log buffer | `8` | `25.34 ms` | `38.83 ms` | `40.34 ms` | `1.53x` vs `re`, `1.59x` vs `regex` |
106
+ | Extract only `WARN` / `ERROR` lines | `8` | `28.58 ms` | `65.54 ms` | `73.55 ms` | `2.29x` vs `re`, `2.57x` vs `regex` |
107
+ | Per-line full-name extraction | `8` | `31.68 ms` | `123.44 ms` | `164.80 ms` | `3.90x` vs `re`, `5.20x` vs `regex` |
108
+
109
+ 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. 🚀
110
+
111
+ ## Installation 📦
112
+
113
+ ```bash
114
+ pip install PyPcre
115
+ ```
116
+
117
+ By default, the package links against the system `libpcre2-8` shared library for fast installs and to inherit OS security updates. See [Building](#building) for manual build details.
118
+
119
+ ## Platform Support (Validated) ✅
120
+
121
+ `Linux`, `macOS`, `Windows`, `WSL`, `FreeBSD`
122
+
123
+
124
+ ## Usage 🛠️
125
+
126
+ If you already use the standard library `re`, migration is often just an import swap:
127
+
128
+ ```python
129
+ import pcre as re
130
+ ```
131
+
132
+ The high-level API stays close to the standard library, so most existing `re` code can move over with little or no rewriting.
133
+
134
+ ### Quick start 🚀
135
+
136
+ ```python
137
+ from pcre import compile, findall, match, search, Flag
138
+
139
+ if match(r"(?P<word>\\w+)", "hello world"):
140
+ print("found word")
141
+
142
+ pattern = compile(rb"\d+", flags=Flag.MULTILINE)
143
+ numbers = pattern.findall(b"line 1\nline 22")
144
+ ```
145
+
146
+ ### API Overview 🧭
147
+
148
+ - Module helpers: `prefixmatch`, `match`, `search`, `fullmatch`, `finditer`,
149
+ `findall`, `split`, `sub`, `subn`, `compile`, `escape`, `purge`, and
150
+ `parallel_map`.
151
+ - `compile()` returns a `Pattern` object with the familiar matching helpers
152
+ plus `split()`, `sub()`, and `subn()`.
153
+ - `Pattern` exposes `.pattern`, `.flags`, `.jit`, `.groupindex`, and `.groups`
154
+ for introspection.
155
+ - `Match` objects expose the usual `group()`, `groups()`, `groupdict()`,
156
+ `start()`, `end()`, `span()`, and `expand()` methods, along with `.re`,
157
+ `.string`, `.pos`, `.endpos`, `.lastindex`, `.lastgroup`, and `.regs`.
158
+ - Flags are available through `pcre.Flag` and familiar aliases such as
159
+ `IGNORECASE`, `MULTILINE`, `DOTALL`, `VERBOSE`, `ASCII`, `UNICODE`, and
160
+ `NOFLAG`.
161
+ - Errors are raised as `pcre.PcreError`; `error` and `PatternError` are kept as
162
+ compatibility aliases.
163
+
164
+ ### Common examples 🧪
165
+
166
+ Compiled patterns:
167
+
168
+ ```python
169
+ from pcre import compile, Flag
170
+
171
+ pattern = compile(r"(?P<name>[A-Za-z]+)", flags=Flag.CASELESS)
172
+ match = pattern.search("User: alice")
173
+ print(match.group("name")) # alice
174
+ ```
175
+
176
+ Substitution:
177
+
178
+ ```python
179
+ from pcre import sub
180
+
181
+ result = sub(r"\d+", "#", "room 101")
182
+ print(result) # room #
183
+ ```
184
+
185
+ Bytes:
186
+
187
+ ```python
188
+ from pcre import compile
189
+
190
+ pattern = compile(br"\w+")
191
+ print(pattern.findall(b"ab cd")) # [b'ab', b'cd']
192
+ ```
193
+
194
+ ### Stdlib `re` compatibility 🔁
195
+
196
+ - Module-level helpers and the `Pattern` class follow the same call shapes as
197
+ the standard library `re` module, including `pos`, `endpos`, and `flags`
198
+ behavior.
199
+ - Python 3.15's `prefixmatch()` alias is available at both the module level
200
+ and on compiled `Pattern` objects, and `re.NOFLAG` is re-exported as the
201
+ zero-value compatibility alias.
202
+ - `Pattern` mirrors `re.Pattern` attributes like `.pattern`, `.groupindex`,
203
+ and `.groups`, while `Match` objects surface the familiar `.re`, `.string`,
204
+ `.pos`, `.endpos`, `.lastindex`, `.lastgroup`, `.regs`, and `.expand()` API.
205
+ - Substitution helpers enforce the same type rules as the standard library
206
+ `re` module: string patterns require string replacements, byte patterns
207
+ require bytes-like replacements, and callable replacements receive the
208
+ wrapped `Match`.
209
+ - `compile()` accepts native `Flag` values as well as compatible
210
+ `re.RegexFlag` members from the standard library. Supported stdlib flags
211
+ map 1:1 to PCRE2 options (`IGNORECASE→CASELESS`, `MULTILINE→MULTILINE`,
212
+ `DOTALL→DOTALL`, `VERBOSE→EXTENDED`); passing unsupported stdlib flags
213
+ raises a compatibility `ValueError` to prevent silent divergences.
214
+ - `pcre.escape()` delegates directly to `re.escape` for byte and text
215
+ patterns so escaping semantics remain identical.
216
+ - String patterns enable Unicode behavior by default. Byte patterns do not.
217
+
218
+ ### `regex` package compatibility 🔄
219
+
220
+ The [`regex`](https://pypi.org/project/regex/) package interprets
221
+ `\uXXXX` and `\UXXXXXXXX` escapes as UTF-8 code points, while PCRE2 expects
222
+ hexadecimal escapes to use the `\x{...}` form. Enable `Flag.COMPAT_UNICODE_ESCAPE` to
223
+ translate those escapes automatically when compiling patterns:
224
+
225
+ ```python
226
+ from pcre import compile, Flag
227
+
228
+ pattern = compile(r"\\U0001F600", flags=Flag.COMPAT_UNICODE_ESCAPE)
229
+ assert pattern.pattern == r"\\x{0001F600}"
230
+ ```
231
+
232
+ Set the default behavior globally with `pcre.configure(compat_regex=True)`
233
+ so that subsequent calls to `compile()` and the module-level helpers apply
234
+ the conversion without repeating the flag.
235
+
236
+ ### Common issues ⚠️
237
+
238
+ - Unsupported stdlib flags such as `re.DEBUG`, `re.LOCALE`, and `re.ASCII`
239
+ raise `ValueError`. If you want ASCII-style behavior, use `pcre.ASCII` or
240
+ `Flag.NO_UTF | Flag.NO_UCP`.
241
+ - Replacement types must match the subject type: text patterns use `str`
242
+ replacements, while byte patterns use bytes-like replacements.
243
+ - If you are porting patterns from the third-party `regex` package, check
244
+ `\u` and `\U` escapes first. That is the most common compatibility gap.
245
+ - Most users do not need to tune caching, JIT, or threading. The defaults are
246
+ intended to work well out of the box.
247
+
248
+ ### Optional runtime controls 🎛️
249
+
250
+ - `pcre.configure(jit=False)` disables JIT globally. `Flag.JIT` and
251
+ `Flag.NO_JIT` let you override that per pattern.
252
+ - `pcre.set_cache_limit()`, `pcre.get_cache_limit()`, and `pcre.clear_cache()`
253
+ control the high-level compile cache.
254
+ - `pcre.configure_threads()`, `pcre.configure_thread_pool()`,
255
+ `shutdown_thread_pool()`, `Flag.THREADS`, and `Flag.NO_THREADS` are available
256
+ if you want to opt into or restrict threaded execution.
257
+
258
+ ## Building 🏗️
259
+
260
+ The extension links against an existing `libpcre2-8` installation. Install the development headers for your platform before building,
261
+ for example `apt install libpcre2-dev` on Debian/Ubuntu, `dnf install pcre2-devel`
262
+ on Fedora/RHEL derivatives, or `brew install pcre2` on macOS.
263
+
264
+ If the headers or library live in a non-standard location, you can export one
265
+ or more of the following environment variables prior to invoking the build
266
+ (`pip install .`, `python -m build`, etc.):
267
+
268
+ - `PYPCRE_ROOT`
269
+ - `PYPCRE_INCLUDE_DIR`
270
+ - `PYPCRE_LIBRARY_DIR`
271
+ - `PYPCRE_LIBRARY_PATH` *(pathsep-separated directories or explicit library files to
272
+ prioritize when resolving `libpcre2-8`)*
273
+ - `PYPCRE_LIBRARIES`
274
+ - `PYPCRE_CFLAGS`
275
+ - `PYPCRE_LDFLAGS`
276
+
277
+ If you would rather force a source build, set `PYPCRE_BUILD_FROM_SOURCE=1`
278
+ before installing.
279
+
280
+ When `pkg-config` is available, the build automatically picks up the
281
+ required include and link flags via `pkg-config --cflags/--libs libpcre2-8`.
282
+ Without `pkg-config`, the build script scans common installation prefixes for
283
+ Linux distributions (Debian, Ubuntu, Fedora/RHEL/CentOS, openSUSE, Alpine),
284
+ FreeBSD, and macOS (including Homebrew) to locate the headers and
285
+ libraries.
286
+
287
+ If your system ships `libpcre2-8` under `/usr` but you also maintain a
288
+ manually built copy under `/usr/local`, export `PYPCRE_LIBRARY_PATH` (and, if
289
+ needed, a matching `PYPCRE_INCLUDE_DIR`) so the build links against the desired
290
+ location.