|
23 | 23 | # ... print(*_iter_doc(uplt)) |
24 | 24 | import inspect |
25 | 25 | import re |
| 26 | +from typing import Any, Callable, TypeVar, cast, overload |
26 | 27 |
|
27 | 28 | from . import ic # noqa: F401 |
28 | 29 |
|
| 30 | +_F = TypeVar("_F", bound=Callable[..., Any]) |
| 31 | +_T = TypeVar("_T") |
29 | 32 |
|
30 | | -def _obfuscate_kwargs(func): |
| 33 | + |
| 34 | +def _obfuscate_kwargs(func: _F) -> _F: |
31 | 35 | """ |
32 | | - Obfuscate keyword args. |
| 36 | + Mark keyword arguments as compact in generated API documentation. |
33 | 37 | """ |
34 | 38 | return _obfuscate_signature(func, lambda **kwargs: None) |
35 | 39 |
|
36 | 40 |
|
37 | | -def _obfuscate_params(func): |
| 41 | +def _obfuscate_params(func: _F) -> _F: |
38 | 42 | """ |
39 | | - Obfuscate all parameters. |
| 43 | + Mark all parameters as compact in generated API documentation. |
40 | 44 | """ |
41 | 45 | return _obfuscate_signature(func, lambda *args, **kwargs: None) |
42 | 46 |
|
43 | 47 |
|
44 | | -def _obfuscate_signature(func, dummy): |
| 48 | +def _obfuscate_signature(func: _F, dummy: Callable[..., Any]) -> _F: |
45 | 49 | """ |
46 | | - Obfuscate a misleading or incomplete call signature. |
47 | | - Instead users should inspect the parameter table. |
| 50 | + Mark a misleading or incomplete signature as compact in generated docs. |
| 51 | +
|
| 52 | + The callable's actual signature remains available to Python and language |
| 53 | + servers; Sphinx reads the marker below when rendering API headings. |
48 | 54 | """ |
49 | | - # Obfuscate signature by converting to *args **kwargs. Note this does |
50 | | - # not change behavior of function! Copy parameters from a dummy function |
51 | | - # because I'm too lazy to figure out inspect.Parameters API |
52 | | - # See: https://stackoverflow.com/a/33112180/4970632 |
53 | | - sig = inspect.signature(func) |
54 | | - sig_repl = inspect.signature(dummy) |
55 | | - func.__signature__ = sig.replace(parameters=tuple(sig_repl.parameters.values())) |
| 55 | + # Keep the compact signature available to documentation tooling without |
| 56 | + # changing the callable's runtime signature. Sphinx uses this marker to |
| 57 | + # avoid filling API headings with inherited or dynamically routed options. |
| 58 | + setattr(func, "__ultraplot_doc_signature__", str(inspect.signature(dummy))) |
56 | 59 | return func |
57 | 60 |
|
58 | 61 |
|
59 | | -def _concatenate_inherited(func, prepend_summary=False): |
| 62 | +def _concatenate_inherited( |
| 63 | + func: _F, prepend_summary: bool = False |
| 64 | +) -> _F: |
60 | 65 | """ |
61 | 66 | Concatenate docstrings from a matplotlib axes method with a ultraplot |
62 | | - axes method and obfuscate the call signature. |
| 67 | + axes method and mark its generated-documentation signature as compact. |
63 | 68 | """ |
64 | 69 | import matplotlib.axes as maxes |
65 | 70 | import matplotlib.figure as mfigure |
@@ -102,7 +107,7 @@ def _concatenate_inherited(func, prepend_summary=False): |
102 | 107 | """ |
103 | 108 |
|
104 | 109 | # Return docstring |
105 | | - # NOTE: Also obfuscate parameters to avoid partial coverage of call signatures |
| 110 | + # Keep generated API headings compact to avoid showing partial call signatures. |
106 | 111 | func.__doc__ = inspect.cleandoc(doc) |
107 | 112 | func = _obfuscate_params(func) |
108 | 113 | return func |
@@ -143,17 +148,24 @@ def __missing__(self, key): |
143 | 148 | return dict.__getitem__(self, key) |
144 | 149 | raise KeyError(key) |
145 | 150 |
|
146 | | - def __call__(self, obj): |
| 151 | + @overload |
| 152 | + def __call__(self, obj: str) -> str: ... |
| 153 | + |
| 154 | + @overload |
| 155 | + def __call__(self, obj: _T) -> _T: ... |
| 156 | + |
| 157 | + def __call__(self, obj: _T | str) -> _T | str: |
147 | 158 | """ |
148 | 159 | Add snippets to the string or object using ``%(name)s`` substitution. Here |
149 | 160 | ``%(name)s`` is used rather than ``.format`` to support invalid identifiers. |
150 | 161 | """ |
151 | 162 | if isinstance(obj, str): |
152 | 163 | obj %= self # add snippets to a string |
153 | 164 | else: |
154 | | - obj.__doc__ = inspect.getdoc(obj) # also dedents the docstring |
155 | | - if obj.__doc__: |
156 | | - obj.__doc__ %= self # insert snippets after dedent |
| 165 | + documented = cast(Any, obj) |
| 166 | + documented.__doc__ = inspect.getdoc(documented) # also dedents the docstring |
| 167 | + if documented.__doc__: |
| 168 | + documented.__doc__ %= self # insert snippets after dedent |
157 | 169 | return obj |
158 | 170 |
|
159 | 171 | def __setitem__(self, key, value): |
|
0 commit comments