Mock data generation factories for Python types with support for dataclasses, Pydantic, SQLAlchemy, and more
—
Extensible system for registering custom value generators, configuring factory behavior, and adapting polyfactory to specific types and use cases. The provider system allows fine-grained control over how values are generated for any type.
Register custom value generators for specific types, overriding default generation behavior with domain-specific logic.
@classmethod
def add_provider(cls, provider_type: Any, provider_function: Callable[[], Any]) -> None:
"""
Register a custom provider function for a specific type.
Parameters:
- provider_type: The type to register the provider for
- provider_function: Callable with no arguments that generates values for the type
"""
@classmethod
def get_provider_map(cls) -> dict[type, Callable[[], Any]]:
"""
Get the current mapping of types to provider functions.
Returns:
Dictionary mapping types to their provider functions
"""Usage Example:
from dataclasses import dataclass
from polyfactory.factories import DataclassFactory
from enum import Enum
import random
class Priority(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
@dataclass
class Task:
title: str
priority: Priority
estimated_hours: int
class TaskFactory(DataclassFactory[Task]):
__model__ = Task
# Register custom provider for Priority enum
@classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.add_provider(Priority, lambda: random.choice(list(Priority)))
# Custom provider for estimated hours based on priority
def hours_provider():
# This would have access to other generated values in real implementation
return random.randint(1, 40)
cls.add_provider(int, hours_provider) # Override int generation for this factory
task = TaskFactory.build() # Uses custom Priority and hours logicControl randomization for reproducible test data generation across faker and random instances.
@classmethod
def seed_random(cls, seed: int) -> None:
"""
Seed the random number generators for deterministic generation.
Parameters:
- seed: Integer seed value for reproducible random generation
"""Usage Example:
from polyfactory.factories import DataclassFactory
# Set seed for reproducible generation
UserFactory.seed_random(42)
# These will generate the same data every time
user1 = UserFactory.build()
user2 = UserFactory.build()
# Reset with same seed to get identical results
UserFactory.seed_random(42)
user3 = UserFactory.build() # Identical to user1Comprehensive set of configuration attributes that control factory behavior and generation patterns.
class ConfigurableFactory(BaseFactory[T]):
# Core configuration
__model__: type # Required: Model type for the factory
__faker__: Faker # Faker instance for realistic data generation
__random__: Random # Random instance for deterministic generation
# Collection behavior
__randomize_collection_length__: bool = False # Randomize collection sizes
__min_collection_length__: int = 0 # Minimum collection size
__max_collection_length__: int = 5 # Maximum collection size
# Generation behavior
__allow_none_optionals__: bool = True # Allow None for optional fields
__use_defaults__: bool = True # Use model default values when available
__check_model__: bool = True # Validate generated instances
# Persistence handlers
__sync_persistence__: SyncPersistenceProtocol | None = None
__async_persistence__: AsyncPersistenceProtocol | None = None
# Factory registration
__set_as_default_factory_for_type__: bool = False # Register as default factoryConfiguration Examples:
from dataclasses import dataclass
from polyfactory.factories import DataclassFactory
from faker import Faker
@dataclass
class Product:
name: str
tags: list[str]
description: str | None = None
class ProductFactory(DataclassFactory[Product]):
__model__ = Product
# Custom faker instance with specific locale
__faker__ = Faker('es_ES') # Spanish locale
# Collection configuration
__randomize_collection_length__ = True
__min_collection_length__ = 2
__max_collection_length__ = 8
# Always include description (no None values)
__allow_none_optionals__ = False
# Enable model validation
__check_model__ = True
product = ProductFactory.build() # Spanish names, 2-8 tags, always has descriptionRegister providers that apply across all factories for common custom types.
from polyfactory import BaseFactory
from decimal import Decimal
import random
# Global provider for Decimal type
BaseFactory.add_provider(
Decimal,
lambda: Decimal(str(round(random.uniform(0.01, 999.99), 2)))
)
# Now all factories will use this for Decimal fields
from dataclasses import dataclass
@dataclass
class Price:
amount: Decimal
currency: str
class PriceFactory(DataclassFactory[Price]):
__model__ = Price
price = PriceFactory.build() # Uses global Decimal providerCreate factories dynamically for types without explicit factory class definition.
@classmethod
def create_factory(
cls,
model: type,
**kwargs
) -> type[BaseFactory]:
"""
Dynamically create a factory class for a model type.
Parameters:
- model: The model type to create a factory for
- **kwargs: Configuration attributes for the factory class
Returns:
Factory class configured for the specified model
"""Usage Example:
from typing import TypedDict
class OrderDict(TypedDict):
id: int
total: float
items: list[str]
# Create factory dynamically with custom configuration
OrderFactory = BaseFactory.create_factory(
OrderDict,
__min_collection_length__=1,
__max_collection_length__=10,
__allow_none_optionals__=False
)
# Add custom provider to dynamic factory
OrderFactory.add_provider(
float,
lambda: round(random.uniform(10.0, 1000.0), 2)
)
order = OrderFactory.build()Check type support and extend polyfactory's capabilities for custom types.
@classmethod
def is_supported_type(cls, value: Any) -> bool:
"""
Check if a type is supported for automatic generation.
Parameters:
- value: Type or value to check for support
Returns:
True if the type can be automatically generated, False otherwise
"""Usage Example:
from typing import NewType
from polyfactory import BaseFactory
# Custom type
UserId = NewType('UserId', int)
# Check if supported
if not BaseFactory.is_supported_type(UserId):
# Add provider for custom type
BaseFactory.add_provider(
UserId,
lambda: UserId(random.randint(1, 999999))
)
# Now UserId is supported
assert BaseFactory.is_supported_type(UserId)class BaseModelFactory(DataclassFactory[T]):
"""Base factory with common customizations."""
# Common configuration
__randomize_collection_length__ = True
__min_collection_length__ = 1
__max_collection_length__ = 3
__allow_none_optionals__ = False
@classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
# Add common providers to all subclasses
cls.add_provider(
str,
lambda: cls.__faker__.sentence(nb_words=3).rstrip('.')
)
@dataclass
class User:
name: str
tags: list[str]
class UserFactory(BaseModelFactory[User]):
__model__ = User
# Inherits all base customizations
user = UserFactory.build() # Uses inherited behaviorclass ContextAwareFactory(DataclassFactory[Model]):
_context = {}
@classmethod
def with_context(cls, **context):
"""Create factory instance with context."""
factory = type(cls.__name__, (cls,), {})
factory._context = context
return factory
@classmethod
def context_provider(cls, key: str, default_provider: Callable):
"""Provider that uses context if available."""
def provider():
if key in cls._context:
return cls._context[key]
return default_provider()
return provider
# Usage
@dataclass
class TestData:
environment: str
version: str
class TestDataFactory(ContextAwareFactory[TestData]):
__model__ = TestData
@classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.add_provider(
str,
cls.context_provider('environment', lambda: 'development')
)
# Use with context
ProductionFactory = TestDataFactory.with_context(environment='production')
data = ProductionFactory.build() # environment will be 'production'class ConditionalFactory(DataclassFactory[Model]):
@classmethod
def build_with_condition(cls, condition: str, **kwargs):
"""Build instance with conditional logic."""
if condition == 'premium':
cls.add_provider(int, lambda: random.randint(100, 1000))
elif condition == 'basic':
cls.add_provider(int, lambda: random.randint(1, 100))
return cls.build(**kwargs)
# Usage
premium_user = UserFactory.build_with_condition('premium', name='VIP User')
basic_user = UserFactory.build_with_condition('basic', name='Regular User')Install with Tessl CLI
npx tessl i tessl/pypi-polyfactory