mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 04:31:13 +00:00
[Feature] Generate settings menu from pydantic model (#6489)
* Generate settings menu from pydantic model * fix tests * fix test * undo changes --------- Co-authored-by: Henrique Joaquim <henriquecjoaquim@gmail.com>
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
"""Settings Controller Module."""
|
||||
|
||||
import argparse
|
||||
from typing import List, Optional
|
||||
from functools import partial, update_wrapper
|
||||
from types import MethodType
|
||||
from typing import List, Literal, Optional, get_origin
|
||||
|
||||
from openbb_cli.config.constants import AVAILABLE_FLAIRS
|
||||
from openbb_cli.config.menu_text import MenuText
|
||||
|
||||
# pylint: disable=too-many-lines,no-member,too-many-public-methods,C0302
|
||||
# pylint:disable=import-outside-toplevel
|
||||
from openbb_cli.controllers.base_controller import BaseController
|
||||
from openbb_cli.controllers.utils import all_timezones, is_timezone_valid
|
||||
from openbb_cli.models.settings import SettingGroups
|
||||
from openbb_cli.session import Session
|
||||
|
||||
session = Session()
|
||||
@@ -18,356 +16,127 @@ session = Session()
|
||||
class SettingsController(BaseController):
|
||||
"""Settings Controller class."""
|
||||
|
||||
CHOICES_COMMANDS: List[str] = [
|
||||
"interactive",
|
||||
"cls",
|
||||
"promptkit",
|
||||
"exithelp",
|
||||
"rcontext",
|
||||
"richpanel",
|
||||
"tbhint",
|
||||
"overwrite",
|
||||
"version",
|
||||
"console_style",
|
||||
"flair",
|
||||
"timezone",
|
||||
"n_rows",
|
||||
"n_cols",
|
||||
"obbject_msg",
|
||||
"obbject_res",
|
||||
"obbject_display",
|
||||
]
|
||||
_COMMANDS = {
|
||||
v.json_schema_extra.get("command"): {
|
||||
"command": (v.json_schema_extra or {}).get("command"),
|
||||
"group": (v.json_schema_extra or {}).get("group"),
|
||||
"description": v.description,
|
||||
"annotation": v.annotation,
|
||||
"field_name": k,
|
||||
}
|
||||
for k, v in sorted(
|
||||
session.settings.model_fields.items(),
|
||||
key=lambda item: (item[1].json_schema_extra or {}).get("command", ""),
|
||||
)
|
||||
if v.json_schema_extra
|
||||
}
|
||||
CHOICES_COMMANDS: List[str] = list(_COMMANDS.keys())
|
||||
PATH = "/settings/"
|
||||
CHOICES_GENERATION = True
|
||||
|
||||
def __init__(self, queue: Optional[List[str]] = None):
|
||||
"""Initialize the Constructor."""
|
||||
super().__init__(queue)
|
||||
|
||||
for cmd, field in self._COMMANDS.items():
|
||||
group = field.get("group")
|
||||
if group == SettingGroups.feature_flags:
|
||||
self._generate_command(cmd, field, "toggle")
|
||||
elif group == SettingGroups.preferences:
|
||||
self._generate_command(cmd, field, "set")
|
||||
self.update_completer(self.choices_default)
|
||||
|
||||
def print_help(self):
|
||||
"""Print help."""
|
||||
settings = session.settings
|
||||
|
||||
mt = MenuText("settings/")
|
||||
mt.add_info("Feature flags")
|
||||
mt.add_setting(
|
||||
"interactive",
|
||||
settings.USE_INTERACTIVE_DF,
|
||||
description="open dataframes in interactive window",
|
||||
)
|
||||
mt.add_setting(
|
||||
"cls",
|
||||
settings.USE_CLEAR_AFTER_CMD,
|
||||
description="clear console after each command",
|
||||
)
|
||||
mt.add_setting(
|
||||
"promptkit",
|
||||
settings.USE_PROMPT_TOOLKIT,
|
||||
description="enable prompt toolkit (autocomplete and history)",
|
||||
)
|
||||
mt.add_setting(
|
||||
"exithelp",
|
||||
settings.ENABLE_EXIT_AUTO_HELP,
|
||||
description="automatically print help when quitting menu",
|
||||
)
|
||||
mt.add_setting(
|
||||
"rcontext",
|
||||
settings.REMEMBER_CONTEXTS,
|
||||
description="remember contexts between menus",
|
||||
)
|
||||
mt.add_setting(
|
||||
"richpanel",
|
||||
settings.ENABLE_RICH_PANEL,
|
||||
description="colorful rich CLI panel",
|
||||
)
|
||||
mt.add_setting(
|
||||
"tbhint",
|
||||
settings.TOOLBAR_HINT,
|
||||
description="displays usage hints in the bottom toolbar",
|
||||
)
|
||||
mt.add_setting(
|
||||
"overwrite",
|
||||
settings.FILE_OVERWRITE,
|
||||
description="whether to overwrite Excel files if they already exists",
|
||||
)
|
||||
mt.add_setting(
|
||||
"version",
|
||||
settings.SHOW_VERSION,
|
||||
description="whether to show the version in the bottom right corner",
|
||||
)
|
||||
mt.add_setting(
|
||||
"obbject_msg",
|
||||
settings.SHOW_MSG_OBBJECT_REGISTRY,
|
||||
description="show obbject registry message after a new result is added",
|
||||
)
|
||||
mt.add_info("Feature Flags")
|
||||
for k, f in self._COMMANDS.items():
|
||||
if f.get("group") == SettingGroups.feature_flags:
|
||||
mt.add_setting(
|
||||
name=k,
|
||||
status=getattr(session.settings, f["field_name"]),
|
||||
description=f["description"],
|
||||
)
|
||||
mt.add_raw("\n")
|
||||
mt.add_info("Preferences")
|
||||
mt.add_cmd("console_style", description="apply a custom rich style to the CLI")
|
||||
mt.add_cmd("flair", description="choose flair icon")
|
||||
mt.add_cmd("timezone", description="pick timezone")
|
||||
mt.add_cmd(
|
||||
"n_rows", description="number of rows to show on non interactive tables"
|
||||
)
|
||||
mt.add_cmd(
|
||||
"n_cols", description="number of columns to show on non interactive tables"
|
||||
)
|
||||
mt.add_cmd(
|
||||
"obbject_res",
|
||||
description="define the maximum number of obbjects allowed in the registry",
|
||||
)
|
||||
mt.add_cmd(
|
||||
"obbject_display",
|
||||
description="define the maximum number of cached results to display on the help menu",
|
||||
)
|
||||
|
||||
for k, f in self._COMMANDS.items():
|
||||
if f.get("group") == SettingGroups.preferences:
|
||||
mt.add_cmd(
|
||||
name=k,
|
||||
description=f["description"],
|
||||
)
|
||||
session.console.print(text=mt.menu_text, menu="Settings")
|
||||
|
||||
def call_overwrite(self, _):
|
||||
"""Process overwrite command."""
|
||||
session.settings.set_item("FILE_OVERWRITE", not session.settings.FILE_OVERWRITE)
|
||||
def _generate_command(
|
||||
self, cmd_name: str, field: dict, action_type: Literal["toggle", "set"]
|
||||
):
|
||||
"""Generate command call."""
|
||||
|
||||
def call_version(self, _):
|
||||
"""Process version command."""
|
||||
session.settings.SHOW_VERSION = not session.settings.SHOW_VERSION
|
||||
|
||||
def call_interactive(self, _):
|
||||
"""Process interactive command."""
|
||||
session.settings.set_item(
|
||||
"USE_INTERACTIVE_DF", not session.settings.USE_INTERACTIVE_DF
|
||||
)
|
||||
|
||||
def call_cls(self, _):
|
||||
"""Process cls command."""
|
||||
session.settings.set_item(
|
||||
"USE_CLEAR_AFTER_CMD", not session.settings.USE_CLEAR_AFTER_CMD
|
||||
)
|
||||
|
||||
def call_promptkit(self, _):
|
||||
"""Process promptkit command."""
|
||||
session.settings.set_item(
|
||||
"USE_PROMPT_TOOLKIT", not session.settings.USE_PROMPT_TOOLKIT
|
||||
)
|
||||
|
||||
def call_exithelp(self, _):
|
||||
"""Process exithelp command."""
|
||||
session.settings.set_item(
|
||||
"ENABLE_EXIT_AUTO_HELP", not session.settings.ENABLE_EXIT_AUTO_HELP
|
||||
)
|
||||
|
||||
def call_rcontext(self, _):
|
||||
"""Process rcontext command."""
|
||||
session.settings.set_item(
|
||||
"REMEMBER_CONTEXTS", not session.settings.REMEMBER_CONTEXTS
|
||||
)
|
||||
|
||||
def call_dt(self, _):
|
||||
"""Process dt command."""
|
||||
session.settings.set_item("USE_DATETIME", not session.settings.USE_DATETIME)
|
||||
|
||||
def call_richpanel(self, _):
|
||||
"""Process richpanel command."""
|
||||
session.settings.set_item(
|
||||
"ENABLE_RICH_PANEL", not session.settings.ENABLE_RICH_PANEL
|
||||
)
|
||||
|
||||
def call_tbhint(self, _):
|
||||
"""Process tbhint command."""
|
||||
if session.settings.TOOLBAR_HINT:
|
||||
session.console.print("Will take effect when running CLI again.")
|
||||
session.settings.set_item("TOOLBAR_HINT", not session.settings.TOOLBAR_HINT)
|
||||
|
||||
def call_obbject_msg(self, _):
|
||||
"""Process obbject_msg command."""
|
||||
session.settings.set_item(
|
||||
"SHOW_MSG_OBBJECT_REGISTRY",
|
||||
not session.settings.SHOW_MSG_OBBJECT_REGISTRY,
|
||||
)
|
||||
|
||||
def call_console_style(self, other_args: List[str]) -> None:
|
||||
"""Process cosole_style command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="console_style",
|
||||
description="Change your custom console style.",
|
||||
add_help=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--style",
|
||||
dest="style",
|
||||
action="store",
|
||||
required=False,
|
||||
choices=session.style.available_styles,
|
||||
)
|
||||
ns_parser = self.parse_simple_args(parser, other_args)
|
||||
|
||||
if ns_parser and ns_parser.style:
|
||||
session.style.apply(ns_parser.style)
|
||||
session.settings.set_item("RICH_STYLE", ns_parser.style)
|
||||
elif not other_args:
|
||||
session.console.print(
|
||||
f"Current console style: {session.settings.RICH_STYLE}"
|
||||
def _toggle(self, other_args: List[str], field=field) -> None:
|
||||
"""Toggle setting value."""
|
||||
field_name = field["field_name"]
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog=field["command"],
|
||||
description=field["description"],
|
||||
add_help=False,
|
||||
)
|
||||
|
||||
def call_flair(self, other_args: List[str]) -> None:
|
||||
"""Process flair command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="flair",
|
||||
description="Change your custom flair.",
|
||||
add_help=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--flair",
|
||||
dest="flair",
|
||||
action="store",
|
||||
required=False,
|
||||
choices=list(AVAILABLE_FLAIRS.keys()),
|
||||
)
|
||||
ns_parser = self.parse_simple_args(parser, other_args)
|
||||
|
||||
if ns_parser and ns_parser.flair:
|
||||
session.settings.set_item("FLAIR", ns_parser.flair)
|
||||
elif not other_args:
|
||||
session.console.print(f"Current flair: {session.settings.FLAIR}")
|
||||
|
||||
def call_timezone(self, other_args: List[str]) -> None:
|
||||
"""Process timezone command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="timezone",
|
||||
description="Change your custom timezone.",
|
||||
add_help=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--timezone",
|
||||
dest="timezone",
|
||||
action="store",
|
||||
required=False,
|
||||
type=str,
|
||||
choices=all_timezones,
|
||||
)
|
||||
ns_parser = self.parse_simple_args(parser, other_args)
|
||||
|
||||
if ns_parser and ns_parser.timezone:
|
||||
if is_timezone_valid(ns_parser.timezone):
|
||||
session.settings.set_item("TIMEZONE", ns_parser.timezone)
|
||||
else:
|
||||
session.console.print(
|
||||
"Invalid timezone. Please enter a valid timezone."
|
||||
ns_parser = self.parse_simple_args(parser, other_args)
|
||||
if ns_parser:
|
||||
session.settings.set_item(
|
||||
field_name, not getattr(session.settings, field_name)
|
||||
)
|
||||
session.console.print(
|
||||
f"Available timezones are: {', '.join(all_timezones)}"
|
||||
)
|
||||
elif not other_args:
|
||||
session.console.print(f"Current timezone: {session.settings.TIMEZONE}")
|
||||
|
||||
def call_n_rows(self, other_args: List[str]) -> None:
|
||||
"""Process n_rows command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="n_rows",
|
||||
description="Number of rows to show (when not using interactive tables).",
|
||||
add_help=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--rows",
|
||||
dest="rows",
|
||||
action="store",
|
||||
required=False,
|
||||
type=int,
|
||||
)
|
||||
ns_parser = self.parse_simple_args(parser, other_args)
|
||||
|
||||
if ns_parser and ns_parser.rows:
|
||||
session.settings.set_item("ALLOWED_NUMBER_OF_ROWS", ns_parser.rows)
|
||||
|
||||
elif not other_args:
|
||||
session.console.print(
|
||||
f"Current number of rows: {session.settings.ALLOWED_NUMBER_OF_ROWS}"
|
||||
def _set(self, other_args: List[str], field=field) -> None:
|
||||
"""Set preference value."""
|
||||
field_name = field["field_name"]
|
||||
annotation = field["annotation"]
|
||||
command = field["command"]
|
||||
type_ = str if get_origin(annotation) is Literal else annotation
|
||||
choices = None
|
||||
if get_origin(annotation) is Literal:
|
||||
choices = annotation.__args__
|
||||
elif command == "console_style":
|
||||
# To have updated choices for console style
|
||||
choices = session.style.available_styles
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog=command,
|
||||
description=field["description"],
|
||||
add_help=False,
|
||||
)
|
||||
|
||||
def call_n_cols(self, other_args: List[str]) -> None:
|
||||
"""Process n_cols command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="n_cols",
|
||||
description="Number of columns to show (when not using interactive tables).",
|
||||
add_help=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--columns",
|
||||
dest="columns",
|
||||
action="store",
|
||||
required=False,
|
||||
type=int,
|
||||
)
|
||||
ns_parser = self.parse_simple_args(parser, other_args)
|
||||
|
||||
if ns_parser and ns_parser.columns:
|
||||
session.settings.set_item("ALLOWED_NUMBER_OF_COLUMNS", ns_parser.columns)
|
||||
|
||||
elif not other_args:
|
||||
session.console.print(
|
||||
f"Current number of columns: {session.settings.ALLOWED_NUMBER_OF_COLUMNS}"
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--value",
|
||||
dest="value",
|
||||
action="store",
|
||||
required=False,
|
||||
type=type_, # type: ignore[arg-type]
|
||||
choices=choices,
|
||||
)
|
||||
ns_parser = self.parse_simple_args(parser, other_args)
|
||||
if ns_parser:
|
||||
if ns_parser.value:
|
||||
# Console style is applied immediately
|
||||
if command == "console_style":
|
||||
session.style.apply(ns_parser.value)
|
||||
session.settings.set_item(field_name, ns_parser.value)
|
||||
session.console.print(
|
||||
f"[info]Current value:[/info] {getattr(session.settings, field_name)}"
|
||||
)
|
||||
elif not other_args:
|
||||
session.console.print(
|
||||
f"[info]Current value:[/info] {getattr(session.settings, field_name)}"
|
||||
)
|
||||
|
||||
def call_obbject_res(self, other_args: List[str]):
|
||||
"""Process obbject_res command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="obbject_res",
|
||||
description="Maximum allowed number of results to keep in the OBBject Registry.",
|
||||
add_help=False,
|
||||
action = None
|
||||
if action_type == "toggle":
|
||||
action = _toggle
|
||||
elif action_type == "set":
|
||||
action = _set
|
||||
else:
|
||||
raise ValueError(f"Action type '{action_type}' not allowed.")
|
||||
|
||||
bound_method = update_wrapper(
|
||||
wrapper=partial(MethodType(action, self), field=field), wrapped=action
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--number",
|
||||
dest="number",
|
||||
action="store",
|
||||
required=False,
|
||||
type=int,
|
||||
)
|
||||
ns_parser = self.parse_simple_args(parser, other_args)
|
||||
|
||||
if ns_parser and ns_parser.number:
|
||||
session.settings.set_item("N_TO_KEEP_OBBJECT_REGISTRY", ns_parser.number)
|
||||
|
||||
elif not other_args:
|
||||
session.console.print(
|
||||
f"Current maximum allowed number of results to keep in the OBBject registry:"
|
||||
f" {session.settings.N_TO_KEEP_OBBJECT_REGISTRY}"
|
||||
)
|
||||
|
||||
def call_obbject_display(self, other_args: List[str]):
|
||||
"""Process obbject_display command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="obbject_display",
|
||||
description="Number of results to display from the OBBject Registry.",
|
||||
add_help=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--number",
|
||||
dest="number",
|
||||
action="store",
|
||||
required=False,
|
||||
type=int,
|
||||
)
|
||||
ns_parser = self.parse_simple_args(parser, other_args)
|
||||
|
||||
if ns_parser and ns_parser.number:
|
||||
session.settings.set_item("N_TO_DISPLAY_OBBJECT_REGISTRY", ns_parser.number)
|
||||
|
||||
elif not other_args:
|
||||
session.console.print(
|
||||
f"Current number of results to display from the OBBject registry:"
|
||||
f" {session.settings.N_TO_DISPLAY_OBBJECT_REGISTRY}"
|
||||
)
|
||||
setattr(self, f"call_{cmd_name}", bound_method)
|
||||
|
||||
@@ -16,6 +16,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import requests
|
||||
from openbb import obb
|
||||
from openbb_charting.core.backend import create_backend, get_backend
|
||||
from openbb_cli.config.constants import AVAILABLE_FLAIRS, ENV_FILE_SETTINGS
|
||||
from openbb_cli.session import Session
|
||||
@@ -23,8 +24,6 @@ from openbb_core.app.model.charts.charting_settings import ChartingSettings
|
||||
from pytz import all_timezones, timezone
|
||||
from rich.table import Table
|
||||
|
||||
from openbb import obb
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from openbb_charting.core.openbb_figure import OpenBBFigure
|
||||
|
||||
@@ -33,6 +32,8 @@ if TYPE_CHECKING:
|
||||
|
||||
# pylint: disable=too-many-statements,no-member,too-many-branches,C0302
|
||||
|
||||
session = Session()
|
||||
|
||||
|
||||
def remove_file(path: Path) -> bool:
|
||||
"""Remove path.
|
||||
@@ -55,7 +56,7 @@ def remove_file(path: Path) -> bool:
|
||||
shutil.rmtree(path)
|
||||
return True
|
||||
except Exception:
|
||||
Session().console.print(
|
||||
session.console.print(
|
||||
f"\n[bold red]Failed to remove {path}"
|
||||
"\nPlease delete this manually![/bold red]"
|
||||
)
|
||||
@@ -88,17 +89,17 @@ Please feel free to check out our other products:
|
||||
[bold]OpenBB Platform:[/] [cmds]https://openbb.co/products/platform[/cmds]
|
||||
[bold]OpenBB Bot[/]: [cmds]https://openbb.co/products/bot[/cmds]
|
||||
"""
|
||||
Session().console.print(text)
|
||||
session.console.print(text)
|
||||
|
||||
|
||||
def print_guest_block_msg():
|
||||
"""Block guest users from using the cli."""
|
||||
if Session().is_local():
|
||||
Session().console.print(
|
||||
if session.is_local():
|
||||
session.console.print(
|
||||
"[info]You are currently logged as a guest.[/info]\n"
|
||||
"[info]Login to use this feature.[/info]\n\n"
|
||||
"[info]If you don't have an account, you can create one here: [/info]"
|
||||
f"[cmds]{Session().settings.HUB_URL + '/register'}\n[/cmds]"
|
||||
f"[cmds]{session.settings.HUB_URL + '/register'}\n[/cmds]"
|
||||
)
|
||||
|
||||
|
||||
@@ -115,7 +116,7 @@ def bootup():
|
||||
# pylint: disable=E1101
|
||||
sys.stdout.reconfigure(encoding="utf-8") # type: ignore
|
||||
except Exception as e:
|
||||
Session().console.print(e, "\n")
|
||||
session.console.print(e, "\n")
|
||||
|
||||
|
||||
def welcome_message():
|
||||
@@ -123,8 +124,8 @@ def welcome_message():
|
||||
|
||||
Prints first welcome message, help and a notification if updates are available.
|
||||
"""
|
||||
Session().console.print(
|
||||
f"\nWelcome to OpenBB Platform CLI v{Session().settings.VERSION}"
|
||||
session.console.print(
|
||||
f"\nWelcome to OpenBB Platform CLI v{session.settings.VERSION}"
|
||||
)
|
||||
|
||||
|
||||
@@ -133,16 +134,15 @@ def reset(queue: Optional[List[str]] = None):
|
||||
|
||||
Allows for checking code without quitting.
|
||||
"""
|
||||
Session().console.print("resetting...")
|
||||
Session().reset()
|
||||
debug = Session().settings.DEBUG_MODE
|
||||
dev = Session().settings.DEV_BACKEND
|
||||
session.console.print("resetting...")
|
||||
debug = session.settings.DEBUG_MODE
|
||||
dev = session.settings.DEV_BACKEND
|
||||
|
||||
try:
|
||||
# remove the hub routines
|
||||
if not Session().is_local():
|
||||
if not session.is_local():
|
||||
remove_file(
|
||||
Path(Session().user.preferences.export_directory, "routines", "hub")
|
||||
Path(session.user.preferences.export_directory, "routines", "hub")
|
||||
)
|
||||
|
||||
# if not get_current_user().profile.remember:
|
||||
@@ -157,7 +157,7 @@ def reset(queue: Optional[List[str]] = None):
|
||||
queue_list = ["/".join(queue) if len(queue) > 0 else ""] # type: ignore
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# we run the cli again
|
||||
if Session().is_local():
|
||||
if session.is_local():
|
||||
from openbb_cli.controllers.cli_controller import main
|
||||
|
||||
main(debug, dev, queue_list, module="") # type: ignore
|
||||
@@ -167,7 +167,7 @@ def reset(queue: Optional[List[str]] = None):
|
||||
launch(queue=queue_list)
|
||||
|
||||
except Exception as e:
|
||||
Session().console.print(f"Unfortunately, resetting wasn't possible: {e}\n")
|
||||
session.console.print(f"Unfortunately, resetting wasn't possible: {e}\n")
|
||||
print_goodbye()
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ def first_time_user() -> bool:
|
||||
Whether or not the user is a first time user
|
||||
"""
|
||||
if ENV_FILE_SETTINGS.stat().st_size == 0:
|
||||
Session().settings.set_item("PREVIOUS_USE", True)
|
||||
session.settings.set_item("PREVIOUS_USE", True)
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -258,7 +258,7 @@ def parse_and_split_input(an_input: str, custom_filters: List) -> List[str]:
|
||||
else:
|
||||
filter_input = False
|
||||
|
||||
commands = an_input.split("/") if "-t" not in an_input else [an_input]
|
||||
commands = an_input.split("/") if "timezone" not in an_input else [an_input]
|
||||
|
||||
for command_num, command in enumerate(commands):
|
||||
if command == commands[command_num] == commands[-1] == "":
|
||||
@@ -367,8 +367,8 @@ def print_rich_table( # noqa: PLR0912
|
||||
if export:
|
||||
return
|
||||
|
||||
MAX_COLS = Session().settings.ALLOWED_NUMBER_OF_COLUMNS
|
||||
MAX_ROWS = Session().settings.ALLOWED_NUMBER_OF_ROWS
|
||||
MAX_COLS = session.settings.ALLOWED_NUMBER_OF_COLUMNS
|
||||
MAX_ROWS = session.settings.ALLOWED_NUMBER_OF_ROWS
|
||||
|
||||
# Make a copy of the dataframe to avoid SettingWithCopyWarning
|
||||
df = df.copy()
|
||||
@@ -397,7 +397,7 @@ def print_rich_table( # noqa: PLR0912
|
||||
raise ValueError("Length of headers does not match length of DataFrame.")
|
||||
return output
|
||||
|
||||
if Session().settings.USE_INTERACTIVE_DF:
|
||||
if session.settings.USE_INTERACTIVE_DF:
|
||||
df_outgoing = df.copy()
|
||||
# If headers are provided, use them
|
||||
if headers is not None:
|
||||
@@ -419,7 +419,7 @@ def print_rich_table( # noqa: PLR0912
|
||||
_get_backend().send_table(
|
||||
df_table=df_outgoing,
|
||||
title=title,
|
||||
theme=Session().user.preferences.table_style,
|
||||
theme=session.user.preferences.table_style,
|
||||
)
|
||||
return
|
||||
|
||||
@@ -501,24 +501,24 @@ def print_rich_table( # noqa: PLR0912
|
||||
for idx, x in enumerate(values)
|
||||
]
|
||||
table.add_row(*row_idx)
|
||||
Session().console.print(table)
|
||||
session.console.print(table)
|
||||
else:
|
||||
Session().console.print(df.to_string(col_space=0))
|
||||
session.console.print(df.to_string(col_space=0))
|
||||
|
||||
if exceeds_allowed_columns:
|
||||
Session().console.print(
|
||||
f"[yellow]\nAllowed number of columns exceeded ({Session().settings.ALLOWED_NUMBER_OF_COLUMNS}).\n"
|
||||
session.console.print(
|
||||
f"[yellow]\nAllowed number of columns exceeded ({session.settings.ALLOWED_NUMBER_OF_COLUMNS}).\n"
|
||||
f"The following columns were removed from the output: {', '.join(trimmed_columns)}.\n[/yellow]"
|
||||
)
|
||||
|
||||
if exceeds_allowed_rows:
|
||||
Session().console.print(
|
||||
f"[yellow]\nAllowed number of rows exceeded ({Session().settings.ALLOWED_NUMBER_OF_ROWS}).\n"
|
||||
session.console.print(
|
||||
f"[yellow]\nAllowed number of rows exceeded ({session.settings.ALLOWED_NUMBER_OF_ROWS}).\n"
|
||||
f"{trimmed_rows_count} rows were removed from the output.\n[/yellow]"
|
||||
)
|
||||
|
||||
if exceeds_allowed_columns or exceeds_allowed_rows:
|
||||
Session().console.print(
|
||||
session.console.print(
|
||||
"Use the `--export` flag to analyse the full output on a file."
|
||||
)
|
||||
|
||||
@@ -556,7 +556,7 @@ def get_user_agent() -> str:
|
||||
|
||||
def get_flair() -> str:
|
||||
"""Get a flair icon."""
|
||||
current_flair = str(Session().settings.FLAIR)
|
||||
current_flair = str(session.settings.FLAIR)
|
||||
flair = AVAILABLE_FLAIRS.get(current_flair, current_flair)
|
||||
return flair
|
||||
|
||||
@@ -564,7 +564,7 @@ def get_flair() -> str:
|
||||
def get_dtime() -> str:
|
||||
"""Get a datetime string."""
|
||||
dtime = ""
|
||||
if Session().settings.USE_DATETIME and get_user_timezone_or_invalid() != "INVALID":
|
||||
if session.settings.USE_DATETIME and get_user_timezone_or_invalid() != "INVALID":
|
||||
dtime = datetime.now(timezone(get_user_timezone())).strftime("%Y %b %d, %H:%M")
|
||||
return dtime
|
||||
|
||||
@@ -572,12 +572,10 @@ def get_dtime() -> str:
|
||||
def get_flair_and_username() -> str:
|
||||
"""Get a flair icon and username."""
|
||||
flair = get_flair()
|
||||
dtime = get_dtime()
|
||||
|
||||
if dtime:
|
||||
if dtime := get_dtime():
|
||||
dtime = f"{dtime} "
|
||||
|
||||
username = getattr(Session().user.profile.hub_session, "username", "")
|
||||
username = getattr(session.user.profile.hub_session, "username", "")
|
||||
if username:
|
||||
username = f"[{username}] "
|
||||
|
||||
@@ -608,7 +606,7 @@ def get_user_timezone() -> str:
|
||||
str
|
||||
user timezone based on .env file
|
||||
"""
|
||||
return Session().settings.TIMEZONE
|
||||
return session.settings.TIMEZONE
|
||||
|
||||
|
||||
def get_user_timezone_or_invalid() -> str:
|
||||
@@ -659,7 +657,7 @@ def check_file_type_saved(valid_types: Optional[List[str]] = None):
|
||||
if filename.endswith(tuple(valid_types)):
|
||||
valid_filenames.append(filename)
|
||||
else:
|
||||
Session().console.print(
|
||||
session.console.print(
|
||||
f"[red]Filename '{filename}' provided is not valid!\nPlease use one of the following file types:"
|
||||
f"{','.join(valid_types)}[/red]\n"
|
||||
)
|
||||
@@ -737,7 +735,7 @@ def compose_export_path(func_name: str, dir_path: str) -> Path:
|
||||
|
||||
default_filename = f"{now.strftime('%Y%m%d_%H%M%S')}_{path_cmd}_{func_name}"
|
||||
|
||||
full_path = Path(Session().user.preferences.export_directory) / default_filename
|
||||
full_path = Path(session.user.preferences.export_directory) / default_filename
|
||||
|
||||
return full_path
|
||||
|
||||
@@ -748,9 +746,9 @@ def ask_file_overwrite(file_path: Path) -> Tuple[bool, bool]:
|
||||
Returns two values, the first is a boolean indicating if the file exists and the
|
||||
second is a boolean indicating if the user wants to overwrite the file.
|
||||
"""
|
||||
if Session().settings.FILE_OVERWRITE:
|
||||
if session.settings.FILE_OVERWRITE:
|
||||
return False, True
|
||||
if Session().settings.TEST_MODE:
|
||||
if session.settings.TEST_MODE:
|
||||
return False, True
|
||||
if file_path.exists():
|
||||
overwrite = input("\nFile already exists. Overwrite? [y/n]: ").lower()
|
||||
@@ -902,17 +900,17 @@ def export_data(
|
||||
|
||||
elif saved_path.suffix in [".jpg", ".png"]:
|
||||
if figure is None:
|
||||
Session().console.print("No plot to export.")
|
||||
session.console.print("No plot to export.")
|
||||
continue
|
||||
figure.show(export_image=saved_path, margin=margin)
|
||||
else:
|
||||
Session().console.print("Wrong export file specified.")
|
||||
session.console.print("Wrong export file specified.")
|
||||
continue
|
||||
|
||||
if saved_path.exists():
|
||||
Session().console.print(f"Saved file: {saved_path}")
|
||||
session.console.print(f"Saved file: {saved_path}")
|
||||
else:
|
||||
Session().console.print(f"Failed to save file: {saved_path}")
|
||||
session.console.print(f"Failed to save file: {saved_path}")
|
||||
|
||||
if figure is not None:
|
||||
figure._exported = True # pylint: disable=protected-access
|
||||
@@ -956,7 +954,7 @@ def request(
|
||||
# If there are headers, check if there is a user agent, if not add one.
|
||||
# Some requests seem to work only with a specific user agent, so we want to be able to override it.
|
||||
headers = kwargs.pop("headers", {})
|
||||
timeout = timeout or Session().user.preferences.request_timeout
|
||||
timeout = timeout or session.user.preferences.request_timeout
|
||||
|
||||
if "User-Agent" not in headers:
|
||||
headers["User-Agent"] = get_user_agent()
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
"""Settings model."""
|
||||
|
||||
from typing import Any
|
||||
from enum import Enum
|
||||
from typing import Any, Literal
|
||||
|
||||
from dotenv import dotenv_values, set_key
|
||||
from openbb_cli.config.constants import ENV_FILE_SETTINGS
|
||||
from openbb_cli.config.constants import AVAILABLE_FLAIRS, ENV_FILE_SETTINGS
|
||||
from openbb_core.app.version import get_package_version
|
||||
from pydantic import BaseModel, ConfigDict, model_validator
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from pytz import all_timezones
|
||||
|
||||
VERSION = get_package_version("openbb-cli")
|
||||
|
||||
|
||||
class SettingGroups(Enum):
|
||||
"""Setting types."""
|
||||
|
||||
feature_flags = "feature_flag"
|
||||
preferences = "preference"
|
||||
|
||||
|
||||
class Settings(BaseModel):
|
||||
"""Settings model."""
|
||||
|
||||
@@ -21,37 +30,125 @@ class Settings(BaseModel):
|
||||
DEBUG_MODE: bool = False
|
||||
DEV_BACKEND: bool = False
|
||||
|
||||
# FEATURE FLAGS
|
||||
FILE_OVERWRITE: bool = False
|
||||
SHOW_VERSION: bool = True
|
||||
USE_INTERACTIVE_DF: bool = True
|
||||
USE_CLEAR_AFTER_CMD: bool = False
|
||||
USE_DATETIME: bool = True
|
||||
USE_PROMPT_TOOLKIT: bool = True
|
||||
ENABLE_EXIT_AUTO_HELP: bool = True
|
||||
REMEMBER_CONTEXTS: bool = True
|
||||
ENABLE_RICH_PANEL: bool = True
|
||||
TOOLBAR_HINT: bool = True
|
||||
SHOW_MSG_OBBJECT_REGISTRY: bool = False
|
||||
|
||||
# GENERAL
|
||||
TIMEZONE: str = "America/New_York"
|
||||
FLAIR: str = ":openbb"
|
||||
PREVIOUS_USE: bool = False
|
||||
N_TO_KEEP_OBBJECT_REGISTRY: int = 10
|
||||
N_TO_DISPLAY_OBBJECT_REGISTRY: int = 5
|
||||
|
||||
# STYLE
|
||||
RICH_STYLE: str = "dark"
|
||||
|
||||
# OUTPUT
|
||||
ALLOWED_NUMBER_OF_ROWS: int = 20
|
||||
ALLOWED_NUMBER_OF_COLUMNS: int = 5
|
||||
|
||||
# OPENBB
|
||||
HUB_URL: str = "https://my.openbb.co"
|
||||
BASE_URL: str = "https://payments.openbb.co"
|
||||
|
||||
# GENERAL
|
||||
PREVIOUS_USE: bool = False
|
||||
|
||||
# FEATURE FLAGS
|
||||
FILE_OVERWRITE: bool = Field(
|
||||
default=False,
|
||||
description="whether to overwrite Excel files if they already exists",
|
||||
command="overwrite",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
SHOW_VERSION: bool = Field(
|
||||
default=True,
|
||||
description="whether to show the version in the bottom right corner",
|
||||
command="version",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
USE_INTERACTIVE_DF: bool = Field(
|
||||
default=True,
|
||||
description="display tables in interactive window",
|
||||
command="interactive",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
USE_CLEAR_AFTER_CMD: bool = Field(
|
||||
default=False,
|
||||
description="clear console after each command",
|
||||
command="cls",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
USE_DATETIME: bool = Field(
|
||||
default=True,
|
||||
description="whether to show the date and time before the flair",
|
||||
command="datetime",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
USE_PROMPT_TOOLKIT: bool = Field(
|
||||
default=True,
|
||||
description="enable prompt toolkit (autocomplete and history)",
|
||||
command="promptkit",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
ENABLE_EXIT_AUTO_HELP: bool = Field(
|
||||
default=True,
|
||||
description="automatically print help when quitting menu",
|
||||
command="exithelp",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
REMEMBER_CONTEXTS: bool = Field(
|
||||
default=True,
|
||||
description="remember contexts between menus",
|
||||
command="rcontext",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
ENABLE_RICH_PANEL: bool = Field(
|
||||
default=True,
|
||||
description="enable colorful rich CLI panel",
|
||||
command="richpanel",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
TOOLBAR_HINT: bool = Field(
|
||||
default=True,
|
||||
description="displays usage hints in the bottom toolbar",
|
||||
command="tbhint",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
SHOW_MSG_OBBJECT_REGISTRY: bool = Field(
|
||||
default=False,
|
||||
description="show obbject registry message after a new result is added",
|
||||
command="obbject_msg",
|
||||
group=SettingGroups.feature_flags,
|
||||
)
|
||||
|
||||
# PREFERENCES
|
||||
TIMEZONE: Literal[tuple(all_timezones)] = Field( # type: ignore[valid-type]
|
||||
default="America/New_York",
|
||||
description="pick timezone",
|
||||
command="timezone",
|
||||
group=SettingGroups.preferences,
|
||||
)
|
||||
FLAIR: Literal[tuple(AVAILABLE_FLAIRS)] = Field( # type: ignore[valid-type]
|
||||
default=":openbb",
|
||||
description="choose flair icon",
|
||||
command="flair",
|
||||
group=SettingGroups.preferences,
|
||||
)
|
||||
N_TO_KEEP_OBBJECT_REGISTRY: int = Field(
|
||||
default=10,
|
||||
description="define the maximum number of obbjects allowed in the registry",
|
||||
command="obbject_res",
|
||||
group=SettingGroups.preferences,
|
||||
)
|
||||
N_TO_DISPLAY_OBBJECT_REGISTRY: int = Field(
|
||||
default=5,
|
||||
description="define the maximum number of cached results to display on the help menu",
|
||||
command="obbject_display",
|
||||
group=SettingGroups.preferences,
|
||||
)
|
||||
RICH_STYLE: str = Field(
|
||||
default="dark",
|
||||
description="apply a custom rich style to the CLI",
|
||||
command="console_style",
|
||||
group=SettingGroups.preferences,
|
||||
)
|
||||
ALLOWED_NUMBER_OF_ROWS: int = Field(
|
||||
default=20,
|
||||
description="number of rows to show (when not using interactive tables).",
|
||||
command="n_rows",
|
||||
group=SettingGroups.preferences,
|
||||
)
|
||||
ALLOWED_NUMBER_OF_COLUMNS: int = Field(
|
||||
default=5,
|
||||
description="number of columns to show (when not using interactive tables).",
|
||||
command="n_cols",
|
||||
group=SettingGroups.preferences,
|
||||
)
|
||||
|
||||
model_config = ConfigDict(validate_assignment=True)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
||||
@@ -5,7 +5,7 @@ from unittest.mock import MagicMock, patch
|
||||
import pytest
|
||||
from openbb_cli.controllers.settings_controller import SettingsController
|
||||
|
||||
# pylint: disable=redefined-outer-name, unused-argument
|
||||
# pylint: disable=redefined-outer-name, unused-argument, no-member
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -14,7 +14,7 @@ def mock_session():
|
||||
mock.settings.USE_INTERACTIVE_DF = False
|
||||
mock.settings.ALLOWED_NUMBER_OF_ROWS = 20
|
||||
mock.settings.TIMEZONE = "UTC"
|
||||
|
||||
mock.style.available_styles = ["dark"]
|
||||
mock.settings.set_item = MagicMock()
|
||||
|
||||
yield mock
|
||||
@@ -35,7 +35,7 @@ def test_call_interactive(mock_session):
|
||||
)
|
||||
def test_call_n_rows(input_rows, expected, mock_session):
|
||||
controller = SettingsController()
|
||||
args = ["--rows", str(input_rows)]
|
||||
args = ["--value", str(input_rows)]
|
||||
controller.call_n_rows(args)
|
||||
mock_session.settings.set_item.assert_called_with(
|
||||
"ALLOWED_NUMBER_OF_ROWS", expected
|
||||
@@ -45,7 +45,7 @@ def test_call_n_rows(input_rows, expected, mock_session):
|
||||
def test_call_n_rows_no_args_provided(mock_session):
|
||||
controller = SettingsController()
|
||||
controller.call_n_rows([])
|
||||
mock_session.console.print.assert_called_with("Current number of rows: 20")
|
||||
mock_session.console.print.assert_called_with("[info]Current value:[/info] 20")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -56,22 +56,18 @@ def test_call_n_rows_no_args_provided(mock_session):
|
||||
],
|
||||
)
|
||||
def test_call_timezone(timezone, valid, mock_session):
|
||||
with patch(
|
||||
"openbb_cli.controllers.settings_controller.is_timezone_valid",
|
||||
return_value=valid,
|
||||
):
|
||||
controller = SettingsController()
|
||||
args = ["--timezone", timezone]
|
||||
controller.call_timezone(args)
|
||||
if valid:
|
||||
mock_session.settings.set_item.assert_called_with("TIMEZONE", timezone)
|
||||
else:
|
||||
mock_session.settings.set_item.assert_not_called()
|
||||
controller = SettingsController()
|
||||
args = ["--value", timezone]
|
||||
controller.call_timezone(args)
|
||||
if valid:
|
||||
mock_session.settings.set_item.assert_called_with("TIMEZONE", timezone)
|
||||
else:
|
||||
mock_session.settings.set_item.assert_not_called()
|
||||
|
||||
|
||||
def test_call_console_style(mock_session):
|
||||
controller = SettingsController()
|
||||
args = ["--style", "dark"]
|
||||
args = ["--value", "dark"]
|
||||
controller.call_console_style(args)
|
||||
mock_session.console.print.assert_called()
|
||||
|
||||
@@ -80,25 +76,25 @@ def test_call_console_style_no_args(mock_session):
|
||||
mock_session.settings.RICH_STYLE = "default"
|
||||
controller = SettingsController()
|
||||
controller.call_console_style([])
|
||||
mock_session.console.print.assert_called_with("Current console style: default")
|
||||
mock_session.console.print.assert_called_with("[info]Current value:[/info] default")
|
||||
|
||||
|
||||
def test_call_flair(mock_session):
|
||||
controller = SettingsController()
|
||||
args = ["--flair", "rocket"]
|
||||
args = ["--value", "rocket"]
|
||||
controller.call_flair(args)
|
||||
|
||||
|
||||
def test_call_flair_no_args(mock_session):
|
||||
mock_session.settings.FLAIR = "star"
|
||||
mock_session.settings.FLAIR = "bug"
|
||||
controller = SettingsController()
|
||||
controller.call_flair([])
|
||||
mock_session.console.print.assert_called_with("Current flair: star")
|
||||
mock_session.console.print.assert_called_with("[info]Current value:[/info] bug")
|
||||
|
||||
|
||||
def test_call_obbject_display(mock_session):
|
||||
controller = SettingsController()
|
||||
args = ["--number", "5"]
|
||||
args = ["--value", "5"]
|
||||
controller.call_obbject_display(args)
|
||||
mock_session.settings.set_item.assert_called_once_with(
|
||||
"N_TO_DISPLAY_OBBJECT_REGISTRY", 5
|
||||
@@ -109,16 +105,14 @@ def test_call_obbject_display_no_args(mock_session):
|
||||
mock_session.settings.N_TO_DISPLAY_OBBJECT_REGISTRY = 10
|
||||
controller = SettingsController()
|
||||
controller.call_obbject_display([])
|
||||
mock_session.console.print.assert_called_with(
|
||||
"Current number of results to display from the OBBject registry: 10"
|
||||
)
|
||||
mock_session.console.print.assert_called_with("[info]Current value:[/info] 10")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"args, expected",
|
||||
[
|
||||
(["--rows", "50"], 50),
|
||||
(["--rows", "100"], 100),
|
||||
(["--value", "50"], 50),
|
||||
(["--value", "100"], 100),
|
||||
([], 20),
|
||||
],
|
||||
)
|
||||
@@ -131,4 +125,4 @@ def test_call_n_rows_v2(args, expected, mock_session):
|
||||
"ALLOWED_NUMBER_OF_ROWS", expected
|
||||
)
|
||||
else:
|
||||
mock_session.console.print.assert_called_with("Current number of rows: 20")
|
||||
mock_session.console.print.assert_called_with("[info]Current value:[/info] 20")
|
||||
|
||||
@@ -23,13 +23,13 @@ from openbb_cli.controllers.utils import (
|
||||
@pytest.fixture
|
||||
def mock_session():
|
||||
"""Mock the session and its dependencies."""
|
||||
with patch("openbb_cli.controllers.utils.Session", autospec=True) as mock:
|
||||
mock.return_value.console.print = MagicMock()
|
||||
mock.return_value.is_local = MagicMock(return_value=True)
|
||||
mock.return_value.settings.VERSION = "1.0"
|
||||
mock.return_value.user.profile.hub_session.username = "testuser"
|
||||
mock.return_value.settings.FLAIR = "rocket"
|
||||
yield mock
|
||||
with patch("openbb_cli.controllers.utils.session") as mock_session:
|
||||
mock_session.console.print = MagicMock()
|
||||
mock_session.is_local = MagicMock(return_value=True)
|
||||
mock_session.settings.VERSION = "1.0"
|
||||
mock_session.user.profile.hub_session.username = "testuser"
|
||||
mock_session.settings.FLAIR = "rocket"
|
||||
yield mock_session
|
||||
|
||||
|
||||
def test_remove_file_existing_file():
|
||||
@@ -54,13 +54,13 @@ def test_remove_file_failure(mock_session):
|
||||
"os.remove", side_effect=Exception("Error")
|
||||
):
|
||||
assert not remove_file(Path("/path/to/file"))
|
||||
mock_session.return_value.console.print.assert_called()
|
||||
mock_session.console.print.assert_called()
|
||||
|
||||
|
||||
def test_print_goodbye(mock_session):
|
||||
"""Test printing the goodbye message."""
|
||||
print_goodbye()
|
||||
mock_session.return_value.console.print.assert_called()
|
||||
mock_session.console.print.assert_called()
|
||||
|
||||
|
||||
def test_parse_and_split_input():
|
||||
@@ -87,13 +87,13 @@ def test_parse_and_split_input_special_cases(input_command, expected_output):
|
||||
def test_print_guest_block_msg(mock_session):
|
||||
"""Test printing the guest block message."""
|
||||
print_guest_block_msg()
|
||||
mock_session.return_value.console.print.assert_called()
|
||||
mock_session.console.print.assert_called()
|
||||
|
||||
|
||||
def test_welcome_message(mock_session):
|
||||
"""Test printing the welcome message."""
|
||||
welcome_message()
|
||||
mock_session.return_value.console.print.assert_called_with(
|
||||
mock_session.console.print.assert_called_with(
|
||||
"\nWelcome to OpenBB Platform CLI v1.0"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user