figrecipe 0.5.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 +220 -819
- 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 +29 -0
- figrecipe/_dev/_plotters.py +76 -0
- figrecipe/_dev/_run_demos.py +56 -0
- figrecipe/_dev/demo_plotters/__init__.py +64 -0
- 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/bar_categorical/plot_bar.py +25 -0
- figrecipe/_dev/demo_plotters/bar_categorical/plot_barh.py +25 -0
- figrecipe/_dev/demo_plotters/contour_surface/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_contour.py +30 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_contourf.py +29 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_tricontour.py +28 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_tricontourf.py +28 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_tripcolor.py +29 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_triplot.py +25 -0
- figrecipe/_dev/demo_plotters/distribution/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/distribution/plot_boxplot.py +24 -0
- figrecipe/_dev/demo_plotters/distribution/plot_ecdf.py +24 -0
- figrecipe/_dev/demo_plotters/distribution/plot_hist.py +24 -0
- figrecipe/_dev/demo_plotters/distribution/plot_hist2d.py +25 -0
- figrecipe/_dev/demo_plotters/distribution/plot_violinplot.py +25 -0
- figrecipe/_dev/demo_plotters/image_matrix/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_hexbin.py +25 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_imshow.py +23 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_matshow.py +23 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_pcolor.py +29 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_pcolormesh.py +29 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_spy.py +29 -0
- figrecipe/_dev/demo_plotters/line_curve/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_errorbar.py +28 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_fill.py +29 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_fill_between.py +30 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_fill_betweenx.py +28 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_plot.py +28 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_stackplot.py +29 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_stairs.py +27 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_step.py +27 -0
- figrecipe/_dev/demo_plotters/scatter_points/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/scatter_points/plot_scatter.py +24 -0
- figrecipe/_dev/demo_plotters/special/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/special/plot_eventplot.py +25 -0
- figrecipe/_dev/demo_plotters/special/plot_loglog.py +27 -0
- figrecipe/_dev/demo_plotters/special/plot_pie.py +27 -0
- figrecipe/_dev/demo_plotters/special/plot_semilogx.py +27 -0
- figrecipe/_dev/demo_plotters/special/plot_semilogy.py +27 -0
- figrecipe/_dev/demo_plotters/special/plot_stem.py +27 -0
- figrecipe/_dev/demo_plotters/spectral_signal/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_acorr.py +24 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_angle_spectrum.py +28 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_cohere.py +29 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_csd.py +29 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_magnitude_spectrum.py +28 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_phase_spectrum.py +28 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_psd.py +29 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_specgram.py +30 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_xcorr.py +25 -0
- figrecipe/_dev/demo_plotters/vector_flow/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/vector_flow/plot_barbs.py +30 -0
- figrecipe/_dev/demo_plotters/vector_flow/plot_quiver.py +30 -0
- figrecipe/_dev/demo_plotters/vector_flow/plot_streamplot.py +30 -0
- figrecipe/_editor/__init__.py +278 -0
- 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 +258 -0
- 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/_overrides.py +318 -0
- figrecipe/_editor/_preferences.py +135 -0
- figrecipe/_editor/_render_overrides.py +480 -0
- figrecipe/_editor/_renderer.py +199 -0
- 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 +152 -0
- figrecipe/_editor/_templates/_html.py +502 -0
- 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 +33 -0
- figrecipe/_params/_PLOTTING_METHODS.py +58 -0
- figrecipe/_params/__init__.py +9 -0
- figrecipe/_recorder.py +92 -110
- 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/_seaborn.py +14 -9
- figrecipe/_serializer.py +2 -2
- figrecipe/_signatures/README.md +68 -0
- figrecipe/_signatures/__init__.py +12 -2
- figrecipe/_signatures/_kwargs.py +273 -0
- figrecipe/_signatures/_loader.py +114 -57
- figrecipe/_signatures/_parsing.py +147 -0
- figrecipe/_utils/__init__.py +6 -4
- figrecipe/_utils/_crop.py +10 -4
- figrecipe/_utils/_image_diff.py +37 -33
- figrecipe/_utils/_numpy_io.py +0 -1
- figrecipe/_utils/_units.py +11 -3
- figrecipe/_validator.py +12 -3
- figrecipe/_wrappers/_axes.py +193 -170
- 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 +277 -18
- figrecipe/_wrappers/_panel_labels.py +127 -0
- figrecipe/_wrappers/_plot_helpers.py +143 -0
- figrecipe/_wrappers/_violin_helpers.py +180 -0
- figrecipe/plt.py +0 -1
- figrecipe/pyplot.py +2 -1
- figrecipe/styles/__init__.py +12 -11
- 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 +60 -202
- figrecipe/styles/_style_loader.py +73 -121
- figrecipe/styles/_themes.py +151 -0
- figrecipe/styles/presets/MATPLOTLIB.yaml +95 -0
- figrecipe/styles/presets/SCITEX.yaml +181 -0
- figrecipe-0.7.4.dist-info/METADATA +429 -0
- figrecipe-0.7.4.dist-info/RECORD +188 -0
- figrecipe/_reproducer.py +0 -358
- figrecipe-0.5.0.dist-info/METADATA +0 -336
- figrecipe-0.5.0.dist-info/RECORD +0 -26
- {figrecipe-0.5.0.dist-info → figrecipe-0.7.4.dist-info}/WHEEL +0 -0
- {figrecipe-0.5.0.dist-info → figrecipe-0.7.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
"""Matplotlib function signatures for validation and defaults."""
|
|
4
4
|
|
|
5
|
-
from ._loader import
|
|
5
|
+
from ._loader import (
|
|
6
|
+
get_defaults,
|
|
7
|
+
get_signature,
|
|
8
|
+
list_plotting_methods,
|
|
9
|
+
validate_kwargs,
|
|
10
|
+
)
|
|
6
11
|
|
|
7
|
-
__all__ = [
|
|
12
|
+
__all__ = [
|
|
13
|
+
"get_signature",
|
|
14
|
+
"get_defaults",
|
|
15
|
+
"validate_kwargs",
|
|
16
|
+
"list_plotting_methods",
|
|
17
|
+
]
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Kwargs expansion utilities for matplotlib signature extraction."""
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_setter_type(obj: Any, prop_name: str) -> Optional[str]:
|
|
10
|
+
"""Get type from set_* method docstring."""
|
|
11
|
+
setter_name = f"set_{prop_name}"
|
|
12
|
+
if not hasattr(obj, setter_name):
|
|
13
|
+
return None
|
|
14
|
+
|
|
15
|
+
method = getattr(obj, setter_name)
|
|
16
|
+
if not method.__doc__:
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
match = re.search(
|
|
20
|
+
r"Parameters\s*[-]+\s*\n\s*(\w+)\s*:\s*(.+?)(?:\n\s*\n|\Z)",
|
|
21
|
+
method.__doc__,
|
|
22
|
+
re.DOTALL,
|
|
23
|
+
)
|
|
24
|
+
if match:
|
|
25
|
+
type_str = match.group(2).split("\n")[0].strip()
|
|
26
|
+
return type_str
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def build_kwargs_with_types() -> (
|
|
31
|
+
tuple[
|
|
32
|
+
List[Dict[str, Any]],
|
|
33
|
+
List[Dict[str, Any]],
|
|
34
|
+
List[Dict[str, Any]],
|
|
35
|
+
List[Dict[str, Any]],
|
|
36
|
+
]
|
|
37
|
+
):
|
|
38
|
+
"""Build kwargs lists with types from matplotlib classes."""
|
|
39
|
+
from matplotlib.artist import Artist
|
|
40
|
+
from matplotlib.lines import Line2D
|
|
41
|
+
from matplotlib.patches import Patch
|
|
42
|
+
from matplotlib.text import Text
|
|
43
|
+
|
|
44
|
+
line = Line2D([0], [0])
|
|
45
|
+
patch = Patch()
|
|
46
|
+
text = Text()
|
|
47
|
+
artist = Artist()
|
|
48
|
+
|
|
49
|
+
def get_type(obj, name):
|
|
50
|
+
return get_setter_type(obj, name)
|
|
51
|
+
|
|
52
|
+
ARTIST_KWARGS = [
|
|
53
|
+
{"name": "agg_filter", "type": get_type(artist, "agg_filter"), "default": None},
|
|
54
|
+
{"name": "alpha", "type": get_type(artist, "alpha"), "default": None},
|
|
55
|
+
{"name": "animated", "type": get_type(artist, "animated"), "default": False},
|
|
56
|
+
{"name": "clip_box", "type": get_type(artist, "clip_box"), "default": None},
|
|
57
|
+
{"name": "clip_on", "type": get_type(artist, "clip_on"), "default": True},
|
|
58
|
+
{"name": "clip_path", "type": get_type(artist, "clip_path"), "default": None},
|
|
59
|
+
{"name": "gid", "type": get_type(artist, "gid"), "default": None},
|
|
60
|
+
{"name": "label", "type": get_type(artist, "label"), "default": ""},
|
|
61
|
+
{
|
|
62
|
+
"name": "path_effects",
|
|
63
|
+
"type": get_type(artist, "path_effects"),
|
|
64
|
+
"default": None,
|
|
65
|
+
},
|
|
66
|
+
{"name": "picker", "type": get_type(artist, "picker"), "default": None},
|
|
67
|
+
{"name": "rasterized", "type": get_type(artist, "rasterized"), "default": None},
|
|
68
|
+
{
|
|
69
|
+
"name": "sketch_params",
|
|
70
|
+
"type": get_type(artist, "sketch_params"),
|
|
71
|
+
"default": None,
|
|
72
|
+
},
|
|
73
|
+
{"name": "snap", "type": get_type(artist, "snap"), "default": None},
|
|
74
|
+
{"name": "transform", "type": get_type(artist, "transform"), "default": None},
|
|
75
|
+
{"name": "url", "type": get_type(artist, "url"), "default": None},
|
|
76
|
+
{"name": "visible", "type": get_type(artist, "visible"), "default": True},
|
|
77
|
+
{"name": "zorder", "type": get_type(artist, "zorder"), "default": None},
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
LINE2D_KWARGS = [
|
|
81
|
+
{"name": "color", "type": get_type(line, "color"), "default": None},
|
|
82
|
+
{"name": "linestyle", "type": get_type(line, "linestyle"), "default": "-"},
|
|
83
|
+
{"name": "linewidth", "type": get_type(line, "linewidth"), "default": None},
|
|
84
|
+
{"name": "marker", "type": get_type(line, "marker"), "default": ""},
|
|
85
|
+
{
|
|
86
|
+
"name": "markeredgecolor",
|
|
87
|
+
"type": get_type(line, "markeredgecolor"),
|
|
88
|
+
"default": None,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"name": "markeredgewidth",
|
|
92
|
+
"type": get_type(line, "markeredgewidth"),
|
|
93
|
+
"default": None,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"name": "markerfacecolor",
|
|
97
|
+
"type": get_type(line, "markerfacecolor"),
|
|
98
|
+
"default": None,
|
|
99
|
+
},
|
|
100
|
+
{"name": "markersize", "type": get_type(line, "markersize"), "default": None},
|
|
101
|
+
{"name": "antialiased", "type": get_type(line, "antialiased"), "default": True},
|
|
102
|
+
{
|
|
103
|
+
"name": "dash_capstyle",
|
|
104
|
+
"type": get_type(line, "dash_capstyle"),
|
|
105
|
+
"default": "butt",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "dash_joinstyle",
|
|
109
|
+
"type": get_type(line, "dash_joinstyle"),
|
|
110
|
+
"default": "round",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "solid_capstyle",
|
|
114
|
+
"type": get_type(line, "solid_capstyle"),
|
|
115
|
+
"default": "projecting",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "solid_joinstyle",
|
|
119
|
+
"type": get_type(line, "solid_joinstyle"),
|
|
120
|
+
"default": "round",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "drawstyle",
|
|
124
|
+
"type": get_type(line, "drawstyle"),
|
|
125
|
+
"default": "default",
|
|
126
|
+
},
|
|
127
|
+
{"name": "fillstyle", "type": get_type(line, "fillstyle"), "default": "full"},
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
PATCH_KWARGS = [
|
|
131
|
+
{"name": "color", "type": get_type(patch, "color"), "default": None},
|
|
132
|
+
{"name": "edgecolor", "type": get_type(patch, "edgecolor"), "default": None},
|
|
133
|
+
{"name": "facecolor", "type": get_type(patch, "facecolor"), "default": None},
|
|
134
|
+
{"name": "fill", "type": get_type(patch, "fill"), "default": True},
|
|
135
|
+
{"name": "hatch", "type": get_type(patch, "hatch"), "default": None},
|
|
136
|
+
{"name": "linestyle", "type": get_type(patch, "linestyle"), "default": "-"},
|
|
137
|
+
{"name": "linewidth", "type": get_type(patch, "linewidth"), "default": None},
|
|
138
|
+
{
|
|
139
|
+
"name": "antialiased",
|
|
140
|
+
"type": get_type(patch, "antialiased"),
|
|
141
|
+
"default": None,
|
|
142
|
+
},
|
|
143
|
+
{"name": "capstyle", "type": get_type(patch, "capstyle"), "default": "butt"},
|
|
144
|
+
{"name": "joinstyle", "type": get_type(patch, "joinstyle"), "default": "miter"},
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
TEXT_KWARGS = [
|
|
148
|
+
{"name": "color", "type": get_type(text, "color"), "default": "black"},
|
|
149
|
+
{"name": "fontfamily", "type": get_type(text, "fontfamily"), "default": None},
|
|
150
|
+
{"name": "fontsize", "type": get_type(text, "fontsize"), "default": None},
|
|
151
|
+
{"name": "fontstretch", "type": get_type(text, "fontstretch"), "default": None},
|
|
152
|
+
{"name": "fontstyle", "type": get_type(text, "fontstyle"), "default": "normal"},
|
|
153
|
+
{
|
|
154
|
+
"name": "fontvariant",
|
|
155
|
+
"type": get_type(text, "fontvariant"),
|
|
156
|
+
"default": "normal",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"name": "fontweight",
|
|
160
|
+
"type": get_type(text, "fontweight"),
|
|
161
|
+
"default": "normal",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "horizontalalignment",
|
|
165
|
+
"type": get_type(text, "horizontalalignment"),
|
|
166
|
+
"default": "center",
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"name": "verticalalignment",
|
|
170
|
+
"type": get_type(text, "verticalalignment"),
|
|
171
|
+
"default": "center",
|
|
172
|
+
},
|
|
173
|
+
{"name": "rotation", "type": get_type(text, "rotation"), "default": None},
|
|
174
|
+
{"name": "linespacing", "type": get_type(text, "linespacing"), "default": None},
|
|
175
|
+
{
|
|
176
|
+
"name": "multialignment",
|
|
177
|
+
"type": get_type(text, "multialignment"),
|
|
178
|
+
"default": None,
|
|
179
|
+
},
|
|
180
|
+
{"name": "wrap", "type": get_type(text, "wrap"), "default": False},
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
return ARTIST_KWARGS, LINE2D_KWARGS, PATCH_KWARGS, TEXT_KWARGS
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# Lazy cache for kwargs mapping
|
|
187
|
+
_KWARGS_CACHE: Optional[Dict[str, List[Dict[str, Any]]]] = None
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def get_kwargs_mapping() -> Dict[str, List[Dict[str, Any]]]:
|
|
191
|
+
"""Get kwargs mapping, building it lazily on first call."""
|
|
192
|
+
global _KWARGS_CACHE
|
|
193
|
+
if _KWARGS_CACHE is not None:
|
|
194
|
+
return _KWARGS_CACHE
|
|
195
|
+
|
|
196
|
+
ARTIST_KWARGS, LINE2D_KWARGS, PATCH_KWARGS, TEXT_KWARGS = build_kwargs_with_types()
|
|
197
|
+
|
|
198
|
+
_KWARGS_CACHE = {
|
|
199
|
+
"plot": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
200
|
+
"scatter": ARTIST_KWARGS,
|
|
201
|
+
"bar": PATCH_KWARGS + ARTIST_KWARGS,
|
|
202
|
+
"barh": PATCH_KWARGS + ARTIST_KWARGS,
|
|
203
|
+
"fill": PATCH_KWARGS + ARTIST_KWARGS,
|
|
204
|
+
"fill_between": PATCH_KWARGS + ARTIST_KWARGS,
|
|
205
|
+
"fill_betweenx": PATCH_KWARGS + ARTIST_KWARGS,
|
|
206
|
+
"step": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
207
|
+
"errorbar": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
208
|
+
"hist": PATCH_KWARGS + ARTIST_KWARGS,
|
|
209
|
+
"hist2d": ARTIST_KWARGS,
|
|
210
|
+
"imshow": ARTIST_KWARGS,
|
|
211
|
+
"pcolor": ARTIST_KWARGS,
|
|
212
|
+
"pcolormesh": ARTIST_KWARGS,
|
|
213
|
+
"pcolorfast": ARTIST_KWARGS,
|
|
214
|
+
"contour": ARTIST_KWARGS,
|
|
215
|
+
"contourf": ARTIST_KWARGS,
|
|
216
|
+
"hexbin": ARTIST_KWARGS,
|
|
217
|
+
"quiver": ARTIST_KWARGS,
|
|
218
|
+
"barbs": ARTIST_KWARGS,
|
|
219
|
+
"specgram": ARTIST_KWARGS,
|
|
220
|
+
"psd": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
221
|
+
"csd": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
222
|
+
"cohere": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
223
|
+
"acorr": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
224
|
+
"xcorr": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
225
|
+
"angle_spectrum": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
226
|
+
"magnitude_spectrum": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
227
|
+
"phase_spectrum": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
228
|
+
"stackplot": PATCH_KWARGS + ARTIST_KWARGS,
|
|
229
|
+
"stairs": PATCH_KWARGS + ARTIST_KWARGS,
|
|
230
|
+
"eventplot": ARTIST_KWARGS,
|
|
231
|
+
"broken_barh": PATCH_KWARGS + ARTIST_KWARGS,
|
|
232
|
+
"loglog": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
233
|
+
"semilogx": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
234
|
+
"semilogy": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
235
|
+
"annotate": TEXT_KWARGS + ARTIST_KWARGS,
|
|
236
|
+
"text": TEXT_KWARGS + ARTIST_KWARGS,
|
|
237
|
+
"arrow": PATCH_KWARGS + ARTIST_KWARGS,
|
|
238
|
+
"axhline": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
239
|
+
"axvline": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
240
|
+
"hlines": ARTIST_KWARGS,
|
|
241
|
+
"vlines": ARTIST_KWARGS,
|
|
242
|
+
"axhspan": PATCH_KWARGS + ARTIST_KWARGS,
|
|
243
|
+
"axvspan": PATCH_KWARGS + ARTIST_KWARGS,
|
|
244
|
+
"axline": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
245
|
+
"legend": ARTIST_KWARGS,
|
|
246
|
+
"grid": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
247
|
+
"table": ARTIST_KWARGS,
|
|
248
|
+
"clabel": TEXT_KWARGS + ARTIST_KWARGS,
|
|
249
|
+
"bar_label": TEXT_KWARGS + ARTIST_KWARGS,
|
|
250
|
+
"quiverkey": ARTIST_KWARGS,
|
|
251
|
+
"ecdf": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
252
|
+
"tricontour": ARTIST_KWARGS,
|
|
253
|
+
"tricontourf": ARTIST_KWARGS,
|
|
254
|
+
"tripcolor": ARTIST_KWARGS,
|
|
255
|
+
"triplot": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
256
|
+
"matshow": ARTIST_KWARGS,
|
|
257
|
+
"spy": ARTIST_KWARGS + LINE2D_KWARGS,
|
|
258
|
+
"boxplot": ARTIST_KWARGS,
|
|
259
|
+
"violinplot": ARTIST_KWARGS,
|
|
260
|
+
"pie": PATCH_KWARGS + ARTIST_KWARGS,
|
|
261
|
+
"stem": LINE2D_KWARGS + ARTIST_KWARGS,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return _KWARGS_CACHE
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
__all__ = [
|
|
268
|
+
"get_setter_type",
|
|
269
|
+
"build_kwargs_with_types",
|
|
270
|
+
"get_kwargs_mapping",
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
# EOF
|
figrecipe/_signatures/_loader.py
CHANGED
|
@@ -1,32 +1,68 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
|
|
3
|
+
# Timestamp: "2025-12-23 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/figrecipe/src/figrecipe/_signatures/_loader.py
|
|
5
|
+
|
|
6
|
+
"""Load and query matplotlib function signatures with deep inspection.
|
|
7
|
+
|
|
8
|
+
Parses *args/**kwargs from docstrings and expands them to actual parameters.
|
|
9
|
+
"""
|
|
4
10
|
|
|
5
11
|
import inspect
|
|
6
|
-
from typing import Any, Dict, List, Optional
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
7
13
|
|
|
8
14
|
import matplotlib.pyplot as plt
|
|
9
15
|
|
|
16
|
+
from ._kwargs import get_kwargs_mapping
|
|
17
|
+
from ._parsing import extract_args_from_docstring, parse_parameter_types
|
|
10
18
|
|
|
11
19
|
# Cache for signatures
|
|
12
20
|
_SIGNATURE_CACHE: Dict[str, Dict[str, Any]] = {}
|
|
13
21
|
|
|
14
22
|
|
|
15
|
-
def
|
|
16
|
-
"""
|
|
23
|
+
def _get_type_str(annotation) -> Optional[str]:
|
|
24
|
+
"""Convert annotation to string."""
|
|
25
|
+
if annotation is inspect.Parameter.empty:
|
|
26
|
+
return None
|
|
27
|
+
if hasattr(annotation, "__name__"):
|
|
28
|
+
return annotation.__name__
|
|
29
|
+
return str(annotation)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _serialize_default(default) -> Any:
|
|
33
|
+
"""Serialize default value."""
|
|
34
|
+
if default is inspect.Parameter.empty:
|
|
35
|
+
return None
|
|
36
|
+
if callable(default):
|
|
37
|
+
return f"<{type(default).__name__}>"
|
|
38
|
+
try:
|
|
39
|
+
import json
|
|
40
|
+
|
|
41
|
+
json.dumps(default)
|
|
42
|
+
return default
|
|
43
|
+
except (TypeError, ValueError):
|
|
44
|
+
return repr(default)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_signature(method_name: str, expand_kwargs: bool = True) -> Dict[str, Any]:
|
|
48
|
+
"""Get signature for a matplotlib Axes method with deep inspection.
|
|
17
49
|
|
|
18
50
|
Parameters
|
|
19
51
|
----------
|
|
20
52
|
method_name : str
|
|
21
53
|
Name of the method (e.g., 'plot', 'scatter').
|
|
54
|
+
expand_kwargs : bool
|
|
55
|
+
If True, expand **kwargs to actual parameters.
|
|
22
56
|
|
|
23
57
|
Returns
|
|
24
58
|
-------
|
|
25
59
|
dict
|
|
26
60
|
Signature information with 'args' and 'kwargs' keys.
|
|
61
|
+
Args and kwargs are expanded from docstrings when possible.
|
|
27
62
|
"""
|
|
28
|
-
|
|
29
|
-
|
|
63
|
+
cache_key = f"{method_name}_{expand_kwargs}"
|
|
64
|
+
if cache_key in _SIGNATURE_CACHE:
|
|
65
|
+
return _SIGNATURE_CACHE[cache_key]
|
|
30
66
|
|
|
31
67
|
# Create a temporary axes to introspect
|
|
32
68
|
fig, ax = plt.subplots()
|
|
@@ -41,58 +77,83 @@ def get_signature(method_name: str) -> Dict[str, Any]:
|
|
|
41
77
|
except (ValueError, TypeError):
|
|
42
78
|
return {"args": [], "kwargs": {}}
|
|
43
79
|
|
|
80
|
+
# Parse parameter types from docstring
|
|
81
|
+
param_types = parse_parameter_types(method.__doc__)
|
|
82
|
+
|
|
44
83
|
args = []
|
|
45
84
|
kwargs = {}
|
|
85
|
+
has_var_positional = False
|
|
86
|
+
has_var_keyword = False
|
|
46
87
|
|
|
47
88
|
for name, param in sig.parameters.items():
|
|
48
89
|
if name == "self":
|
|
49
90
|
continue
|
|
50
91
|
|
|
51
92
|
if param.kind == inspect.Parameter.VAR_POSITIONAL:
|
|
52
|
-
|
|
93
|
+
has_var_positional = True
|
|
53
94
|
elif param.kind == inspect.Parameter.VAR_KEYWORD:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
95
|
+
has_var_keyword = True
|
|
96
|
+
else:
|
|
97
|
+
# Try annotation first, then docstring
|
|
98
|
+
typehint = _get_type_str(param.annotation)
|
|
99
|
+
if not typehint:
|
|
100
|
+
typehint = param_types.get(name.lower())
|
|
101
|
+
|
|
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
|
+
):
|
|
107
|
+
args.append(
|
|
108
|
+
{
|
|
109
|
+
"name": name,
|
|
110
|
+
"type": typehint,
|
|
111
|
+
}
|
|
112
|
+
)
|
|
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:
|
|
119
|
+
kwargs[name] = {
|
|
120
|
+
"type": typehint,
|
|
121
|
+
"default": _serialize_default(param.default)
|
|
122
|
+
if param.default is not inspect.Parameter.empty
|
|
123
|
+
else None,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Expand *args from docstring
|
|
127
|
+
if has_var_positional:
|
|
128
|
+
docstring_args = extract_args_from_docstring(method.__doc__, method_name)
|
|
129
|
+
if docstring_args:
|
|
130
|
+
for i, arg in enumerate(docstring_args):
|
|
131
|
+
args.insert(i, arg)
|
|
61
132
|
else:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
133
|
+
args.insert(0, {"name": "*args", "type": "*args"})
|
|
134
|
+
|
|
135
|
+
# Expand **kwargs based on function type
|
|
136
|
+
if has_var_keyword and expand_kwargs:
|
|
137
|
+
kwargs_mapping = get_kwargs_mapping()
|
|
138
|
+
if method_name in kwargs_mapping:
|
|
139
|
+
expanded_kwargs = kwargs_mapping[method_name]
|
|
140
|
+
existing_names = {p["name"] for p in args} | set(kwargs.keys())
|
|
141
|
+
for kwarg in expanded_kwargs:
|
|
142
|
+
if kwarg["name"] not in existing_names:
|
|
143
|
+
kwargs[kwarg["name"]] = {
|
|
144
|
+
"type": kwarg["type"],
|
|
145
|
+
"default": kwarg["default"],
|
|
146
|
+
}
|
|
147
|
+
else:
|
|
148
|
+
kwargs["**kwargs"] = {"type": "**kwargs"}
|
|
149
|
+
elif has_var_keyword:
|
|
150
|
+
kwargs["**kwargs"] = {"type": "**kwargs"}
|
|
67
151
|
|
|
68
152
|
result = {"args": args, "kwargs": kwargs}
|
|
69
|
-
_SIGNATURE_CACHE[
|
|
153
|
+
_SIGNATURE_CACHE[cache_key] = result
|
|
70
154
|
return result
|
|
71
155
|
|
|
72
156
|
|
|
73
|
-
def _get_type_str(annotation) -> Optional[str]:
|
|
74
|
-
"""Convert annotation to string."""
|
|
75
|
-
if annotation is inspect.Parameter.empty:
|
|
76
|
-
return None
|
|
77
|
-
if hasattr(annotation, "__name__"):
|
|
78
|
-
return annotation.__name__
|
|
79
|
-
return str(annotation)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def _serialize_default(default) -> Any:
|
|
83
|
-
"""Serialize default value."""
|
|
84
|
-
if default is inspect.Parameter.empty:
|
|
85
|
-
return None
|
|
86
|
-
if callable(default):
|
|
87
|
-
return f"<{type(default).__name__}>"
|
|
88
|
-
try:
|
|
89
|
-
import json
|
|
90
|
-
json.dumps(default)
|
|
91
|
-
return default
|
|
92
|
-
except (TypeError, ValueError):
|
|
93
|
-
return repr(default)
|
|
94
|
-
|
|
95
|
-
|
|
96
157
|
def get_defaults(method_name: str) -> Dict[str, Any]:
|
|
97
158
|
"""Get default values for a method's kwargs.
|
|
98
159
|
|
|
@@ -137,7 +198,6 @@ def validate_kwargs(
|
|
|
137
198
|
sig = get_signature(method_name)
|
|
138
199
|
known_kwargs = set(sig.get("kwargs", {}).keys()) - {"**kwargs"}
|
|
139
200
|
|
|
140
|
-
# If method accepts **kwargs, all are valid
|
|
141
201
|
if "**kwargs" in sig.get("kwargs", {}):
|
|
142
202
|
return {
|
|
143
203
|
"valid": list(kwargs.keys()),
|
|
@@ -157,30 +217,27 @@ def validate_kwargs(
|
|
|
157
217
|
return {
|
|
158
218
|
"valid": valid,
|
|
159
219
|
"unknown": unknown,
|
|
160
|
-
"missing": [],
|
|
220
|
+
"missing": [],
|
|
161
221
|
}
|
|
162
222
|
|
|
163
223
|
|
|
164
224
|
def list_plotting_methods() -> List[str]:
|
|
165
225
|
"""List all available plotting methods.
|
|
166
226
|
|
|
227
|
+
Uses _params.PLOTTING_METHODS as single source of truth,
|
|
228
|
+
filtered to methods that actually exist on the current matplotlib version.
|
|
229
|
+
|
|
167
230
|
Returns
|
|
168
231
|
-------
|
|
169
232
|
list
|
|
170
233
|
Names of plotting methods.
|
|
171
234
|
"""
|
|
235
|
+
from .._params import PLOTTING_METHODS
|
|
236
|
+
|
|
172
237
|
fig, ax = plt.subplots()
|
|
173
238
|
plt.close(fig)
|
|
174
239
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
"fill_between", "fill_betweenx", "stackplot", "stem",
|
|
180
|
-
"step", "imshow", "pcolor", "pcolormesh", "contour",
|
|
181
|
-
"contourf", "quiver", "barbs", "streamplot", "hexbin",
|
|
182
|
-
"eventplot", "stairs", "ecdf",
|
|
183
|
-
]
|
|
184
|
-
|
|
185
|
-
# Filter to only those that exist
|
|
186
|
-
return [m for m in methods if hasattr(ax, m)]
|
|
240
|
+
return sorted([m for m in PLOTTING_METHODS if hasattr(ax, m)])
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# EOF
|
|
@@ -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
|