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.
This commit is contained in:
ed
2026-06-20 10:01:33 -04:00
parent 57b6778007
commit f1e571c583
+35 -19
View File
@@ -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."""