mocksmith


Namemocksmith JSON
Version 6.0.1 PyPI version JSON
download
home_pageNone
SummaryType-safe data validation and mocking for Python dataclasses and Pydantic models
upload_time2025-08-11 05:24:04
maintainerNone
docs_urlNone
authorGurmeet Saran
requires_python>=3.9
licenseMIT
keywords mock validation dataclass pydantic testing faker types sql
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # mocksmith

[![Unit Tests](https://github.com/gurmeetsaran/mocksmith/actions/workflows/ci.yml/badge.svg)](https://github.com/gurmeetsaran/mocksmith/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/gurmeetsaran/mocksmith/branch/main/graph/badge.svg)](https://codecov.io/gh/gurmeetsaran/mocksmith)
[![PyPI version](https://badge.fury.io/py/mocksmith.svg)](https://badge.fury.io/py/mocksmith)
[![Python Versions](https://img.shields.io/pypi/pyversions/mocksmith.svg)](https://pypi.org/project/mocksmith/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Type-safe data validation with automatic mock generation for Python dataclasses and Pydantic models. Build robust data models with database-aware validation and generate realistic test data with a single decorator.

## Features

- **Type-safe database columns**: Define database columns with proper validation
- **SQL-compliant validation**: All numeric types strictly enforce SQL bounds (TINYINT: -128 to 127, etc.)
- **Instantiation validation**: Types validate at creation time, preventing invalid data from being created
- **Serialization/Deserialization**: Automatic conversion between Python and SQL types
- **Dataclass Integration**: Full support for Python dataclasses with validation
- **Pydantic Integration**: First-class Pydantic support with automatic validation
- **Clean API**: Simple, intuitive interface for both Pydantic AND dataclasses - just `name: Varchar(50)`
- **Comprehensive Types**: STRING (VARCHAR, CHAR, TEXT), NUMERIC (INTEGER, DECIMAL, FLOAT), TEMPORAL (DATE, TIME, TIMESTAMP), and more
- **Mock Data Generation**: Built-in mock/fake data generation that respects all SQL bounds and constraints
- **Constrained Types**: Support for min/max constraints on numeric types - `price: PositiveMoney()`, `age: Integer(ge=0, le=120)`

## Why mocksmith?

### Before (Traditional Approach)
```python
from typing import Annotated
from pydantic import BaseModel, Field, validator
from decimal import Decimal

class Product(BaseModel):
    name: Annotated[str, Field(max_length=100)]
    price: Annotated[Decimal, Field(decimal_places=2, max_digits=10)]
    in_stock: bool = True

    @validator('price')
    def validate_price(cls, v):
        if v < 0:
            raise ValueError('Price must be non-negative')
        return v
```

### After (With mocksmith)
```python
from pydantic import BaseModel
from mocksmith import Varchar, Money, Boolean

class Product(BaseModel):
    name: Varchar(100)         # Enforces VARCHAR(100) constraint
    price: Money()             # Decimal(19,4) - use PositiveMoney() for price > 0
    in_stock: Boolean() = True # Flexible boolean parsing
```

✨ **Benefits:**
- Same clean syntax for both Pydantic and dataclasses
- Automatic SQL constraint validation
- Type conversion (string "99.99" → Decimal)
- Better IDE support and type hints
- Write once, use with either framework

## Installation

```bash
# Standard installation (includes mock generation)
pip install mocksmith

# With Pydantic validation support (recommended)
pip install "mocksmith[pydantic]"
```

The standard installation includes Faker for mock data generation and custom validation logic. Adding Pydantic provides better performance and integration with Pydantic types.

## Import Structure

The library organizes types into two categories:

### Core Database Types (V3 Pattern)
Core database types are available through factory functions from the main package:

```python
from mocksmith import (
    # String types - Factory functions only
    Varchar, Char, Text,
    # Numeric types - Factory functions only
    Integer, DecimalType, Float,
    BigInt, SmallInt, TinyInt,
    Double, Real, Numeric,
    # Temporal types - Factory functions only
    Date, Time, DateTime, Timestamp,
    # Other types - Factory functions only
    Boolean, Binary, VarBinary, Blob,
    # Constrained types
    PositiveInteger, NonNegativeInteger, NegativeInteger, NonPositiveInteger,
    Money, PositiveMoney, NonNegativeMoney, ConstrainedMoney,
    ConstrainedDecimal, ConstrainedFloat
)
```

**⚠️ Breaking Change in V3:** Direct class imports (VARCHAR, INTEGER, etc.) have been removed to prevent confusion. Use factory functions (Varchar, Integer, etc.) exclusively.

### Specialized Types
Specialized types for common use cases are available from the `specialized` submodule:

```python
from mocksmith.specialized import (
    # Geographic types
    CountryCode,  # ISO 3166-1 alpha-2 country codes
    City,         # City names
    State,        # State/province names
    ZipCode,      # Postal codes

    # Contact types
    PhoneNumber,  # Phone numbers
)
```

**Note**: For email and web types, use Pydantic's built-in types instead:
- Email → Use `pydantic.EmailStr`
- URL → Use `pydantic.HttpUrl` or `pydantic.AnyUrl`
- IP addresses → Use `pydantic.IPvAnyAddress`, `pydantic.IPv4Address`, or `pydantic.IPv6Address`

This separation keeps the main namespace clean and makes it clear which types are fundamental database types versus application-specific types.

## Quick Start

### Clean V3 Interface (Works with both Pydantic and Dataclasses!) ✨

```python
from pydantic import BaseModel
from mocksmith import Varchar, Integer, Boolean, Money

class User(BaseModel):
    id: Integer()
    username: Varchar(50)  # Creates a type class with length 50
    email: Varchar(255)
    is_active: Boolean() = True
    balance: Money() = "0.00"

# Automatic validation and type conversion
user = User(
    id=1,
    username="john_doe",
    email="john@example.com",
    is_active="yes",      # Converts to True
    balance="1234.56"     # Converts to Decimal('1234.56')
)
```

The same syntax works with dataclasses! See full examples:
- [`examples/pydantic_example.py`](examples/pydantic_example.py) - Comprehensive Pydantic examples with all features
- [`examples/dataclass_example.py`](examples/dataclass_example.py) - Comprehensive dataclass examples with all features
- [`examples/pydantic_mock_example.py`](examples/pydantic_mock_example.py) - Mock data generation with Pydantic models
- [`examples/dataclass_mock_example.py`](examples/dataclass_mock_example.py) - Mock data generation with dataclasses
- [`examples/constrained_types_example.py`](examples/constrained_types_example.py) - Constrained types with validation and mock generation

### Common Use Cases

**E-commerce Product Model:**

```python
from pydantic import BaseModel
from mocksmith import Varchar, Text, Money, Boolean, Timestamp

class Product(BaseModel):
    sku: Varchar(20)
    name: Varchar(100)
    description: Text()
    price: Money()
    in_stock: Boolean() = True
    created_at: Timestamp()
```

**User Account with Constraints:**

```python
from mocksmith import Integer, PositiveInteger, NonNegativeInteger

class UserAccount(BaseModel):
    user_id: PositiveInteger()
    age: Integer(ge=13, le=120)
    balance_cents: NonNegativeInteger()
```

See complete working examples:
- [`examples/`](examples/) - All example files with detailed documentation
- [`examples/pydantic_example.py`](examples/pydantic_example.py) - All features including constraints
- [`examples/dataclass_example.py`](examples/dataclass_example.py) - All features including constraints

## Mock Data Generation

Generate realistic test data automatically with the `@mockable` decorator:

```python
from dataclasses import dataclass
from mocksmith import Varchar, Integer, Date, mockable
from mocksmith.specialized import PhoneNumber, CountryCode

@mockable
@dataclass
class Address:
    street: Varchar(100)
    city: Varchar(50)
    zip_code: Integer(ge=10000, le=99999)

@mockable
@dataclass
class User:
    id: Integer()
    username: Varchar(50)
    phone: PhoneNumber()
    country: CountryCode()
    birth_date: Date()
    address: Address  # Nested dataclass!

# Generate mock instances
user = User.mock()
print(user.username)  # "Christina Wells"
print(user.phone)     # "(555) 123-4567"
print(user.country)   # "US"
print(user.address.city)  # "New York"  # Nested fields are mocked too!

# With overrides
user = User.mock(username="test_user", country="GB")

# Using builder pattern
user = (User.mock_builder()
        .with_username("john_doe")
        .with_country("CA")
        .build())
```

The same `@mockable` decorator works with Pydantic models! Mock generation:
- Respects all field constraints (length, format, etc.)
- Generates appropriate mock data for each type
- Supports specialized types with realistic data
- Works with both dataclasses and Pydantic models
- Automatically handles Python Enum types with random value selection
- Supports nested dataclasses - automatically generates mock data for nested structures

See mock examples:
- [`examples/dataclass_mock_example.py`](examples/dataclass_mock_example.py) - Complete mock examples with dataclasses including enum support
- [`examples/pydantic_mock_example.py`](examples/pydantic_mock_example.py) - Complete mock examples with Pydantic including enum support and built-in types

## V3 Type Usage Patterns

**Important:** MockSmith V3 uses factory functions exclusively. The old pattern of importing classes directly (VARCHAR, INTEGER, etc.) is no longer supported.

### Correct V3 Pattern

```python
from mocksmith import Varchar, Integer, Boolean  # Factory functions

# Factory functions create type classes for use in annotations
UsernameType = Varchar(30, min_length=3)  # Returns a type class

class User(BaseModel):
    username: UsernameType  # Use the type class
    # Or inline:
    email: Varchar(100, to_lower=True)  # Factory function inline
    age: Integer(gt=0, le=120)
    active: Boolean()
```

### What Changed from V2

```python
# ❌ OLD PATTERN (NO LONGER WORKS - REMOVED IN V3)
from mocksmith import VARCHAR  # This import fails now
varchar_type = VARCHAR(30)  # Would create instance "30" - WRONG!

# ✅ NEW V3 PATTERN (THE ONLY WAY)
from mocksmith import Varchar  # Factory function
UsernameType = Varchar(30)  # Creates type class - CORRECT!
```

### With Pydantic (Full Validation)

```python
from typing import Optional
from pydantic import BaseModel
from mocksmith import Integer, Varchar, Money, Boolean, PositiveInteger, NonNegativeInteger
from decimal import Decimal

# Pattern 1: Direct usage (Recommended - cleanest syntax)
class Product(BaseModel):
    id: Integer()
    name: Varchar(100)
    price: Money()
    in_stock: Boolean() = True

# Pattern 2: With constraints
class ConstrainedModel(BaseModel):
    age: Integer(ge=0, le=120)  # Age between 0-120
    quantity: Integer(gt=0)      # Positive quantity
    discount: Integer(ge=0, le=100, multiple_of=5)  # 0-100%, multiples of 5

# Pattern 3: Factory functions with constraints
class ConstrainedProduct(BaseModel):
    sku: Varchar(20, to_upper=True)  # Auto uppercase
    name: Varchar(100, min_length=3)
    price: DecimalType(10, 2, gt=0)  # precision=10, scale=2, >0

# Pattern 4: Constrained types (common patterns)
class UserAccount(BaseModel):
    user_id: PositiveInteger()       # > 0
    balance: NonNegativeInteger()    # >= 0

# Pattern 5: Optional fields
class OptionalModel(BaseModel):
    required_field: Varchar(50)
    optional_field: Optional[Varchar(50)] = None  # Can be None
    with_default: Boolean() = True                # Has default value

# All patterns can be mixed in the same model!
```

### With Dataclasses (Type Hints Only, No Validation)

```python
from dataclasses import dataclass
from typing import Optional
from decimal import Decimal
from mocksmith import Integer, Varchar, Money, Text

@dataclass
class Product:
    # Same syntax works, but NO validation occurs!
    id: Integer()
    name: Varchar(100)
    price: Money() = Decimal("0.00")
    optional_field: Optional[Text()] = None

# WARNING: Dataclasses don't validate!
product = Product(
    id=999999999999,    # Accepts invalid values!
    name="x" * 1000,    # No length check!
    price="invalid"     # No type check!
)
```

### Important V3 Notes

✅ **DO USE (V3 Pattern):**
- `field: Varchar(50)` - Factory functions for type creation
- `field: Integer(gt=0)` - Factory functions with constraints
- `field: Optional[Varchar(50)] = None` - For nullable fields
- Pydantic `BaseModel` when you need validation
- Constrained types like `PositiveInteger()` for common patterns

❌ **DON'T USE (Removed in V3):**
- `from mocksmith import VARCHAR` - Direct class imports removed
- `VARCHAR(30)` - Would create instance "30", not a type!
- Plain dataclasses if you need validation (use Pydantic instead)

### Type Validation Features

All numeric types enforce SQL bounds and validate at instantiation:
- **TinyInt**: -128 to 127 (8-bit)
- **SmallInt**: -32,768 to 32,767 (16-bit)
- **Integer**: -2,147,483,648 to 2,147,483,647 (32-bit)
- **BigInt**: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 (64-bit)

Optional fields properly handle None values:
```python
class User(BaseModel):
    name: Varchar(50)                        # Required
    nickname: Optional[Varchar(30)] = None   # Optional, can be None

user = User(name="John", nickname=None)  # ✓ Valid
```

### Literal Type Support

MockSmith types work seamlessly with Python's `Literal` type for strict value constraints:

```python
from typing import Literal
from pydantic import BaseModel
from mocksmith import Varchar, Integer, mockable

@mockable
class ServerConfig(BaseModel):
    environment: Literal["dev", "staging", "prod"]
    status_code: Literal[200, 301, 404, 500]
    port: Integer(ge=1024, le=65535)
    log_level: Literal[0, 1, 2, 3, 4, 5]  # 0=OFF, 5=TRACE

# Validation enforces Literal constraints
config = ServerConfig(
    environment="prod",      # ✓ Valid
    status_code=200,        # ✓ Valid
    port=8080,              # ✓ Valid (within range)
    log_level=2             # ✓ Valid
)

# Mock generation respects Literal values
mock = ServerConfig.mock()
# mock.environment will be one of: "dev", "staging", "prod"
# mock.status_code will be one of: 200, 301, 404, 500
```

## Clean Annotation Interface

The library provides a clean, Pythonic interface for defining database types:

### Available Clean Types:

**String Types:**
- `Varchar(length)` → Variable-length string
- `Char(length)` → Fixed-length string
- `Text()` → Large text field
- `String` → Alias for Varchar

**Numeric Types:**
- `Integer()` → 32-bit integer (-2,147,483,648 to 2,147,483,647)
- `BigInt()` → 64-bit integer (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)
- `SmallInt()` → 16-bit integer (-32,768 to 32,767)
- `TinyInt()` → 8-bit integer (-128 to 127)
- `DecimalType(precision, scale)` → Fixed-point decimal
- `Numeric(precision, scale)` → Alias for DecimalType
- `Money()` → Alias for Decimal(19, 4)
- `Float()` → Floating point (generates FLOAT SQL type)
- `Real()` → Floating point (generates REAL SQL type, typically single precision in SQL)
- `Double()` → Double precision

All numeric types:
- Enforce SQL bounds at instantiation (e.g., `TinyInt(200)` raises ValueError)
- Generate mock data within valid ranges (e.g., `TinyInt(gt=5)` generates 6-127, not > 127)
- Support constraints (gt, ge, lt, le, multiple_of)

**Constrained Numeric Types:**
- `PositiveInteger()` → Integer > 0
- `NegativeInteger()` → Integer < 0
- `NonNegativeInteger()` → Integer ≥ 0
- `NonPositiveInteger()` → Integer ≤ 0
- `ConstrainedInteger(ge=x, le=y, multiple_of=z)` → Custom constraints
- `ConstrainedBigInt(...)` → Constrained 64-bit integer
- `ConstrainedSmallInt(...)` → Constrained 16-bit integer
- `ConstrainedTinyInt(...)` → Constrained 8-bit integer

**Temporal Types:**
- `Date()` → Date only
- `Time()` → Time only
- `Timestamp()` → Date and time with timezone
- `DateTime()` → Date and time without timezone

**Other Types:**
- `Boolean()` / `Bool()` → Boolean with flexible parsing
- `Binary(length)` → Fixed binary
- `VarBinary(max_length)` → Variable binary
- `Blob()` → Large binary object

## Pydantic Integration Features

### Pydantic Built-in Types Support

Mocksmith now supports automatic mock generation for Pydantic's built-in types:

```python
from pydantic import BaseModel, EmailStr, HttpUrl, IPvAnyAddress, conint, constr
from mocksmith import mockable

@mockable
class ServerConfig(BaseModel):
    hostname: constr(min_length=1, max_length=253)
    ip_address: IPvAnyAddress
    port: conint(ge=1, le=65535)
    api_url: HttpUrl
    admin_email: EmailStr

# Generate mock with Pydantic types
config = ServerConfig.mock()
print(config.ip_address)  # IPv4Address('192.168.1.100')
print(config.api_url)     # https://example.com
print(config.admin_email) # user@example.com
```

**Tip**: For types that have Pydantic equivalents, prefer using Pydantic's built-in types:
- Use `EmailStr` instead of `mocksmith.specialized.Email`
- Use `HttpUrl` or `AnyUrl` instead of `mocksmith.specialized.URL`
- Use `IPvAnyAddress`, `IPv4Address`, or `IPv6Address` for IP addresses

### Using Pydantic Types in Dataclasses

While Pydantic types can be used as type annotations in dataclasses, there are important limitations:

```python
from dataclasses import dataclass
from pydantic import EmailStr, HttpUrl, conint

@dataclass
class ServerConfig:
    hostname: str
    email: EmailStr  # Works as type hint only
    port: conint(ge=1, le=65535)  # No validation!

# This creates an instance WITHOUT validation
server = ServerConfig(
    hostname="api.example.com",
    email="invalid-email",  # Not validated!
    port=99999  # Out of range but accepted!
)
```

**Key Points**:
- Pydantic types in dataclasses serve as type hints only
- No automatic validation occurs
- Mock generation works but produces regular Python types (str, int, etc.)
- For validation, use Pydantic's BaseModel instead

See the Pydantic types limitations section in [`examples/dataclass_example.py`](examples/dataclass_example.py) for a complete comparison.

### Supported Pydantic Types for Mock Generation

The `@mockable` decorator supports automatic mock generation for the following Pydantic types:

#### Network Types
- `HttpUrl` - Generates valid HTTP/HTTPS URLs
- `AnyHttpUrl` - Generates any HTTP scheme URLs
- `EmailStr` - Generates valid email addresses
- `IPvAnyAddress` - Generates IPv4 or IPv6 addresses (80% IPv4, 20% IPv6)
- `IPvAnyInterface` - Generates IP addresses with CIDR notation
- `IPvAnyNetwork` - Generates IP network addresses

#### Numeric Types
- `PositiveInt` - Integers > 0
- `NegativeInt` - Integers < 0
- `NonNegativeInt` - Integers >= 0
- `NonPositiveInt` - Integers <= 0
- `PositiveFloat` - Floats > 0
- `NegativeFloat` - Floats < 0
- `NonNegativeFloat` - Floats >= 0
- `NonPositiveFloat` - Floats <= 0

#### String/Identifier Types
- `UUID1`, `UUID3`, `UUID4`, `UUID5` - Generates UUIDs (currently all as UUID4)
- `SecretStr` - Generates password-like strings
- `Json` - Generates valid JSON strings

#### Date/Time Types
- `FutureDate` - Generates dates in the future
- `PastDate` - Generates dates in the past
- `FutureDatetime` - Generates datetimes in the future
- `PastDatetime` - Generates datetimes in the past

#### Constraint Types
- `conint(ge=1, le=100)` - Integers with min/max constraints
- `confloat(ge=0.0, le=1.0)` - Floats with min/max constraints
- `constr(min_length=1, max_length=50)` - Strings with length constraints
- `constr(pattern=r"^[A-Z]{3}[0-9]{3}$")` - Strings matching regex patterns (limited support)
- `conlist(item_type, min_length=1, max_length=10)` - Lists with constraints

#### Example Usage

```python
from pydantic import BaseModel, EmailStr, HttpUrl, conint, PositiveInt
from mocksmith import mockable

@mockable
class UserProfile(BaseModel):
    user_id: PositiveInt
    email: EmailStr
    website: HttpUrl
    age: conint(ge=18, le=120)

# Generate mock data
user = UserProfile.mock()
print(user.email)     # "john.doe@example.com"
print(user.website)   # "https://example.com"
print(user.age)       # 42 (between 18-120)
```

**Note**: When using Pydantic types in dataclasses (not BaseModel), the types work as annotations only without validation. The mock generation still works but produces regular Python types.

### Handling Unsupported Types

When `@mockable` encounters an unsupported type, it attempts to handle it intelligently:

1. **Common types** (Path, Set, FrozenSet) - Now supported with appropriate mock values
2. **Auto-instantiable types** - Tries to create instances with `()`, `None`, `""`, or `0`
3. **Truly unsupported types** - Returns `None` with a warning to help identify gaps in type support

#### Newly Supported Types
```python
from dataclasses import dataclass
from pathlib import Path
from typing import Set, FrozenSet
from mocksmith import mockable

@mockable
@dataclass
class Config:
    config_path: Path        # ✓ Generates Path('/tmp/mock_file.txt')
    data_dir: Path          # ✓ Smart naming: Path('/tmp/mock_directory')
    tags: Set[str]          # ✓ Generates {'tag1', 'tag2', ...}
    frozen_tags: FrozenSet[int]  # ✓ Generates frozenset({1, 2, 3})

config = Config.mock()
# All fields get appropriate mock values!
```

#### Warning System
```python
class CustomType:
    def __init__(self, required_arg):
        # Cannot be auto-instantiated
        pass

@mockable
@dataclass
class Example:
    name: str                              # ✓ Supported
    custom_required: CustomType            # ⚠️ Warning issued, returns None
    custom_optional: Optional[CustomType] = None  # ⚠️ Warning issued (if attempted), returns None

# Console output:
# UserWarning: mocksmith: Unsupported type 'CustomType' for field 'custom_required'.
# Returning None. Consider making this field Optional or providing a mock override.
```

**Important Notes**:
- **All unsupported types trigger warnings** - This helps identify gaps in mocksmith's type support
- **Warnings help improve mocksmith** - If you encounter warnings, please file an issue on GitHub
- **Optional fields** - May show warnings ~80% of the time (when generation is attempted)
- **Override unsupported types** - Use `mock()` with overrides: `Example.mock(custom_required=CustomType('value'))`
- **Pydantic models** - Make unsupported fields `Optional` to avoid validation errors

### Optional Fields Pattern

Python's `Optional` type indicates fields that can be None:

```python
from typing import Optional
from pydantic import BaseModel
from mocksmith import Varchar, Integer, Text

class Example(BaseModel):
    # Required field
    required_field: Varchar(50)

    # Optional field (can be None)
    optional_field: Optional[Varchar(50)] = None

    # Field with default value
    status: Varchar(20) = "active"
```

**Best Practice**: For optional fields, use `Optional[Type]` with `= None`:
```python
bio: Optional[Text()] = None           # Clear and explicit
phone: Optional[Varchar(20)] = None    # Optional field with no default
```

### Automatic Type Conversion

```python
from pydantic import BaseModel
from mocksmith import Money, Boolean, Date, Timestamp

class Order(BaseModel):
    # String to Decimal conversion
    total: Money()

    # Flexible boolean parsing
    is_paid: Boolean()

    # String to date conversion
    order_date: Date()

    # String to datetime conversion
    created_at: Timestamp(with_timezone=False)

# All these string values are automatically converted
order = Order(
    total="99.99",           # → Decimal('99.99')
    is_paid="yes",           # → True
    order_date="2023-12-15", # → date(2023, 12, 15)
    created_at="2023-12-15T10:30:00"  # → datetime
)
```

### Field Validation with Pydantic

```python
from pydantic import BaseModel, field_validator
from mocksmith import Varchar, Integer, Money

class Product(BaseModel):
    name: Varchar(50)
    price: Money()
    quantity: Integer()

    @field_validator('price')
    def price_must_be_positive(cls, v):
        if v <= 0:
            raise ValueError('Price must be positive')
        return v

    @field_validator('quantity')
    def quantity_non_negative(cls, v):
        if v < 0:
            raise ValueError('Quantity cannot be negative')
        return v
```

### Model Configuration

```python
from pydantic import BaseModel, ConfigDict
from mocksmith import Varchar, Money, Timestamp

class StrictModel(BaseModel):
    model_config = ConfigDict(
        # Validate on assignment
        validate_assignment=True,
        # Use Enum values
        use_enum_values=True,
        # Custom JSON encoders
        json_encoders={
            Decimal: str,
            datetime: lambda v: v.isoformat()
        }
    )

    name: Varchar(100)
    price: Money()
    updated_at: Timestamp()
```

## Working Examples

For complete working examples, see the [`examples/`](examples/) directory:

- [`dataclass_example.py`](examples/dataclass_example.py) - Comprehensive dataclass examples including:
  - All data types (String, Numeric, Date/Time, Binary, Boolean)
  - Constrained numeric types (PositiveInteger, NonNegativeInteger, etc.)
  - Custom constraints (min_value, max_value, multiple_of)
  - TINYINT usage for small bounded values
  - REAL vs FLOAT distinction
  - SQL serialization
  - Validation and error handling

- [`pydantic_example.py`](examples/pydantic_example.py) - Comprehensive Pydantic examples including:
  - All data types with automatic validation
  - Field validators and computed properties
  - Constrained types with complex business logic
  - JSON serialization with custom encoders

- [`dataclass_mock_example.py`](examples/dataclass_mock_example.py) - Mock data generation examples:
  - Using `@mockable` decorator with dataclasses
  - Generating mock instances with `.mock()`
  - Override specific fields
  - Type-safe builder pattern
  - Specialized types (Email, CountryCode, etc.)

- [`pydantic_mock_example.py`](examples/pydantic_mock_example.py) - Mock data generation with Pydantic:
  - Using `@mockable` decorator with Pydantic models
  - Same mock API as dataclasses
  - Automatic validation of generated data
  - Specialized types with DBTypeValidator
  - Model configuration and validation on assignment
  - TINYINT and REAL type usage
  - Boolean type conversions

- [`constrained_types_example.py`](examples/constrained_types_example.py) - Constrained types with validation:
  - PositiveMoney, NonNegativeMoney, ConstrainedMoney usage
  - ConstrainedDecimal with precision and range constraints
  - ConstrainedFloat for percentages and probabilities
  - Mock generation respecting all constraints
  - Validation examples showing error handling
  - Builder pattern with constrained types

### Example: E-commerce Order System

```python
from dataclasses import dataclass
from typing import Optional
from datetime import datetime, date
from decimal import Decimal

from mocksmith import Varchar, Integer, Date, DecimalType, Text, BigInt, Timestamp
@dataclass
class Customer:
    customer_id: Integer()
    first_name: Varchar(50)
    last_name: Varchar(50)
    email: Varchar(100)
    phone: Optional[Varchar(20)]
    date_of_birth: Optional[Date()]

@dataclass
class Order:
    order_id: BigInt()
    customer_id: Integer()
    order_date: Timestamp(with_timezone=False)
    total_amount: DecimalType(12, 2)
    status: Varchar(20)
    notes: Optional[Text()]

# Create instances
customer = Customer(
    customer_id=1,
    first_name="Jane",
    last_name="Smith",
    email="jane.smith@email.com",
    phone="+1-555-0123",
    date_of_birth=date(1990, 5, 15)
)

order = Order(
    order_id=1001,
    customer_id=1,
    order_date=datetime(2023, 12, 15, 14, 30, 0),
    total_amount=Decimal("299.99"),
    status="pending",
    notes="Rush delivery requested"
)

# Convert to SQL-ready format
print(order.to_sql_dict())
```

For more complete examples including financial systems, authentication, and SQL testing integration,
see the [`examples/`](examples/) directory.

### Validation in Dataclasses

Plain dataclasses don't provide validation for mocksmith types. For validation, use Pydantic BaseModel:

```python
from pydantic import BaseModel
from mocksmith import SmallInt

class Config(BaseModel):  # Use BaseModel for validation
    hour: SmallInt(ge=0, le=23)

# Validation happens automatically
try:
    config = Config(hour=24)  # Raises ValidationError
except ValidationError as e:
    print(f"Validation error: {e}")

config = Config(hour=12)  # Works fine
```

## Advanced Features

### Custom Validation with Pydantic

```python
from pydantic import BaseModel

class CustomProduct(BaseModel):
    sku: Varchar(20)  # Required field
    name: Varchar(100)  # Required field
    description: Optional[Varchar(500)] = None  # Optional field
```

### Working with Different Types

```python
# Integer types with range validation
small_value = SMALLINT()
small_value.validate(32767)  # OK
# small_value.validate(32768)  # Raises ValueError - out of range

# Decimal with precision
money = DECIMAL(19, 4)
money.validate("12345.6789")  # OK
# money.validate("12345.67890")  # Raises ValueError - too many decimal places

# Time with precision
timestamp = TIMESTAMP(precision=0)  # No fractional seconds
timestamp.validate("2023-12-15T10:30:45.123456")  # Microseconds will be truncated

# Boolean accepts various formats
bool_type = BOOLEAN()
bool_type.deserialize("yes")    # True
bool_type.deserialize("1")      # True
bool_type.deserialize("false")  # False
bool_type.deserialize(0)        # False
```

### Constrained Numeric Types

**Important:** All numeric types in mocksmith strictly enforce SQL bounds and validate at instantiation time. For example, `TinyInt` enforces the TINYINT range of -128 to 127, preventing invalid data from being created or generated.

The library provides specialized numeric types with built-in constraints for common validation scenarios:

```python
from mocksmith import Integer, PositiveInteger, NonNegativeInteger

# Enhanced Integer functions - no constraints = standard type
id: Integer()                    # Standard 32-bit integer
quantity: Integer(ge=0)   # With constraints (same as NonNegativeInteger)
discount: Integer(ge=0, le=100)  # Percentage 0-100
price: Integer(gt=0)    # Same as PositiveInteger()

# Specialized constraint types
id: PositiveInteger()            # > 0
quantity: NonNegativeInteger()   # >= 0
```

For complete examples with both dataclasses and Pydantic, see:
- [`examples/dataclass_example.py`](examples/dataclass_example.py) - All constraint examples with dataclasses
- [`examples/pydantic_example.py`](examples/pydantic_example.py) - All constraint examples with Pydantic

**Available Constraint Options:**

```python
# Enhanced Integer functions - no constraints = standard type
Integer()                   # Standard 32-bit integer
Integer(ge=0)        # With constraints
Integer(gt=0)      # Shortcut for > 0
BigInt()                    # Standard 64-bit integer
BigInt(ge=0, le=1000000)  # With constraints
SmallInt()                  # Standard 16-bit integer
SmallInt(multiple_of=10)    # With constraints

# Specialized constraint types
PositiveInteger()           # > 0
NegativeInteger()           # < 0
NonNegativeInteger()        # >= 0
NonPositiveInteger()        # <= 0

# Full constraint options
Integer(
    gt=10,             # Value must be greater than 10
    ge=10,             # Value must be greater than or equal to 10
    lt=100,            # Value must be less than 100
    le=100,            # Value must be less than or equal to 100
    multiple_of=5,     # Must be divisible by this
)
```

### Constrained Money and Decimal Types

mocksmith provides constrained versions of Money and Decimal types using Pydantic's constraint system:

```python
from mocksmith import (
    ConstrainedMoney, PositiveMoney, NonNegativeMoney,
    ConstrainedDecimal, ConstrainedFloat
)

# Money with constraints
price: PositiveMoney()                          # > 0
balance: NonNegativeMoney()                     # >= 0
discount: ConstrainedMoney(ge=0, le=100)        # 0-100 range
payment: ConstrainedMoney(gt=0, le=10000)       # 0 < payment <= 10000

# Decimal with precision and constraints
weight: ConstrainedDecimal(10, 2, gt=0)         # Positive weight, max 10 digits, 2 decimal places
temperature: ConstrainedDecimal(5, 2, ge=-273.15)  # Above absolute zero

# Float with constraints
percentage: ConstrainedFloat(ge=0.0, le=1.0)    # 0-1 range
rate: ConstrainedFloat(gt=0, lt=0.5)            # 0 < rate < 0.5
```

These constrained types:
- Work seamlessly with Pydantic validation
- Generate appropriate mock data respecting constraints
- Provide the same clean API as other mocksmith types
- Fall back gracefully if Pydantic is not available

**Example Usage:**

```python
from pydantic import BaseModel
from mocksmith import mockable, PositiveMoney, NonNegativeMoney, ConstrainedMoney, ConstrainedFloat

@mockable
class Order(BaseModel):
    subtotal: PositiveMoney()                    # Must be > 0
    discount: ConstrainedMoney(ge=0, le=50)      # 0-50 range
    tax: NonNegativeMoney()                      # >= 0
    discount_rate: ConstrainedFloat(ge=0, le=0.3)  # 0-30%

# Validation works
order = Order(
    subtotal="100.00",    # ✓ Converts to Decimal
    discount="25.00",     # ✓ Within 0-50 range
    tax="8.50",          # ✓ Non-negative
    discount_rate=0.15   # ✓ 15% is within 0-30%
)

# Mock generation respects constraints
mock_order = Order.mock()
assert mock_order.subtotal > 0
assert 0 <= mock_order.discount <= 50
assert mock_order.tax >= 0
assert 0 <= mock_order.discount_rate <= 0.3
```

## Migration Guide from V2 to V3

### Breaking Changes

V3 introduces a critical breaking change to prevent confusion and subtle bugs:

1. **Direct class imports are removed** - `from mocksmith import VARCHAR` no longer works
2. **Only factory functions are available** - Use `Varchar()`, not `VARCHAR()`
3. **DBTypeValidator is removed** - Types work directly with Pydantic

### Why This Change?

In V2, importing and using `VARCHAR(30)` would create a type class. In V3's simplified pattern, this would create a string instance with value "30" - highly confusing! To prevent this dangerous ambiguity, direct class access has been removed entirely.

### Migration Steps

```python
# ❌ OLD V2 CODE (No longer works)
from mocksmith import VARCHAR, INTEGER, BOOLEAN
from mocksmith.pydantic_integration import DBTypeValidator
from typing import Annotated

class User(BaseModel):
    username: Annotated[str, DBTypeValidator(VARCHAR(30))]
    age: Annotated[int, DBTypeValidator(INTEGER())]
    active: Annotated[bool, DBTypeValidator(BOOLEAN())]

# ✅ NEW V3 CODE (Clean and simple)
from mocksmith import Varchar, Integer, Boolean

class User(BaseModel):
    username: Varchar(30)  # Direct usage!
    age: Integer()
    active: Boolean()
```

### Common Migration Patterns

| Old V2 Pattern | New V3 Pattern |
|----------------|----------------|
| `from mocksmith import VARCHAR` | `from mocksmith import Varchar` |
| `from mocksmith.types.string import VARCHAR` | Not available - use factory functions |
| `Annotated[str, DBTypeValidator(VARCHAR(30))]` | `Varchar(30)` |
| `VARCHAR(30)` (creates type) | `Varchar(30)` (creates type) |
| `INTEGER()` | `Integer()` |
| `DECIMAL(10, 2)` | `DecimalType(10, 2)` |
| `BOOLEAN()` | `Boolean()` |
| `DATE()` | `Date()` |
| `TIMESTAMP()` | `Timestamp()` |

### Benefits of V3

1. **Cleaner API** - No more `DBTypeValidator` or `Annotated` boilerplate
2. **Type safety** - Factory functions always return type classes
3. **No confusion** - Can't accidentally create instances when you mean types
4. **Better IDE support** - Direct type usage improves autocomplete
5. **Simpler codebase** - Less complexity, easier to maintain

## Development

1. Clone the repository:
```bash
git clone https://github.com/gurmeetsaran/mocksmith.git
cd mocksmith
```

2. Install Poetry (if not already installed):
```bash
curl -sSL https://install.python-poetry.org | python3 -
```

3. Install dependencies:
```bash
poetry install
```

4. Set up pre-commit hooks:
```bash
poetry run pre-commit install
```

5. Run tests:
```bash
make test
```

### Development Commands

- `make lint` - Run linting (ruff + pyright)
- `make format` - Format code (black + isort + ruff fix)
- `make test` - Run tests
- `make test-cov` - Run tests with coverage
- `make check-all` - Run all checks (lint + format check + tests)
- `make check-consistency` - Verify pre-commit, Makefile, and CI are in sync

### Ensuring Consistency

To ensure your development environment matches CI/CD:

```bash
# Check that pre-commit hooks match Makefile and GitHub Actions
make check-consistency
```

This will verify that all tools (black, isort, ruff, pyright) are configured consistently across:
- Pre-commit hooks (`.pre-commit-config.yaml`)
- Makefile commands
- GitHub Actions workflows

## License

MIT


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "mocksmith",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "mock, validation, dataclass, pydantic, testing, faker, types, sql",
    "author": "Gurmeet Saran",
    "author_email": "gurmeetx@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/c2/72/18d9c52e62785e724a40250e6ee2883a5fefc6a517cb483415c3b3451b6b/mocksmith-6.0.1.tar.gz",
    "platform": null,
    "description": "# mocksmith\n\n[![Unit Tests](https://github.com/gurmeetsaran/mocksmith/actions/workflows/ci.yml/badge.svg)](https://github.com/gurmeetsaran/mocksmith/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/gurmeetsaran/mocksmith/branch/main/graph/badge.svg)](https://codecov.io/gh/gurmeetsaran/mocksmith)\n[![PyPI version](https://badge.fury.io/py/mocksmith.svg)](https://badge.fury.io/py/mocksmith)\n[![Python Versions](https://img.shields.io/pypi/pyversions/mocksmith.svg)](https://pypi.org/project/mocksmith/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nType-safe data validation with automatic mock generation for Python dataclasses and Pydantic models. Build robust data models with database-aware validation and generate realistic test data with a single decorator.\n\n## Features\n\n- **Type-safe database columns**: Define database columns with proper validation\n- **SQL-compliant validation**: All numeric types strictly enforce SQL bounds (TINYINT: -128 to 127, etc.)\n- **Instantiation validation**: Types validate at creation time, preventing invalid data from being created\n- **Serialization/Deserialization**: Automatic conversion between Python and SQL types\n- **Dataclass Integration**: Full support for Python dataclasses with validation\n- **Pydantic Integration**: First-class Pydantic support with automatic validation\n- **Clean API**: Simple, intuitive interface for both Pydantic AND dataclasses - just `name: Varchar(50)`\n- **Comprehensive Types**: STRING (VARCHAR, CHAR, TEXT), NUMERIC (INTEGER, DECIMAL, FLOAT), TEMPORAL (DATE, TIME, TIMESTAMP), and more\n- **Mock Data Generation**: Built-in mock/fake data generation that respects all SQL bounds and constraints\n- **Constrained Types**: Support for min/max constraints on numeric types - `price: PositiveMoney()`, `age: Integer(ge=0, le=120)`\n\n## Why mocksmith?\n\n### Before (Traditional Approach)\n```python\nfrom typing import Annotated\nfrom pydantic import BaseModel, Field, validator\nfrom decimal import Decimal\n\nclass Product(BaseModel):\n    name: Annotated[str, Field(max_length=100)]\n    price: Annotated[Decimal, Field(decimal_places=2, max_digits=10)]\n    in_stock: bool = True\n\n    @validator('price')\n    def validate_price(cls, v):\n        if v < 0:\n            raise ValueError('Price must be non-negative')\n        return v\n```\n\n### After (With mocksmith)\n```python\nfrom pydantic import BaseModel\nfrom mocksmith import Varchar, Money, Boolean\n\nclass Product(BaseModel):\n    name: Varchar(100)         # Enforces VARCHAR(100) constraint\n    price: Money()             # Decimal(19,4) - use PositiveMoney() for price > 0\n    in_stock: Boolean() = True # Flexible boolean parsing\n```\n\n\u2728 **Benefits:**\n- Same clean syntax for both Pydantic and dataclasses\n- Automatic SQL constraint validation\n- Type conversion (string \"99.99\" \u2192 Decimal)\n- Better IDE support and type hints\n- Write once, use with either framework\n\n## Installation\n\n```bash\n# Standard installation (includes mock generation)\npip install mocksmith\n\n# With Pydantic validation support (recommended)\npip install \"mocksmith[pydantic]\"\n```\n\nThe standard installation includes Faker for mock data generation and custom validation logic. Adding Pydantic provides better performance and integration with Pydantic types.\n\n## Import Structure\n\nThe library organizes types into two categories:\n\n### Core Database Types (V3 Pattern)\nCore database types are available through factory functions from the main package:\n\n```python\nfrom mocksmith import (\n    # String types - Factory functions only\n    Varchar, Char, Text,\n    # Numeric types - Factory functions only\n    Integer, DecimalType, Float,\n    BigInt, SmallInt, TinyInt,\n    Double, Real, Numeric,\n    # Temporal types - Factory functions only\n    Date, Time, DateTime, Timestamp,\n    # Other types - Factory functions only\n    Boolean, Binary, VarBinary, Blob,\n    # Constrained types\n    PositiveInteger, NonNegativeInteger, NegativeInteger, NonPositiveInteger,\n    Money, PositiveMoney, NonNegativeMoney, ConstrainedMoney,\n    ConstrainedDecimal, ConstrainedFloat\n)\n```\n\n**\u26a0\ufe0f Breaking Change in V3:** Direct class imports (VARCHAR, INTEGER, etc.) have been removed to prevent confusion. Use factory functions (Varchar, Integer, etc.) exclusively.\n\n### Specialized Types\nSpecialized types for common use cases are available from the `specialized` submodule:\n\n```python\nfrom mocksmith.specialized import (\n    # Geographic types\n    CountryCode,  # ISO 3166-1 alpha-2 country codes\n    City,         # City names\n    State,        # State/province names\n    ZipCode,      # Postal codes\n\n    # Contact types\n    PhoneNumber,  # Phone numbers\n)\n```\n\n**Note**: For email and web types, use Pydantic's built-in types instead:\n- Email \u2192 Use `pydantic.EmailStr`\n- URL \u2192 Use `pydantic.HttpUrl` or `pydantic.AnyUrl`\n- IP addresses \u2192 Use `pydantic.IPvAnyAddress`, `pydantic.IPv4Address`, or `pydantic.IPv6Address`\n\nThis separation keeps the main namespace clean and makes it clear which types are fundamental database types versus application-specific types.\n\n## Quick Start\n\n### Clean V3 Interface (Works with both Pydantic and Dataclasses!) \u2728\n\n```python\nfrom pydantic import BaseModel\nfrom mocksmith import Varchar, Integer, Boolean, Money\n\nclass User(BaseModel):\n    id: Integer()\n    username: Varchar(50)  # Creates a type class with length 50\n    email: Varchar(255)\n    is_active: Boolean() = True\n    balance: Money() = \"0.00\"\n\n# Automatic validation and type conversion\nuser = User(\n    id=1,\n    username=\"john_doe\",\n    email=\"john@example.com\",\n    is_active=\"yes\",      # Converts to True\n    balance=\"1234.56\"     # Converts to Decimal('1234.56')\n)\n```\n\nThe same syntax works with dataclasses! See full examples:\n- [`examples/pydantic_example.py`](examples/pydantic_example.py) - Comprehensive Pydantic examples with all features\n- [`examples/dataclass_example.py`](examples/dataclass_example.py) - Comprehensive dataclass examples with all features\n- [`examples/pydantic_mock_example.py`](examples/pydantic_mock_example.py) - Mock data generation with Pydantic models\n- [`examples/dataclass_mock_example.py`](examples/dataclass_mock_example.py) - Mock data generation with dataclasses\n- [`examples/constrained_types_example.py`](examples/constrained_types_example.py) - Constrained types with validation and mock generation\n\n### Common Use Cases\n\n**E-commerce Product Model:**\n\n```python\nfrom pydantic import BaseModel\nfrom mocksmith import Varchar, Text, Money, Boolean, Timestamp\n\nclass Product(BaseModel):\n    sku: Varchar(20)\n    name: Varchar(100)\n    description: Text()\n    price: Money()\n    in_stock: Boolean() = True\n    created_at: Timestamp()\n```\n\n**User Account with Constraints:**\n\n```python\nfrom mocksmith import Integer, PositiveInteger, NonNegativeInteger\n\nclass UserAccount(BaseModel):\n    user_id: PositiveInteger()\n    age: Integer(ge=13, le=120)\n    balance_cents: NonNegativeInteger()\n```\n\nSee complete working examples:\n- [`examples/`](examples/) - All example files with detailed documentation\n- [`examples/pydantic_example.py`](examples/pydantic_example.py) - All features including constraints\n- [`examples/dataclass_example.py`](examples/dataclass_example.py) - All features including constraints\n\n## Mock Data Generation\n\nGenerate realistic test data automatically with the `@mockable` decorator:\n\n```python\nfrom dataclasses import dataclass\nfrom mocksmith import Varchar, Integer, Date, mockable\nfrom mocksmith.specialized import PhoneNumber, CountryCode\n\n@mockable\n@dataclass\nclass Address:\n    street: Varchar(100)\n    city: Varchar(50)\n    zip_code: Integer(ge=10000, le=99999)\n\n@mockable\n@dataclass\nclass User:\n    id: Integer()\n    username: Varchar(50)\n    phone: PhoneNumber()\n    country: CountryCode()\n    birth_date: Date()\n    address: Address  # Nested dataclass!\n\n# Generate mock instances\nuser = User.mock()\nprint(user.username)  # \"Christina Wells\"\nprint(user.phone)     # \"(555) 123-4567\"\nprint(user.country)   # \"US\"\nprint(user.address.city)  # \"New York\"  # Nested fields are mocked too!\n\n# With overrides\nuser = User.mock(username=\"test_user\", country=\"GB\")\n\n# Using builder pattern\nuser = (User.mock_builder()\n        .with_username(\"john_doe\")\n        .with_country(\"CA\")\n        .build())\n```\n\nThe same `@mockable` decorator works with Pydantic models! Mock generation:\n- Respects all field constraints (length, format, etc.)\n- Generates appropriate mock data for each type\n- Supports specialized types with realistic data\n- Works with both dataclasses and Pydantic models\n- Automatically handles Python Enum types with random value selection\n- Supports nested dataclasses - automatically generates mock data for nested structures\n\nSee mock examples:\n- [`examples/dataclass_mock_example.py`](examples/dataclass_mock_example.py) - Complete mock examples with dataclasses including enum support\n- [`examples/pydantic_mock_example.py`](examples/pydantic_mock_example.py) - Complete mock examples with Pydantic including enum support and built-in types\n\n## V3 Type Usage Patterns\n\n**Important:** MockSmith V3 uses factory functions exclusively. The old pattern of importing classes directly (VARCHAR, INTEGER, etc.) is no longer supported.\n\n### Correct V3 Pattern\n\n```python\nfrom mocksmith import Varchar, Integer, Boolean  # Factory functions\n\n# Factory functions create type classes for use in annotations\nUsernameType = Varchar(30, min_length=3)  # Returns a type class\n\nclass User(BaseModel):\n    username: UsernameType  # Use the type class\n    # Or inline:\n    email: Varchar(100, to_lower=True)  # Factory function inline\n    age: Integer(gt=0, le=120)\n    active: Boolean()\n```\n\n### What Changed from V2\n\n```python\n# \u274c OLD PATTERN (NO LONGER WORKS - REMOVED IN V3)\nfrom mocksmith import VARCHAR  # This import fails now\nvarchar_type = VARCHAR(30)  # Would create instance \"30\" - WRONG!\n\n# \u2705 NEW V3 PATTERN (THE ONLY WAY)\nfrom mocksmith import Varchar  # Factory function\nUsernameType = Varchar(30)  # Creates type class - CORRECT!\n```\n\n### With Pydantic (Full Validation)\n\n```python\nfrom typing import Optional\nfrom pydantic import BaseModel\nfrom mocksmith import Integer, Varchar, Money, Boolean, PositiveInteger, NonNegativeInteger\nfrom decimal import Decimal\n\n# Pattern 1: Direct usage (Recommended - cleanest syntax)\nclass Product(BaseModel):\n    id: Integer()\n    name: Varchar(100)\n    price: Money()\n    in_stock: Boolean() = True\n\n# Pattern 2: With constraints\nclass ConstrainedModel(BaseModel):\n    age: Integer(ge=0, le=120)  # Age between 0-120\n    quantity: Integer(gt=0)      # Positive quantity\n    discount: Integer(ge=0, le=100, multiple_of=5)  # 0-100%, multiples of 5\n\n# Pattern 3: Factory functions with constraints\nclass ConstrainedProduct(BaseModel):\n    sku: Varchar(20, to_upper=True)  # Auto uppercase\n    name: Varchar(100, min_length=3)\n    price: DecimalType(10, 2, gt=0)  # precision=10, scale=2, >0\n\n# Pattern 4: Constrained types (common patterns)\nclass UserAccount(BaseModel):\n    user_id: PositiveInteger()       # > 0\n    balance: NonNegativeInteger()    # >= 0\n\n# Pattern 5: Optional fields\nclass OptionalModel(BaseModel):\n    required_field: Varchar(50)\n    optional_field: Optional[Varchar(50)] = None  # Can be None\n    with_default: Boolean() = True                # Has default value\n\n# All patterns can be mixed in the same model!\n```\n\n### With Dataclasses (Type Hints Only, No Validation)\n\n```python\nfrom dataclasses import dataclass\nfrom typing import Optional\nfrom decimal import Decimal\nfrom mocksmith import Integer, Varchar, Money, Text\n\n@dataclass\nclass Product:\n    # Same syntax works, but NO validation occurs!\n    id: Integer()\n    name: Varchar(100)\n    price: Money() = Decimal(\"0.00\")\n    optional_field: Optional[Text()] = None\n\n# WARNING: Dataclasses don't validate!\nproduct = Product(\n    id=999999999999,    # Accepts invalid values!\n    name=\"x\" * 1000,    # No length check!\n    price=\"invalid\"     # No type check!\n)\n```\n\n### Important V3 Notes\n\n\u2705 **DO USE (V3 Pattern):**\n- `field: Varchar(50)` - Factory functions for type creation\n- `field: Integer(gt=0)` - Factory functions with constraints\n- `field: Optional[Varchar(50)] = None` - For nullable fields\n- Pydantic `BaseModel` when you need validation\n- Constrained types like `PositiveInteger()` for common patterns\n\n\u274c **DON'T USE (Removed in V3):**\n- `from mocksmith import VARCHAR` - Direct class imports removed\n- `VARCHAR(30)` - Would create instance \"30\", not a type!\n- Plain dataclasses if you need validation (use Pydantic instead)\n\n### Type Validation Features\n\nAll numeric types enforce SQL bounds and validate at instantiation:\n- **TinyInt**: -128 to 127 (8-bit)\n- **SmallInt**: -32,768 to 32,767 (16-bit)\n- **Integer**: -2,147,483,648 to 2,147,483,647 (32-bit)\n- **BigInt**: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 (64-bit)\n\nOptional fields properly handle None values:\n```python\nclass User(BaseModel):\n    name: Varchar(50)                        # Required\n    nickname: Optional[Varchar(30)] = None   # Optional, can be None\n\nuser = User(name=\"John\", nickname=None)  # \u2713 Valid\n```\n\n### Literal Type Support\n\nMockSmith types work seamlessly with Python's `Literal` type for strict value constraints:\n\n```python\nfrom typing import Literal\nfrom pydantic import BaseModel\nfrom mocksmith import Varchar, Integer, mockable\n\n@mockable\nclass ServerConfig(BaseModel):\n    environment: Literal[\"dev\", \"staging\", \"prod\"]\n    status_code: Literal[200, 301, 404, 500]\n    port: Integer(ge=1024, le=65535)\n    log_level: Literal[0, 1, 2, 3, 4, 5]  # 0=OFF, 5=TRACE\n\n# Validation enforces Literal constraints\nconfig = ServerConfig(\n    environment=\"prod\",      # \u2713 Valid\n    status_code=200,        # \u2713 Valid\n    port=8080,              # \u2713 Valid (within range)\n    log_level=2             # \u2713 Valid\n)\n\n# Mock generation respects Literal values\nmock = ServerConfig.mock()\n# mock.environment will be one of: \"dev\", \"staging\", \"prod\"\n# mock.status_code will be one of: 200, 301, 404, 500\n```\n\n## Clean Annotation Interface\n\nThe library provides a clean, Pythonic interface for defining database types:\n\n### Available Clean Types:\n\n**String Types:**\n- `Varchar(length)` \u2192 Variable-length string\n- `Char(length)` \u2192 Fixed-length string\n- `Text()` \u2192 Large text field\n- `String` \u2192 Alias for Varchar\n\n**Numeric Types:**\n- `Integer()` \u2192 32-bit integer (-2,147,483,648 to 2,147,483,647)\n- `BigInt()` \u2192 64-bit integer (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)\n- `SmallInt()` \u2192 16-bit integer (-32,768 to 32,767)\n- `TinyInt()` \u2192 8-bit integer (-128 to 127)\n- `DecimalType(precision, scale)` \u2192 Fixed-point decimal\n- `Numeric(precision, scale)` \u2192 Alias for DecimalType\n- `Money()` \u2192 Alias for Decimal(19, 4)\n- `Float()` \u2192 Floating point (generates FLOAT SQL type)\n- `Real()` \u2192 Floating point (generates REAL SQL type, typically single precision in SQL)\n- `Double()` \u2192 Double precision\n\nAll numeric types:\n- Enforce SQL bounds at instantiation (e.g., `TinyInt(200)` raises ValueError)\n- Generate mock data within valid ranges (e.g., `TinyInt(gt=5)` generates 6-127, not > 127)\n- Support constraints (gt, ge, lt, le, multiple_of)\n\n**Constrained Numeric Types:**\n- `PositiveInteger()` \u2192 Integer > 0\n- `NegativeInteger()` \u2192 Integer < 0\n- `NonNegativeInteger()` \u2192 Integer \u2265 0\n- `NonPositiveInteger()` \u2192 Integer \u2264 0\n- `ConstrainedInteger(ge=x, le=y, multiple_of=z)` \u2192 Custom constraints\n- `ConstrainedBigInt(...)` \u2192 Constrained 64-bit integer\n- `ConstrainedSmallInt(...)` \u2192 Constrained 16-bit integer\n- `ConstrainedTinyInt(...)` \u2192 Constrained 8-bit integer\n\n**Temporal Types:**\n- `Date()` \u2192 Date only\n- `Time()` \u2192 Time only\n- `Timestamp()` \u2192 Date and time with timezone\n- `DateTime()` \u2192 Date and time without timezone\n\n**Other Types:**\n- `Boolean()` / `Bool()` \u2192 Boolean with flexible parsing\n- `Binary(length)` \u2192 Fixed binary\n- `VarBinary(max_length)` \u2192 Variable binary\n- `Blob()` \u2192 Large binary object\n\n## Pydantic Integration Features\n\n### Pydantic Built-in Types Support\n\nMocksmith now supports automatic mock generation for Pydantic's built-in types:\n\n```python\nfrom pydantic import BaseModel, EmailStr, HttpUrl, IPvAnyAddress, conint, constr\nfrom mocksmith import mockable\n\n@mockable\nclass ServerConfig(BaseModel):\n    hostname: constr(min_length=1, max_length=253)\n    ip_address: IPvAnyAddress\n    port: conint(ge=1, le=65535)\n    api_url: HttpUrl\n    admin_email: EmailStr\n\n# Generate mock with Pydantic types\nconfig = ServerConfig.mock()\nprint(config.ip_address)  # IPv4Address('192.168.1.100')\nprint(config.api_url)     # https://example.com\nprint(config.admin_email) # user@example.com\n```\n\n**Tip**: For types that have Pydantic equivalents, prefer using Pydantic's built-in types:\n- Use `EmailStr` instead of `mocksmith.specialized.Email`\n- Use `HttpUrl` or `AnyUrl` instead of `mocksmith.specialized.URL`\n- Use `IPvAnyAddress`, `IPv4Address`, or `IPv6Address` for IP addresses\n\n### Using Pydantic Types in Dataclasses\n\nWhile Pydantic types can be used as type annotations in dataclasses, there are important limitations:\n\n```python\nfrom dataclasses import dataclass\nfrom pydantic import EmailStr, HttpUrl, conint\n\n@dataclass\nclass ServerConfig:\n    hostname: str\n    email: EmailStr  # Works as type hint only\n    port: conint(ge=1, le=65535)  # No validation!\n\n# This creates an instance WITHOUT validation\nserver = ServerConfig(\n    hostname=\"api.example.com\",\n    email=\"invalid-email\",  # Not validated!\n    port=99999  # Out of range but accepted!\n)\n```\n\n**Key Points**:\n- Pydantic types in dataclasses serve as type hints only\n- No automatic validation occurs\n- Mock generation works but produces regular Python types (str, int, etc.)\n- For validation, use Pydantic's BaseModel instead\n\nSee the Pydantic types limitations section in [`examples/dataclass_example.py`](examples/dataclass_example.py) for a complete comparison.\n\n### Supported Pydantic Types for Mock Generation\n\nThe `@mockable` decorator supports automatic mock generation for the following Pydantic types:\n\n#### Network Types\n- `HttpUrl` - Generates valid HTTP/HTTPS URLs\n- `AnyHttpUrl` - Generates any HTTP scheme URLs\n- `EmailStr` - Generates valid email addresses\n- `IPvAnyAddress` - Generates IPv4 or IPv6 addresses (80% IPv4, 20% IPv6)\n- `IPvAnyInterface` - Generates IP addresses with CIDR notation\n- `IPvAnyNetwork` - Generates IP network addresses\n\n#### Numeric Types\n- `PositiveInt` - Integers > 0\n- `NegativeInt` - Integers < 0\n- `NonNegativeInt` - Integers >= 0\n- `NonPositiveInt` - Integers <= 0\n- `PositiveFloat` - Floats > 0\n- `NegativeFloat` - Floats < 0\n- `NonNegativeFloat` - Floats >= 0\n- `NonPositiveFloat` - Floats <= 0\n\n#### String/Identifier Types\n- `UUID1`, `UUID3`, `UUID4`, `UUID5` - Generates UUIDs (currently all as UUID4)\n- `SecretStr` - Generates password-like strings\n- `Json` - Generates valid JSON strings\n\n#### Date/Time Types\n- `FutureDate` - Generates dates in the future\n- `PastDate` - Generates dates in the past\n- `FutureDatetime` - Generates datetimes in the future\n- `PastDatetime` - Generates datetimes in the past\n\n#### Constraint Types\n- `conint(ge=1, le=100)` - Integers with min/max constraints\n- `confloat(ge=0.0, le=1.0)` - Floats with min/max constraints\n- `constr(min_length=1, max_length=50)` - Strings with length constraints\n- `constr(pattern=r\"^[A-Z]{3}[0-9]{3}$\")` - Strings matching regex patterns (limited support)\n- `conlist(item_type, min_length=1, max_length=10)` - Lists with constraints\n\n#### Example Usage\n\n```python\nfrom pydantic import BaseModel, EmailStr, HttpUrl, conint, PositiveInt\nfrom mocksmith import mockable\n\n@mockable\nclass UserProfile(BaseModel):\n    user_id: PositiveInt\n    email: EmailStr\n    website: HttpUrl\n    age: conint(ge=18, le=120)\n\n# Generate mock data\nuser = UserProfile.mock()\nprint(user.email)     # \"john.doe@example.com\"\nprint(user.website)   # \"https://example.com\"\nprint(user.age)       # 42 (between 18-120)\n```\n\n**Note**: When using Pydantic types in dataclasses (not BaseModel), the types work as annotations only without validation. The mock generation still works but produces regular Python types.\n\n### Handling Unsupported Types\n\nWhen `@mockable` encounters an unsupported type, it attempts to handle it intelligently:\n\n1. **Common types** (Path, Set, FrozenSet) - Now supported with appropriate mock values\n2. **Auto-instantiable types** - Tries to create instances with `()`, `None`, `\"\"`, or `0`\n3. **Truly unsupported types** - Returns `None` with a warning to help identify gaps in type support\n\n#### Newly Supported Types\n```python\nfrom dataclasses import dataclass\nfrom pathlib import Path\nfrom typing import Set, FrozenSet\nfrom mocksmith import mockable\n\n@mockable\n@dataclass\nclass Config:\n    config_path: Path        # \u2713 Generates Path('/tmp/mock_file.txt')\n    data_dir: Path          # \u2713 Smart naming: Path('/tmp/mock_directory')\n    tags: Set[str]          # \u2713 Generates {'tag1', 'tag2', ...}\n    frozen_tags: FrozenSet[int]  # \u2713 Generates frozenset({1, 2, 3})\n\nconfig = Config.mock()\n# All fields get appropriate mock values!\n```\n\n#### Warning System\n```python\nclass CustomType:\n    def __init__(self, required_arg):\n        # Cannot be auto-instantiated\n        pass\n\n@mockable\n@dataclass\nclass Example:\n    name: str                              # \u2713 Supported\n    custom_required: CustomType            # \u26a0\ufe0f Warning issued, returns None\n    custom_optional: Optional[CustomType] = None  # \u26a0\ufe0f Warning issued (if attempted), returns None\n\n# Console output:\n# UserWarning: mocksmith: Unsupported type 'CustomType' for field 'custom_required'.\n# Returning None. Consider making this field Optional or providing a mock override.\n```\n\n**Important Notes**:\n- **All unsupported types trigger warnings** - This helps identify gaps in mocksmith's type support\n- **Warnings help improve mocksmith** - If you encounter warnings, please file an issue on GitHub\n- **Optional fields** - May show warnings ~80% of the time (when generation is attempted)\n- **Override unsupported types** - Use `mock()` with overrides: `Example.mock(custom_required=CustomType('value'))`\n- **Pydantic models** - Make unsupported fields `Optional` to avoid validation errors\n\n### Optional Fields Pattern\n\nPython's `Optional` type indicates fields that can be None:\n\n```python\nfrom typing import Optional\nfrom pydantic import BaseModel\nfrom mocksmith import Varchar, Integer, Text\n\nclass Example(BaseModel):\n    # Required field\n    required_field: Varchar(50)\n\n    # Optional field (can be None)\n    optional_field: Optional[Varchar(50)] = None\n\n    # Field with default value\n    status: Varchar(20) = \"active\"\n```\n\n**Best Practice**: For optional fields, use `Optional[Type]` with `= None`:\n```python\nbio: Optional[Text()] = None           # Clear and explicit\nphone: Optional[Varchar(20)] = None    # Optional field with no default\n```\n\n### Automatic Type Conversion\n\n```python\nfrom pydantic import BaseModel\nfrom mocksmith import Money, Boolean, Date, Timestamp\n\nclass Order(BaseModel):\n    # String to Decimal conversion\n    total: Money()\n\n    # Flexible boolean parsing\n    is_paid: Boolean()\n\n    # String to date conversion\n    order_date: Date()\n\n    # String to datetime conversion\n    created_at: Timestamp(with_timezone=False)\n\n# All these string values are automatically converted\norder = Order(\n    total=\"99.99\",           # \u2192 Decimal('99.99')\n    is_paid=\"yes\",           # \u2192 True\n    order_date=\"2023-12-15\", # \u2192 date(2023, 12, 15)\n    created_at=\"2023-12-15T10:30:00\"  # \u2192 datetime\n)\n```\n\n### Field Validation with Pydantic\n\n```python\nfrom pydantic import BaseModel, field_validator\nfrom mocksmith import Varchar, Integer, Money\n\nclass Product(BaseModel):\n    name: Varchar(50)\n    price: Money()\n    quantity: Integer()\n\n    @field_validator('price')\n    def price_must_be_positive(cls, v):\n        if v <= 0:\n            raise ValueError('Price must be positive')\n        return v\n\n    @field_validator('quantity')\n    def quantity_non_negative(cls, v):\n        if v < 0:\n            raise ValueError('Quantity cannot be negative')\n        return v\n```\n\n### Model Configuration\n\n```python\nfrom pydantic import BaseModel, ConfigDict\nfrom mocksmith import Varchar, Money, Timestamp\n\nclass StrictModel(BaseModel):\n    model_config = ConfigDict(\n        # Validate on assignment\n        validate_assignment=True,\n        # Use Enum values\n        use_enum_values=True,\n        # Custom JSON encoders\n        json_encoders={\n            Decimal: str,\n            datetime: lambda v: v.isoformat()\n        }\n    )\n\n    name: Varchar(100)\n    price: Money()\n    updated_at: Timestamp()\n```\n\n## Working Examples\n\nFor complete working examples, see the [`examples/`](examples/) directory:\n\n- [`dataclass_example.py`](examples/dataclass_example.py) - Comprehensive dataclass examples including:\n  - All data types (String, Numeric, Date/Time, Binary, Boolean)\n  - Constrained numeric types (PositiveInteger, NonNegativeInteger, etc.)\n  - Custom constraints (min_value, max_value, multiple_of)\n  - TINYINT usage for small bounded values\n  - REAL vs FLOAT distinction\n  - SQL serialization\n  - Validation and error handling\n\n- [`pydantic_example.py`](examples/pydantic_example.py) - Comprehensive Pydantic examples including:\n  - All data types with automatic validation\n  - Field validators and computed properties\n  - Constrained types with complex business logic\n  - JSON serialization with custom encoders\n\n- [`dataclass_mock_example.py`](examples/dataclass_mock_example.py) - Mock data generation examples:\n  - Using `@mockable` decorator with dataclasses\n  - Generating mock instances with `.mock()`\n  - Override specific fields\n  - Type-safe builder pattern\n  - Specialized types (Email, CountryCode, etc.)\n\n- [`pydantic_mock_example.py`](examples/pydantic_mock_example.py) - Mock data generation with Pydantic:\n  - Using `@mockable` decorator with Pydantic models\n  - Same mock API as dataclasses\n  - Automatic validation of generated data\n  - Specialized types with DBTypeValidator\n  - Model configuration and validation on assignment\n  - TINYINT and REAL type usage\n  - Boolean type conversions\n\n- [`constrained_types_example.py`](examples/constrained_types_example.py) - Constrained types with validation:\n  - PositiveMoney, NonNegativeMoney, ConstrainedMoney usage\n  - ConstrainedDecimal with precision and range constraints\n  - ConstrainedFloat for percentages and probabilities\n  - Mock generation respecting all constraints\n  - Validation examples showing error handling\n  - Builder pattern with constrained types\n\n### Example: E-commerce Order System\n\n```python\nfrom dataclasses import dataclass\nfrom typing import Optional\nfrom datetime import datetime, date\nfrom decimal import Decimal\n\nfrom mocksmith import Varchar, Integer, Date, DecimalType, Text, BigInt, Timestamp\n@dataclass\nclass Customer:\n    customer_id: Integer()\n    first_name: Varchar(50)\n    last_name: Varchar(50)\n    email: Varchar(100)\n    phone: Optional[Varchar(20)]\n    date_of_birth: Optional[Date()]\n\n@dataclass\nclass Order:\n    order_id: BigInt()\n    customer_id: Integer()\n    order_date: Timestamp(with_timezone=False)\n    total_amount: DecimalType(12, 2)\n    status: Varchar(20)\n    notes: Optional[Text()]\n\n# Create instances\ncustomer = Customer(\n    customer_id=1,\n    first_name=\"Jane\",\n    last_name=\"Smith\",\n    email=\"jane.smith@email.com\",\n    phone=\"+1-555-0123\",\n    date_of_birth=date(1990, 5, 15)\n)\n\norder = Order(\n    order_id=1001,\n    customer_id=1,\n    order_date=datetime(2023, 12, 15, 14, 30, 0),\n    total_amount=Decimal(\"299.99\"),\n    status=\"pending\",\n    notes=\"Rush delivery requested\"\n)\n\n# Convert to SQL-ready format\nprint(order.to_sql_dict())\n```\n\nFor more complete examples including financial systems, authentication, and SQL testing integration,\nsee the [`examples/`](examples/) directory.\n\n### Validation in Dataclasses\n\nPlain dataclasses don't provide validation for mocksmith types. For validation, use Pydantic BaseModel:\n\n```python\nfrom pydantic import BaseModel\nfrom mocksmith import SmallInt\n\nclass Config(BaseModel):  # Use BaseModel for validation\n    hour: SmallInt(ge=0, le=23)\n\n# Validation happens automatically\ntry:\n    config = Config(hour=24)  # Raises ValidationError\nexcept ValidationError as e:\n    print(f\"Validation error: {e}\")\n\nconfig = Config(hour=12)  # Works fine\n```\n\n## Advanced Features\n\n### Custom Validation with Pydantic\n\n```python\nfrom pydantic import BaseModel\n\nclass CustomProduct(BaseModel):\n    sku: Varchar(20)  # Required field\n    name: Varchar(100)  # Required field\n    description: Optional[Varchar(500)] = None  # Optional field\n```\n\n### Working with Different Types\n\n```python\n# Integer types with range validation\nsmall_value = SMALLINT()\nsmall_value.validate(32767)  # OK\n# small_value.validate(32768)  # Raises ValueError - out of range\n\n# Decimal with precision\nmoney = DECIMAL(19, 4)\nmoney.validate(\"12345.6789\")  # OK\n# money.validate(\"12345.67890\")  # Raises ValueError - too many decimal places\n\n# Time with precision\ntimestamp = TIMESTAMP(precision=0)  # No fractional seconds\ntimestamp.validate(\"2023-12-15T10:30:45.123456\")  # Microseconds will be truncated\n\n# Boolean accepts various formats\nbool_type = BOOLEAN()\nbool_type.deserialize(\"yes\")    # True\nbool_type.deserialize(\"1\")      # True\nbool_type.deserialize(\"false\")  # False\nbool_type.deserialize(0)        # False\n```\n\n### Constrained Numeric Types\n\n**Important:** All numeric types in mocksmith strictly enforce SQL bounds and validate at instantiation time. For example, `TinyInt` enforces the TINYINT range of -128 to 127, preventing invalid data from being created or generated.\n\nThe library provides specialized numeric types with built-in constraints for common validation scenarios:\n\n```python\nfrom mocksmith import Integer, PositiveInteger, NonNegativeInteger\n\n# Enhanced Integer functions - no constraints = standard type\nid: Integer()                    # Standard 32-bit integer\nquantity: Integer(ge=0)   # With constraints (same as NonNegativeInteger)\ndiscount: Integer(ge=0, le=100)  # Percentage 0-100\nprice: Integer(gt=0)    # Same as PositiveInteger()\n\n# Specialized constraint types\nid: PositiveInteger()            # > 0\nquantity: NonNegativeInteger()   # >= 0\n```\n\nFor complete examples with both dataclasses and Pydantic, see:\n- [`examples/dataclass_example.py`](examples/dataclass_example.py) - All constraint examples with dataclasses\n- [`examples/pydantic_example.py`](examples/pydantic_example.py) - All constraint examples with Pydantic\n\n**Available Constraint Options:**\n\n```python\n# Enhanced Integer functions - no constraints = standard type\nInteger()                   # Standard 32-bit integer\nInteger(ge=0)        # With constraints\nInteger(gt=0)      # Shortcut for > 0\nBigInt()                    # Standard 64-bit integer\nBigInt(ge=0, le=1000000)  # With constraints\nSmallInt()                  # Standard 16-bit integer\nSmallInt(multiple_of=10)    # With constraints\n\n# Specialized constraint types\nPositiveInteger()           # > 0\nNegativeInteger()           # < 0\nNonNegativeInteger()        # >= 0\nNonPositiveInteger()        # <= 0\n\n# Full constraint options\nInteger(\n    gt=10,             # Value must be greater than 10\n    ge=10,             # Value must be greater than or equal to 10\n    lt=100,            # Value must be less than 100\n    le=100,            # Value must be less than or equal to 100\n    multiple_of=5,     # Must be divisible by this\n)\n```\n\n### Constrained Money and Decimal Types\n\nmocksmith provides constrained versions of Money and Decimal types using Pydantic's constraint system:\n\n```python\nfrom mocksmith import (\n    ConstrainedMoney, PositiveMoney, NonNegativeMoney,\n    ConstrainedDecimal, ConstrainedFloat\n)\n\n# Money with constraints\nprice: PositiveMoney()                          # > 0\nbalance: NonNegativeMoney()                     # >= 0\ndiscount: ConstrainedMoney(ge=0, le=100)        # 0-100 range\npayment: ConstrainedMoney(gt=0, le=10000)       # 0 < payment <= 10000\n\n# Decimal with precision and constraints\nweight: ConstrainedDecimal(10, 2, gt=0)         # Positive weight, max 10 digits, 2 decimal places\ntemperature: ConstrainedDecimal(5, 2, ge=-273.15)  # Above absolute zero\n\n# Float with constraints\npercentage: ConstrainedFloat(ge=0.0, le=1.0)    # 0-1 range\nrate: ConstrainedFloat(gt=0, lt=0.5)            # 0 < rate < 0.5\n```\n\nThese constrained types:\n- Work seamlessly with Pydantic validation\n- Generate appropriate mock data respecting constraints\n- Provide the same clean API as other mocksmith types\n- Fall back gracefully if Pydantic is not available\n\n**Example Usage:**\n\n```python\nfrom pydantic import BaseModel\nfrom mocksmith import mockable, PositiveMoney, NonNegativeMoney, ConstrainedMoney, ConstrainedFloat\n\n@mockable\nclass Order(BaseModel):\n    subtotal: PositiveMoney()                    # Must be > 0\n    discount: ConstrainedMoney(ge=0, le=50)      # 0-50 range\n    tax: NonNegativeMoney()                      # >= 0\n    discount_rate: ConstrainedFloat(ge=0, le=0.3)  # 0-30%\n\n# Validation works\norder = Order(\n    subtotal=\"100.00\",    # \u2713 Converts to Decimal\n    discount=\"25.00\",     # \u2713 Within 0-50 range\n    tax=\"8.50\",          # \u2713 Non-negative\n    discount_rate=0.15   # \u2713 15% is within 0-30%\n)\n\n# Mock generation respects constraints\nmock_order = Order.mock()\nassert mock_order.subtotal > 0\nassert 0 <= mock_order.discount <= 50\nassert mock_order.tax >= 0\nassert 0 <= mock_order.discount_rate <= 0.3\n```\n\n## Migration Guide from V2 to V3\n\n### Breaking Changes\n\nV3 introduces a critical breaking change to prevent confusion and subtle bugs:\n\n1. **Direct class imports are removed** - `from mocksmith import VARCHAR` no longer works\n2. **Only factory functions are available** - Use `Varchar()`, not `VARCHAR()`\n3. **DBTypeValidator is removed** - Types work directly with Pydantic\n\n### Why This Change?\n\nIn V2, importing and using `VARCHAR(30)` would create a type class. In V3's simplified pattern, this would create a string instance with value \"30\" - highly confusing! To prevent this dangerous ambiguity, direct class access has been removed entirely.\n\n### Migration Steps\n\n```python\n# \u274c OLD V2 CODE (No longer works)\nfrom mocksmith import VARCHAR, INTEGER, BOOLEAN\nfrom mocksmith.pydantic_integration import DBTypeValidator\nfrom typing import Annotated\n\nclass User(BaseModel):\n    username: Annotated[str, DBTypeValidator(VARCHAR(30))]\n    age: Annotated[int, DBTypeValidator(INTEGER())]\n    active: Annotated[bool, DBTypeValidator(BOOLEAN())]\n\n# \u2705 NEW V3 CODE (Clean and simple)\nfrom mocksmith import Varchar, Integer, Boolean\n\nclass User(BaseModel):\n    username: Varchar(30)  # Direct usage!\n    age: Integer()\n    active: Boolean()\n```\n\n### Common Migration Patterns\n\n| Old V2 Pattern | New V3 Pattern |\n|----------------|----------------|\n| `from mocksmith import VARCHAR` | `from mocksmith import Varchar` |\n| `from mocksmith.types.string import VARCHAR` | Not available - use factory functions |\n| `Annotated[str, DBTypeValidator(VARCHAR(30))]` | `Varchar(30)` |\n| `VARCHAR(30)` (creates type) | `Varchar(30)` (creates type) |\n| `INTEGER()` | `Integer()` |\n| `DECIMAL(10, 2)` | `DecimalType(10, 2)` |\n| `BOOLEAN()` | `Boolean()` |\n| `DATE()` | `Date()` |\n| `TIMESTAMP()` | `Timestamp()` |\n\n### Benefits of V3\n\n1. **Cleaner API** - No more `DBTypeValidator` or `Annotated` boilerplate\n2. **Type safety** - Factory functions always return type classes\n3. **No confusion** - Can't accidentally create instances when you mean types\n4. **Better IDE support** - Direct type usage improves autocomplete\n5. **Simpler codebase** - Less complexity, easier to maintain\n\n## Development\n\n1. Clone the repository:\n```bash\ngit clone https://github.com/gurmeetsaran/mocksmith.git\ncd mocksmith\n```\n\n2. Install Poetry (if not already installed):\n```bash\ncurl -sSL https://install.python-poetry.org | python3 -\n```\n\n3. Install dependencies:\n```bash\npoetry install\n```\n\n4. Set up pre-commit hooks:\n```bash\npoetry run pre-commit install\n```\n\n5. Run tests:\n```bash\nmake test\n```\n\n### Development Commands\n\n- `make lint` - Run linting (ruff + pyright)\n- `make format` - Format code (black + isort + ruff fix)\n- `make test` - Run tests\n- `make test-cov` - Run tests with coverage\n- `make check-all` - Run all checks (lint + format check + tests)\n- `make check-consistency` - Verify pre-commit, Makefile, and CI are in sync\n\n### Ensuring Consistency\n\nTo ensure your development environment matches CI/CD:\n\n```bash\n# Check that pre-commit hooks match Makefile and GitHub Actions\nmake check-consistency\n```\n\nThis will verify that all tools (black, isort, ruff, pyright) are configured consistently across:\n- Pre-commit hooks (`.pre-commit-config.yaml`)\n- Makefile commands\n- GitHub Actions workflows\n\n## License\n\nMIT\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Type-safe data validation and mocking for Python dataclasses and Pydantic models",
    "version": "6.0.1",
    "project_urls": {
        "Bug Tracker": "https://github.com/gurmeetsaran/mocksmith/issues",
        "Homepage": "https://github.com/gurmeetsaran/mocksmith",
        "Repository": "https://github.com/gurmeetsaran/mocksmith"
    },
    "split_keywords": [
        "mock",
        " validation",
        " dataclass",
        " pydantic",
        " testing",
        " faker",
        " types",
        " sql"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d1ea6d7e4425c97d77b9057da8295603db171914761ef13c391e84aeb19996e5",
                "md5": "e4f1f8d641565af8ef59ad6ce5f097c5",
                "sha256": "dc3d70d586b2b33e1bd2eefed73b9cc1265615157bdf3b52aef563974fb49da6"
            },
            "downloads": -1,
            "filename": "mocksmith-6.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e4f1f8d641565af8ef59ad6ce5f097c5",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 47191,
            "upload_time": "2025-08-11T05:24:03",
            "upload_time_iso_8601": "2025-08-11T05:24:03.248272Z",
            "url": "https://files.pythonhosted.org/packages/d1/ea/6d7e4425c97d77b9057da8295603db171914761ef13c391e84aeb19996e5/mocksmith-6.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "c27218d9c52e62785e724a40250e6ee2883a5fefc6a517cb483415c3b3451b6b",
                "md5": "24fc8e818a3f9eb1961a9ffac7679ca0",
                "sha256": "5820932ed715a26ebce7b782b55a1a9c758b41ae0e9bb848edcae7afa391bc79"
            },
            "downloads": -1,
            "filename": "mocksmith-6.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "24fc8e818a3f9eb1961a9ffac7679ca0",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 49507,
            "upload_time": "2025-08-11T05:24:04",
            "upload_time_iso_8601": "2025-08-11T05:24:04.788594Z",
            "url": "https://files.pythonhosted.org/packages/c2/72/18d9c52e62785e724a40250e6ee2883a5fefc6a517cb483415c3b3451b6b/mocksmith-6.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-11 05:24:04",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "gurmeetsaran",
    "github_project": "mocksmith",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "mocksmith"
}
        
Elapsed time: 1.32146s