purere2
RE2 in pure Python: linear-time, ReDoS-safe regular expressions — no C extension, no backtracking.
Python's built-in re (like PCRE and Perl) backtracks, so a pattern like
(a+)+$ against a non-matching string can run for years on a few dozen
characters — the classic ReDoS denial-of-service. purere2 compiles every
pattern to an NFA and runs it with a Pike VM, so matching is always linear
in the input and no pattern can blow up. That guarantee is exactly why
RE2 exists — and why it has no
backreferences or lookaround.
pip install purere2
import purere2
# linear time: this returns instantly; re.search would hang for minutes
purere2.search(r"(a+)+$", "a" * 50 + "!") # None, in microseconds
purere2.search(r"(\w+)@(\w+)", "x@y").groups() # ('x', 'y')
purere2.findall(r"\d{4}-\d\d-\d\d", "2026-06-19") # ['2026-06-19']
The API mirrors the common subset of the stdlib re module
(compile, search, match, fullmatch, finditer, findall, sub,
subn, split, flags I/M/S, named groups), so it is close to a drop-in
replacement for running untrusted or LLM-generated patterns safely.
Why pure Python
google-re2 and pyre2 already wrap RE2 — but they need the RE2 C++
library (and a compiler, or a matching binary wheel). There was no pure
Python RE2, even though RE2/J (Java) and
RE2JS (JavaScript) have existed for years.
purere2 is the missing one: zero dependencies, zero binaries, runs anywhere
Python runs — Pyodide/WASM, AWS Lambda, locked-down sandboxes — exactly where
you most want to run a pattern you don't trust.
Speed, stated honestly. purere2 compiles each pattern to a lazy DFA (built
on the fly, like RE2's own) so that for a plain recognizer — no capture
groups, no ^ $ \b — every character is a memoized table lookup. On common
patterns this makes it competitive with, and often faster than, the
C-backed google-re2 binding, because purere2 stays in-process while the
binding pays Python↔C overhead on every call. It is still slower than the
stdlib re (a C engine with no FFI cost) on patterns re can handle.
Capture groups and assertions fall back to a Pike VM, which is slower but
always linear.
Measured on a 124 KB corpus (python tools/bench.py); your numbers will vary:
| pattern | purere2 | stdlib re |
google-re2 |
|---|---|---|---|
\w+ (findall) |
31 ms | 2.4 ms | 43 ms |
[a-z_]+[0-9]* |
25 ms | 1.6 ms | 31 ms |
alpha|bravo|… |
14 ms | 0.7 ms | 9 ms |
(\w+)@(\w+)\.(\w+) |
74 ms | 3.8 ms | 6 ms |
The point is not to beat re — it can't. The point is safety and
portability: run an untrusted or LLM-generated pattern with a guaranteed
linear-time bound, anywhere Python runs, with no C extension.
Verified against the real RE2
Conformance is differential, the same way purefzf
checks itself against the fzf binary: random RE2 patterns and inputs are run
through both purere2 and google-re2 and compared byte-for-byte. Across
150,000+ random checks, agreement is ~99.996%; the residue is one
documented edge (a lazy quantifier nested in a greedy loop) — see
EXPECTED_DIVERGENCES.md. The conformance test locks
that level, so any regression fails CI. There is also a ReDoS-safety suite of
patterns that hang stdlib re and must finish in milliseconds here.
Supported syntax (v0.1)
Literals, ., character classes [...] with ranges / negation / POSIX
[[:alpha:]], perl classes \d \w \s (ASCII, per RE2) and negations,
anchors ^ $ \A \z \b \B, groups (...) / (?:...) / (?P<name>...),
alternation |, quantifiers * + ? {m} {m,n} greedy and lazy, inline flags
(?i) (?m) (?s) and scoped (?i:...), escapes including \xHH / \x{...}.
Intentionally absent (this is what makes it safe): backreferences and
lookaround. (a)\1 raises RegexError. Deferred to a later version: Unicode
property classes \p{...} and full Unicode case folding.
License
Release files for purere2 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| purere2-0.1.2.tar.gz | 21.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| purere2-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:38.6 kB
Release files / purere2-0.1.2.tar.gz
| Download URL | purere2-0.1.2.tar.gz |
|---|---|
| Size | 21.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
668129e847b385a87685f3a51683ca1430b20ef4c194042582d463041a5a739e
|
|
BLAKE2b-256 checksum How to use checksums |
741d6b84c54a2601f3d9ab9461849c3aedd382283a8a7d5cd1e01c7ee5bde4b8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 19, 2026.
Transparency logRelease files / purere2-0.1.2-py3-none-any.whl
| Download URL | purere2-0.1.2-py3-none-any.whl |
|---|---|
| Size | 17.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
843b146399f348758135637cd5cc45403c96c6a8837c75b2ae817bba8f71dfd0
|
|
BLAKE2b-256 checksum How to use checksums |
21afa54fc8bf712db912736c8067862d43fe826307464237bb1957cce9edf2f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 19, 2026.
Transparency log