Regex Tester — Python source
Test regular expressions against any text with all flags (g/i/m/s/u/y). See every match, its index and capture groups, plus the constructed pattern. 100% in-browser.
This is the Python implementation — the same logic the interactive tool runs, in a shareable, citable form.
"""Regular expression tester: match a pattern against input text and report
each match with its capture groups and character index.
Language: Python
CosmoDev polyglot showcase port of the ``regex-tester`` tool.
Ported from src/tools/RegexTester.tsx — display source, part of CosmoDev's
polyglot tool pages.
Compiles the pattern with the chosen flags and walks the matches via the
stdlib :mod:`re` module, surfacing the full match, its index, and the capture
groups. The JS flags map onto :mod:`re` constants: i→re.IGNORECASE,
m→re.MULTILINE, s→re.DOTALL; Python 3 ``str`` patterns are Unicode by default,
so u is a no-op. g selects :func:`re.finditer` vs :func:`re.search` (first
match), capped at 2000 to mirror the JS source. The :mod:`re` engine shares
JS's backreference and lookahead support, so pattern semantics match the
island closely. The sticky flag y has no ``re`` equivalent; documented as
unsupported.
"""
from __future__ import annotations
import re
from dataclasses import dataclass, field
@dataclass
class RegexMatch:
"""One match: full text, its starting index, and capture groups.
Unmatched optional groups appear as ``None`` — the equivalent of the JS
source's ``undefined`` capture entries.
"""
match: str
index: int
groups: list[str | None] = field(default_factory=list)
@dataclass
class RegexResult:
matches: list[RegexMatch]
error: str | None = None
def _compile_flags(flags: str) -> int:
"""Translate the JS flag letters into Python ``re`` flag bits."""
table = {
"i": re.IGNORECASE,
"m": re.MULTILINE,
"s": re.DOTALL,
"u": re.UNICODE, # default for str in Python 3; kept for parity
}
bits = 0
for f in flags:
bits |= table.get(f, 0)
return bits
def run_regex(pattern: str, flags: str, test_str: str) -> RegexResult:
"""Compile ``pattern`` (with ``flags``) and run it against ``test_str``.
With the ``g`` flag all matches are returned (≤2000); without it, only the
first. An empty pattern yields no matches and no error; a malformed pattern
returns the engine's error message rather than raising.
"""
if not pattern:
return RegexResult(matches=[], error=None)
try:
compiled = re.compile(pattern, _compile_flags(flags))
except re.error as exc:
return RegexResult(matches=[], error=str(exc))
out: list[RegexMatch] = []
if "g" in flags:
for i, m in enumerate(compiled.finditer(test_str)):
if i >= 2000:
break
out.append(RegexMatch(m.group(0), m.start(), list(m.groups())))
else:
m = compiled.search(test_str)
if m:
out.append(RegexMatch(m.group(0), m.start(), list(m.groups())))
return RegexResult(matches=out, error=None)
def format_matches(result: RegexResult) -> str:
"""Render a result as the tool's UI does:
``N. "match" @ index → [g1, g2]``, or ``(none)`` when empty."""
if result.error is not None:
return result.error
if not result.matches:
return "(none)"
lines = []
for i, m in enumerate(result.matches, 1):
line = f'{i}. "{m.match}" @ {m.index}'
if m.groups:
line += " → [" + ", ".join(g or "" for g in m.groups) + "]"
lines.append(line)
return "\n".join(lines)
if __name__ == "__main__":
res = run_regex(r"\b(\w+)@(\w+)\.(\w+)\b", "gi", "mail@x.com nope bob@test.org")
print(format_matches(res))
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →