From f1e571c5831d7a24c6c315d7b3ae7a867331e912 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 20 Jun 2026 10:01:33 -0400 Subject: [PATCH] refactor(mcp_client): migrate L1334 py_get_docstring to Result[T] (Phase 7 site 2) Added py_get_docstring_result inside Result Variants region. Legacy py_get_docstring (str) now delegates to it. --- src/mcp_client.py | 54 ++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/mcp_client.py b/src/mcp_client.py index 80e6d589..5012275f 100644 --- a/src/mcp_client.py +++ b/src/mcp_client.py @@ -911,6 +911,31 @@ def py_check_syntax_result(path: str) -> Result[str]: return Result(data="", errors=[ErrorInfo(kind=ErrorKind.INVALID_INPUT, message=f"SyntaxError in {path} at line {e.lineno}, offset {e.offset}: {e.msg}\n{e.text}", source="mcp.py_check_syntax_result")]) except Exception as e: return Result(data="", errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=str(e), source="mcp.py_check_syntax_result", original=e)]) + +def py_get_docstring_result(path: str, name: str) -> Result[str]: + resolved = _resolve_and_check_result(path) + if not resolved.ok: + return Result(data="", errors=resolved.errors) + p = resolved.data + if isinstance(p, NilPath): + return Result(data="", errors=resolved.errors) + if not p.is_file() or p.suffix != ".py": + return Result(data="", errors=[ErrorInfo(kind=ErrorKind.INVALID_INPUT, message=f"not a python file: {path}", source="mcp.py_get_docstring_result")]) + try: + code = p.read_text(encoding="utf-8") + tree = ast.parse(code) + if not name or name == "module": + doc = ast.get_docstring(tree) + return Result(data=doc if doc else "No module docstring found.") + node = _get_symbol_node(tree, name) + if not node: + return Result(data="", errors=[ErrorInfo(kind=ErrorKind.NOT_FOUND, message=f"could not find symbol '{name}' in {path}", source="mcp.py_get_docstring_result")]) + if isinstance(node, (ast.AsyncFunctionDef, ast.FunctionDef, ast.ClassDef, ast.Module)): + doc = ast.get_docstring(node) + return Result(data=doc if doc else f"No docstring found for '{name}'.") + return Result(data=f"No docstring found for '{name}'.") + except Exception as e: + return Result(data="", errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=str(e), source="mcp.py_get_docstring_result", original=e)]) #endregion: Result Variants def edit_file(path: str, old_string: str, new_string: str, replace_all: bool = False) -> str: @@ -1314,25 +1339,16 @@ def py_get_hierarchy(path: str, class_name: str) -> str: return "; ".join(e.ui_message() for e in resolved.errors) def py_get_docstring(path: str, name: str) -> str: - """Extracts the docstring for a specific module, class, or function.""" - p, err = _resolve_and_check(path) - if err: return err - assert p is not None - if not p.is_file() or p.suffix != ".py": return f"ERROR: not a python file: {path}" - try: - code = p.read_text(encoding="utf-8") - tree = ast.parse(code) - if not name or name == "module": - doc = ast.get_docstring(tree) - return doc if doc else "No module docstring found." - node = _get_symbol_node(tree, name) - if not node: return f"ERROR: could not find symbol '{name}' in {path}" - if isinstance(node, (ast.AsyncFunctionDef, ast.FunctionDef, ast.ClassDef, ast.Module)): - doc = ast.get_docstring(node) - return doc if doc else f"No docstring found for '{name}'." - return f"No docstring found for '{name}'." - except Exception as e: - return f"ERROR getting docstring for '{name}': {e}" + """Extracts the docstring for a specific module, class, or function. + + Thin wrapper over py_get_docstring_result; the legacy str shape is + preserved for backward compatibility, but the try/except Exception + lives in the Result variant. + """ + resolved = py_get_docstring_result(path, name) + if resolved.ok: + return resolved.data + return "; ".join(e.ui_message() for e in resolved.errors) def derive_code_path(target: str, max_depth: int = 5) -> str: """Recursively traces the execution path of a specific function or method."""