Files
OpenBB/cli/openbb_cli/controllers/platform_controller_factory.py
Danglewood 9a30186174 [V5] CLI 2.0 - Pluggable Backends, Non-TTY Default, HTTP Dispatcher, Spec Files + Codegen (#7433)
* stash some changes

* add more robust testing

* mypy

* point PR at V5

* introduce spec file

* codespell

* test fix

* fix workflow environment setup

* fix workflow environment setup

* fix workflow environment setup

* add pyyaml to dependencies

* split lint jobs

* fix workflow environment setup

* fix workflow environment setup

* workflow env setup

* workflow env setup

* clean up code comments

* add auth hook entrypoints

* codespell

* add codegen feature

* codespell

* move _unpack into dispatchers for consistency with codegen packages

* surface nested models in the response

* fix missing coverage in CI

* socrata updates

* test fix

* detect plotly output

* add --include and --exclude flags from generate-extension command

* cap test matrix at python 3.14

* no useless comments

* platform controller command description split

* merge URL overloads from path params

* exclude none and unset from model dump

---------

Co-authored-by: deeleeramone <>
Co-authored-by: Copilot <copilot@github.com>
2026-06-01 19:14:38 +03:00

83 lines
2.7 KiB
Python

"""Platform controller factory."""
from __future__ import annotations
from typing import Any
from openbb_cli.backend import Backend, LocalBackend
from openbb_cli.controllers.base_platform_controller import PlatformController
class PlatformControllerFactory:
"""Factory to create a platform controller from a ``Backend``."""
def __init__(
self,
platform_router: type | None = None,
*,
backend: Backend | None = None,
router_name: str | None = None,
reference: dict[str, Any] | None = None,
) -> None:
if backend is None and platform_router is None:
raise ValueError("Either ``backend`` or ``platform_router`` is required.")
if backend is not None:
if router_name is None:
raise ValueError("``router_name`` is required when ``backend`` is set.")
self._backend: Backend = backend
self._router_name: str = router_name
else:
del reference
assert platform_router is not None # noqa: S101
self._backend = LocalBackend()
self._router_name = _derive_router_name(platform_router)
self._translators, self._paths = self._backend.get_translators_for_path(
self._router_name
)
@property
def router_name(self) -> str:
return self._router_name
@property
def controller_name(self) -> str:
return f"{self._router_name.capitalize()}Controller"
def create(self) -> type:
"""Create the platform controller class for this router."""
choices_menus: list[str] = []
choices_commands: list[str] = []
for key, value in self._paths.items():
if value == "path":
continue
choices_menus.append(key)
for name in self._translators:
if any(
f"{self._router_name}_{path}_" in f"{name}_" for path in self._paths
):
continue
choices_commands.append(name.replace(f"{self._router_name}_", ""))
attributes: dict[str, Any] = {
"CHOICES_GENERATION": True,
"CHOICES_MENUS": choices_menus,
"CHOICES_COMMANDS": choices_commands,
"_factory_backend": self._backend,
"_factory_translators": self._translators,
"_factory_paths": self._paths,
}
return type(self.controller_name, (PlatformController,), attributes)
def _derive_router_name(platform_router: type) -> str:
"""Replicate the legacy class-name → router-name derivation."""
return (
str(type(platform_router))
.rsplit(".", maxsplit=1)[-1]
.replace("'>", "")
.replace("ROUTER_", "")
.lower()
)