openbb-cli 1.0.1__tar.gz → 1.0.3__tar.gz

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.
Files changed (47) hide show
  1. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/PKG-INFO +2 -2
  2. openbb_cli-1.0.3/openbb_cli/argparse_translator/argparse_argument.py +63 -0
  3. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/argparse_translator/argparse_class_processor.py +7 -7
  4. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/argparse_translator/argparse_translator.py +70 -252
  5. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/argparse_translator/obbject_registry.py +42 -9
  6. openbb_cli-1.0.3/openbb_cli/argparse_translator/reference_processor.py +142 -0
  7. openbb_cli-1.0.3/openbb_cli/argparse_translator/utils.py +76 -0
  8. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/cli.py +9 -1
  9. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/controllers/base_controller.py +107 -39
  10. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/controllers/base_platform_controller.py +72 -29
  11. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/controllers/choices.py +22 -5
  12. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/controllers/cli_controller.py +9 -16
  13. openbb_cli-1.0.3/openbb_cli/controllers/settings_controller.py +142 -0
  14. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/controllers/utils.py +54 -48
  15. openbb_cli-1.0.3/openbb_cli/models/settings.py +167 -0
  16. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/session.py +0 -3
  17. openbb_cli-1.0.3/openbb_cli/utils/utils.py +34 -0
  18. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/pyproject.toml +2 -2
  19. openbb_cli-1.0.1/openbb_cli/controllers/settings_controller.py +0 -373
  20. openbb_cli-1.0.1/openbb_cli/models/settings.py +0 -73
  21. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/README.md +0 -0
  22. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/__init__.py +0 -0
  23. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/argparse_translator/__init__.py +0 -0
  24. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/routines/routine_example.openbb +0 -0
  25. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/Consolas.ttf +0 -0
  26. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/dark.mpfstyle.json +0 -0
  27. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/dark.mplrc.json +0 -0
  28. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/dark.mplstyle +0 -0
  29. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/dark.pltstyle.json +0 -0
  30. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/dark.richstyle.json +0 -0
  31. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/light.mpfstyle.json +0 -0
  32. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/light.mplrc.json +0 -0
  33. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/light.mplstyle +0 -0
  34. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/light.pltstyle.json +0 -0
  35. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/light.richstyle.json +0 -0
  36. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/default/tables.pltstyle.json +0 -0
  37. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/assets/styles/user/openbb.richstyle.json +0 -0
  38. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/config/__init__.py +0 -0
  39. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/config/completer.py +0 -0
  40. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/config/console.py +0 -0
  41. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/config/constants.py +0 -0
  42. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/config/menu_text.py +0 -0
  43. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/config/setup.py +0 -0
  44. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/config/style.py +0 -0
  45. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/controllers/hub_service.py +0 -0
  46. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/controllers/platform_controller_factory.py +0 -0
  47. {openbb_cli-1.0.1 → openbb_cli-1.0.3}/openbb_cli/controllers/script_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openbb-cli
3
- Version: 1.0.1
3
+ Version: 1.0.3
4
4
  Summary: Investment Research for Everyone, Anywhere.
5
5
  Home-page: https://openbb.co
6
6
  License: AGPL-3.0-only
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
- Requires-Dist: openbb[all] (>=4.2.1,<5.0.0)
15
+ Requires-Dist: openbb[all] (>=4.2.3,<5.0.0)
16
16
  Requires-Dist: openpyxl (>=3.1.2,<4.0.0)
17
17
  Requires-Dist: prompt-toolkit (>=3.0.16,<4.0.0)
18
18
  Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
@@ -0,0 +1,63 @@
1
+ """Pydantic models for argparse arguments and argument groups."""
2
+
3
+ from typing import (
4
+ Any,
5
+ List,
6
+ Literal,
7
+ Optional,
8
+ Tuple,
9
+ )
10
+
11
+ from pydantic import BaseModel, model_validator
12
+
13
+ SEP = "__"
14
+
15
+
16
+ class ArgparseArgumentModel(BaseModel):
17
+ """Pydantic model for an argparse argument."""
18
+
19
+ name: str
20
+ type: Any
21
+ dest: str
22
+ default: Any
23
+ required: bool
24
+ action: Literal["store_true", "store"]
25
+ help: Optional[str]
26
+ nargs: Optional[Literal["+"]]
27
+ choices: Optional[Tuple]
28
+
29
+ @model_validator(mode="after") # type: ignore
30
+ @classmethod
31
+ def validate_action(cls, values: "ArgparseArgumentModel"):
32
+ """Validate the action based on the type."""
33
+ if values.type is bool and values.action != "store_true":
34
+ raise ValueError('If type is bool, action must be "store_true"')
35
+ return values
36
+
37
+ @model_validator(mode="after") # type: ignore
38
+ @classmethod
39
+ def remove_props_on_store_true(cls, values: "ArgparseArgumentModel"):
40
+ """Remove type, nargs, and choices if action is store_true."""
41
+ if values.action == "store_true":
42
+ values.type = None
43
+ values.nargs = None
44
+ values.choices = None
45
+ return values
46
+
47
+ # override
48
+ def model_dump(self, **kwargs):
49
+ """Override the model_dump method to remove empty choices."""
50
+ res = super().model_dump(**kwargs)
51
+
52
+ # Check if choices is present and if it's an empty tuple remove it
53
+ if "choices" in res and not res["choices"]:
54
+ del res["choices"]
55
+
56
+ return res
57
+
58
+
59
+ class ArgparseArgumentGroupModel(BaseModel):
60
+ """Pydantic model for a custom argument group."""
61
+
62
+ name: str
63
+ arguments: List[ArgparseArgumentModel]
@@ -1,19 +1,19 @@
1
+ """Module for the ArgparseClassProcessor class."""
2
+
1
3
  import inspect
2
4
  from typing import Any, Dict, Optional, Type
3
5
 
4
6
  # TODO: this needs to be done differently
5
7
  from openbb_core.app.static.container import Container
6
8
 
7
- from openbb_cli.argparse_translator.argparse_translator import (
8
- ArgparseTranslator,
9
- ReferenceToCustomArgumentsProcessor,
9
+ from openbb_cli.argparse_translator.argparse_translator import ArgparseTranslator
10
+ from openbb_cli.argparse_translator.reference_processor import (
11
+ ReferenceToArgumentsProcessor,
10
12
  )
11
13
 
12
14
 
13
15
  class ArgparseClassProcessor:
14
- """
15
- Process a target class to create ArgparseTranslators for its methods.
16
- """
16
+ """Process a target class to create ArgparseTranslators for its methods."""
17
17
 
18
18
  # reference variable used to create custom groups for the ArgpaseTranslators
19
19
  _reference: Dict[str, Any] = {}
@@ -77,7 +77,7 @@ class ArgparseClassProcessor:
77
77
  reference = {route: cls._reference[route]} if route in cls._reference else {}
78
78
  if not reference:
79
79
  return {}
80
- rp = ReferenceToCustomArgumentsProcessor(reference)
80
+ rp = ReferenceToArgumentsProcessor(reference)
81
81
  return rp.custom_groups.get(route, {}) # type: ignore
82
82
 
83
83
  @classmethod
@@ -1,8 +1,9 @@
1
+ """Module for translating a function into an argparse program."""
2
+
1
3
  import argparse
2
4
  import inspect
3
5
  import re
4
6
  from copy import deepcopy
5
- from enum import Enum
6
7
  from typing import (
7
8
  Any,
8
9
  Callable,
@@ -19,181 +20,37 @@ from typing import (
19
20
  )
20
21
 
21
22
  from openbb_core.app.model.field import OpenBBField
22
- from pydantic import BaseModel, model_validator
23
+ from pydantic import BaseModel
23
24
  from typing_extensions import Annotated
24
25
 
26
+ from openbb_cli.argparse_translator.argparse_argument import (
27
+ ArgparseArgumentGroupModel,
28
+ ArgparseArgumentModel,
29
+ )
30
+ from openbb_cli.argparse_translator.utils import (
31
+ get_argument_choices,
32
+ get_argument_optional_choices,
33
+ in_group,
34
+ remove_argument,
35
+ set_optional_choices,
36
+ )
37
+
25
38
  # pylint: disable=protected-access
26
39
 
27
40
  SEP = "__"
28
41
 
29
42
 
30
- class ArgparseActionType(Enum):
31
- store = "store"
32
- store_true = "store_true"
33
-
34
-
35
- class CustomArgument(BaseModel):
36
- name: str
37
- type: Optional[Any]
38
- dest: str
39
- default: Any
40
- required: bool
41
- action: Literal["store_true", "store"]
42
- help: str
43
- nargs: Optional[Literal["+"]]
44
- choices: Optional[Tuple]
45
-
46
- @model_validator(mode="after") # type: ignore
47
- @classmethod
48
- def validate_action(cls, values: "CustomArgument"):
49
- if values.type is bool and values.action != "store_true":
50
- raise ValueError('If type is bool, action must be "store_true"')
51
- return values
52
-
53
- @model_validator(mode="after") # type: ignore
54
- @classmethod
55
- def remove_props_on_store_true(cls, values: "CustomArgument"):
56
- if values.action == "store_true":
57
- values.type = None
58
- values.nargs = None
59
- values.choices = None
60
- return values
61
-
62
- # override
63
- def model_dump(self, **kwargs):
64
- res = super().model_dump(**kwargs)
65
-
66
- # Check if choices is present and if it's an empty tuple remove it
67
- if "choices" in res and not res["choices"]:
68
- del res["choices"]
69
-
70
- return res
71
-
72
-
73
- class CustomArgumentGroup(BaseModel):
74
- name: str
75
- arguments: List[CustomArgument]
76
-
77
-
78
- class ReferenceToCustomArgumentsProcessor:
79
- def __init__(self, reference: Dict[str, Dict]):
80
- """Initializes the ReferenceToCustomArgumentsProcessor."""
81
- self.reference = reference
82
- self.custom_groups: Dict[str, List[CustomArgumentGroup]] = {}
83
-
84
- self.build_custom_groups()
85
-
86
- @staticmethod
87
- def _make_type_parsable(type_: str) -> type:
88
- """Make the type parsable by removing the annotations."""
89
- if "Union" in type_ and "str" in type_:
90
- return str
91
- if "Union" in type_ and "int" in type_:
92
- return int
93
- if type_ in ["date", "datetime.time", "time"]:
94
- return str
95
-
96
- if any(x in type_ for x in ["gt=", "ge=", "lt=", "le="]):
97
- if "Annotated" in type_:
98
- type_ = type_.replace("Annotated[", "").replace("]", "")
99
- type_ = type_.split(",")[0]
100
-
101
- return eval(type_) # noqa: S307, E501 pylint: disable=eval-used
102
-
103
- def _parse_type(self, type_: str) -> type:
104
- """Parse the type from the string representation."""
105
- type_ = self._make_type_parsable(type_) # type: ignore
106
-
107
- if get_origin(type_) is Literal:
108
- type_ = type(get_args(type_)[0]) # type: ignore
109
-
110
- return type_ # type: ignore
111
-
112
- def _get_nargs(self, type_: type) -> Optional[Union[int, str]]:
113
- """Get the nargs for the given type."""
114
- if get_origin(type_) is list:
115
- return "+"
116
- return None
117
-
118
- def _get_choices(self, type_: str, custom_choices: Any) -> Tuple:
119
- """Get the choices for the given type."""
120
- type_ = self._make_type_parsable(type_) # type: ignore
121
- type_origin = get_origin(type_)
122
-
123
- choices = ()
124
-
125
- if type_origin is Literal:
126
- choices = get_args(type_)
127
-
128
- if type_origin is list:
129
- type_ = get_args(type_)[0]
130
-
131
- if get_origin(type_) is Literal:
132
- choices = get_args(type_)
133
-
134
- if type_origin is Union and type(None) in get_args(type_):
135
- # remove NoneType from the args
136
- args = [arg for arg in get_args(type_) if arg != type(None)]
137
- # if there is only one arg left, use it
138
- if len(args) > 1:
139
- raise ValueError("Union with NoneType should have only one type left")
140
- type_ = args[0]
141
-
142
- if get_origin(type_) is Literal:
143
- choices = get_args(type_)
144
-
145
- if custom_choices:
146
- return tuple(custom_choices)
147
-
148
- return choices
149
-
150
- def build_custom_groups(self):
151
- """Build the custom groups from the reference."""
152
- for route, v in self.reference.items():
153
- for provider, args in v["parameters"].items():
154
- if provider == "standard":
155
- continue
156
-
157
- custom_arguments = []
158
- for arg in args:
159
- if arg.get("standard"):
160
- continue
161
-
162
- type_ = self._parse_type(arg["type"])
163
-
164
- custom_arguments.append(
165
- CustomArgument(
166
- name=arg["name"],
167
- type=type_,
168
- dest=arg["name"],
169
- default=arg["default"],
170
- required=not (arg["optional"]),
171
- action="store" if type_ != bool else "store_true",
172
- help=arg["description"],
173
- nargs=self._get_nargs(type_), # type: ignore
174
- choices=self._get_choices(
175
- arg["type"], custom_choices=arg["choices"]
176
- ),
177
- )
178
- )
179
-
180
- group = CustomArgumentGroup(name=provider, arguments=custom_arguments)
181
-
182
- if route not in self.custom_groups:
183
- self.custom_groups[route] = []
184
-
185
- self.custom_groups[route].append(group)
186
-
187
-
188
43
  class ArgparseTranslator:
44
+ """Class to translate a function into an argparse program."""
45
+
189
46
  def __init__(
190
47
  self,
191
48
  func: Callable,
192
- custom_argument_groups: Optional[List[CustomArgumentGroup]] = None,
49
+ custom_argument_groups: Optional[List[ArgparseArgumentGroupModel]] = None,
193
50
  add_help: Optional[bool] = True,
194
51
  ):
195
52
  """
196
- Initializes the ArgparseTranslator.
53
+ Initialize the ArgparseTranslator.
197
54
 
198
55
  Args:
199
56
  func (Callable): The function to translate into an argparse program.
@@ -225,45 +82,6 @@ class ArgparseTranslator:
225
82
  def _handle_argument_in_groups(self, argument, group):
226
83
  """Handle the argument and add it to the parser."""
227
84
 
228
- def _in_group(arg, group_title):
229
- for action_group in self._parser._action_groups:
230
- if action_group.title == group_title:
231
- for action in action_group._group_actions:
232
- opts = action.option_strings
233
- if (opts and opts[0] == arg) or action.dest == arg:
234
- return True
235
- return False
236
-
237
- def _remove_argument(arg) -> List[Optional[str]]:
238
- groups_w_arg = []
239
-
240
- # remove the argument from the parser
241
- for action in self._parser._actions:
242
- opts = action.option_strings
243
- if (opts and opts[0] == arg) or action.dest == arg:
244
- self._parser._remove_action(action)
245
- break
246
-
247
- # remove from all groups
248
- for action_group in self._parser._action_groups:
249
- for action in action_group._group_actions:
250
- opts = action.option_strings
251
- if (opts and opts[0] == arg) or action.dest == arg:
252
- action_group._group_actions.remove(action)
253
- groups_w_arg.append(action_group.title)
254
-
255
- # remove from _action_groups dict
256
- self._parser._option_string_actions.pop(f"--{arg}", None)
257
-
258
- return groups_w_arg
259
-
260
- def _get_arg_choices(arg) -> Tuple:
261
- for action in self._parser._actions:
262
- opts = action.option_strings
263
- if (opts and opts[0] == arg) or action.dest == arg:
264
- return tuple(action.choices or ())
265
- return ()
266
-
267
85
  def _update_providers(
268
86
  input_string: str, new_provider: List[Optional[str]]
269
87
  ) -> str:
@@ -285,23 +103,27 @@ class ArgparseTranslator:
285
103
  kwargs = argument.model_dump(exclude={"name"}, exclude_none=True)
286
104
  model_choices = kwargs.get("choices", ()) or ()
287
105
  # extend choices
288
- choices = tuple(set(_get_arg_choices(argument.name) + model_choices))
106
+ existing_choices = get_argument_choices(self._parser, argument.name)
107
+ choices = tuple(set(existing_choices + model_choices))
108
+ optional_choices = bool(existing_choices and not model_choices)
289
109
 
290
110
  # check if the argument is in the required arguments
291
- if _in_group(argument.name, group_title="required arguments"):
111
+ if in_group(self._parser, argument.name, group_title="required arguments"):
292
112
  for action in self._required._group_actions:
293
113
  if action.dest == argument.name and choices:
294
114
  # update choices
295
115
  action.choices = choices
116
+ set_optional_choices(action, optional_choices)
296
117
  return
297
118
 
298
119
  # check if the argument is in the optional arguments
299
- if _in_group(argument.name, group_title="optional arguments"):
120
+ if in_group(self._parser, argument.name, group_title="optional arguments"):
300
121
  for action in self._parser._actions:
301
122
  if action.dest == argument.name:
302
123
  # update choices
303
124
  if choices:
304
125
  action.choices = choices
126
+ set_optional_choices(action, optional_choices)
305
127
  if argument.name not in self.signature.parameters:
306
128
  # update help
307
129
  action.help = _update_providers(
@@ -309,9 +131,16 @@ class ArgparseTranslator:
309
131
  )
310
132
  return
311
133
 
134
+ # we need to check if the optional choices were set in other group
135
+ # before we remove the argument from the group, otherwise we will lose info
136
+ if not optional_choices:
137
+ optional_choices = get_argument_optional_choices(
138
+ self._parser, argument.name
139
+ )
140
+
312
141
  # if the argument is in use, remove it from all groups
313
142
  # and return the groups that had the argument
314
- groups_w_arg = _remove_argument(argument.name)
143
+ groups_w_arg = remove_argument(self._parser, argument.name)
315
144
  groups_w_arg.append(group.title) # add current group
316
145
 
317
146
  # add it to the optional arguments group instead
@@ -319,16 +148,17 @@ class ArgparseTranslator:
319
148
  kwargs["choices"] = choices # update choices
320
149
  # add provider info to the help
321
150
  kwargs["help"] = _update_providers(argument.help or "", groups_w_arg)
322
- self._parser.add_argument(f"--{argument.name}", **kwargs)
151
+ action = self._parser.add_argument(f"--{argument.name}", **kwargs)
152
+ set_optional_choices(action, optional_choices)
323
153
 
324
154
  @property
325
155
  def parser(self) -> argparse.ArgumentParser:
156
+ """Get the argparse parser."""
326
157
  return deepcopy(self._parser)
327
158
 
328
159
  @staticmethod
329
160
  def _build_description(func_doc: str) -> str:
330
- """Builds the description of the argparse program from the function docstring."""
331
-
161
+ """Build the description of the argparse program from the function docstring."""
332
162
  patterns = ["openbb\n ======", "Parameters\n ----------"]
333
163
 
334
164
  if func_doc:
@@ -341,31 +171,34 @@ class ArgparseTranslator:
341
171
 
342
172
  @staticmethod
343
173
  def _param_is_default(param: inspect.Parameter) -> bool:
344
- """Returns True if the parameter has a default value."""
174
+ """Return True if the parameter has a default value."""
345
175
  return param.default != inspect.Parameter.empty
346
176
 
347
177
  def _get_action_type(self, param: inspect.Parameter) -> str:
348
- """Returns the argparse action type for the given parameter."""
178
+ """Return the argparse action type for the given parameter."""
349
179
  param_type = self.type_hints[param.name]
180
+ type_origin = get_origin(param_type)
350
181
 
351
- if param_type == bool:
352
- return ArgparseActionType.store_true.value
353
- return ArgparseActionType.store.value
182
+ if param_type == bool or (
183
+ type_origin is Union and bool in get_args(param_type)
184
+ ):
185
+ return "store_true"
186
+ return "store"
354
187
 
355
188
  def _get_type_and_choices(
356
189
  self, param: inspect.Parameter
357
190
  ) -> Tuple[Type[Any], Tuple[Any, ...]]:
358
- """Returns the type and choices for the given parameter."""
191
+ """Return the type and choices for the given parameter."""
359
192
  param_type = self.type_hints[param.name]
360
193
  type_origin = get_origin(param_type)
361
194
 
362
- choices = ()
195
+ choices: tuple[Any, ...] = ()
363
196
 
364
197
  if type_origin is Literal:
365
198
  choices = get_args(param_type)
366
199
  param_type = type(choices[0]) # type: ignore
367
200
 
368
- if type_origin is list: # TODO: dict should also go here
201
+ if type_origin is list:
369
202
  param_type = get_args(param_type)[0]
370
203
 
371
204
  if get_origin(param_type) is Literal:
@@ -413,32 +246,26 @@ class ArgparseTranslator:
413
246
 
414
247
  @classmethod
415
248
  def _get_argument_custom_help(cls, param: inspect.Parameter) -> Optional[str]:
416
- """Returns the help annotation for the given parameter."""
249
+ """Return the help annotation for the given parameter."""
417
250
  base_annotation = param.annotation
418
251
  _, custom_annotations = cls._split_annotation(base_annotation, OpenBBField)
419
252
  help_annotation = (
420
253
  custom_annotations[0].description if custom_annotations else None
421
254
  )
422
- if not help_annotation:
423
- # try to get it from the docstring
424
- pass
425
255
  return help_annotation
426
256
 
427
257
  @classmethod
428
258
  def _get_argument_custom_choices(cls, param: inspect.Parameter) -> Optional[str]:
429
- """Returns the help annotation for the given parameter."""
259
+ """Return the help annotation for the given parameter."""
430
260
  base_annotation = param.annotation
431
261
  _, custom_annotations = cls._split_annotation(base_annotation, OpenBBField)
432
262
  choices_annotation = (
433
263
  custom_annotations[0].choices if custom_annotations else None
434
264
  )
435
- if not choices_annotation:
436
- # try to get it from the docstring
437
- pass
438
265
  return choices_annotation
439
266
 
440
267
  def _get_nargs(self, param: inspect.Parameter) -> Optional[str]:
441
- """Returns the nargs annotation for the given parameter."""
268
+ """Return the nargs annotation for the given parameter."""
442
269
  param_type = self.type_hints[param.name]
443
270
  origin = get_origin(param_type)
444
271
 
@@ -453,11 +280,8 @@ class ArgparseTranslator:
453
280
  return None
454
281
 
455
282
  def _generate_argparse_arguments(self, parameters) -> None:
456
- """Generates the argparse arguments from the function parameters."""
283
+ """Generate the argparse arguments from the function parameters."""
457
284
  for param in parameters.values():
458
- # TODO : how to handle kwargs?
459
- # it's possible to add unknown arguments when parsing as follows:
460
- # args, unknown_args = parser.parse_known_args()
461
285
  if param.name == "kwargs":
462
286
  continue
463
287
 
@@ -505,32 +329,27 @@ class ArgparseTranslator:
505
329
 
506
330
  required = not self._param_is_default(param)
507
331
 
508
- kwargs = {
509
- "type": param_type,
510
- "dest": param.name,
511
- "default": param.default,
512
- "required": required,
513
- "action": self._get_action_type(param),
514
- "help": self._get_argument_custom_help(param),
515
- "nargs": self._get_nargs(param),
516
- }
517
-
518
- if choices:
519
- kwargs["choices"] = choices
520
-
521
- if param_type == bool:
522
- # store_true action does not accept the below kwargs
523
- kwargs.pop("type")
524
- kwargs.pop("nargs")
332
+ argument = ArgparseArgumentModel(
333
+ name=param.name,
334
+ type=param_type,
335
+ dest=param.name,
336
+ default=param.default,
337
+ required=required,
338
+ action=self._get_action_type(param),
339
+ help=self._get_argument_custom_help(param),
340
+ nargs=self._get_nargs(param),
341
+ choices=choices,
342
+ )
343
+ kwargs = argument.model_dump(exclude={"name"}, exclude_none=True)
525
344
 
526
345
  if required:
527
346
  self._required.add_argument(
528
- f"--{param.name}",
347
+ f"--{argument.name}",
529
348
  **kwargs,
530
349
  )
531
350
  else:
532
351
  self._parser.add_argument(
533
- f"--{param.name}",
352
+ f"--{argument.name}",
534
353
  **kwargs,
535
354
  )
536
355
 
@@ -556,7 +375,6 @@ class ArgparseTranslator:
556
375
  # for each argument in the signature that is a custom type, we need to
557
376
  # update the kwargs with the custom type kwargs
558
377
  for param in self.signature.parameters.values():
559
- # TODO : how to handle kwargs?
560
378
  if param.name == "kwargs":
561
379
  continue
562
380
  param_type, _ = self._get_type_and_choices(param)
@@ -571,7 +389,7 @@ class ArgparseTranslator:
571
389
  parsed_args: Optional[argparse.Namespace] = None,
572
390
  ) -> Any:
573
391
  """
574
- Executes the original function with the parsed arguments.
392
+ Execute the original function with the parsed arguments.
575
393
 
576
394
  Args:
577
395
  parsed_args (Optional[argparse.Namespace], optional): The parsed arguments. Defaults to None.
@@ -602,7 +420,7 @@ class ArgparseTranslator:
602
420
 
603
421
  def parse_args_and_execute(self) -> Any:
604
422
  """
605
- Parses the arguments and executes the original function.
423
+ Parse the arguments and executes the original function.
606
424
 
607
425
  Returns:
608
426
  Any: The return value of the original function.
@@ -612,7 +430,7 @@ class ArgparseTranslator:
612
430
 
613
431
  def translate(self) -> Callable:
614
432
  """
615
- Wraps the original function with an argparse program.
433
+ Wrap the original function with an argparse program.
616
434
 
617
435
  Returns:
618
436
  Callable: The original function wrapped with an argparse program.