Blog · 3 min read
When a tool says “Python regex”, ask which one
Kristof Polleunis · August 19, 2026
Python has two regex engines in common use, and they are not the same language.
There's re, in the standard library. And there's regex
on PyPI — a drop-in replacement that is a strict superset, maintained
separately, and depended on by a lot more code than people realise.
Every regex tool I've used treats "Python" as one thing. It isn't, and the gap is bigger than a couple of flags.
Three constructs that only exist in one of them
Set operations in character classes.
[\w--\d] is meant to say "word characters, minus the digits". Here is
what that one pattern actually returns — verified on Python 3.14 and
regex 2026.7.19:
import re # the standard library
import regex # THIRD-PARTY, from PyPI: pip install regex
re.findall(r"[\w--\d]+", "abc123def") # error: bad character range
regex.findall(r"[\w--\d]+", "abc123def") # ['abc123def'] — everything
regex.findall(r"(?V1)[\w--\d]+", "abc123def") # ['abc', 'def'] — what you meant
One pattern, three answers. The standard library at least fails loudly.
The trap is inside regex itself: set operations only work in its V1
mode, and its default mode — V0, kept for re compatibility — quietly
reads the same class as "word characters, a dash, and digits". That
matches everything, including every digit you were trying to exclude.
That's the dangerous shape: not a syntax error, just a different answer.
Fuzzy matching.
regex.findall(r"(?:foobar){e<=1}", "fooba") # matches
{e<=1} means "allow at most one error" — insertion, deletion or
substitution. There is nothing like it in re. If you've ever
hand-rolled Levenshtein because you needed approximate matching, regex
had it built in.
Recursion.
regex.findall(r"\((?:[^()]|(?R))*\)", "a(b(c)d)e")
(?R) recurses the whole pattern, so you can match balanced parentheses
— the thing every regex tutorial tells you regex cannot do. re cannot.
regex can.
And one that moved
Possessive quantifiers — a*+, a++, a?+ — arrived in re with
Python 3.11. Before that they were regex-only.
So "does Python support possessive quantifiers" now has three answers depending on your interpreter version and which module you imported. A tool that models "Python" as a single static flavour gets this wrong for somebody no matter what it picks.
Why this matters more than it sounds
The failure mode isn't "my pattern didn't work." That's the good case — you find out immediately.
The failure mode is a pattern that works in two places, differently.
[\w--\d] is accepted by regex in both of its modes and means opposite
things in them. You develop with (?V1) in a scratch file, deploy
without it, and the bug surfaces on input you didn't test.
I ship a regex tool, and I modelled Python as one flavour for longer than I should have. Fixing it meant treating the engine as a runtime setting, not a static property: a toggle that changes which module the sidecar imports, which constructs the editor offers, what the generated code says, and how the explanation panel describes what you built.
That last one matters more than expected. If the tool tells you {e<=1}
is a quantifier repeating a group between e and 1 times, it isn't
helping — it's confidently wrong.
How to know which one you have
import re
print(re.__name__) # 're' or 'regex' — check what you actually imported
Sounds trivial. It isn't, because import regex as re is a common
idiom, and from that point on every re.something in the file is the
third-party engine. Someone reading the code six months later sees re.
and assumes stdlib.
If you depend on regex features, importing it as re is the pattern
most likely to mislead the next reader. import regex and use regex.
explicitly.
The practical rule
Before trusting any regex tool with a Python pattern, ask it which module
it ran. If it can't tell you, it ran re — which is right about 90% of
the time and silently wrong about the rest.
And if you're on Python 3.11+, check whether you still need regex at
all. Possessive quantifiers were often the only reason it was in the
dependency list.
I maintain RegexPilot, a macOS regex editor
that bundles CPython and runs your pattern through the real module — re
or regex, switchable — rather than approximating either.
More from this series: