RE2 in pure Python: linear-time, ReDoS-safe regular expressions - no C extension, no backtracking
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file purere2-0.1.2.tar.gz.
File metadata
- Download URL: purere2-0.1.2.tar.gz
- Upload date:
- Size: 21.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
668129e847b385a87685f3a51683ca1430b20ef4c194042582d463041a5a739e
|
|
| MD5 |
dcfa2d610562b305d34e3419374e9347
|
|
| BLAKE2b-256 |
741d6b84c54a2601f3d9ab9461849c3aedd382283a8a7d5cd1e01c7ee5bde4b8
|
Provenance
The following attestation bundles were made for purere2-0.1.2.tar.gz:
Publisher:
release.yml on adam2go/purere2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
purere2-0.1.2.tar.gz -
Subject digest:
668129e847b385a87685f3a51683ca1430b20ef4c194042582d463041a5a739e - Sigstore transparency entry: 1867281366
- Sigstore integration time:
-
Permalink:
adam2go/purere2@269dae699f60e2acdfd6ee39be6215d6c4a3a8bf -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/adam2go
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@269dae699f60e2acdfd6ee39be6215d6c4a3a8bf -
Trigger Event:
release
-
Statement type:
File details
Details for the file purere2-0.1.2-py3-none-any.whl.
File metadata
- Download URL: purere2-0.1.2-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
843b146399f348758135637cd5cc45403c96c6a8837c75b2ae817bba8f71dfd0
|
|
| MD5 |
9b1e3ee59bb5e940be21a6d63efc7348
|
|
| BLAKE2b-256 |
21afa54fc8bf712db912736c8067862d43fe826307464237bb1957cce9edf2f1
|
Provenance
The following attestation bundles were made for purere2-0.1.2-py3-none-any.whl:
Publisher:
release.yml on adam2go/purere2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
purere2-0.1.2-py3-none-any.whl -
Subject digest:
843b146399f348758135637cd5cc45403c96c6a8837c75b2ae817bba8f71dfd0 - Sigstore transparency entry: 1867281444
- Sigstore integration time:
-
Permalink:
adam2go/purere2@269dae699f60e2acdfd6ee39be6215d6c4a3a8bf -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/adam2go
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@269dae699f60e2acdfd6ee39be6215d6c4a3a8bf -
Trigger Event:
release
-
Statement type: