Skip to content

nshaibu/pydantic-mini

Repository files navigation

pydantic-mini

pydantic-mini is a lightweight Python library that extends the functionality of Python's native dataclass by providing built-in validation, serialization, and support for custom validators. It is designed to be simple, minimalistic, and based entirely on Python’s standard library, making it perfect for projects, data validation, and object-relational mapping (ORM) without relying on third-party dependencies.

Features

  • Type and Value Validation:

    • Enforces type validation for fields using field annotations.
    • Includes built-in validators for common field types.
  • Custom Validators:

    • Easily define your own custom validation functions for specific fields.
  • Serialization Support:

    • Instances can be serialized to JSON, dictionaries, and CSV formats.
  • Lightweight and Fast:

    • Built entirely on Python’s standard library, no external dependencies are required.
  • Supports Multiple Input Formats:

    • Accepts data in various formats, including JSON, dictionaries, CSV, etc.
  • Simple ORM Capabilities:

    • Use the library to build lightweight ORMs (Object-Relational Mappers) for basic data management.

Installation

You can install pydantic-mini from PyPI once it's available:

pip install pydantic-mini

Alternatively, you can clone this repository and use the code directly in your project.


Usage

1. Define a Dataclass with Validation

from pydantic_mini import BaseModel

class Person(BaseModel):
    name: str
    age: int

2. Adding Validators For Individual Fields

You can define your own validators.

import typing
from pydantic_mini import BaseModel, MiniAnnotated, Attrib
from pydantic_mini.exceptions import ValidationError


# NOTE: All validators can be used for field values transformation
# by returning the transformed value from the validator function or method.

# NOTE: Validators must raise ValidationError if validation condition fails.

# NOTE: pydantic_mini use type annotation to enforce type constraints.

# Custom validation for not accepting name kofi
def kofi_not_accepted(instance, value: str):
  if value == "kofi":
    # validators must raise ValidationError when validation fails.
    raise ValidationError("Age must be a positive number.")

  # If you want to apply a transformation and save the result into the model, 
  # return the transformed result you want to save. For instance, if you want the names to be capitalized, 
  # return the capitalized version.
  return value.upper()


class Employee(BaseModel):
  name: MiniAnnotated[str, Attrib(max_length=20, validators=[kofi_not_accepted])]
  age: MiniAnnotated[int, Attrib(default=40, gt=20)]
  email: MiniAnnotated[str, Attrib(pattern=r"^\S+@\S+\.\S+$")]
  school: str

  # You can define validators by adding a method with the name 
  # "validate_<FIELD_NAME>" e.g to validate school name
  def validate_school(self, value, field):
    if len(value) > 20:
      raise ValidationError("School names cannot be greater than 20")

  # You can apply a general rule or transformation to all fields by implementing
  # the method "validate". it takes the argument value and field
  def validate(self, value, field):
    if len(value) > 10:
      raise ValidationError("Too long")


# implement model __model_init__
class C(BaseModel):
  i: int
  j: typing.Optional[int]
  database: InitVar[typing.Optional[DatabaseType]] = None

  def __model_init__(self, database):
    if self.j is None and database is not None:
      self.j = database.lookup('j')

NOTE: All validators can applied transformations to a field when they return the transformed value.

3. Creating Instances from Different Formats

From JSON:

import json
from pydantic_mini import Basemodel

class PersonModel(BaseModel):
  name: str
  age: int

data = '{"name": "John", "age": 30}'
person = PersonModel.loads(data, _format="json")
print(person)

From Dictionary:

data = {"name": "Alice", "age": 25}
person = PersonModel.loads(data, _format="dict")
print(person)

From CSV:

csv_data = "name,age\nJohn,30\nAlice,25"
people = PersonModel.loads(csv_data, _format="csv")
for person in people:
    print(person)

4. Serialization

pydantic-mini supports serializing instances to JSON, dictionaries, or CSV formats.

# Serialize to JSON
json_data = person.dump(_format="json")
print(json_data)

# Serialize to a dictionary
person_dict = person.dump(_format="dict")
print(person_dict)

5. Simple ORM Use Case

You can use this library to create simple ORMs for in-memory databases.

# Example: Create a simple in-memory ORM for a list of "Person" instances
people_db = []

# Add a new person to the database
new_person = Person(name="John", age=30)
people_db.append(new_person)

# Query the database (e.g., filter by age)
adults = [p for p in people_db if p.age >= 18]
print(adults)

Supported Formats

  • JSON: Convert data to and from JSON format easily.
  • Dict: Instantiating and serializing data as dictionaries.
  • CSV: Read from and write to CSV format directly.

Example

class EventResult(BaseModel):
    error: bool
    task_id: str
    event_name: str
    content: typing.Any
    init_params: typing.Optional[typing.Dict[str, typing.Any]]
    call_params: typing.Optional[typing.Dict[str, typing.Any]]
    process_id: MiniAnnotated[int, Attrib(default_factory=lambda: os.getpid())]
    creation_time: MiniAnnotated[float, Attrib(default_factory=lambda: datetime.now().timestamp())]

    class Config:
        unsafe_hash = False
        frozen = False
        eq = True

Contributing

Contributions are welcome! If you'd like to help improve the library, please fork the repository and submit a pull request.


License

pydantic-mini is open-source and available under the GPL License.


About

Dataclass with validation

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages