How to Create Custom OBBject Extensions in the OpenBB Platform
Custom OBBject extensions in the OpenBB Platform are created by registering a cached accessor on the OBBject class through Python entry points, allowing developers to augment data containers without modifying core library code.
The OpenBB Platform provides a flexible extension system that lets developers add functionality to the OBBject class—the pandas-like data container used throughout the library. By creating custom OBBject extensions, you can attach domain-specific methods and accessors that become automatically available on every data object. This guide explains the complete implementation using the actual source code from the OpenBB-finance/OpenBB repository.
Understanding the Extension Architecture
The extension mechanism relies on three coordinated components that handle discovery, registration, and runtime behavior.
The Extension Model
The Extension class in openbb_platform/core/openbb_core/app/model/extension.py defines the metadata and registration logic for every OBBject extension. It validates configuration flags such as on_command_output, command_output_paths, results_only, and immutable, and exposes the obbject_accessor property that returns a decorator for registering accessors on the OBBject class.
The ExtensionLoader
The ExtensionLoader class in openbb_platform/core/openbb_core/app/extension_loader.py acts as a singleton that discovers installed extensions via the Python entry-point group openbb_obbject_extension. At runtime, it loads each extension, builds a dictionary mapping extension names to Extension instances in obbject_objects, and registers callbacks for extensions configured to act on command output.
Entry Point Discovery
Extensions are discovered through standard Python packaging entry points declared in pyproject.toml. The loader specifically looks for the group openbb_obbject_extension and imports the specified Extension instance when the package is installed.
Creating Your First OBBject Extension
Building an extension requires three steps: configuring the package metadata, instantiating the Extension class, and registering an accessor function.
Project Configuration
Declare the entry point in your package's pyproject.toml to enable discovery:
[tool.poetry.plugins."openbb_obbject_extension"]
my_stats = "my_extension.extension:my_ext"
This tells the ExtensionLoader to import my_ext from my_extension.extension when the package is installed.
Implementing the Extension Class
Create an Extension instance with appropriate metadata and validation flags:
from openbb_core.app.model.extension import Extension
my_ext = Extension(
name="my_stats",
description="Adds statistical helpers to OBBject",
on_command_output=False,
immutable=True,
)
The constructor validates that if on_command_output=False, you cannot set command_output_paths, results_only=True, or immutable=False. Violating this rule raises a ValueError during instantiation.
Registering the Accessor
Use the obbject_accessor property to attach a cached accessor to OBBject:
@my_ext.obbject_accessor
def stats(self):
"""
`self` is the OBBject instance. The result is cached on first access.
"""
return self.df.describe()
The decorator stores a CachedAccessor descriptor on OBBject under the key my_stats. When obbject.my_stats is accessed, the function runs once, its result is cached on the instance, and subsequent accesses return the cached object. If an accessor name already exists, registration issues a UserWarning but overwrites the attribute.
Hooking Into Command Output
Extensions can modify or react to command output by enabling specific configuration flags and security settings.
Configuration Flags for Command Output
Set on_command_output=True when you need the extension to process data after a command executes:
my_ext = Extension(
name="price_plot",
on_command_output=True,
command_output_paths=["/stock/price"],
immutable=False,
)
The command_output_paths parameter restricts the extension to specific endpoints, while immutable=False allows the accessor to mutate returned objects.
Security Validation Requirements
Extensions that act on command output or mutate data require explicit permission through system settings. You must set allow_on_command_output: true in system_settings.json or the environment variable OPENBB_ALLOW_ON_COMMAND_OUTPUT. For mutable extensions, also set allow_mutable_extensions: true or OPENBB_ALLOW_MUTABLE_EXTENSIONS.
If these flags are missing, Extension.__init__ raises a RuntimeError explaining the required configuration.
Complete Implementation Example
Here is a minimal yet functional OBBject extension package structure:
my_extension/
│ pyproject.toml
│ my_extension/
│ __init__.py
│ extension.py
[tool.poetry]
name = "my_extension"
version = "0.1.0"
[tool.poetry.plugins."openbb_obbject_extension"]
my_stats = "my_extension.extension:my_ext"
from openbb_core.app.model.extension import Extension
my_ext = Extension(
name="my_stats",
description="Statistical helpers",
on_command_output=False,
immutable=True,
)
@my_ext.obbject_accessor
def stats(self):
return self.df.describe()
Usage
from openbb_core.app.model.obbject import OBBject
data = OBBject({"a": [1, 2, 3], "b": [4, 5, 6]})
print(data.my_stats)
Summary
- Entry Point Registration: Declare extensions in
pyproject.tomlunder theopenbb_obbject_extensiongroup to enable discovery byExtensionLoader. - Accessor Pattern: Use
Extension.obbject_accessorto register cached accessors onOBBject, with automatic caching on first access. - Validation Rules: Extensions with
on_command_output=Falsecannot setcommand_output_paths,results_only=True, orimmutable=False. - Security Gates: Command-output and mutable extensions require
allow_on_command_outputandallow_mutable_extensionsin system settings or environment variables. - File References: Core logic resides in
openbb_platform/core/openbb_core/app/model/extension.pyandopenbb_platform/core/openbb_core/app/extension_loader.py.
Frequently Asked Questions
What is the difference between immutable=True and immutable=False in an OBBject extension?
When immutable=True, the accessor must return a new object without modifying the original OBBject instance. When immutable=False, the accessor can mutate the returned data or the extension instance itself. Mutable extensions require the allow_mutable_extensions system setting to be enabled, whereas immutable extensions work without additional configuration.
Why does my extension raise a ValueError during initialization?
The Extension class validates that incompatible flags are not combined. If on_command_output=False, you cannot set command_output_paths, results_only=True, or immutable=False. The constructor raises ValueError if these restrictions are violated, as these features only apply when hooking into command output.
How does the ExtensionLoader discover my custom extension?
The ExtensionLoader singleton calls entry_points(group="openbb_obbject_extension") at runtime to find all packages declaring that entry point group. It then loads the specified module, imports the Extension instance, and registers its accessor on the OBBject class. This process happens automatically when the OpenBB Platform initializes.
Can I override an existing OBBject accessor with my extension?
Yes, but the system warns you first. If you register an accessor with a name that already exists on OBBject, Extension.register_accessor issues a UserWarning before overwriting the existing attribute. This allows extension authors to customize or replace built-in functionality while alerting users to potential conflicts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →