Nymbo commited on
Commit
e48cd48
·
verified ·
1 Parent(s): 88f05c4

Create _docstrings.py

Browse files
Files changed (1) hide show
  1. Modules/_docstrings.py +112 -0
Modules/_docstrings.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from typing import Any, Annotated, get_args, get_origin, get_type_hints
5
+
6
+
7
+ def _typename(tp: Any) -> str:
8
+ """Return a readable type name from a type or annotation."""
9
+ try:
10
+ if hasattr(tp, "__name__"):
11
+ return tp.__name__ # e.g. int, str
12
+ if getattr(tp, "__module__", None) and getattr(tp, "__qualname__", None):
13
+ return f"{tp.__module__}.{tp.__qualname__}"
14
+ return str(tp).replace("typing.", "")
15
+ except Exception:
16
+ return str(tp)
17
+
18
+
19
+ def _extract_base_and_meta(annotation: Any) -> tuple[Any, str | None]:
20
+ """Given an annotation, return (base_type, first string metadata) if Annotated, else (annotation, None)."""
21
+ try:
22
+ if get_origin(annotation) is Annotated:
23
+ args = get_args(annotation)
24
+ base = args[0] if args else annotation
25
+ # Grab the first string metadata if present
26
+ for meta in args[1:]:
27
+ if isinstance(meta, str):
28
+ return base, meta
29
+ return base, None
30
+ return annotation, None
31
+ except Exception:
32
+ return annotation, None
33
+
34
+
35
+ def autodoc(summary: str | None = None, returns: str | None = None, *, force: bool = False):
36
+ """
37
+ Decorator that auto-generates a concise Google-style docstring from a function's
38
+ type hints and Annotated metadata. Useful for Gradio MCP where docstrings are
39
+ used for tool descriptions and parameter docs.
40
+
41
+ Args:
42
+ summary: Optional one-line summary for the function. If not provided,
43
+ will generate a simple sentence from the function name.
44
+ returns: Optional return value description. If not provided, only the
45
+ return type will be listed (if available).
46
+ force: When True, overwrite an existing docstring. Default False.
47
+
48
+ Returns:
49
+ The original function with its __doc__ populated (unless skipped).
50
+ """
51
+
52
+ def decorator(func):
53
+ # Skip if docstring already present and not forcing
54
+ if not force and func.__doc__ and func.__doc__.strip():
55
+ return func
56
+
57
+ try:
58
+ # include_extras=True to retain Annotated metadata
59
+ hints = get_type_hints(func, include_extras=True, globalns=getattr(func, "__globals__", None))
60
+ except Exception:
61
+ hints = {}
62
+
63
+ sig = inspect.signature(func)
64
+
65
+ lines: list[str] = []
66
+ # Summary line
67
+ if summary and summary.strip():
68
+ lines.append(summary.strip())
69
+ else:
70
+ pretty = func.__name__.replace("_", " ").strip().capitalize()
71
+ if not pretty.endswith("."):
72
+ pretty += "."
73
+ lines.append(pretty)
74
+
75
+ # Args section
76
+ if sig.parameters:
77
+ lines.append("")
78
+ lines.append("Args:")
79
+ for name, param in sig.parameters.items():
80
+ if name == "self":
81
+ continue
82
+ annot = hints.get(name, param.annotation)
83
+ base, meta = _extract_base_and_meta(annot)
84
+ tname = _typename(base) if base is not inspect._empty else None
85
+ desc = meta or ""
86
+ if tname and tname != str(inspect._empty):
87
+ lines.append(f" {name} ({tname}): {desc}".rstrip())
88
+ else:
89
+ lines.append(f" {name}: {desc}".rstrip())
90
+
91
+ # Returns section
92
+ ret_hint = hints.get("return", sig.return_annotation)
93
+ if returns or (ret_hint and ret_hint is not inspect.Signature.empty):
94
+ lines.append("")
95
+ lines.append("Returns:")
96
+ if returns:
97
+ lines.append(f" {returns}")
98
+ else:
99
+ base, meta = _extract_base_and_meta(ret_hint)
100
+ rtype = _typename(base)
101
+ if meta:
102
+ lines.append(f" {rtype}: {meta}")
103
+ else:
104
+ lines.append(f" {rtype}")
105
+
106
+ func.__doc__ = "\n".join(lines).strip() + "\n"
107
+ return func
108
+
109
+ return decorator
110
+
111
+
112
+ __all__ = ["autodoc"]