stringent
stringent is a powerful Python library that seamlessly parses strings into Pydantic models using flexible pattern matching. Whether you're working with pipe-separated values, space-separated data, JSON strings, or custom formats, stringent makes it easy to convert unstructured strings into validated, type-safe Python objects.
Features
✨ Flexible Pattern Matching - Parse strings using format-like patterns (e.g., {name} | {age} | {city})
🔗 Pattern Chaining - Chain multiple patterns using the | operator to try patterns in order until one matches
🔄 Automatic Input Handling - Seamlessly handles both dictionary and string inputs without code changes
🎯 Pydantic Integration - Built on Pydantic 2.0+ for robust validation and type safety
📦 JSON Support - Built-in JSON parsing with automatic fallback to pattern matching
🚀 JsonParsableModel - Automatic JSON string parsing for API integrations and message queues
🔀 Union Types - Organize parsing strategies using union types for maximum flexibility
🧬 Inheritance Support - Parse patterns are inherited and can be overridden in subclasses
Installation
pip install stringent
Quick Start
from pydantic import BaseModel, EmailStr
from typing import Literal
from stringent import parse, parse_json, ParsableModel
class Info(BaseModel):
name: str
age: int
city: str
class Record(ParsableModel):
id: int
info: Info = parse_json() | parse('{name} | {age} | {city}') | parse('{name} {age} {city}')
email: EmailStr
status: Literal['Active', 'Inactive']
# Parse the data - handles dicts, strings, and JSON automatically
data = [
{'id': 1, 'info': {'name': 'Alice', 'age': 30, 'city': 'New York'}, 'email': 'alice@example.com', 'status': 'Active'},
{'id': 3, 'info': 'Charlie | 27 | Chicago', 'email': 'charlie@example.com', 'status': 'Active'},
{'id': 5, 'info': 'Eve 35 Dallas', 'email': 'eve@example.com', 'status': 'Inactive'},
{'id': 8, 'info': '{"name": "Joe", "age": 55, "city": "Tampa"}', 'email': 'joe@example.com', 'status': 'Active'},
]
for item in data:
record = Record(**item)
print(record)
Output:
id=1 info=Info(name='Alice', age=30, city='New York') email='alice@example.com' status='Active'
id=3 info=Info(name='Charlie', age=27, city='Chicago') email='charlie@example.com' status='Active'
id=5 info=Info(name='Eve', age=35, city='Dallas') email='eve@example.com' status='Inactive'
id=8 info=Info(name='Joe', age=55, city='Tampa') email='joe@example.com' status='Active'
Why stringent?
Working with mixed data formats is a common challenge in data processing. You might receive:
- Dictionary objects from APIs
- Pipe-separated strings from legacy systems
- Space-separated values from log files
- JSON strings from message queues
stringent eliminates the need for manual parsing logic by automatically handling all these formats with a single, declarative definition.
Key Use Cases
- API Integration - Handle inconsistent data formats from different endpoints
- Data Migration - Parse legacy data formats while maintaining type safety
- Log Processing - Parse structured log entries into validated models
- ETL Pipelines - Transform unstructured strings into typed data structures
- Configuration Parsing - Support multiple configuration formats with fallback patterns
Documentation
Comprehensive documentation is available in the docs directory:
- Getting Started - Installation and basic concepts
- Basic Usage - Field-level parsing, pattern chaining, and common patterns
- JSON Parsing - Automatic JSON parsing with JsonParsableModel
- Regex Parsing - Parse strings using regular expressions with named groups
- Error Handling - Error recovery and partial parsing
- FastAPI Integration - Using stringent with FastAPI
- Advanced Patterns - Union types, inheritance, and complex scenarios
- API Reference - Complete API documentation
- Documentation Index - Overview and quick links
Requirements
- Python 3.10 or higher
- Pydantic 2.0 or higher
- parse 1.20 or higher
Dependencies
pydantic>=2.0.0- For Pydantic model integration and validationparse>=1.20.0- For string parsing functionality
Examples
Pattern Chaining
Try multiple patterns in order until one matches:
from stringent import parse, ParsableModel
from pydantic import BaseModel
class Info(BaseModel):
name: str
age: int
city: str
class Record(ParsableModel):
info: Info = parse('{name} | {age} | {city}') | parse('{name} {age} {city}')
# Both formats work automatically
record1 = Record(info="Alice | 30 | NYC")
record2 = Record(info="Bob 25 Chicago")
JSON Parsing
Automatically parse JSON strings with fallback to pattern matching:
from stringent import parse_json, parse, ParsableModel
from pydantic import BaseModel
class Info(BaseModel):
name: str
age: int
class Record(ParsableModel):
info: Info = parse_json() | parse('{name} | {age}')
# JSON string
record1 = Record(info='{"name": "Alice", "age": 30}')
# Pattern string (fallback)
record2 = Record(info="Bob | 25")
Union Types
Use union types to organize parsing strategies:
from typing import Union
from stringent import ParsableModel
from pydantic import BaseModel
class Info(ParsableModel):
name: str
age: int
class PipeInfo(Info):
_model_parse_pattern = '{name} | {age}'
class SpaceInfo(Info):
_model_parse_pattern = '{name} {age}'
class Record(ParsableModel):
info: Union[PipeInfo, SpaceInfo]
# Automatically selects the correct type
record1 = Record(info="Alice | 30") # Uses PipeInfo
record2 = Record(info="Bob 25") # Uses SpaceInfo
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Getting Started
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Open Issues
Looking for something to work on? Check out our open issues! We have many enhancement ideas organized by priority:
- High Priority Features - Core functionality improvements
- Medium Priority Features - Additional capabilities and optimizations
- Documentation & Infrastructure - Docs, examples, and tooling improvements
Issues labeled with good first issue are great for newcomers. See the full list of enhancement issues for more ideas.
Development
To set up a development environment:
# Clone the repository
git clone https://github.com/eddiethedean/stringent.git
cd stringent
# Create a virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run linting
ruff check .
ruff format .
# Run type checking
mypy stringent/
License
This project is licensed under the MIT License - see the LICENSE file for details.
Author
Odos Matthews
- GitHub: @eddiethedean
- Email: odosmatthews@gmail.com
Acknowledgments
Made with ❤️ for the Python community
Metadata
Release files for stringent 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| stringent-0.3.1.tar.gz | 30.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| stringent-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 43.1 kB
Release files / stringent-0.3.1.tar.gz
| Download URL | stringent-0.3.1.tar.gz |
|---|---|
| Size | 30.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fe4dc6d7ec8a3ed5c456aae0deb8688ef4b38c390bb3941621b00ecafe71e453
|
|
BLAKE2b-256 checksum How to use checksums |
6303a05fb643887d1429f76fe315a0237ad0c62be28ee6c9ade3970b96763ffd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.19
|
Release files / stringent-0.3.1-py3-none-any.whl
| Download URL | stringent-0.3.1-py3-none-any.whl |
|---|---|
| Size | 12.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b5167825321a5df584e62809d0fd7f5c081440ee97c29299141356cde74a82fb
|
|
BLAKE2b-256 checksum How to use checksums |
ec3d5b75bdb11fe7128392ce2f0f8a9d7a761f678e40d48c099825a5fdb1d821
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.19
|