Safe boilerplate-free static regular expressions with MyPy support
Project description
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
- IDE support: Full autocomplete and type checking for regex groups
- Runtime safety: Compile-time validation of regex patterns
- Zero 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")
print(match.required) # Type: str (always present)
print(match.optional) # Type: str | None (might be None)
match = OptionalRegex.match("hello")
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!
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:
- Validate that your regex patterns are syntactically correct
- Automatically infer types for named capture groups
- Provide intelligent autocomplete in your IDE
- Catch attempts to access non-existent groups at compile time
- 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 at class definition time:
# This will raise a compile-time error:
class BadRegex(StaticRegex):
REGEX = r"(?P<bad>[unclosed" # SyntaxError!
Performance
- Regex patterns are compiled once when the class is defined, not on each use
- Basically zero runtime overhead compared to using
re.compile()directly - Type checking happens at build time with 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.
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 re_static-0.1.0.tar.gz.
File metadata
- Download URL: re_static-0.1.0.tar.gz
- Upload date:
- Size: 58.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.8.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f27874398bd3df0198b5d87ca8e6dd965d85906e01d413b84c1ed3e27494f3f9
|
|
| MD5 |
5fdbc28f1535c1e9edb6e22a47c467be
|
|
| BLAKE2b-256 |
505e32fcfdd3f1a8899cdff815f97328ce9444adfd3f642207677de59cd0f87a
|
File details
Details for the file re_static-0.1.0-py3-none-any.whl.
File metadata
- Download URL: re_static-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.8.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
967b3ff1d81c21fffea2ae5d85b952db4f3aadab673f85333be3f560be6289cc
|
|
| MD5 |
24deafc6b8ff434121144f2d5aeeb1da
|
|
| BLAKE2b-256 |
2ef4d48cb8320d8dbe9fc70bd029050af4552498568b8740e99c0d2bbf4392b2
|