Factory System Quick Reference
Glossary
| Term | Definition | Example |
|---|---|---|
| Factory | Registry that creates objects from configuration | Factory() |
| Loader | Automatically finds classes/functions in modules | Loader(template="pkg.{task}") |
| Parser | Transforms config values into Python objects | OPTIMIZERS = Factory() |
| Context | Orchestrates factories, loaders, and parsers | Context[DataType](...) |
| BuilderKey | Tuple of (task, name) identifying a constructor | ("classification", "resnet") |
| Decorator | Function that modifies objects after creation | add_normalization(dataset, params) |
| Template | Module path pattern with {task} placeholder | "myapp.models.{task}" |
| Singleton | Global instance of a factory/loader | DATASETS = Factory() |
| Constructor | Function or class that creates objects | ResNetModel or create_resnet() |
| Wildcard | Using None to match any value | (None, "adam") matches any task |
Common Patterns
Creating a Factory
from ainxt.factory import Factory
# Basic factory
factory = Factory()
# Factory with decorator support
factory = Factory(decorator=my_decorator_factory)
# Combining factories
combined = factory1 + factory2
Registering Constructors
# Register class
factory.register("classification", "resnet", ResNetModel)
# Register function
factory.register(None, "create", lambda x: x * 2)
# Register with wildcards
factory.register(None, "default", DefaultModel) # Matches any task
Creating a Loader
from ainxt.factory import Loader
loader = Loader(
template="myapp.{task}.models", # Module template
tasks=["nlp", "vision"], # Tasks to scan
kind=Model, # Type to find
pattern=r"^[A-Z].*Model$", # Name pattern
strict=True # Only from this module
)
Setting Up Parsers
# Create parser factory
OPTIMIZERS = Factory()
OPTIMIZERS.register(None, "adam", AdamOptimizer)
OPTIMIZERS.register(None, "sgd", SGDOptimizer)
# Register as parser
PARSERS = {"optimizer": OPTIMIZERS}
# Use in config
config = {
"optimizer": {
"name": "adam",
"learning_rate": 0.001
}
}
Using Context
from ainxt.scripts.context import Context
# Create context
context = Context[DataType](
encoder=MyEncoder(),
decoder=MyDecoder(),
dataset_builder=DATASETS,
model_builder=MODELS,
parsers=PARSERS
)
# Load objects
dataset = context.load_dataset(config)
model = context.load_model(config)
File Organization
project/
├── context.py # Main Context definition
├── myapp/
│ ├── data/
│ │ └── datasets/
│ │ ├── __init__.py
│ │ ├── classification.py # Classification datasets
│ │ └── detection.py # Detection datasets
│ ├── models/
│ │ ├── __init__.py
│ │ ├── classification.py # Classification models
│ │ └── detection.py # Detection models
│ ├── parsers/
│ │ ├── __init__.py
│ │ ├── optimizer.py # Optimizer parsers
│ │ └── augmentation.py # Augmentation parsers
│ └── serving/
│ ├── __init__.py
│ └── singletons.py # Global factories
└── config/
└── experiments/
└── example.yaml # Configuration files
Configuration Structure
Basic Configuration
dataset:
task: classification
name: imagenet
path: /data/imagenet
model:
task: classification
name: resnet50
num_classes: 1000
With Parsers
dataset:
name: custom_dataset
# This triggers the augmentation parser
augmentation:
name: flip
probability: 0.5
model:
name: classifier
# This triggers the optimizer parser
optimizer:
name: adam
learning_rate: 0.001
With Decorators
dataset:
name: basic_dataset
path: /data
# These trigger decorators
normalize:
mean: [0.5, 0.5, 0.5]
cache: 100
Python Concepts for Beginners
The ** Operator
# Unpacks dictionary as keyword arguments
config = {"a": 1, "b": 2}
func(**config) # Same as func(a=1, b=2)
The * Operator
Optional Type Hints
BuilderKey Tuples
# Tuples are immutable pairs
key = ("classification", "resnet") # Can't be changed
task, name = key # Unpack tuple
Common Commands
Creating Singletons
from ainxt.serving import (
create_dataset_factory,
create_model_factory,
create_parsers
)
# Create global factories
DATASETS = create_dataset_factory("myapp", tasks)
MODELS = create_model_factory("myapp", tasks)
PARSERS = create_parsers("myapp", register_singular=True)
Combining with Core aiNXT
from ainxt.serving import DATASETS as CORE_DATASETS
# Extend core with your components
DATASETS = CORE_DATASETS + create_dataset_factory("myapp", tasks)
Debugging Factories
# List all registered constructors
for key in factory:
print(key)
# Check if constructor exists
if ("classification", "resnet") in factory:
print("ResNet is registered")
# Get constructor directly
constructor = factory[("classification", "resnet")]
Troubleshooting
Problem: "No constructor found"
# Check registration
print(list(factory.keys())) # See all keys
# Check task/name spelling
factory.build("classificaiton", "resnet") # Typo!
Problem: "Module not found"
# Verify module exists
import myapp.models.classification # Test import
# Check template
loader = Loader(template="myapp.models.{task}") # Correct path?
Problem: Parser not working
# Ensure parser key matches config key
PARSERS = {"optimizer": OPTIMIZERS} # Key is "optimizer"
config = {
"optimizer": {...} # Must match parser key
}
Problem: Wildcard not matching
# Remember None is wildcard
factory.register(None, "default", DefaultClass) # Matches any task
factory.build("any_task", "default") # This works!
Advanced Tips
1. Lazy Loading
# Use cached_property for expensive operations
from functools import cached_property
class Loader:
@cached_property
def _constructors(self):
# Only computed once
return self._scan_modules()
2. Type Safety
from typing import TypeVar
T = TypeVar("T")
class Factory(Generic[T]):
def build(self, ...) -> T:
# Return type matches Factory type
pass
3. Custom Conditions
# Filter constructors with custom logic
def has_gpu_support(constructor):
return hasattr(constructor, 'cuda_enabled')
loader = Loader(
template="models.{task}",
condition=has_gpu_support
)
4. Dynamic Registration
# Register based on available packages
try:
import torch
factory.register(None, "torch_model", TorchModel)
except ImportError:
factory.register(None, "numpy_model", NumpyModel)
Quick Examples
Minimal Factory Setup
from ainxt.factory import Factory
f = Factory()
f.register(None, "test", lambda x: x * 2)
result = f.build(None, "test", x=5) # Returns 10
Minimal Loader Setup
from ainxt.factory import Loader
loader = Loader(
template="myapp.components",
kind=MyBaseClass
)
factory = Factory(loader)
Minimal Parser Setup
PARSERS = {"transform": Factory()}
PARSERS["transform"].register(None, "double", lambda x: x * 2)
config = {"value": 5, "transform": {"name": "double", "x": 5}}
parsed = parse_config(config, PARSERS) # transform becomes 10
Minimal Context Setup
Key Takeaways
- Factories are dictionaries of constructors
- Loaders find constructors automatically
- Parsers transform config to objects
- Context brings everything together
- None acts as a wildcard in keys
- Templates use
{task}for dynamic paths - Decorators modify objects after creation
- BuilderKeys are
(task, name)tuples
Remember: The system is designed to separate configuration from implementation, making experiments easy to define and share!