0
# Customization
1
2
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.
3
4
## Capabilities
5
6
### Provider Registration
7
8
Register custom value generators for specific types, overriding default generation behavior with domain-specific logic.
9
10
```python { .api }
11
@classmethod
12
def add_provider(cls, provider_type: Any, provider_function: Callable[[], Any]) -> None:
13
"""
14
Register a custom provider function for a specific type.
15
16
Parameters:
17
- provider_type: The type to register the provider for
18
- provider_function: Callable with no arguments that generates values for the type
19
"""
20
21
@classmethod
22
def get_provider_map(cls) -> dict[type, Callable[[], Any]]:
23
"""
24
Get the current mapping of types to provider functions.
25
26
Returns:
27
Dictionary mapping types to their provider functions
28
"""
29
```
30
31
**Usage Example:**
32
```python
33
from dataclasses import dataclass
34
from polyfactory.factories import DataclassFactory
35
from enum import Enum
36
import random
37
38
class Priority(Enum):
39
LOW = "low"
40
MEDIUM = "medium"
41
HIGH = "high"
42
43
@dataclass
44
class Task:
45
title: str
46
priority: Priority
47
estimated_hours: int
48
49
class TaskFactory(DataclassFactory[Task]):
50
__model__ = Task
51
52
# Register custom provider for Priority enum
53
@classmethod
54
def __init_subclass__(cls, **kwargs):
55
super().__init_subclass__(**kwargs)
56
cls.add_provider(Priority, lambda: random.choice(list(Priority)))
57
58
# Custom provider for estimated hours based on priority
59
def hours_provider():
60
# This would have access to other generated values in real implementation
61
return random.randint(1, 40)
62
63
cls.add_provider(int, hours_provider) # Override int generation for this factory
64
65
task = TaskFactory.build() # Uses custom Priority and hours logic
66
```
67
68
### Random Seed Control
69
70
Control randomization for reproducible test data generation across faker and random instances.
71
72
```python { .api }
73
@classmethod
74
def seed_random(cls, seed: int) -> None:
75
"""
76
Seed the random number generators for deterministic generation.
77
78
Parameters:
79
- seed: Integer seed value for reproducible random generation
80
"""
81
```
82
83
**Usage Example:**
84
```python
85
from polyfactory.factories import DataclassFactory
86
87
# Set seed for reproducible generation
88
UserFactory.seed_random(42)
89
90
# These will generate the same data every time
91
user1 = UserFactory.build()
92
user2 = UserFactory.build()
93
94
# Reset with same seed to get identical results
95
UserFactory.seed_random(42)
96
user3 = UserFactory.build() # Identical to user1
97
```
98
99
### Factory Configuration Attributes
100
101
Comprehensive set of configuration attributes that control factory behavior and generation patterns.
102
103
```python { .api }
104
class ConfigurableFactory(BaseFactory[T]):
105
# Core configuration
106
__model__: type # Required: Model type for the factory
107
__faker__: Faker # Faker instance for realistic data generation
108
__random__: Random # Random instance for deterministic generation
109
110
# Collection behavior
111
__randomize_collection_length__: bool = False # Randomize collection sizes
112
__min_collection_length__: int = 0 # Minimum collection size
113
__max_collection_length__: int = 5 # Maximum collection size
114
115
# Generation behavior
116
__allow_none_optionals__: bool = True # Allow None for optional fields
117
__use_defaults__: bool = True # Use model default values when available
118
__check_model__: bool = True # Validate generated instances
119
120
# Persistence handlers
121
__sync_persistence__: SyncPersistenceProtocol | None = None
122
__async_persistence__: AsyncPersistenceProtocol | None = None
123
124
# Factory registration
125
__set_as_default_factory_for_type__: bool = False # Register as default factory
126
```
127
128
**Configuration Examples:**
129
```python
130
from dataclasses import dataclass
131
from polyfactory.factories import DataclassFactory
132
from faker import Faker
133
134
@dataclass
135
class Product:
136
name: str
137
tags: list[str]
138
description: str | None = None
139
140
class ProductFactory(DataclassFactory[Product]):
141
__model__ = Product
142
143
# Custom faker instance with specific locale
144
__faker__ = Faker('es_ES') # Spanish locale
145
146
# Collection configuration
147
__randomize_collection_length__ = True
148
__min_collection_length__ = 2
149
__max_collection_length__ = 8
150
151
# Always include description (no None values)
152
__allow_none_optionals__ = False
153
154
# Enable model validation
155
__check_model__ = True
156
157
product = ProductFactory.build() # Spanish names, 2-8 tags, always has description
158
```
159
160
### Global Type Providers
161
162
Register providers that apply across all factories for common custom types.
163
164
```python
165
from polyfactory import BaseFactory
166
from decimal import Decimal
167
import random
168
169
# Global provider for Decimal type
170
BaseFactory.add_provider(
171
Decimal,
172
lambda: Decimal(str(round(random.uniform(0.01, 999.99), 2)))
173
)
174
175
# Now all factories will use this for Decimal fields
176
from dataclasses import dataclass
177
178
@dataclass
179
class Price:
180
amount: Decimal
181
currency: str
182
183
class PriceFactory(DataclassFactory[Price]):
184
__model__ = Price
185
186
price = PriceFactory.build() # Uses global Decimal provider
187
```
188
189
### Custom Factory Creation
190
191
Create factories dynamically for types without explicit factory class definition.
192
193
```python { .api }
194
@classmethod
195
def create_factory(
196
cls,
197
model: type,
198
**kwargs
199
) -> type[BaseFactory]:
200
"""
201
Dynamically create a factory class for a model type.
202
203
Parameters:
204
- model: The model type to create a factory for
205
- **kwargs: Configuration attributes for the factory class
206
207
Returns:
208
Factory class configured for the specified model
209
"""
210
```
211
212
**Usage Example:**
213
```python
214
from typing import TypedDict
215
216
class OrderDict(TypedDict):
217
id: int
218
total: float
219
items: list[str]
220
221
# Create factory dynamically with custom configuration
222
OrderFactory = BaseFactory.create_factory(
223
OrderDict,
224
__min_collection_length__=1,
225
__max_collection_length__=10,
226
__allow_none_optionals__=False
227
)
228
229
# Add custom provider to dynamic factory
230
OrderFactory.add_provider(
231
float,
232
lambda: round(random.uniform(10.0, 1000.0), 2)
233
)
234
235
order = OrderFactory.build()
236
```
237
238
### Type Support and Extensions
239
240
Check type support and extend polyfactory's capabilities for custom types.
241
242
```python { .api }
243
@classmethod
244
def is_supported_type(cls, value: Any) -> bool:
245
"""
246
Check if a type is supported for automatic generation.
247
248
Parameters:
249
- value: Type or value to check for support
250
251
Returns:
252
True if the type can be automatically generated, False otherwise
253
"""
254
```
255
256
**Usage Example:**
257
```python
258
from typing import NewType
259
from polyfactory import BaseFactory
260
261
# Custom type
262
UserId = NewType('UserId', int)
263
264
# Check if supported
265
if not BaseFactory.is_supported_type(UserId):
266
# Add provider for custom type
267
BaseFactory.add_provider(
268
UserId,
269
lambda: UserId(random.randint(1, 999999))
270
)
271
272
# Now UserId is supported
273
assert BaseFactory.is_supported_type(UserId)
274
```
275
276
### Advanced Customization Patterns
277
278
#### Factory Inheritance with Custom Behavior
279
280
```python
281
class BaseModelFactory(DataclassFactory[T]):
282
"""Base factory with common customizations."""
283
284
# Common configuration
285
__randomize_collection_length__ = True
286
__min_collection_length__ = 1
287
__max_collection_length__ = 3
288
__allow_none_optionals__ = False
289
290
@classmethod
291
def __init_subclass__(cls, **kwargs):
292
super().__init_subclass__(**kwargs)
293
294
# Add common providers to all subclasses
295
cls.add_provider(
296
str,
297
lambda: cls.__faker__.sentence(nb_words=3).rstrip('.')
298
)
299
300
@dataclass
301
class User:
302
name: str
303
tags: list[str]
304
305
class UserFactory(BaseModelFactory[User]):
306
__model__ = User
307
# Inherits all base customizations
308
309
user = UserFactory.build() # Uses inherited behavior
310
```
311
312
#### Context-Aware Providers
313
314
```python
315
class ContextAwareFactory(DataclassFactory[Model]):
316
_context = {}
317
318
@classmethod
319
def with_context(cls, **context):
320
"""Create factory instance with context."""
321
factory = type(cls.__name__, (cls,), {})
322
factory._context = context
323
return factory
324
325
@classmethod
326
def context_provider(cls, key: str, default_provider: Callable):
327
"""Provider that uses context if available."""
328
def provider():
329
if key in cls._context:
330
return cls._context[key]
331
return default_provider()
332
return provider
333
334
# Usage
335
@dataclass
336
class TestData:
337
environment: str
338
version: str
339
340
class TestDataFactory(ContextAwareFactory[TestData]):
341
__model__ = TestData
342
343
@classmethod
344
def __init_subclass__(cls, **kwargs):
345
super().__init_subclass__(**kwargs)
346
cls.add_provider(
347
str,
348
cls.context_provider('environment', lambda: 'development')
349
)
350
351
# Use with context
352
ProductionFactory = TestDataFactory.with_context(environment='production')
353
data = ProductionFactory.build() # environment will be 'production'
354
```
355
356
#### Conditional Generation
357
358
```python
359
class ConditionalFactory(DataclassFactory[Model]):
360
@classmethod
361
def build_with_condition(cls, condition: str, **kwargs):
362
"""Build instance with conditional logic."""
363
364
if condition == 'premium':
365
cls.add_provider(int, lambda: random.randint(100, 1000))
366
elif condition == 'basic':
367
cls.add_provider(int, lambda: random.randint(1, 100))
368
369
return cls.build(**kwargs)
370
371
# Usage
372
premium_user = UserFactory.build_with_condition('premium', name='VIP User')
373
basic_user = UserFactory.build_with_condition('basic', name='Regular User')
374
```