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.
Files changed (42) hide show
  1. openbb_cli/__init__.py +1 -0
  2. openbb_cli/argparse_translator/__init__.py +0 -0
  3. openbb_cli/argparse_translator/argparse_class_processor.py +148 -0
  4. openbb_cli/argparse_translator/argparse_translator.py +627 -0
  5. openbb_cli/argparse_translator/obbject_registry.py +94 -0
  6. openbb_cli/assets/routines/routine_example.openbb +21 -0
  7. openbb_cli/assets/styles/default/Consolas.ttf +0 -0
  8. openbb_cli/assets/styles/default/dark.mpfstyle.json +47 -0
  9. openbb_cli/assets/styles/default/dark.mplrc.json +7 -0
  10. openbb_cli/assets/styles/default/dark.mplstyle +96 -0
  11. openbb_cli/assets/styles/default/dark.pltstyle.json +132 -0
  12. openbb_cli/assets/styles/default/dark.richstyle.json +9 -0
  13. openbb_cli/assets/styles/default/light.mpfstyle.json +47 -0
  14. openbb_cli/assets/styles/default/light.mplrc.json +7 -0
  15. openbb_cli/assets/styles/default/light.mplstyle +95 -0
  16. openbb_cli/assets/styles/default/light.pltstyle.json +871 -0
  17. openbb_cli/assets/styles/default/light.richstyle.json +9 -0
  18. openbb_cli/assets/styles/default/tables.pltstyle.json +102 -0
  19. openbb_cli/assets/styles/user/openbb.richstyle.json +9 -0
  20. openbb_cli/cli.py +20 -0
  21. openbb_cli/config/__init__.py +1 -0
  22. openbb_cli/config/completer.py +427 -0
  23. openbb_cli/config/console.py +93 -0
  24. openbb_cli/config/constants.py +80 -0
  25. openbb_cli/config/menu_text.py +165 -0
  26. openbb_cli/config/setup.py +11 -0
  27. openbb_cli/config/style.py +108 -0
  28. openbb_cli/controllers/base_controller.py +941 -0
  29. openbb_cli/controllers/base_platform_controller.py +349 -0
  30. openbb_cli/controllers/choices.py +324 -0
  31. openbb_cli/controllers/cli_controller.py +946 -0
  32. openbb_cli/controllers/hub_service.py +107 -0
  33. openbb_cli/controllers/platform_controller_factory.py +58 -0
  34. openbb_cli/controllers/script_parser.py +488 -0
  35. openbb_cli/controllers/settings_controller.py +373 -0
  36. openbb_cli/controllers/utils.py +967 -0
  37. openbb_cli/models/settings.py +73 -0
  38. openbb_cli/session.py +92 -0
  39. openbb_cli-1.0.0.dist-info/METADATA +91 -0
  40. openbb_cli-1.0.0.dist-info/RECORD +42 -0
  41. openbb_cli-1.0.0.dist-info/WHEEL +4 -0
  42. openbb_cli-1.0.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,627 @@
1
+ import argparse
2
+ import inspect
3
+ import re
4
+ from copy import deepcopy
5
+ from enum import Enum
6
+ from typing import (
7
+ Any,
8
+ Callable,
9
+ Dict,
10
+ List,
11
+ Literal,
12
+ Optional,
13
+ Tuple,
14
+ Type,
15
+ Union,
16
+ get_args,
17
+ get_origin,
18
+ get_type_hints,
19
+ )
20
+
21
+ from openbb_core.app.model.field import OpenBBField
22
+ from pydantic import BaseModel, model_validator
23
+ from typing_extensions import Annotated
24
+
25
+ # pylint: disable=protected-access
26
+
27
+ SEP = "__"
28
+
29
+
30
+ class ArgparseActionType(Enum):
31
+ store = "store"
32
+ store_true = "store_true"
33
+
34
+
35
+ class CustomArgument(BaseModel):
36
+
37
+ name: str
38
+ type: Optional[Any]
39
+ dest: str
40
+ default: Any
41
+ required: bool
42
+ action: Literal["store_true", "store"]
43
+ help: str
44
+ nargs: Optional[Literal["+"]]
45
+ choices: Optional[Tuple]
46
+
47
+ @model_validator(mode="after") # type: ignore
48
+ @classmethod
49
+ def validate_action(cls, values: "CustomArgument"):
50
+ if values.type is bool and values.action != "store_true":
51
+ raise ValueError('If type is bool, action must be "store_true"')
52
+ return values
53
+
54
+ @model_validator(mode="after") # type: ignore
55
+ @classmethod
56
+ def remove_props_on_store_true(cls, values: "CustomArgument"):
57
+ if values.action == "store_true":
58
+ values.type = None
59
+ values.nargs = None
60
+ values.choices = None
61
+ return values
62
+
63
+ # override
64
+ def model_dump(self, **kwargs):
65
+
66
+ res = super().model_dump(**kwargs)
67
+
68
+ # Check if choices is present and if it's an empty tuple remove it
69
+ if "choices" in res and not res["choices"]:
70
+ del res["choices"]
71
+
72
+ return res
73
+
74
+
75
+ class CustomArgumentGroup(BaseModel):
76
+ name: str
77
+ arguments: List[CustomArgument]
78
+
79
+
80
+ class ReferenceToCustomArgumentsProcessor:
81
+ def __init__(self, reference: Dict[str, Dict]):
82
+ """Initializes the ReferenceToCustomArgumentsProcessor."""
83
+ self.reference = reference
84
+ self.custom_groups: Dict[str, List[CustomArgumentGroup]] = {}
85
+
86
+ self.build_custom_groups()
87
+
88
+ @staticmethod
89
+ def _make_type_parsable(type_: str) -> type:
90
+ """Make the type parsable by removing the annotations."""
91
+ if "Union" in type_ and "str" in type_:
92
+ return str
93
+ if "Union" in type_ and "int" in type_:
94
+ return int
95
+ if type_ in ["date", "datetime.time", "time"]:
96
+ return str
97
+
98
+ if any(x in type_ for x in ["gt=", "ge=", "lt=", "le="]):
99
+ if "Annotated" in type_:
100
+ type_ = type_.replace("Annotated[", "").replace("]", "")
101
+ type_ = type_.split(",")[0]
102
+
103
+ return eval(type_) # noqa: S307, E501 pylint: disable=eval-used
104
+
105
+ def _parse_type(self, type_: str) -> type:
106
+ """Parse the type from the string representation."""
107
+ type_ = self._make_type_parsable(type_) # type: ignore
108
+
109
+ if get_origin(type_) is Literal:
110
+ type_ = type(get_args(type_)[0]) # type: ignore
111
+
112
+ return type_ # type: ignore
113
+
114
+ def _get_nargs(self, type_: type) -> Optional[Union[int, str]]:
115
+ """Get the nargs for the given type."""
116
+ if get_origin(type_) is list:
117
+ return "+"
118
+ return None
119
+
120
+ def _get_choices(self, type_: str, custom_choices: Any) -> Tuple:
121
+ """Get the choices for the given type."""
122
+ type_ = self._make_type_parsable(type_) # type: ignore
123
+ type_origin = get_origin(type_)
124
+
125
+ choices = ()
126
+
127
+ if type_origin is Literal:
128
+ choices = get_args(type_)
129
+
130
+ if type_origin is list:
131
+ type_ = get_args(type_)[0]
132
+
133
+ if get_origin(type_) is Literal:
134
+ choices = get_args(type_)
135
+
136
+ if type_origin is Union and type(None) in get_args(type_):
137
+ # remove NoneType from the args
138
+ args = [arg for arg in get_args(type_) if arg != type(None)]
139
+ # if there is only one arg left, use it
140
+ if len(args) > 1:
141
+ raise ValueError("Union with NoneType should have only one type left")
142
+ type_ = args[0]
143
+
144
+ if get_origin(type_) is Literal:
145
+ choices = get_args(type_)
146
+
147
+ if custom_choices:
148
+ return tuple(custom_choices)
149
+
150
+ return choices
151
+
152
+ def build_custom_groups(self):
153
+ """Build the custom groups from the reference."""
154
+ for route, v in self.reference.items():
155
+
156
+ for provider, args in v["parameters"].items():
157
+ if provider == "standard":
158
+ continue
159
+
160
+ custom_arguments = []
161
+ for arg in args:
162
+ if arg.get("standard"):
163
+ continue
164
+
165
+ type_ = self._parse_type(arg["type"])
166
+
167
+ custom_arguments.append(
168
+ CustomArgument(
169
+ name=arg["name"],
170
+ type=type_,
171
+ dest=arg["name"],
172
+ default=arg["default"],
173
+ required=not (arg["optional"]),
174
+ action="store" if type_ != bool else "store_true",
175
+ help=arg["description"],
176
+ nargs=self._get_nargs(type_), # type: ignore
177
+ choices=self._get_choices(
178
+ arg["type"], custom_choices=arg["choices"]
179
+ ),
180
+ )
181
+ )
182
+
183
+ group = CustomArgumentGroup(name=provider, arguments=custom_arguments)
184
+
185
+ if route not in self.custom_groups:
186
+ self.custom_groups[route] = []
187
+
188
+ self.custom_groups[route].append(group)
189
+
190
+
191
+ class ArgparseTranslator:
192
+ def __init__(
193
+ self,
194
+ func: Callable,
195
+ custom_argument_groups: Optional[List[CustomArgumentGroup]] = None,
196
+ add_help: Optional[bool] = True,
197
+ ):
198
+ """
199
+ Initializes the ArgparseTranslator.
200
+
201
+ Args:
202
+ func (Callable): The function to translate into an argparse program.
203
+ add_help (Optional[bool], optional): Whether to add the help argument. Defaults to False.
204
+ """
205
+ self.func = func
206
+ self.signature = inspect.signature(func)
207
+ self.type_hints = get_type_hints(func)
208
+ self.provider_parameters: Dict[str, List[str]] = {}
209
+
210
+ self._parser = argparse.ArgumentParser(
211
+ prog=func.__name__,
212
+ description=self._build_description(func.__doc__), # type: ignore
213
+ formatter_class=argparse.RawTextHelpFormatter,
214
+ add_help=add_help if add_help else False,
215
+ )
216
+ self._required = self._parser.add_argument_group("required arguments")
217
+
218
+ if any(param in self.type_hints for param in self.signature.parameters):
219
+ self._generate_argparse_arguments(self.signature.parameters)
220
+
221
+ if custom_argument_groups:
222
+ for group in custom_argument_groups:
223
+ self.provider_parameters[group.name] = []
224
+ argparse_group = self._parser.add_argument_group(group.name)
225
+ for argument in group.arguments:
226
+ self._handle_argument_in_groups(argument, argparse_group)
227
+
228
+ def _handle_argument_in_groups(self, argument, group):
229
+ """Handle the argument and add it to the parser."""
230
+
231
+ def _in_group(arg, group_title):
232
+ for action_group in self._parser._action_groups:
233
+ if action_group.title == group_title:
234
+ for action in action_group._group_actions:
235
+ opts = action.option_strings
236
+ if (opts and opts[0] == arg) or action.dest == arg:
237
+ return True
238
+ return False
239
+
240
+ def _remove_argument(arg) -> List[Optional[str]]:
241
+ groups_w_arg = []
242
+
243
+ # remove the argument from the parser
244
+ for action in self._parser._actions:
245
+ opts = action.option_strings
246
+ if (opts and opts[0] == arg) or action.dest == arg:
247
+ self._parser._remove_action(action)
248
+ break
249
+
250
+ # remove from all groups
251
+ for action_group in self._parser._action_groups:
252
+ for action in action_group._group_actions:
253
+ opts = action.option_strings
254
+ if (opts and opts[0] == arg) or action.dest == arg:
255
+ action_group._group_actions.remove(action)
256
+ groups_w_arg.append(action_group.title)
257
+
258
+ # remove from _action_groups dict
259
+ self._parser._option_string_actions.pop(f"--{arg}", None)
260
+
261
+ return groups_w_arg
262
+
263
+ def _get_arg_choices(arg) -> Tuple:
264
+ for action in self._parser._actions:
265
+ opts = action.option_strings
266
+ if (opts and opts[0] == arg) or action.dest == arg:
267
+ return tuple(action.choices or ())
268
+ return ()
269
+
270
+ def _update_providers(
271
+ input_string: str, new_provider: List[Optional[str]]
272
+ ) -> str:
273
+ pattern = r"\(provider:\s*(.*?)\)"
274
+ providers = re.findall(pattern, input_string)
275
+ providers.extend(new_provider)
276
+ # remove pattern from help and add with new providers
277
+ input_string = re.sub(pattern, "", input_string).strip()
278
+ return f"{input_string} (provider: {', '.join(providers)})"
279
+
280
+ # check if the argument is already in use, if not, add it
281
+ if f"--{argument.name}" not in self._parser._option_string_actions:
282
+ kwargs = argument.model_dump(exclude={"name"}, exclude_none=True)
283
+ group.add_argument(f"--{argument.name}", **kwargs)
284
+ if group.title in self.provider_parameters:
285
+ self.provider_parameters[group.title].append(argument.name)
286
+
287
+ else:
288
+ kwargs = argument.model_dump(exclude={"name"}, exclude_none=True)
289
+ model_choices = kwargs.get("choices", ()) or ()
290
+ # extend choices
291
+ choices = tuple(set(_get_arg_choices(argument.name) + model_choices))
292
+
293
+ # check if the argument is in the required arguments
294
+ if _in_group(argument.name, group_title="required arguments"):
295
+ for action in self._required._group_actions:
296
+ if action.dest == argument.name and choices:
297
+ # update choices
298
+ action.choices = choices
299
+ return
300
+
301
+ # check if the argument is in the optional arguments
302
+ if _in_group(argument.name, group_title="optional arguments"):
303
+ for action in self._parser._actions:
304
+ if action.dest == argument.name:
305
+ # update choices
306
+ if choices:
307
+ action.choices = choices
308
+ if argument.name not in self.signature.parameters:
309
+ # update help
310
+ action.help = _update_providers(
311
+ action.help or "", [group.title]
312
+ )
313
+ return
314
+
315
+ # if the argument is in use, remove it from all groups
316
+ # and return the groups that had the argument
317
+ groups_w_arg = _remove_argument(argument.name)
318
+ groups_w_arg.append(group.title) # add current group
319
+
320
+ # add it to the optional arguments group instead
321
+ if choices:
322
+ kwargs["choices"] = choices # update choices
323
+ # add provider info to the help
324
+ kwargs["help"] = _update_providers(argument.help or "", groups_w_arg)
325
+ self._parser.add_argument(f"--{argument.name}", **kwargs)
326
+
327
+ @property
328
+ def parser(self) -> argparse.ArgumentParser:
329
+ return deepcopy(self._parser)
330
+
331
+ @staticmethod
332
+ def _build_description(func_doc: str) -> str:
333
+ """Builds the description of the argparse program from the function docstring."""
334
+
335
+ patterns = ["openbb\n ======", "Parameters\n ----------"]
336
+
337
+ if func_doc:
338
+ for pattern in patterns:
339
+ if pattern in func_doc:
340
+ func_doc = func_doc[: func_doc.index(pattern)].strip()
341
+ break
342
+
343
+ return func_doc
344
+
345
+ @staticmethod
346
+ def _param_is_default(param: inspect.Parameter) -> bool:
347
+ """Returns True if the parameter has a default value."""
348
+ return param.default != inspect.Parameter.empty
349
+
350
+ def _get_action_type(self, param: inspect.Parameter) -> str:
351
+ """Returns the argparse action type for the given parameter."""
352
+ param_type = self.type_hints[param.name]
353
+
354
+ if param_type == bool:
355
+ return ArgparseActionType.store_true.value
356
+ return ArgparseActionType.store.value
357
+
358
+ def _get_type_and_choices(
359
+ self, param: inspect.Parameter
360
+ ) -> Tuple[Type[Any], Tuple[Any, ...]]:
361
+ """Returns the type and choices for the given parameter."""
362
+ param_type = self.type_hints[param.name]
363
+ type_origin = get_origin(param_type)
364
+
365
+ choices = ()
366
+
367
+ if type_origin is Literal:
368
+ choices = get_args(param_type)
369
+ param_type = type(choices[0]) # type: ignore
370
+
371
+ if type_origin is list: # TODO: dict should also go here
372
+ param_type = get_args(param_type)[0]
373
+
374
+ if get_origin(param_type) is Literal:
375
+ choices = get_args(param_type)
376
+ param_type = type(choices[0]) # type: ignore
377
+
378
+ if type_origin is Union:
379
+ union_args = get_args(param_type)
380
+ if str in union_args:
381
+ param_type = str
382
+
383
+ # check if it's an Optional, which would be a Union with NoneType
384
+ if type(None) in get_args(param_type):
385
+ # remove NoneType from the args
386
+ args = [arg for arg in get_args(param_type) if arg != type(None)]
387
+ # if there is only one arg left, use it
388
+ if len(args) > 1:
389
+ raise ValueError(
390
+ "Union with NoneType should have only one type left"
391
+ )
392
+ param_type = args[0]
393
+
394
+ if get_origin(param_type) is Literal:
395
+ choices = get_args(param_type)
396
+ param_type = type(choices[0]) # type: ignore
397
+
398
+ # if there are custom choices, override
399
+ choices = self._get_argument_custom_choices(param) or choices # type: ignore
400
+
401
+ return param_type, choices
402
+
403
+ @staticmethod
404
+ def _split_annotation(
405
+ base_annotation: Type[Any], custom_annotation_type: Type
406
+ ) -> Tuple[Type[Any], List[Any]]:
407
+ """Find the base annotation and the custom annotations, namely the OpenBBField."""
408
+ if get_origin(base_annotation) is not Annotated:
409
+ return base_annotation, []
410
+ base_annotation, *maybe_custom_annotations = get_args(base_annotation)
411
+ return base_annotation, [
412
+ annotation
413
+ for annotation in maybe_custom_annotations
414
+ if isinstance(annotation, custom_annotation_type)
415
+ ]
416
+
417
+ @classmethod
418
+ def _get_argument_custom_help(cls, param: inspect.Parameter) -> Optional[str]:
419
+ """Returns the help annotation for the given parameter."""
420
+ base_annotation = param.annotation
421
+ _, custom_annotations = cls._split_annotation(base_annotation, OpenBBField)
422
+ help_annotation = (
423
+ custom_annotations[0].description if custom_annotations else None
424
+ )
425
+ if not help_annotation:
426
+ # try to get it from the docstring
427
+ pass
428
+ return help_annotation
429
+
430
+ @classmethod
431
+ def _get_argument_custom_choices(cls, param: inspect.Parameter) -> Optional[str]:
432
+ """Returns the help annotation for the given parameter."""
433
+ base_annotation = param.annotation
434
+ _, custom_annotations = cls._split_annotation(base_annotation, OpenBBField)
435
+ choices_annotation = (
436
+ custom_annotations[0].choices if custom_annotations else None
437
+ )
438
+ if not choices_annotation:
439
+ # try to get it from the docstring
440
+ pass
441
+ return choices_annotation
442
+
443
+ def _get_nargs(self, param: inspect.Parameter) -> Optional[str]:
444
+ """Returns the nargs annotation for the given parameter."""
445
+ param_type = self.type_hints[param.name]
446
+ origin = get_origin(param_type)
447
+
448
+ if origin is list:
449
+ return "+"
450
+
451
+ if origin is Union and any(
452
+ get_origin(arg) is list for arg in get_args(param_type)
453
+ ):
454
+ return "+"
455
+
456
+ return None
457
+
458
+ def _generate_argparse_arguments(self, parameters) -> None:
459
+ """Generates the argparse arguments from the function parameters."""
460
+ for param in parameters.values():
461
+ # TODO : how to handle kwargs?
462
+ # it's possible to add unknown arguments when parsing as follows:
463
+ # args, unknown_args = parser.parse_known_args()
464
+ if param.name == "kwargs":
465
+ continue
466
+
467
+ param_type, choices = self._get_type_and_choices(param)
468
+
469
+ # if the param is a custom type, we need to flatten it
470
+ if inspect.isclass(param_type) and issubclass(param_type, BaseModel):
471
+ # update type hints with the custom type fields
472
+ type_hints = get_type_hints(param_type)
473
+ # prefix the type hints keys with the param name
474
+ type_hints = {
475
+ f"{param.name}{SEP}{key}": value
476
+ for key, value in type_hints.items()
477
+ }
478
+ self.type_hints.update(type_hints)
479
+ # create a signature from the custom type
480
+ sig = inspect.signature(param_type)
481
+
482
+ # add help to the annotation
483
+ annotated_parameters: List[inspect.Parameter] = []
484
+ for child_param in sig.parameters.values():
485
+ new_child_param = child_param.replace(
486
+ name=f"{param.name}{SEP}{child_param.name}",
487
+ annotation=Annotated[
488
+ child_param.annotation,
489
+ OpenBBField(
490
+ description=param_type.model_json_schema()[
491
+ "properties"
492
+ ][child_param.name].get("description", None)
493
+ ),
494
+ ],
495
+ kind=inspect.Parameter.KEYWORD_ONLY,
496
+ )
497
+ annotated_parameters.append(new_child_param)
498
+
499
+ # replacing with the annotated parameters
500
+ new_signature = inspect.Signature(
501
+ parameters=annotated_parameters,
502
+ return_annotation=sig.return_annotation,
503
+ )
504
+ self._generate_argparse_arguments(new_signature.parameters)
505
+
506
+ # the custom type itself should not be added as an argument
507
+ continue
508
+
509
+ required = not self._param_is_default(param)
510
+
511
+ kwargs = {
512
+ "type": param_type,
513
+ "dest": param.name,
514
+ "default": param.default,
515
+ "required": required,
516
+ "action": self._get_action_type(param),
517
+ "help": self._get_argument_custom_help(param),
518
+ "nargs": self._get_nargs(param),
519
+ }
520
+
521
+ if choices:
522
+ kwargs["choices"] = choices
523
+
524
+ if param_type == bool:
525
+ # store_true action does not accept the below kwargs
526
+ kwargs.pop("type")
527
+ kwargs.pop("nargs")
528
+
529
+ if required:
530
+ self._required.add_argument(
531
+ f"--{param.name}",
532
+ **kwargs,
533
+ )
534
+ else:
535
+ self._parser.add_argument(
536
+ f"--{param.name}",
537
+ **kwargs,
538
+ )
539
+
540
+ @staticmethod
541
+ def _unflatten_args(args: dict) -> Dict[str, Any]:
542
+ """Unflatten the args that were flattened by the custom types."""
543
+ result: Dict[str, Any] = {}
544
+ for key, value in args.items():
545
+ if SEP in key:
546
+ parts = key.split(SEP)
547
+ nested_dict = result
548
+ for part in parts[:-1]:
549
+ if part not in nested_dict:
550
+ nested_dict[part] = {}
551
+ nested_dict = nested_dict[part]
552
+ nested_dict[parts[-1]] = value
553
+ else:
554
+ result[key] = value
555
+ return result
556
+
557
+ def _update_with_custom_types(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
558
+ """Update the kwargs with the custom types."""
559
+ # for each argument in the signature that is a custom type, we need to
560
+ # update the kwargs with the custom type kwargs
561
+ for param in self.signature.parameters.values():
562
+ # TODO : how to handle kwargs?
563
+ if param.name == "kwargs":
564
+ continue
565
+ param_type, _ = self._get_type_and_choices(param)
566
+ if inspect.isclass(param_type) and issubclass(param_type, BaseModel):
567
+ custom_type_kwargs = kwargs[param.name]
568
+ kwargs[param.name] = param_type(**custom_type_kwargs)
569
+
570
+ return kwargs
571
+
572
+ def execute_func(
573
+ self,
574
+ parsed_args: Optional[argparse.Namespace] = None,
575
+ ) -> Any:
576
+ """
577
+ Executes the original function with the parsed arguments.
578
+
579
+ Args:
580
+ parsed_args (Optional[argparse.Namespace], optional): The parsed arguments. Defaults to None.
581
+
582
+ Returns:
583
+ Any: The return value of the original function.
584
+
585
+ """
586
+ kwargs = self._unflatten_args(vars(parsed_args))
587
+ kwargs = self._update_with_custom_types(kwargs)
588
+
589
+ provider = kwargs.get("provider")
590
+ provider_args = []
591
+ if provider and provider in self.provider_parameters:
592
+ provider_args = self.provider_parameters[provider]
593
+ else:
594
+ for args in self.provider_parameters.values():
595
+ provider_args.extend(args)
596
+
597
+ # remove kwargs that doesn't match the signature or provider parameters
598
+ kwargs = {
599
+ key: value
600
+ for key, value in kwargs.items()
601
+ if key in self.signature.parameters or key in provider_args
602
+ }
603
+
604
+ return self.func(**kwargs)
605
+
606
+ def parse_args_and_execute(self) -> Any:
607
+ """
608
+ Parses the arguments and executes the original function.
609
+
610
+ Returns:
611
+ Any: The return value of the original function.
612
+ """
613
+ parsed_args = self._parser.parse_args()
614
+ return self.execute_func(parsed_args)
615
+
616
+ def translate(self) -> Callable:
617
+ """
618
+ Wraps the original function with an argparse program.
619
+
620
+ Returns:
621
+ Callable: The original function wrapped with an argparse program.
622
+ """
623
+
624
+ def wrapper_func():
625
+ return self.parse_args_and_execute()
626
+
627
+ return wrapper_func