r"""Parse a function call string to extract the function name, positional arguments, and keyword arguments, including nested function calls. Args: call (str): A string in the format `func(arg1, arg2, kwarg=value)`. Returns: tuple: (function_name (str), positional_ar
(
call: str,
)
| 412 | |
| 413 | # Functions for function call evaluation |
| 414 | def parse_function_call( |
| 415 | call: str, |
| 416 | ) -> Tuple[Optional[str], Optional[List[Any]], Optional[Dict[str, Any]]]: |
| 417 | r"""Parse a function call string to extract the function name, |
| 418 | positional arguments, and keyword arguments, including |
| 419 | nested function calls. |
| 420 | |
| 421 | Args: |
| 422 | call (str): A string in the format `func(arg1, arg2, kwarg=value)`. |
| 423 | |
| 424 | Returns: |
| 425 | tuple: (function_name (str), positional_args (list), |
| 426 | keyword_args (dict)) or (None, None, None). |
| 427 | """ |
| 428 | |
| 429 | def preprocess_input(call: str) -> str: |
| 430 | r"""Remove formatting like code blocks and whitespace.""" |
| 431 | if call.strip().startswith("```python"): |
| 432 | call = call.strip().removeprefix("```python").removesuffix("```") |
| 433 | return textwrap.dedent(call).strip() |
| 434 | |
| 435 | def evaluate_arg(arg): |
| 436 | r"""Recursively evaluate arguments, including nested calls.""" |
| 437 | if isinstance(arg, ast.Call): |
| 438 | # Recursively parse nested calls |
| 439 | func_name, args, kwargs = parse_function_call(ast.unparse(arg)) |
| 440 | return func_name, args, kwargs |
| 441 | elif isinstance( |
| 442 | arg, ast.Constant |
| 443 | ): # Handle literals like numbers, strings, etc. |
| 444 | return arg.value |
| 445 | elif isinstance(arg, ast.List): # Handle list literals |
| 446 | return [evaluate_arg(el) for el in arg.elts] |
| 447 | elif isinstance(arg, ast.Dict): # Handle dictionary literals |
| 448 | return { |
| 449 | evaluate_arg(k): evaluate_arg(v) |
| 450 | for k, v in zip(arg.keys, arg.values) |
| 451 | } |
| 452 | elif isinstance(arg, ast.Tuple): # Handle tuple literals |
| 453 | return tuple(evaluate_arg(el) for el in arg.elts) |
| 454 | else: |
| 455 | return ast.literal_eval(arg) # Safely evaluate other types |
| 456 | |
| 457 | call = preprocess_input(call) |
| 458 | parsed_calls = [] |
| 459 | |
| 460 | try: |
| 461 | # Parse the string into an AST |
| 462 | parsed_calls = call.split(";") |
| 463 | for single_call in parsed_calls: |
| 464 | tree = ast.parse(single_call, mode='eval') |
| 465 | |
| 466 | # Ensure it's a function call |
| 467 | if isinstance(tree.body, ast.Call): |
| 468 | # Extract function name |
| 469 | if isinstance( |
| 470 | tree.body.func, ast.Name |
| 471 | ): # Simple function call |
no test coverage detected