openbb-cli 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openbb_cli/__init__.py +1 -0
- openbb_cli/argparse_translator/__init__.py +0 -0
- openbb_cli/argparse_translator/argparse_class_processor.py +148 -0
- openbb_cli/argparse_translator/argparse_translator.py +627 -0
- openbb_cli/argparse_translator/obbject_registry.py +94 -0
- openbb_cli/assets/routines/routine_example.openbb +21 -0
- openbb_cli/assets/styles/default/Consolas.ttf +0 -0
- openbb_cli/assets/styles/default/dark.mpfstyle.json +47 -0
- openbb_cli/assets/styles/default/dark.mplrc.json +7 -0
- openbb_cli/assets/styles/default/dark.mplstyle +96 -0
- openbb_cli/assets/styles/default/dark.pltstyle.json +132 -0
- openbb_cli/assets/styles/default/dark.richstyle.json +9 -0
- openbb_cli/assets/styles/default/light.mpfstyle.json +47 -0
- openbb_cli/assets/styles/default/light.mplrc.json +7 -0
- openbb_cli/assets/styles/default/light.mplstyle +95 -0
- openbb_cli/assets/styles/default/light.pltstyle.json +871 -0
- openbb_cli/assets/styles/default/light.richstyle.json +9 -0
- openbb_cli/assets/styles/default/tables.pltstyle.json +102 -0
- openbb_cli/assets/styles/user/openbb.richstyle.json +9 -0
- openbb_cli/cli.py +20 -0
- openbb_cli/config/__init__.py +1 -0
- openbb_cli/config/completer.py +427 -0
- openbb_cli/config/console.py +93 -0
- openbb_cli/config/constants.py +80 -0
- openbb_cli/config/menu_text.py +165 -0
- openbb_cli/config/setup.py +11 -0
- openbb_cli/config/style.py +108 -0
- openbb_cli/controllers/base_controller.py +941 -0
- openbb_cli/controllers/base_platform_controller.py +349 -0
- openbb_cli/controllers/choices.py +324 -0
- openbb_cli/controllers/cli_controller.py +946 -0
- openbb_cli/controllers/hub_service.py +107 -0
- openbb_cli/controllers/platform_controller_factory.py +58 -0
- openbb_cli/controllers/script_parser.py +488 -0
- openbb_cli/controllers/settings_controller.py +373 -0
- openbb_cli/controllers/utils.py +967 -0
- openbb_cli/models/settings.py +73 -0
- openbb_cli/session.py +92 -0
- openbb_cli-1.0.0.dist-info/METADATA +91 -0
- openbb_cli-1.0.0.dist-info/RECORD +42 -0
- openbb_cli-1.0.0.dist-info/WHEEL +4 -0
- openbb_cli-1.0.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,946 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Main CLI Module."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import contextlib
|
|
6
|
+
import difflib
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
import webbrowser
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from functools import partial, update_wrapper
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from types import MethodType
|
|
17
|
+
from typing import Any, Dict, List, Optional
|
|
18
|
+
|
|
19
|
+
import pandas as pd
|
|
20
|
+
import requests
|
|
21
|
+
from openbb import obb
|
|
22
|
+
from openbb_cli.config import constants
|
|
23
|
+
from openbb_cli.config.constants import (
|
|
24
|
+
ASSETS_DIRECTORY,
|
|
25
|
+
ENV_FILE_SETTINGS,
|
|
26
|
+
HOME_DIRECTORY,
|
|
27
|
+
REPOSITORY_DIRECTORY,
|
|
28
|
+
)
|
|
29
|
+
from openbb_cli.config.menu_text import MenuText
|
|
30
|
+
from openbb_cli.controllers.base_controller import BaseController
|
|
31
|
+
from openbb_cli.controllers.platform_controller_factory import (
|
|
32
|
+
PlatformControllerFactory,
|
|
33
|
+
)
|
|
34
|
+
from openbb_cli.controllers.script_parser import is_reset, parse_openbb_script
|
|
35
|
+
from openbb_cli.controllers.utils import (
|
|
36
|
+
bootup,
|
|
37
|
+
first_time_user,
|
|
38
|
+
get_flair_and_username,
|
|
39
|
+
parse_and_split_input,
|
|
40
|
+
print_goodbye,
|
|
41
|
+
print_rich_table,
|
|
42
|
+
reset,
|
|
43
|
+
suppress_stdout,
|
|
44
|
+
welcome_message,
|
|
45
|
+
)
|
|
46
|
+
from openbb_cli.session import Session
|
|
47
|
+
from prompt_toolkit.formatted_text import HTML
|
|
48
|
+
from prompt_toolkit.styles import Style
|
|
49
|
+
from pydantic import BaseModel
|
|
50
|
+
|
|
51
|
+
PLATFORM_ROUTERS = {
|
|
52
|
+
d: "menu" if not isinstance(getattr(obb, d), BaseModel) else "command"
|
|
53
|
+
for d in dir(obb)
|
|
54
|
+
if "_" not in d
|
|
55
|
+
}
|
|
56
|
+
NON_DATA_ROUTERS = ["coverage", "account", "reference", "system", "user"]
|
|
57
|
+
DATA_PROCESSING_ROUTERS = ["technical", "quantitative", "econometrics"]
|
|
58
|
+
|
|
59
|
+
# pylint: disable=too-many-public-methods,import-outside-toplevel, too-many-function-args
|
|
60
|
+
# pylint: disable=too-many-branches,no-member,C0302,too-many-return-statements, inconsistent-return-statements
|
|
61
|
+
|
|
62
|
+
logger = logging.getLogger(__name__)
|
|
63
|
+
|
|
64
|
+
env_file = str(ENV_FILE_SETTINGS)
|
|
65
|
+
session = Session()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class CLIController(BaseController):
|
|
69
|
+
"""CLI Controller class."""
|
|
70
|
+
|
|
71
|
+
CHOICES_COMMANDS = ["record", "stop", "exe", "results"]
|
|
72
|
+
CHOICES_MENUS = [
|
|
73
|
+
"settings",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
for router, value in PLATFORM_ROUTERS.items():
|
|
77
|
+
if value == "menu":
|
|
78
|
+
CHOICES_MENUS.append(router)
|
|
79
|
+
else:
|
|
80
|
+
CHOICES_COMMANDS.append(router)
|
|
81
|
+
|
|
82
|
+
PATH = "/"
|
|
83
|
+
CHOICES_GENERATION = False
|
|
84
|
+
|
|
85
|
+
def __init__(self, jobs_cmds: Optional[List[str]] = None):
|
|
86
|
+
"""Construct CLI controller."""
|
|
87
|
+
self.ROUTINE_FILES: Dict[str, str] = dict()
|
|
88
|
+
self.ROUTINE_DEFAULT_FILES: Dict[str, str] = dict()
|
|
89
|
+
self.ROUTINE_PERSONAL_FILES: Dict[str, str] = dict()
|
|
90
|
+
self.ROUTINE_CHOICES: Dict[str, Any] = dict()
|
|
91
|
+
|
|
92
|
+
super().__init__(jobs_cmds)
|
|
93
|
+
|
|
94
|
+
self.queue: List[str] = list()
|
|
95
|
+
|
|
96
|
+
if jobs_cmds:
|
|
97
|
+
self.queue = parse_and_split_input(
|
|
98
|
+
an_input=" ".join(jobs_cmds), custom_filters=[]
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
self.update_success = False
|
|
102
|
+
|
|
103
|
+
self._generate_platform_commands()
|
|
104
|
+
|
|
105
|
+
self.update_runtime_choices()
|
|
106
|
+
|
|
107
|
+
def _generate_platform_commands(self):
|
|
108
|
+
"""Generate Platform based commands/menus."""
|
|
109
|
+
|
|
110
|
+
def method_call_class(self, _, controller, name, parent_path, target):
|
|
111
|
+
self.queue = self.load_class(
|
|
112
|
+
controller, name, parent_path, target, self.queue
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# pylint: disable=unused-argument
|
|
116
|
+
def method_call_command(self, _, router: str):
|
|
117
|
+
"""Call command."""
|
|
118
|
+
mdl = getattr(obb, router)
|
|
119
|
+
df = pd.DataFrame.from_dict(mdl.model_dump(), orient="index")
|
|
120
|
+
if isinstance(df.columns, pd.RangeIndex):
|
|
121
|
+
df.columns = [str(i) for i in df.columns]
|
|
122
|
+
return print_rich_table(df, show_index=True)
|
|
123
|
+
|
|
124
|
+
for router, value in PLATFORM_ROUTERS.items():
|
|
125
|
+
target = getattr(obb, router)
|
|
126
|
+
|
|
127
|
+
if value == "menu":
|
|
128
|
+
pcf = PlatformControllerFactory(
|
|
129
|
+
target, reference=obb.reference["paths"] # type: ignore
|
|
130
|
+
)
|
|
131
|
+
DynamicController = pcf.create()
|
|
132
|
+
|
|
133
|
+
# Bind the method to the class
|
|
134
|
+
bound_method = MethodType(method_call_class, self)
|
|
135
|
+
|
|
136
|
+
# Update the wrapper and set the attribute
|
|
137
|
+
bound_method = update_wrapper( # type: ignore
|
|
138
|
+
partial(
|
|
139
|
+
bound_method,
|
|
140
|
+
controller=DynamicController,
|
|
141
|
+
name=router,
|
|
142
|
+
target=target,
|
|
143
|
+
parent_path=self.path,
|
|
144
|
+
),
|
|
145
|
+
method_call_class,
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
bound_method = MethodType(method_call_command, self)
|
|
149
|
+
bound_method = update_wrapper( # type: ignore
|
|
150
|
+
partial(bound_method, router=router),
|
|
151
|
+
method_call_command,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
setattr(self, f"call_{router}", bound_method)
|
|
155
|
+
|
|
156
|
+
def update_runtime_choices(self):
|
|
157
|
+
"""Update runtime choices."""
|
|
158
|
+
routines_directory = Path(session.user.preferences.export_directory, "routines")
|
|
159
|
+
|
|
160
|
+
if session.prompt_session and session.settings.USE_PROMPT_TOOLKIT:
|
|
161
|
+
# choices: dict = self.choices_default
|
|
162
|
+
choices: dict = {c: {} for c in self.controller_choices} # type: ignore
|
|
163
|
+
|
|
164
|
+
self.ROUTINE_FILES = {
|
|
165
|
+
filepath.name: filepath # type: ignore
|
|
166
|
+
for filepath in routines_directory.rglob("*.openbb")
|
|
167
|
+
}
|
|
168
|
+
self.ROUTINE_DEFAULT_FILES = {
|
|
169
|
+
filepath.name: filepath # type: ignore
|
|
170
|
+
for filepath in Path(routines_directory / "hub" / "default").rglob(
|
|
171
|
+
"*.openbb"
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
self.ROUTINE_PERSONAL_FILES = {
|
|
175
|
+
filepath.name: filepath # type: ignore
|
|
176
|
+
for filepath in Path(routines_directory / "hub" / "personal").rglob(
|
|
177
|
+
"*.openbb"
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
choices["exe"] = {
|
|
182
|
+
"--file": {
|
|
183
|
+
filename: {} for filename in list(self.ROUTINE_FILES.keys())
|
|
184
|
+
},
|
|
185
|
+
"-f": "--file",
|
|
186
|
+
"--example": None,
|
|
187
|
+
"-e": "--example",
|
|
188
|
+
"--input": None,
|
|
189
|
+
"-i": "--input",
|
|
190
|
+
"--url": None,
|
|
191
|
+
"--help": None,
|
|
192
|
+
"-h": "--help",
|
|
193
|
+
}
|
|
194
|
+
choices["record"] = {
|
|
195
|
+
"--name": None,
|
|
196
|
+
"-n": "--name",
|
|
197
|
+
"--description": None,
|
|
198
|
+
"-d": "--description",
|
|
199
|
+
"--public": None,
|
|
200
|
+
"-p": "--public",
|
|
201
|
+
"--tag1": {c: None for c in constants.SCRIPT_TAGS},
|
|
202
|
+
"--tag2": {c: None for c in constants.SCRIPT_TAGS},
|
|
203
|
+
"--tag3": {c: None for c in constants.SCRIPT_TAGS},
|
|
204
|
+
"--help": None,
|
|
205
|
+
"-h": "--help",
|
|
206
|
+
}
|
|
207
|
+
choices["stop"] = {"--help": None, "-h": "--help"}
|
|
208
|
+
choices["results"] = {"--help": None, "-h": "--help"}
|
|
209
|
+
|
|
210
|
+
self.update_completer(choices)
|
|
211
|
+
|
|
212
|
+
def print_help(self):
|
|
213
|
+
"""Print help."""
|
|
214
|
+
mt = MenuText("")
|
|
215
|
+
|
|
216
|
+
mt.add_info("Configure the platform and manage your account")
|
|
217
|
+
for router, value in PLATFORM_ROUTERS.items():
|
|
218
|
+
if router not in NON_DATA_ROUTERS or router in ["reference", "coverage"]:
|
|
219
|
+
continue
|
|
220
|
+
if value == "menu":
|
|
221
|
+
menu_description = (
|
|
222
|
+
obb.reference["routers"] # type: ignore
|
|
223
|
+
.get(f"{self.PATH}{router}", {})
|
|
224
|
+
.get("description")
|
|
225
|
+
) or ""
|
|
226
|
+
mt.add_menu(
|
|
227
|
+
name=router,
|
|
228
|
+
description=menu_description.split(".")[0].lower(),
|
|
229
|
+
)
|
|
230
|
+
else:
|
|
231
|
+
mt.add_cmd(router)
|
|
232
|
+
|
|
233
|
+
mt.add_info("\nConfigure your CLI")
|
|
234
|
+
mt.add_menu(
|
|
235
|
+
"settings",
|
|
236
|
+
description="enable and disable feature flags, preferences and settings",
|
|
237
|
+
)
|
|
238
|
+
mt.add_raw("\n")
|
|
239
|
+
mt.add_info("Record and execute your own .openbb routine scripts")
|
|
240
|
+
mt.add_cmd("record", description="start recording current session")
|
|
241
|
+
mt.add_cmd(
|
|
242
|
+
"stop", description="stop session recording and convert to .openbb routine"
|
|
243
|
+
)
|
|
244
|
+
mt.add_cmd(
|
|
245
|
+
"exe",
|
|
246
|
+
description="execute .openbb routine scripts (use exe --example for an example)",
|
|
247
|
+
)
|
|
248
|
+
mt.add_raw("\n")
|
|
249
|
+
mt.add_info("Retrieve data from different asset classes and providers")
|
|
250
|
+
|
|
251
|
+
for router, value in PLATFORM_ROUTERS.items():
|
|
252
|
+
if router in NON_DATA_ROUTERS or router in DATA_PROCESSING_ROUTERS:
|
|
253
|
+
continue
|
|
254
|
+
if value == "menu":
|
|
255
|
+
menu_description = (
|
|
256
|
+
obb.reference["routers"] # type: ignore
|
|
257
|
+
.get(f"{self.PATH}{router}", {})
|
|
258
|
+
.get("description")
|
|
259
|
+
) or ""
|
|
260
|
+
mt.add_menu(
|
|
261
|
+
name=router,
|
|
262
|
+
description=menu_description.split(".")[0].lower(),
|
|
263
|
+
)
|
|
264
|
+
else:
|
|
265
|
+
mt.add_cmd(router)
|
|
266
|
+
|
|
267
|
+
if any(router in PLATFORM_ROUTERS for router in DATA_PROCESSING_ROUTERS):
|
|
268
|
+
mt.add_info("\nAnalyze and process previously obtained data")
|
|
269
|
+
|
|
270
|
+
for router, value in PLATFORM_ROUTERS.items():
|
|
271
|
+
if router not in DATA_PROCESSING_ROUTERS:
|
|
272
|
+
continue
|
|
273
|
+
if value == "menu":
|
|
274
|
+
menu_description = (
|
|
275
|
+
obb.reference["routers"] # type: ignore
|
|
276
|
+
.get(f"{self.PATH}{router}", {})
|
|
277
|
+
.get("description")
|
|
278
|
+
) or ""
|
|
279
|
+
mt.add_menu(
|
|
280
|
+
name=router,
|
|
281
|
+
description=menu_description.split(".")[0].lower(),
|
|
282
|
+
)
|
|
283
|
+
else:
|
|
284
|
+
mt.add_cmd(router)
|
|
285
|
+
|
|
286
|
+
mt.add_raw("\n")
|
|
287
|
+
mt.add_cmd("results")
|
|
288
|
+
if session.obbject_registry.obbjects:
|
|
289
|
+
mt.add_info("\nCached Results")
|
|
290
|
+
for key, value in list(session.obbject_registry.all.items())[ # type: ignore
|
|
291
|
+
: session.settings.N_TO_DISPLAY_OBBJECT_REGISTRY
|
|
292
|
+
]:
|
|
293
|
+
mt.add_raw(
|
|
294
|
+
f"[yellow]OBB{key}[/yellow]: {value['command']}",
|
|
295
|
+
left_spacing=True,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
session.console.print(text=mt.menu_text, menu="Home")
|
|
299
|
+
self.update_runtime_choices()
|
|
300
|
+
|
|
301
|
+
def parse_input(self, an_input: str) -> List:
|
|
302
|
+
"""Overwrite the BaseController parse_input for `askobb` and 'exe'.
|
|
303
|
+
|
|
304
|
+
This will allow us to search for something like "P/E" ratio.
|
|
305
|
+
"""
|
|
306
|
+
# Filtering out sorting parameters with forward slashes like P/E
|
|
307
|
+
sort_filter = r"((\ -q |\ --question|\ ).*?(/))"
|
|
308
|
+
# Filter out urls
|
|
309
|
+
url = r"(exe (--url )?(https?://)?my\.openbb\.(dev|co)/u/.*/routine/.*)"
|
|
310
|
+
custom_filters = [sort_filter, url]
|
|
311
|
+
return parse_and_split_input(an_input=an_input, custom_filters=custom_filters)
|
|
312
|
+
|
|
313
|
+
def call_settings(self, _):
|
|
314
|
+
"""Process settings command."""
|
|
315
|
+
from openbb_cli.controllers.settings_controller import (
|
|
316
|
+
SettingsController,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
self.queue = self.load_class(SettingsController, self.queue)
|
|
320
|
+
|
|
321
|
+
def call_exe(self, other_args: List[str]):
|
|
322
|
+
"""Process exe command."""
|
|
323
|
+
# Merge rest of string path to other_args and remove queue since it is a dir
|
|
324
|
+
other_args += self.queue
|
|
325
|
+
|
|
326
|
+
if not other_args:
|
|
327
|
+
session.console.print(
|
|
328
|
+
"[info]Provide a path to the routine you wish to execute. For an example, please use "
|
|
329
|
+
"`exe --example`.\n[/info]"
|
|
330
|
+
)
|
|
331
|
+
return
|
|
332
|
+
parser = argparse.ArgumentParser(
|
|
333
|
+
add_help=False,
|
|
334
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
335
|
+
prog="exe",
|
|
336
|
+
description="Execute automated routine script. For an example, please use "
|
|
337
|
+
"`exe --example`.",
|
|
338
|
+
)
|
|
339
|
+
parser.add_argument(
|
|
340
|
+
"--file",
|
|
341
|
+
"-f",
|
|
342
|
+
help="The path or .openbb file to run.",
|
|
343
|
+
dest="file",
|
|
344
|
+
required="-h" not in other_args
|
|
345
|
+
and "--help" not in other_args
|
|
346
|
+
and "-e" not in other_args
|
|
347
|
+
and "--example" not in other_args
|
|
348
|
+
and "--url" not in other_args
|
|
349
|
+
and "my.openbb" not in other_args[0],
|
|
350
|
+
type=str,
|
|
351
|
+
nargs="+",
|
|
352
|
+
)
|
|
353
|
+
parser.add_argument(
|
|
354
|
+
"-i",
|
|
355
|
+
"--input",
|
|
356
|
+
help="Select multiple inputs to be replaced in the routine and separated by commas. E.g. GME,AMC,BTC-USD",
|
|
357
|
+
dest="routine_args",
|
|
358
|
+
type=lambda s: [str(item) for item in s.split(",")],
|
|
359
|
+
)
|
|
360
|
+
parser.add_argument(
|
|
361
|
+
"-e",
|
|
362
|
+
"--example",
|
|
363
|
+
help="Run an example script to understand how routines can be used.",
|
|
364
|
+
dest="example",
|
|
365
|
+
action="store_true",
|
|
366
|
+
default=False,
|
|
367
|
+
)
|
|
368
|
+
parser.add_argument(
|
|
369
|
+
"--url", help="URL to run openbb script from.", dest="url", type=str
|
|
370
|
+
)
|
|
371
|
+
if other_args and "-" not in other_args[0][0]:
|
|
372
|
+
if other_args[0].startswith("my.") or other_args[0].startswith("http"):
|
|
373
|
+
other_args.insert(0, "--url")
|
|
374
|
+
else:
|
|
375
|
+
other_args.insert(0, "--file")
|
|
376
|
+
ns_parser = self.parse_known_args_and_warn(parser, other_args)
|
|
377
|
+
if ns_parser:
|
|
378
|
+
if ns_parser.example:
|
|
379
|
+
routine_path = ASSETS_DIRECTORY / "routines" / "routine_example.openbb"
|
|
380
|
+
session.console.print( # TODO: Point to docs when ready
|
|
381
|
+
"[info]Executing an example, please visit our docs "
|
|
382
|
+
"to learn how to create your own script.[/info]\n"
|
|
383
|
+
)
|
|
384
|
+
time.sleep(3)
|
|
385
|
+
elif ns_parser.url:
|
|
386
|
+
if not ns_parser.url.startswith(
|
|
387
|
+
"https"
|
|
388
|
+
) and not ns_parser.url.startswith("http:"):
|
|
389
|
+
url = "https://" + ns_parser.url
|
|
390
|
+
elif ns_parser.url.startswith("http://"):
|
|
391
|
+
url = ns_parser.url.replace("http://", "https://")
|
|
392
|
+
else:
|
|
393
|
+
url = ns_parser.url
|
|
394
|
+
username = url.split("/")[-3]
|
|
395
|
+
script_name = url.split("/")[-1]
|
|
396
|
+
file_name = f"{username}_{script_name}.openbb"
|
|
397
|
+
final_url = f"{url}?raw=true"
|
|
398
|
+
response = requests.get(final_url, timeout=10)
|
|
399
|
+
if response.status_code != 200:
|
|
400
|
+
session.console.print(
|
|
401
|
+
"[red]Could not find the requested script.[/red]"
|
|
402
|
+
)
|
|
403
|
+
return
|
|
404
|
+
routine_text = response.json()["script"]
|
|
405
|
+
file_path = Path(session.user.preferences.export_directory, "routines")
|
|
406
|
+
routine_path = file_path / file_name
|
|
407
|
+
with open(routine_path, "w") as file:
|
|
408
|
+
file.write(routine_text)
|
|
409
|
+
self.update_runtime_choices()
|
|
410
|
+
|
|
411
|
+
elif ns_parser.file:
|
|
412
|
+
file_path = " ".join(ns_parser.file) # type: ignore
|
|
413
|
+
# if string is not in this format "default/file.openbb" then check for files in ROUTINE_FILES
|
|
414
|
+
full_path = file_path
|
|
415
|
+
hub_routine = file_path.split("/") # type: ignore
|
|
416
|
+
# Change with: my.openbb.co
|
|
417
|
+
if hub_routine[0] == "default":
|
|
418
|
+
routine_path = Path(
|
|
419
|
+
self.ROUTINE_DEFAULT_FILES.get(hub_routine[1], full_path)
|
|
420
|
+
)
|
|
421
|
+
elif hub_routine[0] == "personal":
|
|
422
|
+
routine_path = Path(
|
|
423
|
+
self.ROUTINE_PERSONAL_FILES.get(hub_routine[1], full_path)
|
|
424
|
+
)
|
|
425
|
+
else:
|
|
426
|
+
routine_path = Path(self.ROUTINE_FILES.get(file_path, full_path)) # type: ignore
|
|
427
|
+
else:
|
|
428
|
+
return
|
|
429
|
+
|
|
430
|
+
try:
|
|
431
|
+
with open(routine_path) as fp:
|
|
432
|
+
raw_lines = list(fp)
|
|
433
|
+
|
|
434
|
+
# Capture ARGV either as list if args separated by commas or as single value
|
|
435
|
+
if ns_parser.routine_args:
|
|
436
|
+
script_inputs = (
|
|
437
|
+
ns_parser.routine_args
|
|
438
|
+
if "," not in ns_parser.routine_args
|
|
439
|
+
else ns_parser.routine_args.split(",")
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
err, parsed_script = parse_openbb_script(
|
|
443
|
+
raw_lines=raw_lines,
|
|
444
|
+
script_inputs=script_inputs if ns_parser.routine_args else None,
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
# If there err output is not an empty string then it means there was an
|
|
448
|
+
# issue in parsing the routine and therefore we don't want to feed it
|
|
449
|
+
# to the terminal
|
|
450
|
+
if err:
|
|
451
|
+
session.console.print(err)
|
|
452
|
+
return
|
|
453
|
+
|
|
454
|
+
self.queue = [
|
|
455
|
+
val
|
|
456
|
+
for val in parse_and_split_input(
|
|
457
|
+
an_input=parsed_script, custom_filters=[]
|
|
458
|
+
)
|
|
459
|
+
if val
|
|
460
|
+
]
|
|
461
|
+
|
|
462
|
+
if "export" in self.queue[0]:
|
|
463
|
+
export_path = self.queue[0].split(" ")[1]
|
|
464
|
+
# If the path selected does not start from the user root, give relative location from root
|
|
465
|
+
if export_path[0] == "~":
|
|
466
|
+
export_path = export_path.replace(
|
|
467
|
+
"~", HOME_DIRECTORY.as_posix()
|
|
468
|
+
)
|
|
469
|
+
elif export_path[0] != "/":
|
|
470
|
+
export_path = os.path.join(
|
|
471
|
+
os.path.dirname(os.path.abspath(__file__)), export_path
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# Check if the directory exists
|
|
475
|
+
if os.path.isdir(export_path):
|
|
476
|
+
session.console.print(
|
|
477
|
+
f"Export data to be saved in the selected folder: '{export_path}'"
|
|
478
|
+
)
|
|
479
|
+
else:
|
|
480
|
+
os.makedirs(export_path)
|
|
481
|
+
session.console.print(
|
|
482
|
+
f"[green]Folder '{export_path}' successfully created.[/green]"
|
|
483
|
+
)
|
|
484
|
+
self.queue = self.queue[1:]
|
|
485
|
+
|
|
486
|
+
except FileNotFoundError:
|
|
487
|
+
session.console.print(
|
|
488
|
+
f"[red]File '{routine_path}' doesn't exist.[/red]"
|
|
489
|
+
)
|
|
490
|
+
return
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def handle_job_cmds(jobs_cmds: Optional[List[str]]) -> Optional[List[str]]:
|
|
494
|
+
"""Handle job commands."""
|
|
495
|
+
# If the path selected does not start from the user root,
|
|
496
|
+
# give relative location from root
|
|
497
|
+
if jobs_cmds is not None and jobs_cmds:
|
|
498
|
+
logger.info("INPUT: %s", "/".join(jobs_cmds))
|
|
499
|
+
|
|
500
|
+
export_path = ""
|
|
501
|
+
if jobs_cmds and "export" in jobs_cmds[0]:
|
|
502
|
+
commands = jobs_cmds[0].split("/")
|
|
503
|
+
first_split = commands[0].split(" ")
|
|
504
|
+
if len(first_split) > 1:
|
|
505
|
+
export_path = first_split[1]
|
|
506
|
+
jobs_cmds = ["/".join(commands[1:])]
|
|
507
|
+
if not export_path:
|
|
508
|
+
return jobs_cmds
|
|
509
|
+
if export_path[0] == "~":
|
|
510
|
+
export_path = export_path.replace("~", HOME_DIRECTORY.as_posix())
|
|
511
|
+
elif export_path[0] != "/":
|
|
512
|
+
export_path = os.path.join(
|
|
513
|
+
os.path.dirname(os.path.abspath(__file__)), export_path
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
# Check if the directory exists
|
|
517
|
+
if os.path.isdir(export_path):
|
|
518
|
+
session.console.print(
|
|
519
|
+
f"Export data to be saved in the selected folder: '{export_path}'"
|
|
520
|
+
)
|
|
521
|
+
else:
|
|
522
|
+
os.makedirs(export_path)
|
|
523
|
+
session.console.print(
|
|
524
|
+
f"[green]Folder '{export_path}' successfully created.[/green]"
|
|
525
|
+
)
|
|
526
|
+
return jobs_cmds
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
# pylint: disable=unused-argument
|
|
530
|
+
def run_cli(jobs_cmds: Optional[List[str]] = None, test_mode=False):
|
|
531
|
+
"""Run the CLI menu."""
|
|
532
|
+
ret_code = 1
|
|
533
|
+
t_controller = CLIController(jobs_cmds)
|
|
534
|
+
an_input = ""
|
|
535
|
+
|
|
536
|
+
jobs_cmds = handle_job_cmds(jobs_cmds)
|
|
537
|
+
|
|
538
|
+
bootup()
|
|
539
|
+
if not jobs_cmds:
|
|
540
|
+
welcome_message()
|
|
541
|
+
|
|
542
|
+
if first_time_user():
|
|
543
|
+
with contextlib.suppress(EOFError):
|
|
544
|
+
webbrowser.open("https://docs.openbb.co/cli")
|
|
545
|
+
|
|
546
|
+
t_controller.print_help()
|
|
547
|
+
|
|
548
|
+
while ret_code:
|
|
549
|
+
|
|
550
|
+
# There is a command in the queue
|
|
551
|
+
if t_controller.queue and len(t_controller.queue) > 0:
|
|
552
|
+
# If the command is quitting the menu we want to return in here
|
|
553
|
+
if t_controller.queue[0] in ("q", "..", "quit"):
|
|
554
|
+
print_goodbye()
|
|
555
|
+
break
|
|
556
|
+
|
|
557
|
+
# Consume 1 element from the queue
|
|
558
|
+
an_input = t_controller.queue[0]
|
|
559
|
+
t_controller.queue = t_controller.queue[1:]
|
|
560
|
+
|
|
561
|
+
# Print the current location because this was an instruction and we want user to know what was the action
|
|
562
|
+
if an_input and an_input.split(" ")[0] in t_controller.CHOICES_COMMANDS:
|
|
563
|
+
session.console.print(f"{get_flair_and_username()} / $ {an_input}")
|
|
564
|
+
|
|
565
|
+
# Get input command from user
|
|
566
|
+
else:
|
|
567
|
+
try:
|
|
568
|
+
# Get input from user using auto-completion
|
|
569
|
+
if session.prompt_session and session.settings.USE_PROMPT_TOOLKIT:
|
|
570
|
+
# Check if toolbar hint was enabled
|
|
571
|
+
if session.settings.TOOLBAR_HINT:
|
|
572
|
+
an_input = session.prompt_session.prompt( # type: ignore[union-attr]
|
|
573
|
+
f"{get_flair_and_username()} / $ ",
|
|
574
|
+
completer=t_controller.completer,
|
|
575
|
+
search_ignore_case=True,
|
|
576
|
+
bottom_toolbar=HTML(
|
|
577
|
+
'<style bg="ansiblack" fg="ansiwhite">[h]</style> help menu '
|
|
578
|
+
'<style bg="ansiblack" fg="ansiwhite">[q]</style> return to previous menu '
|
|
579
|
+
'<style bg="ansiblack" fg="ansiwhite">[e]</style> exit the program '
|
|
580
|
+
'<style bg="ansiblack" fg="ansiwhite">[cmd -h]</style> '
|
|
581
|
+
"see usage and available options "
|
|
582
|
+
),
|
|
583
|
+
style=Style.from_dict(
|
|
584
|
+
{
|
|
585
|
+
"bottom-toolbar": "#ffffff bg:#333333",
|
|
586
|
+
}
|
|
587
|
+
),
|
|
588
|
+
)
|
|
589
|
+
else:
|
|
590
|
+
an_input = session.prompt_session.prompt( # type: ignore[union-attr]
|
|
591
|
+
f"{get_flair_and_username()} / $ ",
|
|
592
|
+
completer=t_controller.completer,
|
|
593
|
+
search_ignore_case=True,
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
# Get input from user without auto-completion
|
|
597
|
+
else:
|
|
598
|
+
an_input = input(f"{get_flair_and_username()} / $ ")
|
|
599
|
+
|
|
600
|
+
except (KeyboardInterrupt, EOFError):
|
|
601
|
+
print_goodbye()
|
|
602
|
+
break
|
|
603
|
+
|
|
604
|
+
try:
|
|
605
|
+
# Process the input command
|
|
606
|
+
t_controller.queue = t_controller.switch(an_input)
|
|
607
|
+
|
|
608
|
+
if an_input in ("q", "quit", "..", "exit", "e"):
|
|
609
|
+
print_goodbye()
|
|
610
|
+
break
|
|
611
|
+
|
|
612
|
+
# Check if the user wants to reset application
|
|
613
|
+
if an_input in ("r", "reset") or t_controller.update_success:
|
|
614
|
+
reset(t_controller.queue if t_controller.queue else [])
|
|
615
|
+
break
|
|
616
|
+
|
|
617
|
+
except SystemExit:
|
|
618
|
+
logger.exception(
|
|
619
|
+
"The command '%s' doesn't exist on the / menu.",
|
|
620
|
+
an_input,
|
|
621
|
+
)
|
|
622
|
+
session.console.print(
|
|
623
|
+
f"[red]The command '{an_input}' doesn't exist on the / menu.[/red]\n",
|
|
624
|
+
)
|
|
625
|
+
similar_cmd = difflib.get_close_matches(
|
|
626
|
+
an_input.split(" ")[0] if " " in an_input else an_input,
|
|
627
|
+
t_controller.controller_choices,
|
|
628
|
+
n=1,
|
|
629
|
+
cutoff=0.7,
|
|
630
|
+
)
|
|
631
|
+
if similar_cmd:
|
|
632
|
+
an_input = similar_cmd[0]
|
|
633
|
+
if " " in an_input:
|
|
634
|
+
candidate_input = (
|
|
635
|
+
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
|
636
|
+
)
|
|
637
|
+
if candidate_input == an_input:
|
|
638
|
+
an_input = ""
|
|
639
|
+
t_controller.queue = []
|
|
640
|
+
session.console.print("\n")
|
|
641
|
+
continue
|
|
642
|
+
an_input = candidate_input
|
|
643
|
+
|
|
644
|
+
session.console.print(f"[green]Replacing by '{an_input}'.[/green]")
|
|
645
|
+
t_controller.queue.insert(0, an_input)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def insert_start_slash(cmds: List[str]) -> List[str]:
|
|
649
|
+
"""Insert a slash at the beginning of a command sequence."""
|
|
650
|
+
if not cmds[0].startswith("/"):
|
|
651
|
+
cmds[0] = f"/{cmds[0]}"
|
|
652
|
+
if cmds[0].startswith("/home"):
|
|
653
|
+
cmds[0] = f"/{cmds[0][5:]}"
|
|
654
|
+
return cmds
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def run_scripts(
|
|
658
|
+
path: Path,
|
|
659
|
+
test_mode: bool = False,
|
|
660
|
+
verbose: bool = False,
|
|
661
|
+
routines_args: Optional[List[str]] = None,
|
|
662
|
+
special_arguments: Optional[Dict[str, str]] = None,
|
|
663
|
+
output: bool = True,
|
|
664
|
+
):
|
|
665
|
+
"""Run given .openbb scripts.
|
|
666
|
+
|
|
667
|
+
Parameters
|
|
668
|
+
----------
|
|
669
|
+
path : str
|
|
670
|
+
The location of the .openbb file
|
|
671
|
+
test_mode : bool
|
|
672
|
+
Whether the CLI is in test mode
|
|
673
|
+
verbose : bool
|
|
674
|
+
Whether to run tests in verbose mode
|
|
675
|
+
routines_args : List[str]
|
|
676
|
+
One or multiple inputs to be replaced in the routine and separated by commas.
|
|
677
|
+
E.g. GME,AMC,BTC-USD
|
|
678
|
+
special_arguments: Optional[Dict[str, str]]
|
|
679
|
+
Replace `${key=default}` with `value` for every key in the dictionary
|
|
680
|
+
output: bool
|
|
681
|
+
Whether to log tests to txt files
|
|
682
|
+
"""
|
|
683
|
+
if not path.exists():
|
|
684
|
+
session.console.print(f"File '{path}' doesn't exist. Launching base CLI.\n")
|
|
685
|
+
if not test_mode:
|
|
686
|
+
run_cli()
|
|
687
|
+
|
|
688
|
+
# THIS NEEDS TO BE REFACTORED!!! - ITS USED FOR TESTING
|
|
689
|
+
with path.open() as fp:
|
|
690
|
+
raw_lines = [x for x in fp if (not is_reset(x)) and ("#" not in x) and x]
|
|
691
|
+
raw_lines = [
|
|
692
|
+
raw_line.strip("\n") for raw_line in raw_lines if raw_line.strip("\n")
|
|
693
|
+
]
|
|
694
|
+
|
|
695
|
+
if routines_args:
|
|
696
|
+
lines = []
|
|
697
|
+
for rawline in raw_lines:
|
|
698
|
+
templine = rawline
|
|
699
|
+
for i, arg in enumerate(routines_args):
|
|
700
|
+
templine = templine.replace(f"$ARGV[{i}]", arg)
|
|
701
|
+
lines.append(templine)
|
|
702
|
+
# Handle new testing arguments:
|
|
703
|
+
elif special_arguments:
|
|
704
|
+
lines = []
|
|
705
|
+
for line in raw_lines:
|
|
706
|
+
new_line = re.sub(
|
|
707
|
+
r"\${[^{]+=[^{]+}",
|
|
708
|
+
lambda x: replace_dynamic(x, special_arguments), # type: ignore
|
|
709
|
+
line,
|
|
710
|
+
)
|
|
711
|
+
lines.append(new_line)
|
|
712
|
+
|
|
713
|
+
else:
|
|
714
|
+
lines = raw_lines
|
|
715
|
+
|
|
716
|
+
if test_mode and "exit" not in lines[-1]:
|
|
717
|
+
lines.append("exit")
|
|
718
|
+
|
|
719
|
+
# Deals with the export with a path with "/" in it
|
|
720
|
+
export_folder = ""
|
|
721
|
+
if "export" in lines[0]:
|
|
722
|
+
export_folder = lines[0].split("export ")[1].rstrip()
|
|
723
|
+
lines = lines[1:]
|
|
724
|
+
|
|
725
|
+
simulate_argv = f"/{'/'.join([line.rstrip() for line in lines])}"
|
|
726
|
+
file_cmds = simulate_argv.replace("//", "/home/").split()
|
|
727
|
+
file_cmds = insert_start_slash(file_cmds) if file_cmds else file_cmds
|
|
728
|
+
file_cmds = (
|
|
729
|
+
[f"export {export_folder}{' '.join(file_cmds)}"]
|
|
730
|
+
if export_folder
|
|
731
|
+
else [" ".join(file_cmds)]
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
if not test_mode or verbose:
|
|
735
|
+
run_cli(file_cmds, test_mode=True)
|
|
736
|
+
else:
|
|
737
|
+
with suppress_stdout():
|
|
738
|
+
session.console.print(f"To ensure: {output}")
|
|
739
|
+
if output:
|
|
740
|
+
timestamp = datetime.now().timestamp()
|
|
741
|
+
stamp_str = str(timestamp).replace(".", "")
|
|
742
|
+
whole_path = Path(REPOSITORY_DIRECTORY / "integration_test_output")
|
|
743
|
+
whole_path.mkdir(parents=True, exist_ok=True)
|
|
744
|
+
first_cmd = file_cmds[0].split("/")[1]
|
|
745
|
+
with open(
|
|
746
|
+
whole_path / f"{stamp_str}_{first_cmd}_output.txt", "w"
|
|
747
|
+
) as output_file, contextlib.redirect_stdout(output_file):
|
|
748
|
+
run_cli(file_cmds, test_mode=True)
|
|
749
|
+
else:
|
|
750
|
+
run_cli(file_cmds, test_mode=True)
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
def replace_dynamic(match: re.Match, special_arguments: Dict[str, str]) -> str:
|
|
754
|
+
"""Replace ${key=default} with value in special_arguments if it exists, else with default.
|
|
755
|
+
|
|
756
|
+
Parameters
|
|
757
|
+
----------
|
|
758
|
+
match: re.Match[str]
|
|
759
|
+
The match object
|
|
760
|
+
special_arguments: Dict[str, str]
|
|
761
|
+
The key value pairs to replace in the scripts
|
|
762
|
+
|
|
763
|
+
Returns
|
|
764
|
+
----------
|
|
765
|
+
str
|
|
766
|
+
The new string
|
|
767
|
+
"""
|
|
768
|
+
|
|
769
|
+
cleaned = match[0].replace("{", "").replace("}", "").replace("$", "")
|
|
770
|
+
key, default = cleaned.split("=")
|
|
771
|
+
dict_value = special_arguments.get(key, default)
|
|
772
|
+
if dict_value:
|
|
773
|
+
return dict_value
|
|
774
|
+
return default
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
def run_routine(file: str, routines_args=Optional[str]):
|
|
778
|
+
"""Execute command routine from .openbb file."""
|
|
779
|
+
user_routine_path = Path(session.user.preferences.export_directory, "routines")
|
|
780
|
+
default_routine_path = ASSETS_DIRECTORY / "routines" / file
|
|
781
|
+
|
|
782
|
+
if user_routine_path.exists():
|
|
783
|
+
run_scripts(path=user_routine_path, routines_args=routines_args)
|
|
784
|
+
elif default_routine_path.exists():
|
|
785
|
+
run_scripts(path=default_routine_path, routines_args=routines_args)
|
|
786
|
+
else:
|
|
787
|
+
session.console.print(
|
|
788
|
+
f"Routine not found, please put your `.openbb` file into : {user_routine_path}."
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
# pylint: disable=unused-argument
|
|
793
|
+
def main(
|
|
794
|
+
debug: bool,
|
|
795
|
+
dev: bool,
|
|
796
|
+
path_list: List[str],
|
|
797
|
+
routines_args: Optional[List[str]] = None,
|
|
798
|
+
**kwargs,
|
|
799
|
+
):
|
|
800
|
+
"""Run the CLI with various options.
|
|
801
|
+
|
|
802
|
+
Parameters
|
|
803
|
+
----------
|
|
804
|
+
debug : bool
|
|
805
|
+
Whether to run the CLI in debug mode
|
|
806
|
+
dev:
|
|
807
|
+
Points backend towards development environment instead of production
|
|
808
|
+
test : bool
|
|
809
|
+
Whether to run the CLI in integrated test mode
|
|
810
|
+
filtert : str
|
|
811
|
+
Filter test files with given string in name
|
|
812
|
+
paths : List[str]
|
|
813
|
+
The paths to run for scripts or to test
|
|
814
|
+
verbose : bool
|
|
815
|
+
Whether to show output from tests
|
|
816
|
+
routines_args : List[str]
|
|
817
|
+
One or multiple inputs to be replaced in the routine and separated by commas.
|
|
818
|
+
E.g. GME,AMC,BTC-USD
|
|
819
|
+
"""
|
|
820
|
+
if debug:
|
|
821
|
+
session.settings.DEBUG_MODE = True
|
|
822
|
+
|
|
823
|
+
if dev:
|
|
824
|
+
session.settings.DEV_BACKEND = True
|
|
825
|
+
session.settings.BASE_URL = "https://payments.openbb.dev/"
|
|
826
|
+
session.settings.HUB_URL = "https://my.openbb.dev"
|
|
827
|
+
|
|
828
|
+
if isinstance(path_list, list) and path_list[0].endswith(".openbb"):
|
|
829
|
+
run_routine(file=path_list[0], routines_args=routines_args)
|
|
830
|
+
elif path_list:
|
|
831
|
+
argv_cmds = list([" ".join(path_list).replace(" /", "/home/")])
|
|
832
|
+
argv_cmds = insert_start_slash(argv_cmds) if argv_cmds else argv_cmds
|
|
833
|
+
run_cli(argv_cmds)
|
|
834
|
+
else:
|
|
835
|
+
run_cli()
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
def parse_args_and_run():
|
|
839
|
+
"""Parse input arguments and run CLI."""
|
|
840
|
+
parser = argparse.ArgumentParser(
|
|
841
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
842
|
+
prog="cli",
|
|
843
|
+
description="The OpenBB Platform CLI.",
|
|
844
|
+
)
|
|
845
|
+
parser.add_argument(
|
|
846
|
+
"-d",
|
|
847
|
+
"--debug",
|
|
848
|
+
dest="debug",
|
|
849
|
+
action="store_true",
|
|
850
|
+
default=False,
|
|
851
|
+
help="Runs the CLI in debug mode.",
|
|
852
|
+
)
|
|
853
|
+
parser.add_argument(
|
|
854
|
+
"--dev",
|
|
855
|
+
dest="dev",
|
|
856
|
+
action="store_true",
|
|
857
|
+
default=False,
|
|
858
|
+
help="Points backend towards development environment instead of production",
|
|
859
|
+
)
|
|
860
|
+
parser.add_argument(
|
|
861
|
+
"--file",
|
|
862
|
+
help="The path or .openbb file to run.",
|
|
863
|
+
dest="path",
|
|
864
|
+
nargs="+",
|
|
865
|
+
default="",
|
|
866
|
+
type=str,
|
|
867
|
+
)
|
|
868
|
+
parser.add_argument(
|
|
869
|
+
"-i",
|
|
870
|
+
"--input",
|
|
871
|
+
help=(
|
|
872
|
+
"Select multiple inputs to be replaced in the routine and separated by commas."
|
|
873
|
+
"E.g. GME,AMC,BTC-USD"
|
|
874
|
+
),
|
|
875
|
+
dest="routine_args",
|
|
876
|
+
type=lambda s: [str(item) for item in s.split(",")],
|
|
877
|
+
default=None,
|
|
878
|
+
)
|
|
879
|
+
parser.add_argument(
|
|
880
|
+
"-t",
|
|
881
|
+
"--test",
|
|
882
|
+
action="store_true",
|
|
883
|
+
help=(
|
|
884
|
+
"Run the CLI in testing mode. Also run this option and '-h'"
|
|
885
|
+
" to see testing argument options."
|
|
886
|
+
),
|
|
887
|
+
)
|
|
888
|
+
# The args -m, -f and --HistoryManager.hist_file are used only in reports menu
|
|
889
|
+
# by papermill and that's why they have suppress help.
|
|
890
|
+
parser.add_argument(
|
|
891
|
+
"-m",
|
|
892
|
+
help=argparse.SUPPRESS,
|
|
893
|
+
dest="module",
|
|
894
|
+
default="",
|
|
895
|
+
type=str,
|
|
896
|
+
)
|
|
897
|
+
parser.add_argument(
|
|
898
|
+
"-f",
|
|
899
|
+
help=argparse.SUPPRESS,
|
|
900
|
+
dest="module_file",
|
|
901
|
+
default="",
|
|
902
|
+
type=str,
|
|
903
|
+
)
|
|
904
|
+
parser.add_argument(
|
|
905
|
+
"--HistoryManager.hist_file",
|
|
906
|
+
help=argparse.SUPPRESS,
|
|
907
|
+
dest="module_hist_file",
|
|
908
|
+
default="",
|
|
909
|
+
type=str,
|
|
910
|
+
)
|
|
911
|
+
if sys.argv[1:] and "-" not in sys.argv[1][0]:
|
|
912
|
+
sys.argv.insert(1, "--file")
|
|
913
|
+
ns_parser, unknown = parser.parse_known_args()
|
|
914
|
+
|
|
915
|
+
# This ensures that if cli.py receives unknown args it will not start.
|
|
916
|
+
# Use -d flag if you want to see the unknown args.
|
|
917
|
+
if unknown:
|
|
918
|
+
if ns_parser.debug:
|
|
919
|
+
session.console.print(unknown)
|
|
920
|
+
else:
|
|
921
|
+
sys.exit(-1)
|
|
922
|
+
|
|
923
|
+
main(
|
|
924
|
+
ns_parser.debug,
|
|
925
|
+
ns_parser.dev,
|
|
926
|
+
ns_parser.path,
|
|
927
|
+
ns_parser.routine_args,
|
|
928
|
+
module=ns_parser.module,
|
|
929
|
+
module_file=ns_parser.module_file,
|
|
930
|
+
module_hist_file=ns_parser.module_hist_file,
|
|
931
|
+
)
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
def launch(
|
|
935
|
+
debug: bool = False, dev: bool = False, queue: Optional[List[str]] = None
|
|
936
|
+
) -> None:
|
|
937
|
+
"""Launch CLI."""
|
|
938
|
+
|
|
939
|
+
if queue:
|
|
940
|
+
main(debug, dev, queue, module="")
|
|
941
|
+
else:
|
|
942
|
+
parse_args_and_run()
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
if __name__ == "__main__":
|
|
946
|
+
parse_args_and_run()
|