🚨 Shai-Hulud Strikes Again:834 Packages Compromised.Technical Analysis β†’
Socket
Book a DemoInstallSign in
Socket

mocksmith

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mocksmith

Type-safe data validation and mocking for Python dataclasses and Pydantic models

pipPyPI
Version
6.1.0
Maintainers
1

mocksmith

Unit Tests codecov PyPI version Python Versions License: 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)

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)

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

# Standard installation (includes mock generation)
pip install mocksmith

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

Requirements:

  • Python 3.8+ (Python 3.10+ recommended for pipe union syntax support)
  • Faker (included in standard installation)
  • Pydantic 2.0+ (optional, for enhanced validation)

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

Core database types are available through factory functions from the main package:

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

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 Interface (Works with both Pydantic and Dataclasses!) ✨

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:

Common Use Cases

E-commerce Product Model:

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:

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:

Mock Data Generation

Generate realistic test data automatically with the @mockable decorator:

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
  • Python 3.10+ pipe syntax support - field: Type() | None works seamlessly with mocking

Optional Fields with Pipe Syntax (Python 3.10+)

MockSmith fully supports Python 3.10+ pipe union syntax for optional fields:

from mocksmith import Varchar, Integer, BigInt, DateTime, Timestamp, mockable
from pydantic import BaseModel

@mockable
class User(BaseModel):
    # Required fields
    username: Varchar(50)

    # Optional fields using pipe syntax - both work identically!
    email: Varchar(100) | None          # With Annotated
    user_id: BigInt() | None            # Simple type
    age: Integer(ge=0, le=120) | None   # With constraints
    created_at: DateTime() | None       # Temporal type
    last_login: Timestamp() | None      # With timezone

# Mock generation handles optional fields automatically
user = User.mock()
# Optional fields will randomly be None or have valid values (~20% None, ~80% value)
print(user.user_id)     # Could be: 7786676712978416482 or None
print(user.last_login)  # Could be: 2021-05-04 02:28:37+00:00 or None

Note: Both Optional[Type()] and Type() | None syntaxes work identically. Choose based on your Python version and style preference.

See mock examples:

Type Usage Patterns

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

Correct Pattern

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

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

# βœ… NEW PATTERN (THE ONLY WAY)
from mocksmith import Varchar  # Factory function
UsernameType = Varchar(30)  # Creates type class - CORRECT!

With Pydantic (Full Validation)

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 (traditional syntax)
    optional_new: Varchar(50) | None = None       # Can be None (Python 3.10+ pipe syntax)
    with_default: Boolean() = True                # Has default value

# All patterns can be mixed in the same model!

With Dataclasses (Type Hints Only, No Validation)

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 Notes

βœ… DO USE:

  • field: Varchar(50) - Factory functions for type creation
  • field: Integer(gt=0) - Factory functions with constraints
  • field: Optional[Varchar(50)] = None - For nullable fields (traditional syntax)
  • field: Varchar(50) | None - For nullable fields (Python 3.10+ pipe syntax)
  • Pydantic BaseModel when you need validation
  • Constrained types like PositiveInteger() for common patterns

❌ DON'T USE (Removed):

  • 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 with both traditional and modern syntax:

from typing import Optional

class User(BaseModel):
    name: Varchar(50)                        # Required
    nickname: Optional[Varchar(30)] = None   # Optional (traditional syntax)
    bio: Varchar(200) | None = None          # Optional (Python 3.10+ pipe syntax)

user = User(name="John", nickname=None, bio=None)  # βœ“ Valid

# Both syntaxes work the same way - choose based on your Python version
# Python 3.10+ supports both, but pipe syntax is more concise

Literal Type Support

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

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:

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:

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

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:

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

Newly Supported Types

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

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:

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:

bio: Optional[Text()] = None           # Clear and explicit
phone: Optional[Varchar(20)] = None    # Optional field with no default

Automatic Type Conversion

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

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

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/ directory:

  • 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 - 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 - 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 - 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 - 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

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/ directory.

Validation in Dataclasses

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

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

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

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

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:

Available Constraint Options:

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

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:

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

Breaking Changes

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

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

Why This Change?

In previous versions, importing and using VARCHAR(30) would create a type class. In the new 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

# ❌ 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 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 PatternNew Pattern
from mocksmith import VARCHARfrom mocksmith import Varchar
from mocksmith.types.string import VARCHARNot 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

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

Development

  • Clone the repository:
git clone https://github.com/gurmeetsaran/mocksmith.git
cd mocksmith
  • Install Poetry (if not already installed):
curl -sSL https://install.python-poetry.org | python3 -
  • Install dependencies:
poetry install
  • Set up pre-commit hooks:
poetry run pre-commit install
  • Run tests:
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:

# 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

Keywords

mock

FAQs

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts