11import functools
22import inspect
33import json
4- import logging
54import re
6- from collections .abc import Awaitable , Callable , Iterator , Sequence
7- from contextlib import contextmanager
5+ from collections .abc import Awaitable , Callable , Sequence
86from itertools import chain
97from types import GenericAlias
10- from typing import Annotated , Any , Literal , cast , get_args , get_origin , get_type_hints
8+ from typing import Annotated , Any , cast , get_args , get_origin , get_type_hints
119
1210import anyio
1311import anyio .to_thread
1412import pydantic_core
15- from griffe import Docstring , DocstringSectionKind
1613from pydantic import BaseModel , ConfigDict , Field , WithJsonSchema , create_model
1714from pydantic .fields import FieldInfo
1815from pydantic .json_schema import GenerateJsonSchema , JsonSchemaWarningKind
@@ -171,96 +168,35 @@ def pre_parse_json(self, data: dict[str, Any]) -> dict[str, Any]:
171168 )
172169
173170
174- _DocstringStyle = Literal ["google" , "numpy" , "sphinx" ]
175-
176- # Patterns to infer docstring style, adapted from pydantic-ai.
177- # Each entry is (pattern_template, replacement_keywords, style).
178- _DOCSTRING_STYLE_PATTERNS : list [tuple [str , list [str ], _DocstringStyle ]] = [
179- (
180- r"\n[ \t]*:{0}([ \t]+\w+)*:([ \t]+.+)?\n" ,
181- [
182- "param" ,
183- "parameter" ,
184- "arg" ,
185- "argument" ,
186- "key" ,
187- "keyword" ,
188- "type" ,
189- "var" ,
190- "ivar" ,
191- "cvar" ,
192- "vartype" ,
193- "returns" ,
194- "return" ,
195- "rtype" ,
196- "raises" ,
197- "raise" ,
198- "except" ,
199- "exception" ,
200- ],
201- "sphinx" ,
202- ),
203- (
204- r"\n[ \t]*{0}:([ \t]+.+)?\n[ \t]+.+" ,
205- [
206- "args" ,
207- "arguments" ,
208- "params" ,
209- "parameters" ,
210- "keyword args" ,
211- "keyword arguments" ,
212- "raises" ,
213- "exceptions" ,
214- "returns" ,
215- "yields" ,
216- "receives" ,
217- "examples" ,
218- "attributes" ,
219- ],
220- "google" ,
221- ),
222- (
223- r"\n[ \t]*{0}\n[ \t]*---+\n" ,
224- [
225- "deprecated" ,
226- "parameters" ,
227- "other parameters" ,
228- "returns" ,
229- "yields" ,
230- "receives" ,
231- "raises" ,
232- "warns" ,
233- "attributes" ,
234- ],
235- "numpy" ,
236- ),
237- ]
238-
239-
240- def _infer_docstring_style (doc : str ) -> _DocstringStyle :
241- """Infer the docstring style from its content."""
242- for pattern , replacements , style in _DOCSTRING_STYLE_PATTERNS :
243- matches = (
244- re .search (pattern .format (replacement ), doc , re .IGNORECASE | re .MULTILINE ) for replacement in replacements
245- )
246- if any (matches ):
247- return style
248- return "google"
249-
250-
251- @contextmanager
252- def _suppress_griffe_logging () -> Iterator [None ]:
253- """Temporarily suppress griffe's verbose logging."""
254- old_level = logging .root .getEffectiveLevel ()
255- logging .root .setLevel (logging .ERROR )
256- yield
257- logging .root .setLevel (old_level )
171+ # Regex patterns for extracting parameter descriptions from docstrings.
172+ # Supports Google, NumPy, and Sphinx styles without any external dependencies.
173+ _GOOGLE_ARGS_RE = re .compile (
174+ r"(?:Args|Arguments|Parameters)\s*:\s*\n((?:[ \t]+.+\n?)+)" ,
175+ re .IGNORECASE ,
176+ )
177+ _GOOGLE_PARAM_RE = re .compile (
178+ r"^[ \t]+(\w+)\s*(?:\(.+?\))?\s*:\s*(.+(?:\n(?:[ \t]+(?![ \t]*\w+\s*(?:\(.+?\))?\s*:).+))*)" ,
179+ re .MULTILINE ,
180+ )
181+ _SPHINX_PARAM_RE = re .compile (
182+ r":param\s+(\w+)\s*:\s*(.+(?:\n(?:[ \t]+(?!:).+))*)" ,
183+ re .MULTILINE ,
184+ )
185+ _NUMPY_PARAMS_RE = re .compile (
186+ r"(?:Parameters)\s*\n\s*-{3,}\s*\n((?:.*\n?)+?)(?:\n\s*\w+\s*\n\s*-{3,}|\Z)" ,
187+ re .IGNORECASE ,
188+ )
189+ _NUMPY_PARAM_RE = re .compile (
190+ r"^(\w+)\s*(?::.*)?$\n((?:[ \t]+.+\n?)+)" ,
191+ re .MULTILINE ,
192+ )
258193
259194
260195def _parse_docstring_params (func : Callable [..., Any ]) -> dict [str , str ]:
261196 """Parse a function's docstring to extract parameter descriptions.
262197
263- Supports Google, NumPy, and Sphinx-style docstrings with automatic format detection.
198+ Supports Google, NumPy, and Sphinx-style docstrings using simple regex patterns.
199+ No external dependencies required.
264200
265201 Returns:
266202 A dict mapping parameter names to their descriptions.
@@ -269,15 +205,24 @@ def _parse_docstring_params(func: Callable[..., Any]) -> dict[str, str]:
269205 if not doc :
270206 return {}
271207
272- docstring_style = _infer_docstring_style (doc )
273- docstring = Docstring (doc , lineno = 1 , parser = docstring_style )
274-
275- with _suppress_griffe_logging ():
276- sections = docstring .parse ()
277-
278- for section in sections :
279- if section .kind == DocstringSectionKind .parameters :
280- return {p .name : p .description for p in section .value }
208+ # Try Sphinx style first (:param name: description)
209+ sphinx_matches = _SPHINX_PARAM_RE .findall (doc )
210+ if sphinx_matches :
211+ return {name : " " .join (desc .split ()) for name , desc in sphinx_matches }
212+
213+ # Try Google style (Args: / Arguments: / Parameters:)
214+ google_section = _GOOGLE_ARGS_RE .search (doc )
215+ if google_section :
216+ params = _GOOGLE_PARAM_RE .findall (google_section .group (1 ))
217+ if params :
218+ return {name : " " .join (desc .split ()) for name , desc in params }
219+
220+ # Try NumPy style (Parameters\n----------)
221+ numpy_section = _NUMPY_PARAMS_RE .search (doc )
222+ if numpy_section :
223+ params = _NUMPY_PARAM_RE .findall (numpy_section .group (1 ))
224+ if params :
225+ return {name : " " .join (desc .split ()) for name , desc in params }
281226
282227 return {}
283228
0 commit comments