Skip to main content

Parse strings into Pydantic models using pattern matching

Project description

stringent

CI Python Version License: MIT Code style: ruff

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

🔀 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:

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 validation
  • parse>=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.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

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

Acknowledgments

  • Built on Pydantic for robust data validation
  • Uses parse for flexible string parsing

Made with ❤️ for the Python community

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stringent-0.1.0.tar.gz (17.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stringent-0.1.0-py3-none-any.whl (9.1 kB view details)

Uploaded Python 3

File details

Details for the file stringent-0.1.0.tar.gz.

File metadata

  • Download URL: stringent-0.1.0.tar.gz
  • Upload date:
  • Size: 17.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.19

File hashes

Hashes for stringent-0.1.0.tar.gz
Algorithm Hash digest
SHA256 57e8cf06b7f261d902e2691c2965e197a1023c60d597ade24e62cff0061b5d5b
MD5 072b9b514979ab02650f64201957be03
BLAKE2b-256 8b415dc97c6bf50196311ad75e8e716d81da56e513f86b1441e5b2afcc66aa09

See more details on using hashes here.

File details

Details for the file stringent-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: stringent-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.19

File hashes

Hashes for stringent-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 036aeb530148a5a6017aaf25ad3a6cb418afefb31ea815902982864b995f0636
MD5 e6dea7ebbd0cebfa7100d223fae97894
BLAKE2b-256 b6efb196bc83efe28c63225899692741c4a4a75fde41e865ce7170a2e18a8bfa

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page