mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-30 22:11:55 +00:00
* merge branch develop with main * bump desktop version * fix the merge * sync Cargo.lock to desktop 2.0.0 * fix cli conftest.py --------- Co-authored-by: deeleeramone <>
79 lines
2.6 KiB
Python
79 lines
2.6 KiB
Python
"""In-process dispatcher — resolves commands against the real ``obb`` namespace."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import inspect
|
|
from typing import Any
|
|
|
|
from openbb_cli.dispatchers.protocol import Request, Response, ResponseError
|
|
|
|
|
|
class CommandNotFound(KeyError):
|
|
"""Raised when a dotted command path does not resolve under ``obb``."""
|
|
|
|
|
|
class LocalDispatcher:
|
|
"""Single-tenant in-process dispatcher."""
|
|
|
|
def __init__(self) -> None:
|
|
from openbb import obb
|
|
|
|
self._obb = obb
|
|
|
|
@staticmethod
|
|
def _resolve(root: Any, path: str) -> Any:
|
|
node: Any = root
|
|
for part in path.split("."):
|
|
if not part:
|
|
raise CommandNotFound(f"Empty segment in command path: {path!r}")
|
|
try:
|
|
node = getattr(node, part)
|
|
except AttributeError as exc:
|
|
raise CommandNotFound(
|
|
f"Command not found: {path!r} (failed at {part!r})"
|
|
) from exc
|
|
return node
|
|
|
|
@staticmethod
|
|
def _serialize(value: Any) -> Any:
|
|
if value is None:
|
|
return None
|
|
if hasattr(value, "model_dump"):
|
|
return value.model_dump(exclude_unset=True, exclude_none=True)
|
|
if hasattr(value, "to_dict"):
|
|
return value.to_dict(orient="records")
|
|
return value
|
|
|
|
async def dispatch(self, request: Request) -> Response:
|
|
"""Resolve ``request`` against ``obb`` and execute the leaf command."""
|
|
try:
|
|
command = self._resolve(self._obb, request.command)
|
|
if not callable(command): # pragma: no cover
|
|
raise CommandNotFound(
|
|
f"Resolved {request.command!r} but it is not callable"
|
|
)
|
|
if inspect.iscoroutinefunction(command):
|
|
result = await command(**request.params)
|
|
else:
|
|
result = await asyncio.to_thread(command, **request.params)
|
|
return Response(
|
|
id=request.id, ok=True, result=self._serialize(result), error=None
|
|
)
|
|
except CommandNotFound as exc:
|
|
return Response(
|
|
id=request.id,
|
|
ok=False,
|
|
error=ResponseError(type="CommandNotFound", message=str(exc)),
|
|
)
|
|
except Exception as exc: # noqa: BLE001
|
|
return Response(
|
|
id=request.id,
|
|
ok=False,
|
|
error=ResponseError(type=type(exc).__name__, message=str(exc)),
|
|
)
|
|
|
|
async def aclose(self) -> None:
|
|
"""No-op — the local dispatcher holds no external resources."""
|
|
return None
|