Frequently Asked Questions (FAQ)¶
General Questions¶
What is objectory?¶
objectory is a Python library that provides general-purpose object factories. It allows you to instantiate objects dynamically without hardcoding class names, making your code more flexible and maintainable. It supports three main approaches: universal factory, abstract factory, and registry patterns.
When should I use objectory?¶
objectory is useful when you need to:
- Load objects dynamically from configuration files
- Build plugin systems
- Create flexible application architectures
- Avoid tight coupling between classes
- Support multiple implementations that can be switched at runtime
What are the differences between the three factory approaches?¶
- Universal Factory (
factory()): No registration needed, works with any importable object - AbstractFactory: Automatic registration through inheritance, best for related object families
- Registry: Manual registration with decorators or methods, provides fine-grained control
Installation¶
What Python versions are supported?¶
objectory supports Python 3.10 and later. See the get_started page for the compatibility matrix.
How do I install objectory?¶
Using uv:
uv pip install objectory
Using pip:
pip install objectory
For more installation options, see the get_started page.
Can I use objectory without installing it?¶
For development or testing, you can clone the repository and install it in editable mode:
git clone https://github.com/durandtibo/objectory.git
cd objectory
pip install -e .
Usage Questions¶
How do I instantiate an object using the universal factory?¶
from objectory import factory
# Using full module path with positional arguments
obj = factory("collections.Counter", [1, 2, 3])
# Using a single positional argument
obj = factory("pathlib.Path", "/tmp/example")
# With keyword arguments
obj = factory("collections.OrderedDict", a=1, b=2)
Can I use custom class methods for initialization?¶
Yes! Use the _init_ parameter:
class MyClass:
def __init__(self, value):
self.value = value
@classmethod
def from_string(cls, s):
return cls(int(s))
# Using custom initializer
obj = BaseClass.factory(_target_="MyClass", _init_="from_string", s="42")
How do I resolve ambiguous class names?¶
If multiple classes have the same name, use the full qualified name:
# Ambiguous - will raise an error if multiple "Linear" classes exist
obj = BaseClass.factory("Linear")
# Unambiguous - specifies exact class
obj = BaseClass.factory("torch.nn.modules.linear.Linear")
Can I register third-party library classes?¶
Yes! Use register_object():
import torch
from objectory import AbstractFactory
class BaseModule(torch.nn.Module, metaclass=AbstractFactory):
pass
# Register PyTorch's Linear
BaseModule.register_object(torch.nn.Linear)
# Now you can use it
linear = BaseModule.factory("torch.nn.Linear", in_features=10, out_features=5)
How do I see what's registered in a factory?¶
For AbstractFactory:
print(BaseClass.inheritors)
For Registry:
print(registry.registered_names())
Can I use objectory with configuration files?¶
Yes! objectory works great with YAML/JSON configs:
import yaml
from objectory import factory
# Load config
with open("config.yaml") as f:
config = yaml.safe_load(f)
# Instantiate from config
obj = factory(**config)
Advanced Questions¶
How does name resolution work?¶
The name resolution mechanism tries to find objects in this order:
- Exact match in registered objects
- Single match by class name only
- Import and register the object dynamically
See the name resolution documentation for details.
How do I create a plugin system with objectory?¶
You can create a plugin system by using the AbstractFactory metaclass or Registry to register plugin classes dynamically, then instantiate them using the factory pattern based on configuration.
Can I register functions, not just classes?¶
Yes! Both AbstractFactory and Registry support function registration:
from objectory import Registry
registry = Registry()
@registry.register()
def my_function(x, y):
return x + y
result = registry.factory(_target_="my_function", x=1, y=2)
How do I unregister objects?¶
For AbstractFactory:
BaseClass.unregister("MyClass")
For Registry:
registry.unregister("MyClass")
Can I clear all registered objects?¶
For Registry:
# Clear current registry
registry.clear()
# Clear including sub-registries
registry.clear(nested=True)
For AbstractFactory, objects are registered permanently and cannot be cleared (this is by design).
How do I handle circular dependencies?¶
Avoid circular imports by:
- Registering objects in
__init__.pyfiles - Using lazy imports
- Structuring your code to avoid circular dependencies
Can I use objectory with type hints?¶
Yes! For better IDE support:
from typing import TypeVar
from objectory import AbstractFactory
T = TypeVar("T", bound="BaseClass")
class BaseClass(metaclass=AbstractFactory):
@classmethod
def factory(cls, _target_: str, *args, **kwargs) -> "BaseClass":
return super().factory(_target_, *args, **kwargs)
Troubleshooting¶
I get "UnregisteredObjectFactoryError"¶
This means the object hasn't been registered. Solutions:
- Make sure the module containing the class has been imported
- Check the class inherits from the base factory class
- Use
register_object()to manually register it - Verify the class name spelling
My object isn't showing up in inheritors¶
The object must be imported at least once to be registered. Solutions:
- Import the module in your
__init__.py - Use the
register_child_classes()function - Call
register_object()explicitly
I get a "RuntimeError: The target object does not exist"¶
The universal factory cannot find the object. Check:
- Is the module path correct?
- Is the package installed?
- Can you import it manually with
from x import y?
Performance concerns with large numbers of registered objects¶
The registry lookup is O(1) for exact matches and O(n) for name resolution. For better performance:
- Use full qualified names when possible
- Avoid having many objects with the same class name
- Consider using multiple registries to partition objects
Security¶
Is it safe to use user input with factory functions?¶
⚠️ No! Never pass untrusted user input directly to factory functions. The factory can instantiate any Python object, which could be a security risk.
Always validate and sanitize input:
# BAD - Don't do this!
user_class = request.get("class_name")
obj = factory(user_class)
# GOOD - Validate against allowlist
ALLOWED_CLASSES = {"collections.Counter", "collections.defaultdict"}
user_class = request.get("class_name")
if user_class not in ALLOWED_CLASSES:
raise ValueError("Class not allowed")
obj = factory(user_class)
See SECURITY.md for more security guidelines.
Contributing¶
How can I contribute to objectory?¶
We welcome contributions! See CONTRIBUTING.md for guidelines.
I found a bug, what should I do?¶
Please open an issue on GitHub with:
- A clear description of the bug
- Steps to reproduce
- Expected vs actual behavior
- Your Python version and objectory version
Can I request a feature?¶
Yes! Open a feature request issue on GitHub. Please describe:
- The use case
- Why existing features don't work
- Proposed API (if applicable)
Getting Help¶
Where can I ask questions?¶
- Open a discussion on GitHub
- Open an issue for bugs or feature requests