RegexDebugger: Verbose, Step-by-Step Regex Debugging#

class my.regex.RegexDebugger.RegexDebugger(*, definitions: dict[str, str]={}, patterns: dict[str, ~typing.Annotated[~_regex.Pattern, ~pydantic.types.GetPydanticSchema(get_pydantic_core_schema=~my.utils.SyntaxUtils.SyntaxUtils.pyd_schemify.<locals>.<lambda>, get_pydantic_json_schema=None)]] = {}, parsers: dict[str, ~my.regex.RegexStore.RegexParser]={}, options: Options = <factory>)#

Debugging tools for analyzing regex pattern failures.

Extends RegexStore with methods to diagnose why patterns fail to match text. Provides detailed failure analysis by isolating the failing clause and showing which parts of the pattern matched successfully before failure.

I Initial Methods#

classmethod RegexDebugger.new_debugger(store: RegexStore) → Self#

Create a debugger from an existing RegexStore.

The source is loaded before copying, so the default lazy store is safe to pass directly. Definitions, options, routers, and out-of-band flag provenance are copied; immutable compiled pattern objects may be shared by the two stores.

Parameters:

store – RegexStore instance to create debugger from.

Returns:

New RegexDebugger with all patterns from the store.

Examples

Wrap even a lazy store directly to gain the debugging methods:

>>> from my import RegexStore, RegexDebugger
>>> store = RegexStore.new(word=r'\w+')
>>> RegexDebugger.new_debugger(store).keys()
['word']

II Helper Methods#

RegexDebugger.pinpoint_failure(text: Buffer, expr: Regex, prefix: str) → tuple[int, MatchData]#

Identify the first clause to cause an accumulated sub-expression to fail to match.

Parameters:
  • text – Buffer containing text to match against.

  • expr – Atomized regex expression from the pattern body.

  • prefix – Precompiled prefix string to prepend to each snippet.

Returns:

  1. Index of the failing atom (or the atom count, if every atom matched).

  2. MatchData of the last successful match.

RegexDebugger.curate(atoms: Regex, failed_idx: int, flags: Atom) → str#

Curate the given regex snippet to include only the failing clause and its dependencies.

The purpose of this function is to help callers construct a “minimally-failing version” of a problem regex.

Parameters:
  • atoms – Atomized regex expression from the pattern body.

  • failed_idx – Index of the atom where matching failed.

  • flags – Atom containing any regex flags (“modifiers”) to apply to the curated expression.

Returns:

A truncated version of the given expression.

III Primary Methods#

RegexDebugger.debug_failed_match(name: str, text: Buffer) → list[str]#

Identify which clause in the identified pattern caused matching to fail.

Iteratively tests progressively longer subpatterns to find exactly where matching stops, then extracts that clause with its dependencies for testing.

Parameters:
  • name – Name of pattern that failed.

  • text – Buffer containing text that failed to match.

Returns:

List of strings describing the failure with a curated test regex.

IV Public Methods#

RegexDebugger.debug(names: str | list[str], text: str, matched: bool, expected: bool = True, func: str = '') → str#

Generate stdout-ready debug output for a regex test that produced unexpected results.

Parameters:
  • names – Pattern name (or list of names) that were tested.

  • text – Text that was matched against.

  • matched – Whether the pattern actually matched.

  • expected – Whether a match was expected.

  • func – Name of the function used (e.g. ‘match’, ‘search’, ‘findall’).

Returns:

Multi-line debug report showing pattern, text, and failure analysis.

Raises:

ValueError – If matched and expected are both False (no failure to debug).

Examples

Explain a failed match, clause by clause:

>>> from my import RegexDebugger
>>> store = RegexDebugger.new(date=r'(?P<y>\d{4})-(?P<m>\d\d)-(?P<d>\d\d)')
>>> report = store.debug('date', '2024-13-xx', matched=False, func='full')
>>> print(report.splitlines()[1])
Regular expression "DATE.full()" FAILED TO MATCH the full text.

The remainder of the report isolates the failing clause as a standalone, curated expression, alongside the last text position that still matched.

static RegexDebugger.parse_pytest(name: str, case: list | tuple | Set | deque | array | range | dict | str) → tuple[str, str, dict | None]#

Parse a single regex test case (likely from a .yaml file).

Parameters:
  • name – Name of the pattern under test, used to label bare expected values.

  • case – The raw test case – a plain string (any match passes), a (text, expected) vector, or a dict with a text key plus optional func, expect_none, and per-group expectations.

Returns:

  1. The text to match against.

  2. The store function to use (match, full, search, fullsplit, or poly).

  3. The expected captures, or None when the case expects no match at all.

Examples

Dict cases carry their own function and expectations:

>>> RegexDebugger.parse_pytest('word', dict(text='abc', word='abc'))
('abc', 'full', {'word': ['abc']})
>>> RegexDebugger.parse_pytest('word', dict(text='123', expect_none=True))
('123', 'full', None)
classmethod RegexDebugger.pytest(store: RegexStore, name: str, index: int, text: str, func: str, expected: Any, verbose: bool = True) → None#

Run a single test case against the given RegexStore, verifying expected results.

Parameters:
  • store – RegexStore containing the pattern to test.

  • name – Name of the pattern to test.

  • index – Index of the test case (for logging purposes).

  • text – Text to match against.

  • func – Name of the function to use (match|full|search|split|poly).

  • expected – Expected result (None for no match, dict for expected captures).

  • verbose – Verbosity level; values above 1 print the full debugger report on failure.