figrecipe 0.6.0__py3-none-any.whl → 0.7.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- figrecipe/__init__.py +106 -973
- figrecipe/_api/__init__.py +48 -0
- figrecipe/_api/_extract.py +108 -0
- figrecipe/_api/_notebook.py +61 -0
- figrecipe/_api/_panel.py +46 -0
- figrecipe/_api/_save.py +191 -0
- figrecipe/_api/_seaborn_proxy.py +34 -0
- figrecipe/_api/_style_manager.py +153 -0
- figrecipe/_api/_subplots.py +333 -0
- figrecipe/_api/_validate.py +82 -0
- figrecipe/_dev/__init__.py +2 -93
- figrecipe/_dev/_plotters.py +76 -0
- figrecipe/_dev/_run_demos.py +56 -0
- figrecipe/_dev/demo_plotters/__init__.py +35 -166
- figrecipe/_dev/demo_plotters/_categories.py +81 -0
- figrecipe/_dev/demo_plotters/_figure_creators.py +119 -0
- figrecipe/_dev/demo_plotters/_helpers.py +31 -0
- figrecipe/_dev/demo_plotters/_registry.py +50 -0
- figrecipe/_dev/demo_plotters/bar_categorical/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/contour_surface/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/distribution/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/image_matrix/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/line_curve/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/{plot_plot.py → line_curve/plot_plot.py} +3 -2
- figrecipe/_dev/demo_plotters/scatter_points/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/special/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/{plot_pie.py → special/plot_pie.py} +5 -1
- figrecipe/_dev/demo_plotters/spectral_signal/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/vector_flow/__init__.py +4 -0
- figrecipe/_editor/__init__.py +57 -9
- figrecipe/_editor/_bbox/__init__.py +43 -0
- figrecipe/_editor/_bbox/_collections.py +177 -0
- figrecipe/_editor/_bbox/_elements.py +159 -0
- figrecipe/_editor/_bbox/_extract.py +256 -0
- figrecipe/_editor/_bbox/_extract_axes.py +370 -0
- figrecipe/_editor/_bbox/_extract_text.py +342 -0
- figrecipe/_editor/_bbox/_lines.py +173 -0
- figrecipe/_editor/_bbox/_transforms.py +146 -0
- figrecipe/_editor/_flask_app.py +68 -1039
- figrecipe/_editor/_helpers.py +242 -0
- figrecipe/_editor/_hitmap/__init__.py +76 -0
- figrecipe/_editor/_hitmap/_artists/__init__.py +21 -0
- figrecipe/_editor/_hitmap/_artists/_collections.py +345 -0
- figrecipe/_editor/_hitmap/_artists/_images.py +68 -0
- figrecipe/_editor/_hitmap/_artists/_lines.py +107 -0
- figrecipe/_editor/_hitmap/_artists/_patches.py +163 -0
- figrecipe/_editor/_hitmap/_artists/_text.py +190 -0
- figrecipe/_editor/_hitmap/_colors.py +181 -0
- figrecipe/_editor/_hitmap/_detect.py +137 -0
- figrecipe/_editor/_hitmap/_restore.py +154 -0
- figrecipe/_editor/_hitmap_main.py +182 -0
- figrecipe/_editor/_preferences.py +135 -0
- figrecipe/_editor/_render_overrides.py +480 -0
- figrecipe/_editor/_renderer.py +35 -185
- figrecipe/_editor/_routes_axis.py +453 -0
- figrecipe/_editor/_routes_core.py +284 -0
- figrecipe/_editor/_routes_element.py +317 -0
- figrecipe/_editor/_routes_style.py +223 -0
- figrecipe/_editor/_templates/__init__.py +78 -1
- figrecipe/_editor/_templates/_html.py +109 -13
- figrecipe/_editor/_templates/_scripts/__init__.py +120 -0
- figrecipe/_editor/_templates/_scripts/_api.py +228 -0
- figrecipe/_editor/_templates/_scripts/_colors.py +485 -0
- figrecipe/_editor/_templates/_scripts/_core.py +436 -0
- figrecipe/_editor/_templates/_scripts/_debug_snapshot.py +186 -0
- figrecipe/_editor/_templates/_scripts/_element_editor.py +310 -0
- figrecipe/_editor/_templates/_scripts/_files.py +195 -0
- figrecipe/_editor/_templates/_scripts/_hitmap.py +509 -0
- figrecipe/_editor/_templates/_scripts/_inspector.py +315 -0
- figrecipe/_editor/_templates/_scripts/_labels.py +464 -0
- figrecipe/_editor/_templates/_scripts/_legend_drag.py +265 -0
- figrecipe/_editor/_templates/_scripts/_modals.py +226 -0
- figrecipe/_editor/_templates/_scripts/_overlays.py +292 -0
- figrecipe/_editor/_templates/_scripts/_panel_drag.py +334 -0
- figrecipe/_editor/_templates/_scripts/_panel_position.py +279 -0
- figrecipe/_editor/_templates/_scripts/_selection.py +237 -0
- figrecipe/_editor/_templates/_scripts/_tabs.py +89 -0
- figrecipe/_editor/_templates/_scripts/_view_mode.py +107 -0
- figrecipe/_editor/_templates/_scripts/_zoom.py +179 -0
- figrecipe/_editor/_templates/_styles/__init__.py +69 -0
- figrecipe/_editor/_templates/_styles/_base.py +64 -0
- figrecipe/_editor/_templates/_styles/_buttons.py +206 -0
- figrecipe/_editor/_templates/_styles/_color_input.py +123 -0
- figrecipe/_editor/_templates/_styles/_controls.py +265 -0
- figrecipe/_editor/_templates/_styles/_dynamic_props.py +144 -0
- figrecipe/_editor/_templates/_styles/_forms.py +126 -0
- figrecipe/_editor/_templates/_styles/_hitmap.py +184 -0
- figrecipe/_editor/_templates/_styles/_inspector.py +90 -0
- figrecipe/_editor/_templates/_styles/_labels.py +118 -0
- figrecipe/_editor/_templates/_styles/_modals.py +98 -0
- figrecipe/_editor/_templates/_styles/_overlays.py +130 -0
- figrecipe/_editor/_templates/_styles/_preview.py +225 -0
- figrecipe/_editor/_templates/_styles/_selection.py +73 -0
- figrecipe/_params/_DECORATION_METHODS.py +6 -0
- figrecipe/_recorder.py +35 -106
- figrecipe/_recorder_utils.py +124 -0
- figrecipe/_reproducer/__init__.py +18 -0
- figrecipe/_reproducer/_core.py +498 -0
- figrecipe/_reproducer/_custom_plots.py +279 -0
- figrecipe/_reproducer/_seaborn.py +100 -0
- figrecipe/_reproducer/_violin.py +186 -0
- figrecipe/_signatures/_kwargs.py +273 -0
- figrecipe/_signatures/_loader.py +21 -423
- figrecipe/_signatures/_parsing.py +147 -0
- figrecipe/_wrappers/_axes.py +119 -910
- figrecipe/_wrappers/_axes_helpers.py +136 -0
- figrecipe/_wrappers/_axes_plots.py +418 -0
- figrecipe/_wrappers/_axes_seaborn.py +157 -0
- figrecipe/_wrappers/_figure.py +162 -0
- figrecipe/_wrappers/_panel_labels.py +127 -0
- figrecipe/_wrappers/_plot_helpers.py +143 -0
- figrecipe/_wrappers/_violin_helpers.py +180 -0
- figrecipe/styles/__init__.py +8 -6
- figrecipe/styles/_dotdict.py +72 -0
- figrecipe/styles/_finalize.py +134 -0
- figrecipe/styles/_fonts.py +77 -0
- figrecipe/styles/_kwargs_converter.py +178 -0
- figrecipe/styles/_plot_styles.py +209 -0
- figrecipe/styles/_style_applier.py +32 -478
- figrecipe/styles/_style_loader.py +16 -192
- figrecipe/styles/_themes.py +151 -0
- figrecipe/styles/presets/MATPLOTLIB.yaml +2 -1
- figrecipe/styles/presets/SCITEX.yaml +29 -24
- {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/METADATA +37 -2
- figrecipe-0.7.4.dist-info/RECORD +188 -0
- figrecipe/_editor/_bbox.py +0 -978
- figrecipe/_editor/_hitmap.py +0 -937
- figrecipe/_editor/_templates/_scripts.py +0 -2778
- figrecipe/_editor/_templates/_styles.py +0 -1326
- figrecipe/_reproducer.py +0 -975
- figrecipe-0.6.0.dist-info/RECORD +0 -90
- /figrecipe/_dev/demo_plotters/{plot_bar.py → bar_categorical/plot_bar.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_barh.py → bar_categorical/plot_barh.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_contour.py → contour_surface/plot_contour.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_contourf.py → contour_surface/plot_contourf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tricontour.py → contour_surface/plot_tricontour.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tricontourf.py → contour_surface/plot_tricontourf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tripcolor.py → contour_surface/plot_tripcolor.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_triplot.py → contour_surface/plot_triplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_boxplot.py → distribution/plot_boxplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_ecdf.py → distribution/plot_ecdf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hist.py → distribution/plot_hist.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hist2d.py → distribution/plot_hist2d.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_violinplot.py → distribution/plot_violinplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hexbin.py → image_matrix/plot_hexbin.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_imshow.py → image_matrix/plot_imshow.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_matshow.py → image_matrix/plot_matshow.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_pcolor.py → image_matrix/plot_pcolor.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_pcolormesh.py → image_matrix/plot_pcolormesh.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_spy.py → image_matrix/plot_spy.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_errorbar.py → line_curve/plot_errorbar.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill.py → line_curve/plot_fill.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill_between.py → line_curve/plot_fill_between.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill_betweenx.py → line_curve/plot_fill_betweenx.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stackplot.py → line_curve/plot_stackplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stairs.py → line_curve/plot_stairs.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_step.py → line_curve/plot_step.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_scatter.py → scatter_points/plot_scatter.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_eventplot.py → special/plot_eventplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_loglog.py → special/plot_loglog.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_semilogx.py → special/plot_semilogx.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_semilogy.py → special/plot_semilogy.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stem.py → special/plot_stem.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_acorr.py → spectral_signal/plot_acorr.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_angle_spectrum.py → spectral_signal/plot_angle_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_cohere.py → spectral_signal/plot_cohere.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_csd.py → spectral_signal/plot_csd.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_magnitude_spectrum.py → spectral_signal/plot_magnitude_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_phase_spectrum.py → spectral_signal/plot_phase_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_psd.py → spectral_signal/plot_psd.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_specgram.py → spectral_signal/plot_specgram.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_xcorr.py → spectral_signal/plot_xcorr.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_barbs.py → vector_flow/plot_barbs.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_quiver.py → vector_flow/plot_quiver.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_streamplot.py → vector_flow/plot_streamplot.py} +0 -0
- {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/WHEEL +0 -0
- {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/licenses/LICENSE +0 -0
figrecipe/_signatures/_loader.py
CHANGED
|
@@ -9,421 +9,17 @@ Parses *args/**kwargs from docstrings and expands them to actual parameters.
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import inspect
|
|
12
|
-
import re
|
|
13
12
|
from typing import Any, Dict, List, Optional
|
|
14
13
|
|
|
15
14
|
import matplotlib.pyplot as plt
|
|
16
15
|
|
|
16
|
+
from ._kwargs import get_kwargs_mapping
|
|
17
|
+
from ._parsing import extract_args_from_docstring, parse_parameter_types
|
|
18
|
+
|
|
17
19
|
# Cache for signatures
|
|
18
20
|
_SIGNATURE_CACHE: Dict[str, Dict[str, Any]] = {}
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
# -----------------------------------------------------------------------------
|
|
22
|
-
# Docstring parsing
|
|
23
|
-
# -----------------------------------------------------------------------------
|
|
24
|
-
def _parse_parameter_types(docstring: Optional[str]) -> Dict[str, str]:
|
|
25
|
-
"""Extract parameter types from NumPy-style docstring Parameters section."""
|
|
26
|
-
if not docstring:
|
|
27
|
-
return {}
|
|
28
|
-
|
|
29
|
-
types = {}
|
|
30
|
-
|
|
31
|
-
# Find Parameters section
|
|
32
|
-
params_match = re.search(
|
|
33
|
-
r"Parameters\s*[-]+\s*(.*?)(?:\n\s*Returns|\n\s*See Also|\n\s*Notes|\n\s*Examples|\n\s*Other Parameters|\Z)",
|
|
34
|
-
docstring,
|
|
35
|
-
re.DOTALL,
|
|
36
|
-
)
|
|
37
|
-
if not params_match:
|
|
38
|
-
return {}
|
|
39
|
-
|
|
40
|
-
params_text = params_match.group(1)
|
|
41
|
-
|
|
42
|
-
# Parse lines like "x, y : array-like or float" or "fmt : str, optional"
|
|
43
|
-
for match in re.finditer(
|
|
44
|
-
r"^(\w+(?:\s*,\s*\w+)*)\s*:\s*(.+?)(?=\n\s*\n|\n\w+\s*:|\Z)",
|
|
45
|
-
params_text,
|
|
46
|
-
re.MULTILINE | re.DOTALL,
|
|
47
|
-
):
|
|
48
|
-
names_str = match.group(1)
|
|
49
|
-
type_str = match.group(2).split("\n")[0].strip() # First line only
|
|
50
|
-
|
|
51
|
-
# Clean up type string
|
|
52
|
-
type_str = re.sub(r",?\s*optional\s*$", "", type_str).strip()
|
|
53
|
-
type_str = re.sub(
|
|
54
|
-
r",?\s*default[^,]*$", "", type_str, flags=re.IGNORECASE
|
|
55
|
-
).strip()
|
|
56
|
-
|
|
57
|
-
# Handle multiple names like "x, y"
|
|
58
|
-
for name in re.split(r"\s*,\s*", names_str):
|
|
59
|
-
name = name.strip()
|
|
60
|
-
if name:
|
|
61
|
-
types[name.lower()] = type_str
|
|
62
|
-
|
|
63
|
-
return types
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def _parse_args_pattern(
|
|
67
|
-
args_str: str, param_types: Dict[str, str]
|
|
68
|
-
) -> List[Dict[str, Any]]:
|
|
69
|
-
"""Parse args pattern like '[x], y, [fmt]' into list of arg dicts."""
|
|
70
|
-
if not args_str:
|
|
71
|
-
return []
|
|
72
|
-
|
|
73
|
-
args = []
|
|
74
|
-
# Split by comma, handling brackets
|
|
75
|
-
parts = re.split(r",\s*", args_str)
|
|
76
|
-
|
|
77
|
-
for part in parts:
|
|
78
|
-
part = part.strip()
|
|
79
|
-
if not part or part == "/": # Skip empty or positional-only marker
|
|
80
|
-
continue
|
|
81
|
-
|
|
82
|
-
# Check if optional (wrapped in [])
|
|
83
|
-
optional = part.startswith("[") and part.endswith("]")
|
|
84
|
-
if optional:
|
|
85
|
-
name = part[1:-1].strip()
|
|
86
|
-
else:
|
|
87
|
-
# Handle cases like "[X, Y,] Z" where Z is required
|
|
88
|
-
name = part.strip("[]").strip()
|
|
89
|
-
|
|
90
|
-
if name and name not in ("...", "*"):
|
|
91
|
-
# Look up type from parsed parameters
|
|
92
|
-
type_str = param_types.get(name.lower())
|
|
93
|
-
args.append(
|
|
94
|
-
{
|
|
95
|
-
"name": name,
|
|
96
|
-
"type": type_str,
|
|
97
|
-
"optional": optional,
|
|
98
|
-
}
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
return args
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
# Manual *args patterns for functions without parseable call signatures
|
|
105
|
-
MANUAL_ARGS_PATTERNS = {
|
|
106
|
-
"fill": "[x], y, [color]",
|
|
107
|
-
"stackplot": "x, *ys",
|
|
108
|
-
"legend": "[handles], [labels]",
|
|
109
|
-
"stem": "[locs], heads",
|
|
110
|
-
"tricontour": "[triangulation], x, y, z, [levels]",
|
|
111
|
-
"tricontourf": "[triangulation], x, y, z, [levels]",
|
|
112
|
-
"triplot": "[triangulation], x, y, [triangles]",
|
|
113
|
-
"loglog": "[x], y, [fmt]",
|
|
114
|
-
"semilogx": "[x], y, [fmt]",
|
|
115
|
-
"semilogy": "[x], y, [fmt]",
|
|
116
|
-
"barbs": "[X], [Y], U, V, [C]",
|
|
117
|
-
"quiver": "[X], [Y], U, V, [C]",
|
|
118
|
-
"pcolor": "[X], [Y], C",
|
|
119
|
-
"pcolormesh": "[X], [Y], C",
|
|
120
|
-
"pcolorfast": "[X], [Y], C",
|
|
121
|
-
"acorr": "x",
|
|
122
|
-
"xcorr": "x, y",
|
|
123
|
-
"plot": "[x], y, [fmt]",
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def _extract_args_from_docstring(
|
|
128
|
-
docstring: Optional[str], func_name: str = ""
|
|
129
|
-
) -> List[Dict[str, Any]]:
|
|
130
|
-
"""Extract *args as flattened list from docstring call signature."""
|
|
131
|
-
if not docstring:
|
|
132
|
-
return []
|
|
133
|
-
|
|
134
|
-
# First, parse parameter types
|
|
135
|
-
param_types = _parse_parameter_types(docstring)
|
|
136
|
-
|
|
137
|
-
# Check for manual pattern first
|
|
138
|
-
if func_name in MANUAL_ARGS_PATTERNS:
|
|
139
|
-
return _parse_args_pattern(MANUAL_ARGS_PATTERNS[func_name], param_types)
|
|
140
|
-
|
|
141
|
-
# Look for "Call signature:" patterns
|
|
142
|
-
patterns = [
|
|
143
|
-
r"Call signatures?::\s*\n\s*(.*?)(?:\n\n|\n[A-Z])",
|
|
144
|
-
r"^\s*(\w+\([^)]+\))\s*$",
|
|
145
|
-
]
|
|
146
|
-
|
|
147
|
-
for pattern in patterns:
|
|
148
|
-
match = re.search(pattern, docstring, re.MULTILINE | re.DOTALL)
|
|
149
|
-
if match:
|
|
150
|
-
sig_text = match.group(1).strip()
|
|
151
|
-
# Extract first signature line
|
|
152
|
-
first_line = sig_text.split("\n")[0].strip()
|
|
153
|
-
# Parse the args from signature like "plot([x], y, [fmt], *, ...)"
|
|
154
|
-
inner_match = re.search(r"\(([^*]+?)(?:,\s*\*|,\s*data|\))", first_line)
|
|
155
|
-
if inner_match:
|
|
156
|
-
args_str = inner_match.group(1).strip().rstrip(",")
|
|
157
|
-
return _parse_args_pattern(args_str, param_types)
|
|
158
|
-
return []
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
# -----------------------------------------------------------------------------
|
|
162
|
-
# Kwargs expansion via set_* method introspection
|
|
163
|
-
# -----------------------------------------------------------------------------
|
|
164
|
-
def _get_setter_type(obj: Any, prop_name: str) -> Optional[str]:
|
|
165
|
-
"""Get type from set_* method docstring."""
|
|
166
|
-
setter_name = f"set_{prop_name}"
|
|
167
|
-
if not hasattr(obj, setter_name):
|
|
168
|
-
return None
|
|
169
|
-
|
|
170
|
-
method = getattr(obj, setter_name)
|
|
171
|
-
if not method.__doc__:
|
|
172
|
-
return None
|
|
173
|
-
|
|
174
|
-
# Parse Parameters section
|
|
175
|
-
match = re.search(
|
|
176
|
-
r"Parameters\s*[-]+\s*\n\s*(\w+)\s*:\s*(.+?)(?:\n\s*\n|\Z)",
|
|
177
|
-
method.__doc__,
|
|
178
|
-
re.DOTALL,
|
|
179
|
-
)
|
|
180
|
-
if match:
|
|
181
|
-
type_str = match.group(2).split("\n")[0].strip()
|
|
182
|
-
return type_str
|
|
183
|
-
return None
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def _build_kwargs_with_types() -> (
|
|
187
|
-
tuple[
|
|
188
|
-
List[Dict[str, Any]],
|
|
189
|
-
List[Dict[str, Any]],
|
|
190
|
-
List[Dict[str, Any]],
|
|
191
|
-
List[Dict[str, Any]],
|
|
192
|
-
]
|
|
193
|
-
):
|
|
194
|
-
"""Build kwargs lists with types from matplotlib classes."""
|
|
195
|
-
from matplotlib.artist import Artist
|
|
196
|
-
from matplotlib.lines import Line2D
|
|
197
|
-
from matplotlib.patches import Patch
|
|
198
|
-
from matplotlib.text import Text
|
|
199
|
-
|
|
200
|
-
# Create instances for introspection
|
|
201
|
-
line = Line2D([0], [0])
|
|
202
|
-
patch = Patch()
|
|
203
|
-
text = Text()
|
|
204
|
-
artist = Artist()
|
|
205
|
-
|
|
206
|
-
def get_type(obj, name):
|
|
207
|
-
return _get_setter_type(obj, name)
|
|
208
|
-
|
|
209
|
-
ARTIST_KWARGS = [
|
|
210
|
-
{"name": "agg_filter", "type": get_type(artist, "agg_filter"), "default": None},
|
|
211
|
-
{"name": "alpha", "type": get_type(artist, "alpha"), "default": None},
|
|
212
|
-
{"name": "animated", "type": get_type(artist, "animated"), "default": False},
|
|
213
|
-
{"name": "clip_box", "type": get_type(artist, "clip_box"), "default": None},
|
|
214
|
-
{"name": "clip_on", "type": get_type(artist, "clip_on"), "default": True},
|
|
215
|
-
{"name": "clip_path", "type": get_type(artist, "clip_path"), "default": None},
|
|
216
|
-
{"name": "gid", "type": get_type(artist, "gid"), "default": None},
|
|
217
|
-
{"name": "label", "type": get_type(artist, "label"), "default": ""},
|
|
218
|
-
{
|
|
219
|
-
"name": "path_effects",
|
|
220
|
-
"type": get_type(artist, "path_effects"),
|
|
221
|
-
"default": None,
|
|
222
|
-
},
|
|
223
|
-
{"name": "picker", "type": get_type(artist, "picker"), "default": None},
|
|
224
|
-
{"name": "rasterized", "type": get_type(artist, "rasterized"), "default": None},
|
|
225
|
-
{
|
|
226
|
-
"name": "sketch_params",
|
|
227
|
-
"type": get_type(artist, "sketch_params"),
|
|
228
|
-
"default": None,
|
|
229
|
-
},
|
|
230
|
-
{"name": "snap", "type": get_type(artist, "snap"), "default": None},
|
|
231
|
-
{"name": "transform", "type": get_type(artist, "transform"), "default": None},
|
|
232
|
-
{"name": "url", "type": get_type(artist, "url"), "default": None},
|
|
233
|
-
{"name": "visible", "type": get_type(artist, "visible"), "default": True},
|
|
234
|
-
{"name": "zorder", "type": get_type(artist, "zorder"), "default": None},
|
|
235
|
-
]
|
|
236
|
-
|
|
237
|
-
LINE2D_KWARGS = [
|
|
238
|
-
{"name": "color", "type": get_type(line, "color"), "default": None},
|
|
239
|
-
{"name": "linestyle", "type": get_type(line, "linestyle"), "default": "-"},
|
|
240
|
-
{"name": "linewidth", "type": get_type(line, "linewidth"), "default": None},
|
|
241
|
-
{"name": "marker", "type": get_type(line, "marker"), "default": ""},
|
|
242
|
-
{
|
|
243
|
-
"name": "markeredgecolor",
|
|
244
|
-
"type": get_type(line, "markeredgecolor"),
|
|
245
|
-
"default": None,
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
"name": "markeredgewidth",
|
|
249
|
-
"type": get_type(line, "markeredgewidth"),
|
|
250
|
-
"default": None,
|
|
251
|
-
},
|
|
252
|
-
{
|
|
253
|
-
"name": "markerfacecolor",
|
|
254
|
-
"type": get_type(line, "markerfacecolor"),
|
|
255
|
-
"default": None,
|
|
256
|
-
},
|
|
257
|
-
{"name": "markersize", "type": get_type(line, "markersize"), "default": None},
|
|
258
|
-
{"name": "antialiased", "type": get_type(line, "antialiased"), "default": True},
|
|
259
|
-
{
|
|
260
|
-
"name": "dash_capstyle",
|
|
261
|
-
"type": get_type(line, "dash_capstyle"),
|
|
262
|
-
"default": "butt",
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
"name": "dash_joinstyle",
|
|
266
|
-
"type": get_type(line, "dash_joinstyle"),
|
|
267
|
-
"default": "round",
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
"name": "solid_capstyle",
|
|
271
|
-
"type": get_type(line, "solid_capstyle"),
|
|
272
|
-
"default": "projecting",
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
"name": "solid_joinstyle",
|
|
276
|
-
"type": get_type(line, "solid_joinstyle"),
|
|
277
|
-
"default": "round",
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
"name": "drawstyle",
|
|
281
|
-
"type": get_type(line, "drawstyle"),
|
|
282
|
-
"default": "default",
|
|
283
|
-
},
|
|
284
|
-
{"name": "fillstyle", "type": get_type(line, "fillstyle"), "default": "full"},
|
|
285
|
-
]
|
|
286
|
-
|
|
287
|
-
PATCH_KWARGS = [
|
|
288
|
-
{"name": "color", "type": get_type(patch, "color"), "default": None},
|
|
289
|
-
{"name": "edgecolor", "type": get_type(patch, "edgecolor"), "default": None},
|
|
290
|
-
{"name": "facecolor", "type": get_type(patch, "facecolor"), "default": None},
|
|
291
|
-
{"name": "fill", "type": get_type(patch, "fill"), "default": True},
|
|
292
|
-
{"name": "hatch", "type": get_type(patch, "hatch"), "default": None},
|
|
293
|
-
{"name": "linestyle", "type": get_type(patch, "linestyle"), "default": "-"},
|
|
294
|
-
{"name": "linewidth", "type": get_type(patch, "linewidth"), "default": None},
|
|
295
|
-
{
|
|
296
|
-
"name": "antialiased",
|
|
297
|
-
"type": get_type(patch, "antialiased"),
|
|
298
|
-
"default": None,
|
|
299
|
-
},
|
|
300
|
-
{"name": "capstyle", "type": get_type(patch, "capstyle"), "default": "butt"},
|
|
301
|
-
{"name": "joinstyle", "type": get_type(patch, "joinstyle"), "default": "miter"},
|
|
302
|
-
]
|
|
303
|
-
|
|
304
|
-
TEXT_KWARGS = [
|
|
305
|
-
{"name": "color", "type": get_type(text, "color"), "default": "black"},
|
|
306
|
-
{"name": "fontfamily", "type": get_type(text, "fontfamily"), "default": None},
|
|
307
|
-
{"name": "fontsize", "type": get_type(text, "fontsize"), "default": None},
|
|
308
|
-
{"name": "fontstretch", "type": get_type(text, "fontstretch"), "default": None},
|
|
309
|
-
{"name": "fontstyle", "type": get_type(text, "fontstyle"), "default": "normal"},
|
|
310
|
-
{
|
|
311
|
-
"name": "fontvariant",
|
|
312
|
-
"type": get_type(text, "fontvariant"),
|
|
313
|
-
"default": "normal",
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
"name": "fontweight",
|
|
317
|
-
"type": get_type(text, "fontweight"),
|
|
318
|
-
"default": "normal",
|
|
319
|
-
},
|
|
320
|
-
{
|
|
321
|
-
"name": "horizontalalignment",
|
|
322
|
-
"type": get_type(text, "horizontalalignment"),
|
|
323
|
-
"default": "center",
|
|
324
|
-
},
|
|
325
|
-
{
|
|
326
|
-
"name": "verticalalignment",
|
|
327
|
-
"type": get_type(text, "verticalalignment"),
|
|
328
|
-
"default": "center",
|
|
329
|
-
},
|
|
330
|
-
{"name": "rotation", "type": get_type(text, "rotation"), "default": None},
|
|
331
|
-
{"name": "linespacing", "type": get_type(text, "linespacing"), "default": None},
|
|
332
|
-
{
|
|
333
|
-
"name": "multialignment",
|
|
334
|
-
"type": get_type(text, "multialignment"),
|
|
335
|
-
"default": None,
|
|
336
|
-
},
|
|
337
|
-
{"name": "wrap", "type": get_type(text, "wrap"), "default": False},
|
|
338
|
-
]
|
|
339
|
-
|
|
340
|
-
return ARTIST_KWARGS, LINE2D_KWARGS, PATCH_KWARGS, TEXT_KWARGS
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
# Build kwargs with types (lazy initialization)
|
|
344
|
-
_KWARGS_CACHE: Optional[Dict[str, List[Dict[str, Any]]]] = None
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
def _get_kwargs_mapping() -> Dict[str, List[Dict[str, Any]]]:
|
|
348
|
-
"""Get kwargs mapping, building it lazily on first call."""
|
|
349
|
-
global _KWARGS_CACHE
|
|
350
|
-
if _KWARGS_CACHE is not None:
|
|
351
|
-
return _KWARGS_CACHE
|
|
352
|
-
|
|
353
|
-
ARTIST_KWARGS, LINE2D_KWARGS, PATCH_KWARGS, TEXT_KWARGS = _build_kwargs_with_types()
|
|
354
|
-
|
|
355
|
-
_KWARGS_CACHE = {
|
|
356
|
-
"plot": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
357
|
-
"scatter": ARTIST_KWARGS,
|
|
358
|
-
"bar": PATCH_KWARGS + ARTIST_KWARGS,
|
|
359
|
-
"barh": PATCH_KWARGS + ARTIST_KWARGS,
|
|
360
|
-
"fill": PATCH_KWARGS + ARTIST_KWARGS,
|
|
361
|
-
"fill_between": PATCH_KWARGS + ARTIST_KWARGS,
|
|
362
|
-
"fill_betweenx": PATCH_KWARGS + ARTIST_KWARGS,
|
|
363
|
-
"step": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
364
|
-
"errorbar": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
365
|
-
"hist": PATCH_KWARGS + ARTIST_KWARGS,
|
|
366
|
-
"hist2d": ARTIST_KWARGS,
|
|
367
|
-
"imshow": ARTIST_KWARGS,
|
|
368
|
-
"pcolor": ARTIST_KWARGS,
|
|
369
|
-
"pcolormesh": ARTIST_KWARGS,
|
|
370
|
-
"pcolorfast": ARTIST_KWARGS,
|
|
371
|
-
"contour": ARTIST_KWARGS,
|
|
372
|
-
"contourf": ARTIST_KWARGS,
|
|
373
|
-
"hexbin": ARTIST_KWARGS,
|
|
374
|
-
"quiver": ARTIST_KWARGS,
|
|
375
|
-
"barbs": ARTIST_KWARGS,
|
|
376
|
-
"specgram": ARTIST_KWARGS,
|
|
377
|
-
"psd": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
378
|
-
"csd": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
379
|
-
"cohere": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
380
|
-
"acorr": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
381
|
-
"xcorr": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
382
|
-
"angle_spectrum": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
383
|
-
"magnitude_spectrum": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
384
|
-
"phase_spectrum": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
385
|
-
"stackplot": PATCH_KWARGS + ARTIST_KWARGS,
|
|
386
|
-
"stairs": PATCH_KWARGS + ARTIST_KWARGS,
|
|
387
|
-
"eventplot": ARTIST_KWARGS,
|
|
388
|
-
"broken_barh": PATCH_KWARGS + ARTIST_KWARGS,
|
|
389
|
-
"loglog": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
390
|
-
"semilogx": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
391
|
-
"semilogy": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
392
|
-
"annotate": TEXT_KWARGS + ARTIST_KWARGS,
|
|
393
|
-
"text": TEXT_KWARGS + ARTIST_KWARGS,
|
|
394
|
-
"arrow": PATCH_KWARGS + ARTIST_KWARGS,
|
|
395
|
-
"axhline": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
396
|
-
"axvline": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
397
|
-
"hlines": ARTIST_KWARGS,
|
|
398
|
-
"vlines": ARTIST_KWARGS,
|
|
399
|
-
"axhspan": PATCH_KWARGS + ARTIST_KWARGS,
|
|
400
|
-
"axvspan": PATCH_KWARGS + ARTIST_KWARGS,
|
|
401
|
-
"axline": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
402
|
-
"legend": ARTIST_KWARGS,
|
|
403
|
-
"grid": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
404
|
-
"table": ARTIST_KWARGS,
|
|
405
|
-
"clabel": TEXT_KWARGS + ARTIST_KWARGS,
|
|
406
|
-
"bar_label": TEXT_KWARGS + ARTIST_KWARGS,
|
|
407
|
-
"quiverkey": ARTIST_KWARGS,
|
|
408
|
-
"ecdf": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
409
|
-
"tricontour": ARTIST_KWARGS,
|
|
410
|
-
"tricontourf": ARTIST_KWARGS,
|
|
411
|
-
"tripcolor": ARTIST_KWARGS,
|
|
412
|
-
"triplot": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
413
|
-
"matshow": ARTIST_KWARGS,
|
|
414
|
-
"spy": ARTIST_KWARGS + LINE2D_KWARGS,
|
|
415
|
-
"boxplot": ARTIST_KWARGS,
|
|
416
|
-
"violinplot": ARTIST_KWARGS,
|
|
417
|
-
"pie": PATCH_KWARGS + ARTIST_KWARGS,
|
|
418
|
-
"stem": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
return _KWARGS_CACHE
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
# -----------------------------------------------------------------------------
|
|
425
|
-
# Type helpers
|
|
426
|
-
# -----------------------------------------------------------------------------
|
|
427
23
|
def _get_type_str(annotation) -> Optional[str]:
|
|
428
24
|
"""Convert annotation to string."""
|
|
429
25
|
if annotation is inspect.Parameter.empty:
|
|
@@ -448,9 +44,6 @@ def _serialize_default(default) -> Any:
|
|
|
448
44
|
return repr(default)
|
|
449
45
|
|
|
450
46
|
|
|
451
|
-
# -----------------------------------------------------------------------------
|
|
452
|
-
# Main API
|
|
453
|
-
# -----------------------------------------------------------------------------
|
|
454
47
|
def get_signature(method_name: str, expand_kwargs: bool = True) -> Dict[str, Any]:
|
|
455
48
|
"""Get signature for a matplotlib Axes method with deep inspection.
|
|
456
49
|
|
|
@@ -485,7 +78,7 @@ def get_signature(method_name: str, expand_kwargs: bool = True) -> Dict[str, Any
|
|
|
485
78
|
return {"args": [], "kwargs": {}}
|
|
486
79
|
|
|
487
80
|
# Parse parameter types from docstring
|
|
488
|
-
param_types =
|
|
81
|
+
param_types = parse_parameter_types(method.__doc__)
|
|
489
82
|
|
|
490
83
|
args = []
|
|
491
84
|
kwargs = {}
|
|
@@ -506,35 +99,42 @@ def get_signature(method_name: str, expand_kwargs: bool = True) -> Dict[str, Any
|
|
|
506
99
|
if not typehint:
|
|
507
100
|
typehint = param_types.get(name.lower())
|
|
508
101
|
|
|
509
|
-
|
|
510
|
-
|
|
102
|
+
# Handle POSITIONAL_OR_KEYWORD and POSITIONAL_ONLY parameters
|
|
103
|
+
if param.kind in (
|
|
104
|
+
inspect.Parameter.POSITIONAL_ONLY,
|
|
105
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
106
|
+
):
|
|
511
107
|
args.append(
|
|
512
108
|
{
|
|
513
109
|
"name": name,
|
|
514
110
|
"type": typehint,
|
|
515
111
|
}
|
|
516
112
|
)
|
|
517
|
-
|
|
518
|
-
|
|
113
|
+
if param.default is not inspect.Parameter.empty:
|
|
114
|
+
kwargs[name] = {
|
|
115
|
+
"type": typehint,
|
|
116
|
+
"default": _serialize_default(param.default),
|
|
117
|
+
}
|
|
118
|
+
elif param.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
519
119
|
kwargs[name] = {
|
|
520
120
|
"type": typehint,
|
|
521
|
-
"default": _serialize_default(param.default)
|
|
121
|
+
"default": _serialize_default(param.default)
|
|
122
|
+
if param.default is not inspect.Parameter.empty
|
|
123
|
+
else None,
|
|
522
124
|
}
|
|
523
125
|
|
|
524
126
|
# Expand *args from docstring
|
|
525
127
|
if has_var_positional:
|
|
526
|
-
docstring_args =
|
|
128
|
+
docstring_args = extract_args_from_docstring(method.__doc__, method_name)
|
|
527
129
|
if docstring_args:
|
|
528
|
-
# Insert flattened args at the beginning
|
|
529
130
|
for i, arg in enumerate(docstring_args):
|
|
530
131
|
args.insert(i, arg)
|
|
531
132
|
else:
|
|
532
|
-
# No docstring info, keep generic *args
|
|
533
133
|
args.insert(0, {"name": "*args", "type": "*args"})
|
|
534
134
|
|
|
535
135
|
# Expand **kwargs based on function type
|
|
536
136
|
if has_var_keyword and expand_kwargs:
|
|
537
|
-
kwargs_mapping =
|
|
137
|
+
kwargs_mapping = get_kwargs_mapping()
|
|
538
138
|
if method_name in kwargs_mapping:
|
|
539
139
|
expanded_kwargs = kwargs_mapping[method_name]
|
|
540
140
|
existing_names = {p["name"] for p in args} | set(kwargs.keys())
|
|
@@ -598,7 +198,6 @@ def validate_kwargs(
|
|
|
598
198
|
sig = get_signature(method_name)
|
|
599
199
|
known_kwargs = set(sig.get("kwargs", {}).keys()) - {"**kwargs"}
|
|
600
200
|
|
|
601
|
-
# If method accepts **kwargs and we haven't expanded it, all are valid
|
|
602
201
|
if "**kwargs" in sig.get("kwargs", {}):
|
|
603
202
|
return {
|
|
604
203
|
"valid": list(kwargs.keys()),
|
|
@@ -618,7 +217,7 @@ def validate_kwargs(
|
|
|
618
217
|
return {
|
|
619
218
|
"valid": valid,
|
|
620
219
|
"unknown": unknown,
|
|
621
|
-
"missing": [],
|
|
220
|
+
"missing": [],
|
|
622
221
|
}
|
|
623
222
|
|
|
624
223
|
|
|
@@ -638,7 +237,6 @@ def list_plotting_methods() -> List[str]:
|
|
|
638
237
|
fig, ax = plt.subplots()
|
|
639
238
|
plt.close(fig)
|
|
640
239
|
|
|
641
|
-
# Use _params.PLOTTING_METHODS as single source of truth
|
|
642
240
|
return sorted([m for m in PLOTTING_METHODS if hasattr(ax, m)])
|
|
643
241
|
|
|
644
242
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Docstring parsing utilities for matplotlib signature extraction."""
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def parse_parameter_types(docstring: Optional[str]) -> Dict[str, str]:
|
|
10
|
+
"""Extract parameter types from NumPy-style docstring Parameters section."""
|
|
11
|
+
if not docstring:
|
|
12
|
+
return {}
|
|
13
|
+
|
|
14
|
+
types = {}
|
|
15
|
+
|
|
16
|
+
# Find Parameters section
|
|
17
|
+
params_match = re.search(
|
|
18
|
+
r"Parameters\s*[-]+\s*(.*?)(?:\n\s*Returns|\n\s*See Also|\n\s*Notes|\n\s*Examples|\n\s*Other Parameters|\Z)",
|
|
19
|
+
docstring,
|
|
20
|
+
re.DOTALL,
|
|
21
|
+
)
|
|
22
|
+
if not params_match:
|
|
23
|
+
return {}
|
|
24
|
+
|
|
25
|
+
params_text = params_match.group(1)
|
|
26
|
+
|
|
27
|
+
# Parse lines like "x, y : array-like or float" or "fmt : str, optional"
|
|
28
|
+
for match in re.finditer(
|
|
29
|
+
r"^(\w+(?:\s*,\s*\w+)*)\s*:\s*(.+?)(?=\n\s*\n|\n\w+\s*:|\Z)",
|
|
30
|
+
params_text,
|
|
31
|
+
re.MULTILINE | re.DOTALL,
|
|
32
|
+
):
|
|
33
|
+
names_str = match.group(1)
|
|
34
|
+
type_str = match.group(2).split("\n")[0].strip()
|
|
35
|
+
|
|
36
|
+
# Clean up type string
|
|
37
|
+
type_str = re.sub(r",?\s*optional\s*$", "", type_str).strip()
|
|
38
|
+
type_str = re.sub(
|
|
39
|
+
r",?\s*default[^,]*$", "", type_str, flags=re.IGNORECASE
|
|
40
|
+
).strip()
|
|
41
|
+
|
|
42
|
+
# Handle multiple names like "x, y"
|
|
43
|
+
for name in re.split(r"\s*,\s*", names_str):
|
|
44
|
+
name = name.strip()
|
|
45
|
+
if name:
|
|
46
|
+
types[name.lower()] = type_str
|
|
47
|
+
|
|
48
|
+
return types
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def parse_args_pattern(
|
|
52
|
+
args_str: str, param_types: Dict[str, str]
|
|
53
|
+
) -> List[Dict[str, Any]]:
|
|
54
|
+
"""Parse args pattern like '[x], y, [fmt]' into list of arg dicts."""
|
|
55
|
+
if not args_str:
|
|
56
|
+
return []
|
|
57
|
+
|
|
58
|
+
args = []
|
|
59
|
+
parts = re.split(r",\s*", args_str)
|
|
60
|
+
|
|
61
|
+
for part in parts:
|
|
62
|
+
part = part.strip()
|
|
63
|
+
if not part or part == "/":
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
optional = part.startswith("[") and part.endswith("]")
|
|
67
|
+
if optional:
|
|
68
|
+
name = part[1:-1].strip()
|
|
69
|
+
else:
|
|
70
|
+
name = part.strip("[]").strip()
|
|
71
|
+
|
|
72
|
+
if name and name not in ("...", "*"):
|
|
73
|
+
type_str = param_types.get(name.lower())
|
|
74
|
+
args.append(
|
|
75
|
+
{
|
|
76
|
+
"name": name,
|
|
77
|
+
"type": type_str,
|
|
78
|
+
"optional": optional,
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
return args
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Manual *args patterns for functions without parseable call signatures
|
|
86
|
+
MANUAL_ARGS_PATTERNS = {
|
|
87
|
+
"fill": "[x], y, [color]",
|
|
88
|
+
"stackplot": "x, *ys",
|
|
89
|
+
"legend": "[handles], [labels]",
|
|
90
|
+
"stem": "[locs], heads",
|
|
91
|
+
"tricontour": "[triangulation], x, y, z, [levels]",
|
|
92
|
+
"tricontourf": "[triangulation], x, y, z, [levels]",
|
|
93
|
+
"triplot": "[triangulation], x, y, [triangles]",
|
|
94
|
+
"loglog": "[x], y, [fmt]",
|
|
95
|
+
"semilogx": "[x], y, [fmt]",
|
|
96
|
+
"semilogy": "[x], y, [fmt]",
|
|
97
|
+
"barbs": "[X], [Y], U, V, [C]",
|
|
98
|
+
"quiver": "[X], [Y], U, V, [C]",
|
|
99
|
+
"pcolor": "[X], [Y], C",
|
|
100
|
+
"pcolormesh": "[X], [Y], C",
|
|
101
|
+
"pcolorfast": "[X], [Y], C",
|
|
102
|
+
"acorr": "x",
|
|
103
|
+
"xcorr": "x, y",
|
|
104
|
+
"plot": "[x], y, [fmt]",
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def extract_args_from_docstring(
|
|
109
|
+
docstring: Optional[str], func_name: str = ""
|
|
110
|
+
) -> List[Dict[str, Any]]:
|
|
111
|
+
"""Extract *args as flattened list from docstring call signature."""
|
|
112
|
+
if not docstring:
|
|
113
|
+
return []
|
|
114
|
+
|
|
115
|
+
# First, parse parameter types
|
|
116
|
+
param_types = parse_parameter_types(docstring)
|
|
117
|
+
|
|
118
|
+
# Check for manual pattern first
|
|
119
|
+
if func_name in MANUAL_ARGS_PATTERNS:
|
|
120
|
+
return parse_args_pattern(MANUAL_ARGS_PATTERNS[func_name], param_types)
|
|
121
|
+
|
|
122
|
+
# Look for "Call signature:" patterns
|
|
123
|
+
patterns = [
|
|
124
|
+
r"Call signatures?::\s*\n\s*(.*?)(?:\n\n|\n[A-Z])",
|
|
125
|
+
r"^\s*(\w+\([^)]+\))\s*$",
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
for pattern in patterns:
|
|
129
|
+
match = re.search(pattern, docstring, re.MULTILINE | re.DOTALL)
|
|
130
|
+
if match:
|
|
131
|
+
sig_text = match.group(1).strip()
|
|
132
|
+
first_line = sig_text.split("\n")[0].strip()
|
|
133
|
+
inner_match = re.search(r"\(([^*]+?)(?:,\s*\*|,\s*data|\))", first_line)
|
|
134
|
+
if inner_match:
|
|
135
|
+
args_str = inner_match.group(1).strip().rstrip(",")
|
|
136
|
+
return parse_args_pattern(args_str, param_types)
|
|
137
|
+
return []
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
__all__ = [
|
|
141
|
+
"parse_parameter_types",
|
|
142
|
+
"parse_args_pattern",
|
|
143
|
+
"extract_args_from_docstring",
|
|
144
|
+
"MANUAL_ARGS_PATTERNS",
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
# EOF
|