Skip to content
mingalegPublic

About

Safe boilerplate-free static regular expressions with MyPy support

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

re-static

Safe boilerplate-free static regular expressions with MyPy support

re-static provides a powerful way to work with regular expressions in Python that gives you:

  • Type safety: Named capture groups become typed attributes on result objects
  • MyPy integration: Named capture groups are type-checked via a custom MyPy plugin
  • Early validation: Regex patterns are validated at class definition time
  • Low runtime overhead: Patterns are compiled once at class definition time

Installation

pip install re-static

Requires Python 3.11+ and MyPy for full type checking support.

Quick Start

from re_static import StaticRegex

class EmailRegex(StaticRegex):
    REGEX = r"(?P<username>[a-zA-Z0-9._%+-]+)@(?P<domain>[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})"

# Now you get type-safe access to capture groups!
match = EmailRegex.search("Contact us at hello@example.com")
if match:
    print(f"Username: {match.username}")  # Type: str
    print(f"Domain: {match.domain}")      # Type: str

Features

All Standard Regex Methods

re-static supports all the standard regex matching methods:

class DigitsRegex(StaticRegex):
    REGEX = r"(?P<digits>\d+)"

# Match from start of string
result = DigitsRegex.match("123abc")

# Search anywhere in string
result = DigitsRegex.search("abc123def")

# Match entire string
result = DigitsRegex.fullmatch("123")

# Find all matches as a list
results = DigitsRegex.findall("123 456 789")

# Find all matches as an iterator
for result in DigitsRegex.finditer("123 456 789"):
    print(result.digits)

Type-Safe Optional Groups

Optional capture groups are correctly typed as str | None:

class OptionalRegex(StaticRegex):
    REGEX = r"(?P<required>\w+)(?P<optional>\d+)?"

match = OptionalRegex.match("hello123")
if match:
    print(match.required)  # Type: str (always present)
    print(match.optional)  # Type: str | None (might be None)

match = OptionalRegex.match("hello")
if match:
    print(match.required)  # "hello"
    print(match.optional)  # None

Position and Bounds Support

All methods support the standard pos and endpos parameters:

result = EmailRegex.search("prefix hello@example.com suffix", pos=7, endpos=25)

Regex Flags

You can specify regex flags using the REGEX_FLAGS class attribute:

import re

class CaseInsensitiveRegex(StaticRegex):
    REGEX = r"(?P<word>[a-z]+)"
    REGEX_FLAGS = re.IGNORECASE

match = CaseInsensitiveRegex.match("HELLO")  # Works!
if match:
    print(match.word)  # "HELLO"

MyPy Integration

To get full type checking support, configure MyPy to use the re-static plugin by adding this to your pyproject.toml:

[tool.mypy]
plugins = ["re_static.mypy_plugin.plugin"]

With the plugin enabled, MyPy will:

  • Automatically infer types for named capture groups
  • Catch attempts to access non-existent groups
  • Enforce that regex group attributes are only accessed on instances, not classes

Advanced Usage

Complex Patterns

class LogLineRegex(StaticRegex):
    REGEX = r"(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(?P<level>\w+)\] (?P<message>.*?)(?P<error_code> \(code: \d+\))?"

log_line = "2023-12-01 10:30:15 [ERROR] Database connection failed (code: 500)"
match = LogLineRegex.match(log_line)
if match:
    print(f"Time: {match.timestamp}")
    print(f"Level: {match.level}")
    print(f"Message: {match.message}")
    print(f"Error code: {match.error_code}")  # Optional group

Error Handling

If a regex pattern has syntax errors, they'll be caught in two ways:

  1. At class definition time (runtime): re.compile() raises re.error
  2. When running MyPy: the plugin reports an error diagnostic
class BadRegex(StaticRegex):
    REGEX = r"(?P<bad>[unclosed"  # re.error at runtime, mypy error at type-check time

Performance

  • Regex patterns are compiled once when the class is defined, not on each use
  • Low runtime overhead: patterns are compiled once, and each match creates a lightweight instance with group attributes
  • Type checking happens when running MyPy, not at runtime

Development

This project uses modern Python development practices:

  • uv for dependency management
  • pytest for testing
  • mypy for type checking
  • ruff for linting and formatting

See development.md for detailed development instructions.

License

MIT License - see LICENSE for details.

Contributing

Contributions welcome! Please see our development guide and submit pull requests on GitHub.

About

Safe boilerplate-free static regular expressions with MyPy support

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages