from typing import Any, Optional, List import openbb_terminal.config_terminal as cfg from openbb_terminal.core.library.metadata import Metadata from openbb_terminal.core.library.trail_map import TrailMap from openbb_terminal.core.library.operation import Operation from openbb_terminal import feature_flags as obbff # pylint: disable=import-outside-toplevel class MetadataBuilder: @staticmethod def get_option_list(trail: str, trail_map: TrailMap) -> List[str]: option_list = [] for key in trail_map.map_dict: if trail == "": option = key.split(".")[0] elif key.startswith(trail) and key[len(trail)] == ".": option = key[len(trail) + 1 :].split(".")[0] else: option = None if option: option_list.append(option) return list(set(option_list)) @classmethod def build_dir_list(cls, trail: str, trail_map: TrailMap) -> List[str]: option_list = cls.get_option_list(trail=trail, trail_map=trail_map) option_list_full = [] for option in option_list: option_list_full.append(option) option_view_trail = f"{trail}.{option}_chart" if trail_map.get_view_function(trail=option_view_trail): option_list_full.append(f"{option}_chart") return option_list_full @staticmethod def build_docstring(trail: str, dir_list: List[str]) -> str: if trail == "": docstring = """This is the OpenBB Terminal SDK. Use the SDK to get data directly into your jupyter notebook or directly use it in your application. For more information see the official documentation at: https://openbb-finance.github.io/OpenBBTerminal/SDK/ """ else: docstring = ( trail.rsplit(".")[-1].upper() + " Menu\n\nThe SDK commands of the the menu:" ) for command in dir_list: docstring += f"\n\t.{trail}.{command}" return docstring @classmethod def build(cls, trail: str, trail_map: TrailMap) -> Metadata: dir_list = cls.build_dir_list(trail=trail, trail_map=trail_map) docstring = cls.build_docstring(trail=trail, dir_list=dir_list) metadata = Metadata( dir_list=dir_list, docstring=docstring, ) return metadata class Breadcrumb: __version__ = obbff.VERSION def __init__( self, metadata: Optional[Metadata] = None, trail: str = "", trail_map: Optional[TrailMap] = None, ) -> None: """ Generates a 'trail' that allows accessing OpenBB Terminal SDK methods. Example: openbb.forex.get_currency_list() Breadcrumb(trail="").Breadcrumb(trail="forex").Operation(trail="forex.get_currency_list")() Args: metadata (Optional[Metadata], optional): Object to generate Breadcrumb's metadata (__dir__, __doc__). Defaults to None. trail (str, optional): Current trail of the Breadcrumb. Defaults to "". trail_map (Optional[TrailMap], optional): Mapping with all the trails available and matching models and views. Defaults to None. """ trail_map = trail_map or TrailMap() metadata = metadata or MetadataBuilder.build(trail=trail, trail_map=trail_map) self._metadata = metadata self._trail_map = trail_map self._trail = trail self.__doc__ = metadata.docstring if trail == "": BreadcrumbLogger() def __dir__(self): return self._metadata.dir_list def __getattr__(self, name: str) -> Any: trail = self._trail trail_map = self._trail_map if trail == "": trail_next = name else: trail_next = f"{trail}.{name}" if trail_map.get_model_function( trail=trail_next ) or trail_map.get_view_function(trail=trail_next): next_crumb: Any = Operation( trail=trail_next, trail_map=trail_map, ) elif name in self._metadata.dir_list: next_crumb = Breadcrumb( metadata=MetadataBuilder.build(trail=trail_next, trail_map=trail_map), trail=trail_next, trail_map=trail_map, ) else: raise AttributeError( f"Module or method '{trail}' has no attribute '{name}'." ) return next_crumb def about(self): import webbrowser trail = self._trail url = "https://docs.openbb.co/sdk/reference/" url += "/".join(trail.split(".")) webbrowser.open(url) # pylint: disable=R0903 class BreadcrumbLogger: def __init__(self) -> None: self.__check_initialize_logging() def __check_initialize_logging(self): if not cfg.LOGGING_SUPPRESS: self.__initialize_logging() @staticmethod def __initialize_logging() -> None: from openbb_terminal.loggers import setup_logging # pylint: disable=C0415 from openbb_terminal.core.log.generation.settings_logger import ( # pylint: disable=C0415 log_all_settings, ) cfg.LOGGING_SUB_APP = "sdk" setup_logging() log_all_settings()