diff --git a/.github/actions/publish-package/action.yml b/.github/actions/publish-package/action.yml index 18bbe913..f6cfbc39 100644 --- a/.github/actions/publish-package/action.yml +++ b/.github/actions/publish-package/action.yml @@ -22,7 +22,7 @@ runs: shell: bash -l {0} run: | conda config --set always_yes yes --set changeps1 no - conda create -n build-env python=3.10 + conda create -n build-env conda activate build-env mamba install -c conda-forge mamba conda-build anaconda-client conda-verify boa conda config --add channels mantid/label/nightly diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 22da0e45..78018d94 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -25,7 +25,7 @@ jobs: - name: Install Mantid run: | - conda install -c mantid/label/nightly mantid mantidqt + mamba install -c mantid/label/nightly mantid mantidqt - name: Run Tests and Coverage run: | diff --git a/.github/workflows/unit_tests_nightly.yml b/.github/workflows/unit_tests_nightly.yml index b850514e..66749997 100644 --- a/.github/workflows/unit_tests_nightly.yml +++ b/.github/workflows/unit_tests_nightly.yml @@ -25,7 +25,7 @@ jobs: - name: Install Mantid run: | - conda install -c mantid/label/nightly mantid mantidqt + mamba install -c mantid/label/nightly mantid mantidqt - name: Run Tests and Coverage run: | diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml index 15919a78..44b5bc9d 100644 --- a/conda/conda_build_config.yaml +++ b/conda/conda_build_config.yaml @@ -1,2 +1,2 @@ python: - - 3.12 + - =3.12 diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 1ca5ade8..9a22a1db 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -15,8 +15,8 @@ You can then copy the ``mslice`` subfolder (the folder containing a file called ``scripts/ExternalInterfaces/`` folder of *Mantid*, which will make the new version accessible from *MantidWorkbench*. Alternatively, Mslice is available as a conda package via anaconda.org on the `mantid channel `_. -To install the released conda package do ``conda install -c mantid mslice``. -To install the latest nightly conda package do ``conda install -c mantid/label/nightly mslice``. The nightly conda package includes +To install the released conda package do ``mamba install -c mantid mslice``. +To install the latest nightly conda package do ``mamba install -c mantid/label/nightly mslice``. The nightly conda package includes all recent changes of the development version up to and including the day before and is only created when all tests have run successfully. In both cases MSlice can be started by typing ``mslice`` on the command line after activating your conda environment. diff --git a/environment.yml b/environment.yml index 4039100b..5a5c2b99 100644 --- a/environment.yml +++ b/environment.yml @@ -5,7 +5,7 @@ channels: - mantid/label/nightly dependencies: - coverage - - coveralls=3.3.1 # Pinned to a version that works with more recent versions of coverage + - coveralls - ipython - mock - pip diff --git a/pyproject.toml b/pyproject.toml index 7e6bb2bf..118adce8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ classifiers = [ dependencies = [ "ipython", "numpy", - "matplotlib>=1.5", + "matplotlib>=3.8.4", "six", "qtawesome", "pre-commit", @@ -60,7 +60,7 @@ where = ["src"] [tool.pytest.ini_options] pythonpath = ["src"] testpaths = ["tests"] -filterwarnings = ["error"] +filterwarnings = ["error", "ignore::DeprecationWarning"] [tool.coverage.report] include = [ diff --git a/src/mslice/plotting/pyplot.py b/src/mslice/plotting/pyplot.py index 8d049ac8..4133e96e 100644 --- a/src/mslice/plotting/pyplot.py +++ b/src/mslice/plotting/pyplot.py @@ -9,53 +9,102 @@ see globalfiguremanager.py. """ -from contextlib import ExitStack +# fmt: off + +from __future__ import annotations + +from contextlib import AbstractContextManager, ExitStack from enum import Enum import functools import importlib import inspect import logging -from numbers import Number import re import sys import threading import time +from typing import cast, overload from cycler import cycler import matplotlib import matplotlib.colorbar import matplotlib.image from matplotlib import _api -from matplotlib import rcsetup, style +from matplotlib import ( # Re-exported for typing. + cm as cm, get_backend as get_backend, rcParams as rcParams, style as style) from matplotlib import _pylab_helpers, interactive from matplotlib import cbook -try: - from matplotlib import _docstring as docstring -except ImportError: - from matplotlib import docstring -from matplotlib.backend_bases import FigureCanvasBase, MouseButton +from matplotlib import _docstring +from matplotlib.backend_bases import ( + FigureCanvasBase, FigureManagerBase, MouseButton) from matplotlib.figure import Figure, FigureBase, figaspect from matplotlib.gridspec import GridSpec, SubplotSpec -from matplotlib import rcParams, rcParamsDefault, get_backend, rcParamsOrig -from matplotlib.rcsetup import interactive_bk as _interactive_bk +from matplotlib import rcsetup, rcParamsDefault, rcParamsOrig from matplotlib.artist import Artist -from matplotlib.axes import Axes, Subplot -from matplotlib.projections import PolarAxes +from matplotlib.axes import Axes, Subplot # type: ignore +from matplotlib.projections import PolarAxes # type: ignore from matplotlib import mlab # for detrend_none, window_hanning from matplotlib.scale import get_scale_names -from matplotlib import cm -from matplotlib.cm import _colormaps as colormaps, register_cmap -try: - from matplotlib.cm import _get_cmap as get_cmap -except ImportError: - from matplotlib.cm import get_cmap +from matplotlib.cm import _colormaps +from matplotlib.cm import register_cmap # type: ignore +from matplotlib.colors import _color_sequences import numpy as np +from typing import TYPE_CHECKING, cast + +if TYPE_CHECKING: + from collections.abc import Callable, Hashable, Iterable, Sequence + import datetime + import pathlib + import os + from typing import Any, BinaryIO, Literal, TypeVar + from typing_extensions import ParamSpec + + import PIL.Image + from numpy.typing import ArrayLike + + from matplotlib.axis import Tick + from matplotlib.axes._base import _AxesBase + from matplotlib.backend_bases import RendererBase, Event + from matplotlib.cm import ScalarMappable + from matplotlib.contour import ContourSet, QuadContourSet + from matplotlib.collections import ( + Collection, + LineCollection, + BrokenBarHCollection, + PolyCollection, + PathCollection, + EventCollection, + QuadMesh, + ) + from matplotlib.colorbar import Colorbar + from matplotlib.colors import Colormap + from matplotlib.container import ( + BarContainer, + ErrorbarContainer, + StemContainer, + ) + from matplotlib.figure import SubFigure + from matplotlib.legend import Legend + from matplotlib.mlab import GaussianKDE + from matplotlib.image import AxesImage, FigureImage + from matplotlib.patches import FancyArrow, StepPatch, Wedge + from matplotlib.quiver import Barbs, Quiver, QuiverKey + from matplotlib.scale import ScaleBase + from matplotlib.transforms import Transform, Bbox + from matplotlib.typing import ColorType, LineStyleType, MarkerType, HashableList + from matplotlib.widgets import SubplotTool + + _P = ParamSpec('_P') + _R = TypeVar('_R') + _T = TypeVar('_T') + + # We may not need the following imports here: from matplotlib.colors import Normalize -from matplotlib.lines import Line2D +from matplotlib.lines import Line2D, AxLine from matplotlib.text import Text, Annotation from matplotlib.patches import Polygon, Rectangle, Circle, Arrow from matplotlib.widgets import Button, Slider, Widget @@ -67,24 +116,49 @@ LinearLocator, LogLocator, AutoLocator, MultipleLocator, MaxNLocator) from mslice.plotting.globalfiguremanager import set_category -from mslice.plotting.globalfiguremanager import (CATEGORY_CUT, - CATEGORY_SLICE, - GlobalFigureManager) +from mslice.plotting.globalfiguremanager import ( + CATEGORY_CUT, + CATEGORY_SLICE, + GlobalFigureManager, +) _log = logging.getLogger(__name__) -def _copy_docstring_and_deprecators(method, func=None): +# Explicit rename instead of import-as for typing's sake. +colormaps = _colormaps +color_sequences = _color_sequences + + +@overload +def _copy_docstring_and_deprecators( + method: Any, + func: Literal[None] = None +) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: ... + + +@overload +def _copy_docstring_and_deprecators( + method: Any, func: Callable[_P, _R]) -> Callable[_P, _R]: ... + + +def _copy_docstring_and_deprecators( + method: Any, + func: Callable[_P, _R] | None = None +) -> Callable[[Callable[_P, _R]], Callable[_P, _R]] | Callable[_P, _R]: if func is None: - return functools.partial(_copy_docstring_and_deprecators, method) - decorators = [docstring.copy(method)] + return cast('Callable[[Callable[_P, _R]], Callable[_P, _R]]', + functools.partial(_copy_docstring_and_deprecators, method)) + decorators: list[Callable[[Callable[_P, _R]], Callable[_P, _R]]] = [ + _docstring.copy(method) + ] # Check whether the definition of *method* includes @_api.rename_parameter # or @_api.make_keyword_only decorators; if so, propagate them to the # pyplot wrapper as well. - while getattr(method, "__wrapped__", None) is not None: - decorator = _api.deprecation.DECORATORS.get(method) - if decorator: - decorators.append(decorator) + while hasattr(method, "__wrapped__"): + potential_decorator = _api.deprecation.DECORATORS.get(method) + if potential_decorator: + decorators.append(potential_decorator) method = method.__wrapped__ for decorator in decorators[::-1]: func = decorator(func) @@ -94,14 +168,14 @@ def _copy_docstring_and_deprecators(method, func=None): ## Global ## -def _draw_all_if_interactive(): +def _draw_all_if_interactive() -> None: # We will always draw because mslice might be running without # matplotlib interactive for fig in GlobalFigureManager.all_figures(): fig.canvas.draw() -def draw_all(): +def draw_all() -> None: for fig in GlobalFigureManager.all_figures(): fig.canvas.draw_idle() @@ -111,7 +185,7 @@ def draw_all(): _REPL_DISPLAYHOOK = _ReplDisplayHook.NONE -def install_repl_displayhook(): +def install_repl_displayhook() -> None: """ Connect to the display hook of the current shell. @@ -141,79 +215,78 @@ def install_repl_displayhook(): ip.events.register("post_execute", _draw_all_if_interactive) _REPL_DISPLAYHOOK = _ReplDisplayHook.IPYTHON - from IPython.core.pylabtools import backend2gui + from IPython.core.pylabtools import backend2gui # type: ignore + # trigger IPython's eventloop integration, if available ipython_gui_name = backend2gui.get(get_backend()) if ipython_gui_name: ip.enable_gui(ipython_gui_name) -def uninstall_repl_displayhook(): +def uninstall_repl_displayhook() -> None: """Disconnect from the display hook of the current shell.""" global _REPL_DISPLAYHOOK if _REPL_DISPLAYHOOK is _ReplDisplayHook.IPYTHON: - from IPython import get_ipython + from IPython import get_ipython # type: ignore + ip = get_ipython() ip.events.unregister("post_execute", _draw_all_if_interactive) _REPL_DISPLAYHOOK = _ReplDisplayHook.NONE - +# Ensure this appears in the pyplot docs. @_copy_docstring_and_deprecators(matplotlib.set_loglevel) -def set_loglevel(*args, **kwargs): # Ensure this appears in the pyplot docs. +def set_loglevel(*args, **kwargs) -> None: return matplotlib.set_loglevel(*args, **kwargs) @_copy_docstring_and_deprecators(Artist.findobj) -def findobj(o=None, match=None, include_self=True): +def findobj( + o: Artist | None = None, + match: Callable[[Artist], bool] | type[Artist] | None = None, + include_self: bool = True +) -> list[Artist]: if o is None: o = gcf() return o.findobj(match, include_self=include_self) -def _get_required_interactive_framework(backend_mod): - if not hasattr(getattr(backend_mod, "FigureCanvas", None), - "required_interactive_framework"): - _api.warn_deprecated( - "3.6", name="Support for FigureCanvases without a " - "required_interactive_framework attribute") - return None - # Inline this once the deprecation elapses. - return backend_mod.FigureCanvas.required_interactive_framework +_backend_mod: type[matplotlib.backend_bases._Backend] | None = None -_backend_mod = None - -def _get_backend_mod(): +def _get_backend_mod() -> type[matplotlib.backend_bases._Backend]: """ Ensure that a backend is selected and return it. This is currently private, but may be made public in the future. """ if _backend_mod is None: - # Use __getitem__ here to avoid going through the fallback logic (which - # will (re)import pyplot and then call switch_backend if we need to - # resolve the auto sentinel) - switch_backend(dict.__getitem__(rcParams, "backend")) - return _backend_mod + # Use rcParams._get("backend") to avoid going through the fallback + # logic (which will (re)import pyplot and then call switch_backend if + # we need to resolve the auto sentinel) + switch_backend(rcParams._get("backend")) # type: ignore[attr-defined] + return cast(type[matplotlib.backend_bases._Backend], _backend_mod) -def switch_backend(newbackend): +def switch_backend(newbackend: str) -> None: """ - Close all open figures and set the Matplotlib backend. + Set the pyplot backend. + + Switching to an interactive backend is possible only if no event loop for + another interactive backend has started. Switching to and from + non-interactive backends is always possible. - The argument is case-insensitive. Switching to an interactive backend is - possible only if no event loop for another interactive backend has started. - Switching to and from non-interactive backends is always possible. + If the new backend is different than the current backend then all open + Figures will be closed via ``plt.close('all')``. Parameters ---------- newbackend : str - The name of the backend to use. + The case-insensitive name of the backend to use. + """ global _backend_mod # make sure the init is pulled up so we can assign to it later import matplotlib.backends - close("all") if newbackend is rcsetup._auto_backend_sentinel: current_framework = cbook._get_running_interactive_framework() @@ -225,9 +298,8 @@ def switch_backend(newbackend): 'macosx': 'macosx', 'headless': 'agg'} - best_guess = mapping.get(current_framework, None) - if best_guess is not None: - candidates = [best_guess] + if current_framework in mapping: + candidates = [mapping[current_framework]] else: candidates = [] candidates += [ @@ -250,11 +322,13 @@ def switch_backend(newbackend): switch_backend("agg") rcParamsOrig["backend"] = "agg" return + # have to escape the switch on access logic + old_backend = dict.__getitem__(rcParams, 'backend') - backend_mod = importlib.import_module( - cbook._backend_module_name(newbackend)) + module = importlib.import_module(cbook._backend_module_name(newbackend)) + canvas_class = module.FigureCanvas - required_framework = _get_required_interactive_framework(backend_mod) + required_framework = canvas_class.required_interactive_framework if required_framework is not None: current_framework = cbook._get_running_interactive_framework() if (current_framework and required_framework @@ -268,22 +342,22 @@ def switch_backend(newbackend): # Classically, backends can directly export these functions. This should # keep working for backcompat. - new_figure_manager = getattr(backend_mod, "new_figure_manager", None) - # show = getattr(backend_mod, "show", None) + new_figure_manager = getattr(module, "new_figure_manager", None) + show = getattr(module, "show", None) + # In that classical approach, backends are implemented as modules, but # "inherit" default method implementations from backend_bases._Backend. # This is achieved by creating a "class" that inherits from # backend_bases._Backend and whose body is filled with the module globals. class backend_mod(matplotlib.backend_bases._Backend): - locals().update(vars(backend_mod)) + locals().update(vars(module)) - # However, the newer approach for defining new_figure_manager (and, in - # the future, show) is to derive them from canvas methods. In that case, - # also update backend_mod accordingly; also, per-backend customization of + # However, the newer approach for defining new_figure_manager and + # show is to derive them from canvas methods. In that case, also + # update backend_mod accordingly; also, per-backend customization of # draw_if_interactive is disabled. if new_figure_manager is None: - # only try to get the canvas class if have opted into the new scheme - canvas_class = backend_mod.FigureCanvas + def new_figure_manager_given_figure(num, figure): return canvas_class.new_manager(figure, num) @@ -291,16 +365,37 @@ def new_figure_manager(num, *args, FigureClass=Figure, **kwargs): fig = FigureClass(*args, **kwargs) return new_figure_manager_given_figure(num, fig) - def draw_if_interactive(): + def draw_if_interactive() -> None: if matplotlib.is_interactive(): manager = _pylab_helpers.Gcf.get_active() if manager: manager.canvas.draw_idle() - backend_mod.new_figure_manager_given_figure = \ - new_figure_manager_given_figure - backend_mod.new_figure_manager = new_figure_manager - backend_mod.draw_if_interactive = draw_if_interactive + backend_mod.new_figure_manager_given_figure = ( # type: ignore[method-assign] + new_figure_manager_given_figure) + backend_mod.new_figure_manager = ( # type: ignore[method-assign] + new_figure_manager) + backend_mod.draw_if_interactive = ( # type: ignore[method-assign] + draw_if_interactive) + + # If the manager explicitly overrides pyplot_show, use it even if a global + # show is already present, as the latter may be here for backcompat. + manager_class = getattr(canvas_class, "manager_class", None) + # We can't compare directly manager_class.pyplot_show and FMB.pyplot_show because + # pyplot_show is a classmethod so the above constructs are bound classmethods, and + # thus always different (being bound to different classes). We also have to use + # getattr_static instead of vars as manager_class could have no __dict__. + manager_pyplot_show = inspect.getattr_static(manager_class, "pyplot_show", None) + base_pyplot_show = inspect.getattr_static(FigureManagerBase, "pyplot_show", None) + if (show is None + or (manager_pyplot_show is not None + and manager_pyplot_show != base_pyplot_show)): + if not manager_pyplot_show: + raise ValueError( + f"Backend {newbackend} defines neither FigureCanvas.manager_class nor " + f"a toplevel show function") + _pyplot_show = cast('Any', manager_class).pyplot_show + backend_mod.show = _pyplot_show # type: ignore[method-assign] _log.debug("Loaded backend %s version %s.", newbackend, backend_mod.backend_version) @@ -313,16 +408,24 @@ def draw_if_interactive(): # Need to keep a global reference to the backend for compatibility reasons. # See https://github.com/matplotlib/matplotlib/issues/6092 - matplotlib.backends.backend = newbackend + matplotlib.backends.backend = newbackend # type: ignore[attr-defined] + + if not cbook._str_equal(old_backend, newbackend): + if get_fignums(): + _api.warn_deprecated("3.8", message=( + "Auto-close()ing of figures upon backend switching is deprecated since " + "%(since)s and will be removed %(removal)s. To suppress this warning, " + "explicitly call plt.close('all') first.")) + close("all") - # make sure the repl display hook is installed in case we become - # interactive + # Make sure the repl display hook is installed in case we become interactive. install_repl_displayhook() -def _warn_if_gui_out_of_main_thread(): +def _warn_if_gui_out_of_main_thread() -> None: warn = False - if _get_required_interactive_framework(_get_backend_mod()): + canvas_class = cast(type[FigureCanvasBase], _get_backend_mod().FigureCanvas) + if canvas_class.required_interactive_framework: if hasattr(threading, 'get_native_id'): # This compares native thread ids because even if Python-level # Thread objects match, the underlying OS thread (which is what @@ -365,7 +468,7 @@ def draw_if_interactive(*args, **kwargs): # This function's signature is rewritten upon backend-load by switch_backend. -def show(*args, **kwargs): +def show(*args, **kwargs) -> None: """ Display all open figures. @@ -409,10 +512,12 @@ def show(*args, **kwargs): the end of every cell by default. Thus, you usually don't have to call it explicitly there. """ - return gcf().show(*args, **kwargs) + #return gcf().show(*args, **kwargs) + _warn_if_gui_out_of_main_thread() + return _get_backend_mod().show(*args, **kwargs) -def isinteractive(): +def isinteractive() -> bool: """ Return whether plots are updated after every plotting command. @@ -442,7 +547,7 @@ def isinteractive(): return matplotlib.is_interactive() -def ioff(): +def ioff() -> ExitStack: """ Disable interactive mode. @@ -482,7 +587,7 @@ def ioff(): return stack -def ion(): +def ion() -> ExitStack: """ Enable interactive mode. @@ -522,7 +627,7 @@ def ion(): return stack -def pause(interval): +def pause(interval: float) -> None: """ Run the GUI event loop for *interval* seconds. @@ -551,17 +656,20 @@ def pause(interval): @_copy_docstring_and_deprecators(matplotlib.rc) -def rc(group, **kwargs): +def rc(group: str, **kwargs) -> None: matplotlib.rc(group, **kwargs) @_copy_docstring_and_deprecators(matplotlib.rc_context) -def rc_context(rc=None, fname=None): +def rc_context( + rc: dict[str, Any] | None = None, + fname: str | pathlib.Path | os.PathLike | None = None, +) -> AbstractContextManager[None]: return matplotlib.rc_context(rc, fname) @_copy_docstring_and_deprecators(matplotlib.rcdefaults) -def rcdefaults(): +def rcdefaults() -> None: matplotlib.rcdefaults() if matplotlib.is_interactive(): draw_all() @@ -585,13 +693,16 @@ def setp(obj, *args, **kwargs): return matplotlib.artist.setp(obj, *args, **kwargs) -def xkcd(scale=1, length=100, randomness=2): +def xkcd( + scale: float = 1, length: float = 100, randomness: float = 2 +) -> ExitStack: """ - Turn on `xkcd `_ sketch-style drawing mode. This will - only have effect on things drawn after this function is called. + Turn on `xkcd `_ sketch-style drawing mode. + + This will only have an effect on things drawn after this function is called. - For best results, the "Humor Sans" font should be installed: it is - not included with Matplotlib. + For best results, install the `xkcd script `_ + font; xkcd fonts are not packaged with Matplotlib. Parameters ---------- @@ -626,12 +737,11 @@ def xkcd(scale=1, length=100, randomness=2): "xkcd mode is not compatible with text.usetex = True") stack = ExitStack() - stack.callback(dict.update, rcParams, rcParams.copy()) + stack.callback(dict.update, rcParams, rcParams.copy()) # type: ignore from matplotlib import patheffects rcParams.update({ - 'font.family': ['xkcd', 'xkcd Script', 'Humor Sans', 'Comic Neue', - 'Comic Sans MS'], + 'font.family': ['xkcd', 'xkcd Script', 'Comic Neue', 'Comic Sans MS'], 'font.size': 14.0, 'path.sketch': (scale, length, randomness), 'path.effects': [ @@ -654,17 +764,125 @@ def xkcd(scale=1, length=100, randomness=2): ## Figures ## -@_api.make_keyword_only("3.6", "facecolor") -def figure(num=None, # autoincrement if None, else integer from 1-N - figsize=None, # defaults to rc figure.figsize - dpi=None, # defaults to rc figure.dpi - facecolor=None, # defaults to rc figure.facecolor - edgecolor=None, # defaults to rc figure.edgecolor - frameon=True, - FigureClass=Figure, - clear=False, - **kwargs - ): + +def figure( + # autoincrement if None, else integer from 1-N + num: int | str | Figure | SubFigure | None = None, + # defaults to rc figure.figsize + figsize: tuple[float, float] | None = None, + # defaults to rc figure.dpi + dpi: float | None = None, + *, + # defaults to rc figure.facecolor + facecolor: ColorType | None = None, + # defaults to rc figure.edgecolor + edgecolor: ColorType | None = None, + frameon: bool = True, + FigureClass: type[Figure] = Figure, + clear: bool = False, + **kwargs +) -> Figure: + """ + Create a new figure, or activate an existing figure. + + Parameters + ---------- + num : int or str or `.Figure` or `.SubFigure`, optional + A unique identifier for the figure. + + If a figure with that identifier already exists, this figure is made + active and returned. An integer refers to the ``Figure.number`` + attribute, a string refers to the figure label. + + If there is no figure with the identifier or *num* is not given, a new + figure is created, made active and returned. If *num* is an int, it + will be used for the ``Figure.number`` attribute, otherwise, an + auto-generated integer value is used (starting at 1 and incremented + for each new figure). If *num* is a string, the figure label and the + window title is set to this value. If num is a ``SubFigure``, its + parent ``Figure`` is activated. + + figsize : (float, float), default: :rc:`figure.figsize` + Width, height in inches. + + dpi : float, default: :rc:`figure.dpi` + The resolution of the figure in dots-per-inch. + + facecolor : color, default: :rc:`figure.facecolor` + The background color. + + edgecolor : color, default: :rc:`figure.edgecolor` + The border color. + + frameon : bool, default: True + If False, suppress drawing the figure frame. + + FigureClass : subclass of `~matplotlib.figure.Figure` + If set, an instance of this subclass will be created, rather than a + plain `.Figure`. + + clear : bool, default: False + If True and the figure already exists, then it is cleared. + + layout : {'constrained', 'compressed', 'tight', 'none', `.LayoutEngine`, None}, \ +default: None + The layout mechanism for positioning of plot elements to avoid + overlapping Axes decorations (labels, ticks, etc). Note that layout + managers can measurably slow down figure display. + + - 'constrained': The constrained layout solver adjusts axes sizes + to avoid overlapping axes decorations. Can handle complex plot + layouts and colorbars, and is thus recommended. + + See :ref:`constrainedlayout_guide` + for examples. + + - 'compressed': uses the same algorithm as 'constrained', but + removes extra space between fixed-aspect-ratio Axes. Best for + simple grids of axes. + + - 'tight': Use the tight layout mechanism. This is a relatively + simple algorithm that adjusts the subplot parameters so that + decorations do not overlap. See `.Figure.set_tight_layout` for + further details. + + - 'none': Do not use a layout engine. + + - A `.LayoutEngine` instance. Builtin layout classes are + `.ConstrainedLayoutEngine` and `.TightLayoutEngine`, more easily + accessible by 'constrained' and 'tight'. Passing an instance + allows third parties to provide their own layout engine. + + If not given, fall back to using the parameters *tight_layout* and + *constrained_layout*, including their config defaults + :rc:`figure.autolayout` and :rc:`figure.constrained_layout.use`. + + **kwargs + Additional keyword arguments are passed to the `.Figure` constructor. + + Returns + ------- + `~matplotlib.figure.Figure` + + Notes + ----- + A newly created figure is passed to the `~.FigureCanvasBase.new_manager` + method or the `new_figure_manager` function provided by the current + backend, which install a canvas and a manager on the figure. + + Once this is done, :rc:`figure.hooks` are called, one at a time, on the + figure; these hooks allow arbitrary customization of the figure (e.g., + attaching callbacks) or of associated elements (e.g., modifying the + toolbar). See :doc:`/gallery/user_interfaces/mplcvd` for an example of + toolbar customization. + + If you are creating many figures, make sure you explicitly call + `.pyplot.close` on the figures you are not using, because this will + enable pyplot to properly clean up the memory. + + `~matplotlib.rcParams` defines the default values, which can be modified + in the matplotlibrc file. + """ return GlobalFigureManager.get_figure_number(num).figure @@ -690,7 +908,7 @@ def _auto_draw_if_interactive(fig, val): fig.canvas.draw_idle() -def gcf(): +def gcf() -> Figure: """ Get the current figure. @@ -698,24 +916,24 @@ def gcf(): return GlobalFigureManager.get_active_figure().figure -def fignum_exists(num): +def fignum_exists(num: int | str) -> bool: """Return whether the figure with the given id exists.""" return GlobalFigureManager.has_fignum(num) or num in get_figlabels() -def get_fignums(): +def get_fignums() -> list[int]: """Return a list of existing figure numbers.""" return sorted(GlobalFigureManager.figs) -def get_figlabels(): +def get_figlabels() -> list[Any]: """Return a list of existing figure labels.""" figManagers = GlobalFigureManager.get_all_fig_managers() figManagers.sort(key=lambda m: m.num) return [m.canvas.figure.get_label() for m in figManagers] -def get_current_fig_manager(): +def get_current_fig_manager() -> FigureManagerBase | None: figManager = GlobalFigureManager.get_active() if figManager is None: gcf() # creates an active figure as a side effect @@ -724,16 +942,16 @@ def get_current_fig_manager(): @_copy_docstring_and_deprecators(FigureCanvasBase.mpl_connect) -def connect(s, func): +def connect(s: str, func: Callable[[Event], Any]) -> int: return get_current_fig_manager().canvas.mpl_connect(s, func) @_copy_docstring_and_deprecators(FigureCanvasBase.mpl_disconnect) -def disconnect(cid): +def disconnect(cid: int) -> None: return get_current_fig_manager().canvas.mpl_disconnect(cid) -def close(fig=None): +def close(fig: None | int | str | Figure | Literal["all"] = None) -> None: """ Close a figure window. @@ -755,11 +973,11 @@ def close(fig=None): return else: GlobalFigureManager.destroy(figManager.num) - elif fig == 'all': + elif fig == "all": GlobalFigureManager.destroy_all() elif isinstance(fig, int): GlobalFigureManager.destroy(fig) - elif hasattr(fig, 'int'): + elif hasattr(fig, "int"): # if we are dealing with a type UUID, we # can use its integer representation GlobalFigureManager.destroy(fig.int) @@ -771,16 +989,18 @@ def close(fig=None): elif isinstance(fig, Figure): GlobalFigureManager.destroy_fig(fig) else: - raise TypeError("close() argument must be a Figure, an int, a string, " - "or None, not %s" % type(fig)) + raise TypeError( + "close() argument must be a Figure, an int, a string, " + "or None, not %s" % type(fig) + ) -def clf(): +def clf() -> None: """Clear the current figure.""" gcf().clear() -def draw(): +def draw() -> None: """ Redraw the current figure. @@ -796,9 +1016,11 @@ def draw(): @_copy_docstring_and_deprecators(Figure.savefig) -def savefig(*args, **kwargs): +def savefig(*args, **kwargs) -> None: fig = gcf() - res = fig.savefig(*args, **kwargs) + # savefig default implementation has no return, so mypy is unhappy + # presumably this is here because subclasses can return? + res = fig.savefig(*args, **kwargs) # type: ignore[func-returns-value] fig.canvas.draw_idle() # Need this if 'transparent=True', to reset colors. return res @@ -806,16 +1028,23 @@ def savefig(*args, **kwargs): ## Putting things in figures ## -def figlegend(*args, **kwargs): +def figlegend(*args, **kwargs) -> Legend: return gcf().legend(*args, **kwargs) if Figure.legend.__doc__: - figlegend.__doc__ = Figure.legend.__doc__.replace("legend(", "figlegend(") + figlegend.__doc__ = Figure.legend.__doc__ \ + .replace(" legend(", " figlegend(") \ + .replace("fig.legend(", "plt.figlegend(") \ + .replace("ax.plot(", "plt.plot(") ## Axes ## -@docstring.dedent_interpd -def axes(arg=None, **kwargs): + +@_docstring.dedent_interpd +def axes( + arg: None | tuple[float, float, float, float] = None, + **kwargs +) -> matplotlib.axes.Axes: """ Add an Axes to the current figure and make it the current Axes. @@ -832,7 +1061,7 @@ def axes(arg=None, **kwargs): - *None*: A new full window Axes is added using ``subplot(**kwargs)``. - - 4-tuple of floats *rect* = ``[left, bottom, width, height]``. + - 4-tuple of floats *rect* = ``(left, bottom, width, height)``. A new Axes is added with dimensions *rect* in normalized (0, 1) units using `~.Figure.add_axes` on the current figure. @@ -845,7 +1074,7 @@ def axes(arg=None, **kwargs): polar : bool, default: False If True, equivalent to projection='polar'. - sharex, sharey : `~.axes.Axes`, optional + sharex, sharey : `~matplotlib.axes.Axes`, optional Share the x or y `~matplotlib.axis` with sharex and/or sharey. The axis will have the same limits, ticks, and scale as the axis of the shared Axes. @@ -872,17 +1101,6 @@ def axes(arg=None, **kwargs): %(Axes:kwdoc)s - Notes - ----- - If the figure already has an Axes with key (*args*, - *kwargs*) then it will simply make that axes current and - return it. This behavior is deprecated. Meanwhile, if you do - not want this behavior (i.e., you want to force the creation of a - new axes), you must use a unique set of args and kwargs. The Axes - *label* attribute has been exposed for this purpose: if you want - two Axes that are otherwise identical to be added to the figure, - make sure you give them unique labels. - See Also -------- .Figure.add_axes @@ -912,7 +1130,7 @@ def axes(arg=None, **kwargs): return fig.add_axes(arg, **kwargs) -def delaxes(ax=None): +def delaxes(ax: matplotlib.axes.Axes | None = None) -> None: """ Remove an `~.axes.Axes` (defaulting to the current axes) from its figure. """ @@ -921,15 +1139,18 @@ def delaxes(ax=None): ax.remove() -def sca(ax): +def sca(ax: Axes) -> None: """ Set the current Axes to *ax* and the current Figure to the parent of *ax*. """ - figure(ax.figure) - ax.figure.sca(ax) + # Mypy sees ax.figure as potentially None, + # but if you are calling this, it won't be None + # Additionally the slight difference between `Figure` and `FigureBase` mypy catches + figure(ax.figure) # type: ignore[arg-type] + ax.figure.sca(ax) # type: ignore[union-attr] -def cla(): +def cla() -> None: """Clear the current axes.""" # Not generated via boilerplate.py to allow a different docstring. return gca().cla() @@ -937,8 +1158,8 @@ def cla(): ## More ways of creating axes ## -@docstring.dedent_interpd -def subplot(*args, **kwargs): +@_docstring.dedent_interpd +def subplot(*args, **kwargs) -> Axes: """ Add an Axes to the current figure or retrieve an existing Axes. @@ -979,7 +1200,7 @@ def subplot(*args, **kwargs): polar : bool, default: False If True, equivalent to projection='polar'. - sharex, sharey : `~.axes.Axes`, optional + sharex, sharey : `~matplotlib.axes.Axes`, optional Share the x or y `~matplotlib.axis` with sharex and/or sharey. The axis will have the same limits, ticks, and scale as the axis of the shared axes. @@ -989,13 +1210,11 @@ def subplot(*args, **kwargs): Returns ------- - `.axes.SubplotBase`, or another subclass of `~.axes.Axes` + `~.axes.Axes` - The axes of the subplot. The returned axes base class depends on - the projection used. It is `~.axes.Axes` if rectilinear projection - is used and `.projections.polar.PolarAxes` if polar projection - is used. The returned axes is then a subplot subclass of the - base class. + The Axes of the subplot. The returned Axes can actually be an instance + of a subclass, such as `.projections.polar.PolarAxes` for polar + projections. Other Parameters ---------------- @@ -1111,38 +1330,33 @@ def subplot(*args, **kwargs): key = SubplotSpec._from_subplot_args(fig, args) for ax in fig.axes: - # if we found an Axes at the position sort out if we can re-use it - if hasattr(ax, 'get_subplotspec') and ax.get_subplotspec() == key: - # if the user passed no kwargs, re-use - if kwargs == {}: - break - # if the axes class and kwargs are identical, reuse - elif ax._projection_init == fig._process_projection_requirements( - *args, **kwargs - ): - break + # If we found an Axes at the position, we can re-use it if the user passed no + # kwargs or if the axes class and kwargs are identical. + if (ax.get_subplotspec() == key + and (kwargs == {} + or (ax._projection_init + == fig._process_projection_requirements(**kwargs)))): + break else: # we have exhausted the known Axes and none match, make a new one! ax = fig.add_subplot(*args, **kwargs) fig.sca(ax) - axes_to_delete = [other for other in fig.axes - if other != ax and ax.bbox.fully_overlaps(other.bbox)] - if axes_to_delete: - _api.warn_deprecated( - "3.6", message="Auto-removal of overlapping axes is deprecated " - "since %(since)s and will be removed %(removal)s; explicitly call " - "ax.remove() as needed.") - for ax_to_del in axes_to_delete: - delaxes(ax_to_del) - return ax -def subplots(nrows=1, ncols=1, *, sharex=False, sharey=False, squeeze=True, - width_ratios=None, height_ratios=None, - subplot_kw=None, gridspec_kw=None, **fig_kw): +def subplots( + nrows: int = 1, ncols: int = 1, *, + sharex: bool | Literal["none", "all", "row", "col"] = False, + sharey: bool | Literal["none", "all", "row", "col"] = False, + squeeze: bool = True, + width_ratios: Sequence[float] | None = None, + height_ratios: Sequence[float] | None = None, + subplot_kw: dict[str, Any] | None = None, + gridspec_kw: dict[str, Any] | None = None, + **fig_kw +) -> tuple[Figure, Any]: """ Create a figure and a set of subplots. @@ -1216,7 +1430,7 @@ def subplots(nrows=1, ncols=1, *, sharex=False, sharey=False, squeeze=True, ------- fig : `.Figure` - ax : `~.axes.Axes` or array of Axes + ax : `~matplotlib.axes.Axes` or array of Axes *ax* can be either a single `~.axes.Axes` object, or an array of Axes objects if more than one subplot was created. The dimensions of the resulting array can be controlled with the squeeze keyword, see above. @@ -1294,20 +1508,77 @@ def subplots(nrows=1, ncols=1, *, sharex=False, sharey=False, squeeze=True, return fig, axs -def subplot_mosaic(mosaic, *, sharex=False, sharey=False, - width_ratios=None, height_ratios=None, empty_sentinel='.', - subplot_kw=None, gridspec_kw=None, **fig_kw): +@overload +def subplot_mosaic( + mosaic: str, + *, + sharex: bool = ..., + sharey: bool = ..., + width_ratios: ArrayLike | None = ..., + height_ratios: ArrayLike | None = ..., + empty_sentinel: str = ..., + subplot_kw: dict[str, Any] | None = ..., + gridspec_kw: dict[str, Any] | None = ..., + per_subplot_kw: dict[str | tuple[str, ...], dict[str, Any]] | None = ..., + **fig_kw: Any +) -> tuple[Figure, dict[str, matplotlib.axes.Axes]]: ... + + +@overload +def subplot_mosaic( + mosaic: list[HashableList[_T]], + *, + sharex: bool = ..., + sharey: bool = ..., + width_ratios: ArrayLike | None = ..., + height_ratios: ArrayLike | None = ..., + empty_sentinel: _T = ..., + subplot_kw: dict[str, Any] | None = ..., + gridspec_kw: dict[str, Any] | None = ..., + per_subplot_kw: dict[_T | tuple[_T, ...], dict[str, Any]] | None = ..., + **fig_kw: Any +) -> tuple[Figure, dict[_T, matplotlib.axes.Axes]]: ... + + +@overload +def subplot_mosaic( + mosaic: list[HashableList[Hashable]], + *, + sharex: bool = ..., + sharey: bool = ..., + width_ratios: ArrayLike | None = ..., + height_ratios: ArrayLike | None = ..., + empty_sentinel: Any = ..., + subplot_kw: dict[str, Any] | None = ..., + gridspec_kw: dict[str, Any] | None = ..., + per_subplot_kw: dict[Hashable | tuple[Hashable, ...], dict[str, Any]] | None = ..., + **fig_kw: Any +) -> tuple[Figure, dict[Hashable, matplotlib.axes.Axes]]: ... + + +def subplot_mosaic( + mosaic: str | list[HashableList[_T]] | list[HashableList[Hashable]], + *, + sharex: bool = False, + sharey: bool = False, + width_ratios: ArrayLike | None = None, + height_ratios: ArrayLike | None = None, + empty_sentinel: Any = '.', + subplot_kw: dict[str, Any] | None = None, + gridspec_kw: dict[str, Any] | None = None, + per_subplot_kw: dict[str | tuple[str, ...], dict[str, Any]] | + dict[_T | tuple[_T, ...], dict[str, Any]] | + dict[Hashable | tuple[Hashable, ...], dict[str, Any]] | None = None, + **fig_kw: Any +) -> tuple[Figure, dict[str, matplotlib.axes.Axes]] | \ + tuple[Figure, dict[_T, matplotlib.axes.Axes]] | \ + tuple[Figure, dict[Hashable, matplotlib.axes.Axes]]: """ Build a layout of Axes based on ASCII art or nested lists. This is a helper function to build complex GridSpec layouts visually. - .. note:: - - This API is provisional and may be revised in the future based on - early user feedback. - - See :doc:`/tutorials/provisional/mosaic` + See :ref:`mosaic` for an example and full API documentation Parameters @@ -1367,7 +1638,21 @@ def subplot_mosaic(mosaic, *, sharex=False, sharey=False, subplot_kw : dict, optional Dictionary with keywords passed to the `.Figure.add_subplot` call - used to create each subplot. + used to create each subplot. These values may be overridden by + values in *per_subplot_kw*. + + per_subplot_kw : dict, optional + A dictionary mapping the Axes identifiers or tuples of identifiers + to a dictionary of keyword arguments to be passed to the + `.Figure.add_subplot` call used to create each subplot. The values + in these dictionaries have precedence over the values in + *subplot_kw*. + + If *mosaic* is a string, and thus all keys are single characters, + it is possible to use a single string instead of a tuple as keys; + i.e. ``"AB"`` is equivalent to ``("A", "B")``. + + .. versionadded:: 3.7 gridspec_kw : dict, optional Dictionary with keywords passed to the `.GridSpec` constructor used @@ -1389,16 +1674,23 @@ def subplot_mosaic(mosaic, *, sharex=False, sharey=False, """ fig = figure(**fig_kw) - ax_dict = fig.subplot_mosaic( - mosaic, sharex=sharex, sharey=sharey, + ax_dict = fig.subplot_mosaic( # type: ignore[misc] + mosaic, # type: ignore[arg-type] + sharex=sharex, sharey=sharey, height_ratios=height_ratios, width_ratios=width_ratios, subplot_kw=subplot_kw, gridspec_kw=gridspec_kw, - empty_sentinel=empty_sentinel + empty_sentinel=empty_sentinel, + per_subplot_kw=per_subplot_kw, # type: ignore[arg-type] ) return fig, ax_dict -def subplot2grid(shape, loc, rowspan=1, colspan=1, fig=None, **kwargs): +def subplot2grid( + shape: tuple[int, int], loc: tuple[int, int], + rowspan: int = 1, colspan: int = 1, + fig: Figure | None = None, + **kwargs +) -> matplotlib.axes.Axes: """ Create a subplot at a specific location inside a regular grid. @@ -1419,12 +1711,11 @@ def subplot2grid(shape, loc, rowspan=1, colspan=1, fig=None, **kwargs): Returns ------- - `.axes.SubplotBase`, or another subclass of `~.axes.Axes` + `~.axes.Axes` - The axes of the subplot. The returned axes base class depends on the - projection used. It is `~.axes.Axes` if rectilinear projection is used - and `.projections.polar.PolarAxes` if polar projection is used. The - returned axes is then a subplot subclass of the base class. + The Axes of the subplot. The returned Axes can actually be an instance + of a subclass, such as `.projections.polar.PolarAxes` for polar + projections. Notes ----- @@ -1438,30 +1729,15 @@ def subplot2grid(shape, loc, rowspan=1, colspan=1, fig=None, **kwargs): gs = fig.add_gridspec(nrows, ncols) ax = fig.add_subplot(gs[row:row+rowspan, col:col+colspan]) """ - if fig is None: fig = gcf() - rows, cols = shape gs = GridSpec._check_gridspec_exists(fig, rows, cols) - subplotspec = gs.new_subplotspec(loc, rowspan=rowspan, colspan=colspan) - ax = fig.add_subplot(subplotspec, **kwargs) - - axes_to_delete = [other for other in fig.axes - if other != ax and ax.bbox.fully_overlaps(other.bbox)] - if axes_to_delete: - _api.warn_deprecated( - "3.6", message="Auto-removal of overlapping axes is deprecated " - "since %(since)s and will be removed %(removal)s; explicitly call " - "ax.remove() as needed.") - for ax_to_del in axes_to_delete: - delaxes(ax_to_del) + return fig.add_subplot(subplotspec, **kwargs) - return ax - -def twinx(ax=None): +def twinx(ax: matplotlib.axes.Axes | None = None) -> _AxesBase: """ Make and return a second axes that shares the *x*-axis. The new axes will overlay *ax* (or the current axes if *ax* is *None*), and its ticks will be @@ -1477,7 +1753,7 @@ def twinx(ax=None): return ax1 -def twiny(ax=None): +def twiny(ax: matplotlib.axes.Axes | None = None) -> _AxesBase: """ Make and return a second axes that shares the *y*-axis. The new axes will overlay *ax* (or the current axes if *ax* is *None*), and its ticks will be @@ -1493,7 +1769,7 @@ def twiny(ax=None): return ax1 -def subplot_tool(targetfig=None): +def subplot_tool(targetfig: Figure | None = None) -> SubplotTool | None: """ Launch a subplot tool window for a figure. @@ -1501,8 +1777,8 @@ def subplot_tool(targetfig=None): ------- `matplotlib.widgets.SubplotTool` """ - tbar = rcParams['toolbar'] # turn off the navigation toolbar for toolfig - rcParams['toolbar'] = 'None' + tbar = rcParams["toolbar"] # turn off the navigation toolbar for toolfig + rcParams["toolbar"] = "None" if targetfig is None: manager = get_current_fig_manager() targetfig = manager.canvas.figure @@ -1512,17 +1788,17 @@ def subplot_tool(targetfig=None): if manager.canvas.figure == targetfig: break else: - raise RuntimeError('Could not find manager for targetfig') + raise RuntimeError("Could not find manager for targetfig") toolfig = figure(figsize=(6, 3)) toolfig.subplots_adjust(top=0.9) ret = SubplotTool(targetfig, toolfig) - rcParams['toolbar'] = tbar + rcParams["toolbar"] = tbar GlobalFigureManager.set_active(manager) # restore the current figure return ret -def box(on=None): +def box(on: bool | None = None) -> None: """ Turn the axes box on or off on the current axes. @@ -1542,10 +1818,11 @@ def box(on=None): on = not ax.get_frame_on() ax.set_frame_on(on) + ## Axis ## -def xlim(*args, **kwargs): +def xlim(*args, **kwargs) -> tuple[float, float]: """ Get or set the x limits of the current axes. @@ -1582,7 +1859,7 @@ def xlim(*args, **kwargs): return ret -def ylim(*args, **kwargs): +def ylim(*args, **kwargs) -> tuple[float, float]: """ Get or set the y-limits of the current axes. @@ -1619,7 +1896,13 @@ def ylim(*args, **kwargs): return ret -def xticks(ticks=None, labels=None, *, minor=False, **kwargs): +def xticks( + ticks: ArrayLike | None = None, + labels: Sequence[str] | None = None, + *, + minor: bool = False, + **kwargs +) -> tuple[list[Tick] | np.ndarray, list[Text]]: """ Get or set the current tick locations and labels of the x-axis. @@ -1667,8 +1950,9 @@ def xticks(ticks=None, labels=None, *, minor=False, **kwargs): if ticks is None: locs = ax.get_xticks(minor=minor) if labels is not None: - raise TypeError("xticks(): Parameter 'labels' can't be set " - "without setting 'ticks'") + raise TypeError( + "xticks(): Parameter 'labels' can't be set " "without setting 'ticks'" + ) else: locs = ax.set_xticks(ticks, minor=minor) @@ -1682,7 +1966,13 @@ def xticks(ticks=None, labels=None, *, minor=False, **kwargs): return locs, labels -def yticks(ticks=None, labels=None, *, minor=False, **kwargs): +def yticks( + ticks: ArrayLike | None = None, + labels: Sequence[str] | None = None, + *, + minor: bool = False, + **kwargs +) -> tuple[list[Tick] | np.ndarray, list[Text]]: """ Get or set the current tick locations and labels of the y-axis. @@ -1730,8 +2020,9 @@ def yticks(ticks=None, labels=None, *, minor=False, **kwargs): if ticks is None: locs = ax.get_yticks(minor=minor) if labels is not None: - raise TypeError("yticks(): Parameter 'labels' can't be set " - "without setting 'ticks'") + raise TypeError( + "yticks(): Parameter 'labels' can't be set " "without setting 'ticks'" + ) else: locs = ax.set_yticks(ticks, minor=minor) @@ -1745,7 +2036,13 @@ def yticks(ticks=None, labels=None, *, minor=False, **kwargs): return locs, labels -def rgrids(radii=None, labels=None, angle=None, fmt=None, **kwargs): +def rgrids( + radii: ArrayLike | None = None, + labels: Sequence[str | Text] | None = None, + angle: float | None = None, + fmt: str | None = None, + **kwargs +) -> tuple[list[Line2D], list[Text]]: """ Get or set the radial gridlines on the current polar plot. @@ -1806,17 +2103,23 @@ def rgrids(radii=None, labels=None, angle=None, fmt=None, **kwargs): """ ax = gca() if not isinstance(ax, PolarAxes): - raise RuntimeError('rgrids only defined for polar axes') + raise RuntimeError("rgrids only defined for polar axes") if all(p is None for p in [radii, labels, angle, fmt]) and not kwargs: lines = ax.yaxis.get_gridlines() labels = ax.yaxis.get_ticklabels() else: lines, labels = ax.set_rgrids( - radii, labels=labels, angle=angle, fmt=fmt, **kwargs) + radii, labels=labels, angle=angle, fmt=fmt, **kwargs + ) return lines, labels -def thetagrids(angles=None, labels=None, fmt=None, **kwargs): +def thetagrids( + angles: ArrayLike | None = None, + labels: Sequence[str | Text] | None = None, + fmt: str | None = None, + **kwargs +) -> tuple[list[Line2D], list[Text]]: """ Get or set the theta gridlines on the current polar plot. @@ -1874,30 +2177,32 @@ def thetagrids(angles=None, labels=None, fmt=None, **kwargs): """ ax = gca() if not isinstance(ax, PolarAxes): - raise RuntimeError('thetagrids only defined for polar axes') + raise RuntimeError("thetagrids only defined for polar axes") if all(param is None for param in [angles, labels, fmt]) and not kwargs: lines = ax.xaxis.get_ticklines() labels = ax.xaxis.get_ticklabels() else: - lines, labels = ax.set_thetagrids(angles, - labels=labels, fmt=fmt, **kwargs) + lines, labels = ax.set_thetagrids(angles, labels=labels, fmt=fmt, **kwargs) return lines, labels -_NON_PLOT_COMMANDS = { - 'connect', 'disconnect', 'get_current_fig_manager', 'ginput', - 'new_figure_manager', 'waitforbuttonpress'} - - -def get_plot_commands(): +@_api.deprecated("3.7", pending=True) +def get_plot_commands() -> list[str]: """ Get a sorted list of all of the plotting commands. """ + NON_PLOT_COMMANDS = { + 'connect', 'disconnect', 'get_current_fig_manager', 'ginput', + 'new_figure_manager', 'waitforbuttonpress'} + return [name for name in _get_pyplot_commands() + if name not in NON_PLOT_COMMANDS] + + +def _get_pyplot_commands() -> list[str]: # This works by searching for all functions in this module and removing # a few hard-coded exclusions, as well as all of the colormap-setting # functions, and anything marked as private with a preceding underscore. - exclude = {'colormaps', 'colors', 'get_plot_commands', - *_NON_PLOT_COMMANDS, *colormaps} + exclude = {'colormaps', 'colors', 'get_plot_commands', *colormaps} this_module = inspect.getmodule(get_plot_commands) return sorted( name for name, obj in globals().items() @@ -1910,7 +2215,12 @@ def get_plot_commands(): @_copy_docstring_and_deprecators(Figure.colorbar) -def colorbar(mappable=None, cax=None, ax=None, **kwargs): +def colorbar( + mappable: ScalarMappable | None = None, + cax: matplotlib.axes.Axes | None = None, + ax: matplotlib.axes.Axes | Iterable[matplotlib.axes.Axes] | None = None, + **kwargs +) -> Colorbar: if mappable is None: mappable = gci() if mappable is None: @@ -1922,7 +2232,7 @@ def colorbar(mappable=None, cax=None, ax=None, **kwargs): return ret -def clim(vmin=None, vmax=None): +def clim(vmin: float | None = None, vmax: float | None = None) -> None: """ Set the color limits of the current image. @@ -1943,7 +2253,17 @@ def clim(vmin=None, vmax=None): im.set_clim(vmin, vmax) -def set_cmap(cmap): +# eventually this implementation should move here, use indirection for now to +# avoid having two copies of the code floating around. +def get_cmap( + name: Colormap | str | None = None, + lut: int | None = None +) -> Colormap: + return cm._get_cmap(name=name, lut=lut) # type: ignore +get_cmap.__doc__ = cm._get_cmap.__doc__ # type: ignore + + +def set_cmap(cmap: Colormap | str) -> None: """ Set the default colormap, and applies it to the current image if any. @@ -1968,16 +2288,20 @@ def set_cmap(cmap): @_copy_docstring_and_deprecators(matplotlib.image.imread) -def imread(fname, format=None): +def imread( + fname: str | pathlib.Path | BinaryIO, format: str | None = None +) -> np.ndarray: return matplotlib.image.imread(fname, format) @_copy_docstring_and_deprecators(matplotlib.image.imsave) -def imsave(fname, arr, **kwargs): - return matplotlib.image.imsave(fname, arr, **kwargs) +def imsave( + fname: str | os.PathLike | BinaryIO, arr: ArrayLike, **kwargs +) -> None: + matplotlib.image.imsave(fname, arr, **kwargs) -def matshow(A, fignum=None, **kwargs): +def matshow(A: ArrayLike, fignum: None | int = None, **kwargs) -> AxesImage: """ Display an array as a matrix in a new figure window. @@ -1993,19 +2317,16 @@ def matshow(A, fignum=None, **kwargs): A : 2D array-like The matrix to be displayed. - fignum : None or int or False - If *None*, create a new figure window with automatic numbering. + fignum : None or int + If *None*, create a new, appropriately sized figure window. - If a nonzero integer, draw into the figure with the given number - (create it if it does not exist). + If 0, use the current Axes (creating one if there is none, without ever + adjusting the figure size). - If 0, use the current axes (or create one if it does not exist). - - .. note:: - - Because of how `.Axes.matshow` tries to set the figure aspect - ratio to be the one of the array, strange things may happen if you - reuse an existing figure. + Otherwise, create a new Axes on the figure with the given number + (creating it at the appropriate size if it does not exist, but not + adjusting the figure size otherwise). Note that this will be drawn on + top of any preexisting Axes on the figure. Returns ------- @@ -2023,13 +2344,13 @@ def matshow(A, fignum=None, **kwargs): # Extract actual aspect ratio of array and make appropriately sized # figure. fig = figure(fignum, figsize=figaspect(A)) - ax = fig.add_axes([0.15, 0.09, 0.775, 0.775]) + ax = fig.add_axes((0.15, 0.09, 0.775, 0.775)) im = ax.matshow(A, **kwargs) sci(im) return im -def polar(*args, **kwargs): +def polar(*args, **kwargs) -> list[Line2D]: """ Make a polar plot. @@ -2051,135 +2372,276 @@ def polar(*args, **kwargs): return ax.plot(*args, **kwargs) +# If rcParams['backend_fallback'] is true, and an interactive backend is +# requested, ignore rcParams['backend'] and force selection of a backend that +# is compatible with the current running interactive framework. +if (rcParams["backend_fallback"] + and rcParams._get_backend_or_none() in ( # type: ignore + set(rcsetup.interactive_bk) - {'WebAgg', 'nbAgg'}) + and cbook._get_running_interactive_framework()): # type: ignore + rcParams._set("backend", rcsetup._auto_backend_sentinel) # type: ignore + +# fmt: on + + ################# REMAINING CONTENT GENERATED BY boilerplate.py ############## # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Figure.figimage) def figimage( - X, xo=0, yo=0, alpha=None, norm=None, cmap=None, vmin=None, - vmax=None, origin=None, resize=False, **kwargs): + X: ArrayLike, + xo: int = 0, + yo: int = 0, + alpha: float | None = None, + norm: str | Normalize | None = None, + cmap: str | Colormap | None = None, + vmin: float | None = None, + vmax: float | None = None, + origin: Literal["upper", "lower"] | None = None, + resize: bool = False, + **kwargs, +) -> FigureImage: return gcf().figimage( - X, xo=xo, yo=yo, alpha=alpha, norm=norm, cmap=cmap, vmin=vmin, - vmax=vmax, origin=origin, resize=resize, **kwargs) + X, + xo=xo, + yo=yo, + alpha=alpha, + norm=norm, + cmap=cmap, + vmin=vmin, + vmax=vmax, + origin=origin, + resize=resize, + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Figure.text) -def figtext(x, y, s, fontdict=None, **kwargs): +def figtext( + x: float, y: float, s: str, fontdict: dict[str, Any] | None = None, **kwargs +) -> Text: return gcf().text(x, y, s, fontdict=fontdict, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Figure.gca) -def gca(): +def gca() -> Axes: return gcf().gca() # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Figure._gci) -def gci(): +def gci() -> ScalarMappable | None: return gcf()._gci() # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Figure.ginput) def ginput( - n=1, timeout=30, show_clicks=True, - mouse_add=MouseButton.LEFT, mouse_pop=MouseButton.RIGHT, - mouse_stop=MouseButton.MIDDLE): + n: int = 1, + timeout: float = 30, + show_clicks: bool = True, + mouse_add: MouseButton = MouseButton.LEFT, + mouse_pop: MouseButton = MouseButton.RIGHT, + mouse_stop: MouseButton = MouseButton.MIDDLE, +) -> list[tuple[int, int]]: return gcf().ginput( - n=n, timeout=timeout, show_clicks=show_clicks, - mouse_add=mouse_add, mouse_pop=mouse_pop, - mouse_stop=mouse_stop) + n=n, + timeout=timeout, + show_clicks=show_clicks, + mouse_add=mouse_add, + mouse_pop=mouse_pop, + mouse_stop=mouse_stop, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Figure.subplots_adjust) def subplots_adjust( - left=None, bottom=None, right=None, top=None, wspace=None, - hspace=None): - return gcf().subplots_adjust( - left=left, bottom=bottom, right=right, top=top, wspace=wspace, - hspace=hspace) + left: float | None = None, + bottom: float | None = None, + right: float | None = None, + top: float | None = None, + wspace: float | None = None, + hspace: float | None = None, +) -> None: + gcf().subplots_adjust( + left=left, bottom=bottom, right=right, top=top, wspace=wspace, hspace=hspace + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Figure.suptitle) -def suptitle(t, **kwargs): +def suptitle(t: str, **kwargs) -> Text: return gcf().suptitle(t, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Figure.tight_layout) -def tight_layout(*, pad=1.08, h_pad=None, w_pad=None, rect=None): - return gcf().tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) +def tight_layout( + *, + pad: float = 1.08, + h_pad: float | None = None, + w_pad: float | None = None, + rect: tuple[float, float, float, float] | None = None, +) -> None: + gcf().tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Figure.waitforbuttonpress) -def waitforbuttonpress(timeout=-1): +def waitforbuttonpress(timeout: float = -1) -> None | bool: return gcf().waitforbuttonpress(timeout=timeout) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.acorr) @set_category(CATEGORY_CUT) -def acorr(x, *, data=None, **kwargs): - return gca().acorr( - x, **({"data": data} if data is not None else {}), **kwargs) +def acorr( + x: ArrayLike, *, data=None, **kwargs +) -> tuple[np.ndarray, np.ndarray, LineCollection | Line2D, Line2D | None]: + return gca().acorr(x, **({"data": data} if data is not None else {}), **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.angle_spectrum) @set_category(CATEGORY_CUT) def angle_spectrum( - x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, *, - data=None, **kwargs): + x: ArrayLike, + Fs: float | None = None, + Fc: int | None = None, + window: Callable[[ArrayLike], ArrayLike] | ArrayLike | None = None, + pad_to: int | None = None, + sides: Literal["default", "onesided", "twosided"] | None = None, + *, + data=None, + **kwargs, +) -> tuple[np.ndarray, np.ndarray, Line2D]: return gca().angle_spectrum( - x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, sides=sides, - **({"data": data} if data is not None else {}), **kwargs) + x, + Fs=Fs, + Fc=Fc, + window=window, + pad_to=pad_to, + sides=sides, + **({"data": data} if data is not None else {}), + **kwargs, + ) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.annotate) +@set_category(CATEGORY_CUT) +def annotate( + text: str, + xy: tuple[float, float], + xytext: tuple[float, float] | None = None, + xycoords: ( + str + | Artist + | Transform + | Callable[[RendererBase], Bbox | Transform] + | tuple[float, float] + ) = "data", + textcoords: ( + str + | Artist + | Transform + | Callable[[RendererBase], Bbox | Transform] + | tuple[float, float] + | None + ) = None, + arrowprops: dict[str, Any] | None = None, + annotation_clip: bool | None = None, + **kwargs, +) -> Annotation: + return gca().annotate( + text, + xy, + xytext=xytext, + xycoords=xycoords, + textcoords=textcoords, + arrowprops=arrowprops, + annotation_clip=annotation_clip, + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.arrow) @set_category(CATEGORY_CUT) -def arrow(x, y, dx, dy, **kwargs): +def arrow(x: float, y: float, dx: float, dy: float, **kwargs) -> FancyArrow: return gca().arrow(x, y, dx, dy, **kwargs) +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.autoscale) +@set_category(CATEGORY_CUT) +def autoscale( + enable: bool = True, + axis: Literal["both", "x", "y"] = "both", + tight: bool | None = None, +) -> None: + gca().autoscale(enable=enable, axis=axis, tight=tight) + + # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.axhline) @set_category(CATEGORY_CUT) -def axhline(y=0, xmin=0, xmax=1, **kwargs): +def axhline(y: float = 0, xmin: float = 0, xmax: float = 1, **kwargs) -> Line2D: return gca().axhline(y=y, xmin=xmin, xmax=xmax, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.axhspan) @set_category(CATEGORY_CUT) -def axhspan(ymin, ymax, xmin=0, xmax=1, **kwargs): +def axhspan( + ymin: float, ymax: float, xmin: float = 0, xmax: float = 1, **kwargs +) -> Polygon: return gca().axhspan(ymin, ymax, xmin=xmin, xmax=xmax, **kwargs) +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.axis) +@set_category(CATEGORY_CUT) +def axis( + arg: tuple[float, float, float, float] | bool | str | None = None, + /, + *, + emit: bool = True, + **kwargs, +) -> tuple[float, float, float, float]: + return gca().axis(arg, emit=emit, **kwargs) + + # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.axline) @set_category(CATEGORY_CUT) -def axline(xy1, xy2=None, *, slope=None, **kwargs): +def axline( + xy1: tuple[float, float], + xy2: tuple[float, float] | None = None, + *, + slope: float | None = None, + **kwargs, +) -> AxLine: return gca().axline(xy1, xy2=xy2, slope=slope, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.axvline) @set_category(CATEGORY_CUT) -def axvline(x=0, ymin=0, ymax=1, **kwargs): +def axvline(x: float = 0, ymin: float = 0, ymax: float = 1, **kwargs) -> Line2D: return gca().axvline(x=x, ymin=ymin, ymax=ymax, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.axvspan) @set_category(CATEGORY_CUT) -def axvspan(xmin, xmax, ymin=0, ymax=1, **kwargs): +def axvspan( + xmin: float, xmax: float, ymin: float = 0, ymax: float = 1, **kwargs +) -> Polygon: return gca().axvspan(xmin, xmax, ymin=ymin, ymax=ymax, **kwargs) @@ -2187,83 +2649,164 @@ def axvspan(xmin, xmax, ymin=0, ymax=1, **kwargs): @_copy_docstring_and_deprecators(Axes.bar) @set_category(CATEGORY_CUT) def bar( - x, height, width=0.8, bottom=None, *, align='center', - data=None, **kwargs): + x: float | ArrayLike, + height: float | ArrayLike, + width: float | ArrayLike = 0.8, + bottom: float | ArrayLike | None = None, + *, + align: Literal["center", "edge"] = "center", + data=None, + **kwargs, +) -> BarContainer: return gca().bar( - x, height, width=width, bottom=bottom, align=align, - **({"data": data} if data is not None else {}), **kwargs) + x, + height, + width=width, + bottom=bottom, + align=align, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.barbs) @set_category(CATEGORY_CUT) -def barbs(*args, data=None, **kwargs): - return gca().barbs( - *args, **({"data": data} if data is not None else {}), - **kwargs) +def barbs(*args, data=None, **kwargs) -> Barbs: + return gca().barbs(*args, **({"data": data} if data is not None else {}), **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.barh) @set_category(CATEGORY_CUT) def barh( - y, width, height=0.8, left=None, *, align='center', - data=None, **kwargs): + y: float | ArrayLike, + width: float | ArrayLike, + height: float | ArrayLike = 0.8, + left: float | ArrayLike | None = None, + *, + align: Literal["center", "edge"] = "center", + data=None, + **kwargs, +) -> BarContainer: return gca().barh( - y, width, height=height, left=left, align=align, - **({"data": data} if data is not None else {}), **kwargs) + y, + width, + height=height, + left=left, + align=align, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.bar_label) @set_category(CATEGORY_CUT) def bar_label( - container, labels=None, *, fmt='%g', label_type='edge', - padding=0, **kwargs): + container: BarContainer, + labels: ArrayLike | None = None, + *, + fmt: str | Callable[[float], str] = "%g", + label_type: Literal["center", "edge"] = "edge", + padding: float = 0, + **kwargs, +) -> list[Annotation]: return gca().bar_label( - container, labels=labels, fmt=fmt, label_type=label_type, - padding=padding, **kwargs) + container, + labels=labels, + fmt=fmt, + label_type=label_type, + padding=padding, + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.boxplot) @set_category(CATEGORY_CUT) def boxplot( - x, notch=None, sym=None, vert=None, whis=None, - positions=None, widths=None, patch_artist=None, - bootstrap=None, usermedians=None, conf_intervals=None, - meanline=None, showmeans=None, showcaps=None, showbox=None, - showfliers=None, boxprops=None, labels=None, flierprops=None, - medianprops=None, meanprops=None, capprops=None, - whiskerprops=None, manage_ticks=True, autorange=False, - zorder=None, capwidths=None, *, data=None): + x: ArrayLike | Sequence[ArrayLike], + notch: bool | None = None, + sym: str | None = None, + vert: bool | None = None, + whis: float | tuple[float, float] | None = None, + positions: ArrayLike | None = None, + widths: float | ArrayLike | None = None, + patch_artist: bool | None = None, + bootstrap: int | None = None, + usermedians: ArrayLike | None = None, + conf_intervals: ArrayLike | None = None, + meanline: bool | None = None, + showmeans: bool | None = None, + showcaps: bool | None = None, + showbox: bool | None = None, + showfliers: bool | None = None, + boxprops: dict[str, Any] | None = None, + labels: Sequence[str] | None = None, + flierprops: dict[str, Any] | None = None, + medianprops: dict[str, Any] | None = None, + meanprops: dict[str, Any] | None = None, + capprops: dict[str, Any] | None = None, + whiskerprops: dict[str, Any] | None = None, + manage_ticks: bool = True, + autorange: bool = False, + zorder: float | None = None, + capwidths: float | ArrayLike | None = None, + *, + data=None, +) -> dict[str, Any]: return gca().boxplot( - x, notch=notch, sym=sym, vert=vert, whis=whis, - positions=positions, widths=widths, patch_artist=patch_artist, - bootstrap=bootstrap, usermedians=usermedians, - conf_intervals=conf_intervals, meanline=meanline, - showmeans=showmeans, showcaps=showcaps, showbox=showbox, - showfliers=showfliers, boxprops=boxprops, labels=labels, - flierprops=flierprops, medianprops=medianprops, - meanprops=meanprops, capprops=capprops, - whiskerprops=whiskerprops, manage_ticks=manage_ticks, - autorange=autorange, zorder=zorder, capwidths=capwidths, - **({"data": data} if data is not None else {})) + x, + notch=notch, + sym=sym, + vert=vert, + whis=whis, + positions=positions, + widths=widths, + patch_artist=patch_artist, + bootstrap=bootstrap, + usermedians=usermedians, + conf_intervals=conf_intervals, + meanline=meanline, + showmeans=showmeans, + showcaps=showcaps, + showbox=showbox, + showfliers=showfliers, + boxprops=boxprops, + labels=labels, + flierprops=flierprops, + medianprops=medianprops, + meanprops=meanprops, + capprops=capprops, + whiskerprops=whiskerprops, + manage_ticks=manage_ticks, + autorange=autorange, + zorder=zorder, + capwidths=capwidths, + **({"data": data} if data is not None else {}), + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.broken_barh) @set_category(CATEGORY_CUT) -def broken_barh(xranges, yrange, *, data=None, **kwargs): +def broken_barh( + xranges: Sequence[tuple[float, float]], + yrange: tuple[float, float], + *, + data=None, + **kwargs, +) -> BrokenBarHCollection: return gca().broken_barh( - xranges, yrange, - **({"data": data} if data is not None else {}), **kwargs) + xranges, yrange, **({"data": data} if data is not None else {}), **kwargs + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.clabel) @set_category(CATEGORY_CUT) -def clabel(CS, levels=None, **kwargs): +def clabel(CS: ContourSet, levels: ArrayLike | None = None, **kwargs) -> list[Text]: return gca().clabel(CS, levels=levels, **kwargs) @@ -2271,35 +2814,61 @@ def clabel(CS, levels=None, **kwargs): @_copy_docstring_and_deprecators(Axes.cohere) @set_category(CATEGORY_CUT) def cohere( - x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, - window=mlab.window_hanning, noverlap=0, pad_to=None, - sides='default', scale_by_freq=None, *, data=None, **kwargs): + x: ArrayLike, + y: ArrayLike, + NFFT: int = 256, + Fs: float = 2, + Fc: int = 0, + detrend: ( + Literal["none", "mean", "linear"] | Callable[[ArrayLike], ArrayLike] + ) = mlab.detrend_none, + window: Callable[[ArrayLike], ArrayLike] | ArrayLike = mlab.window_hanning, + noverlap: int = 0, + pad_to: int | None = None, + sides: Literal["default", "onesided", "twosided"] = "default", + scale_by_freq: bool | None = None, + *, + data=None, + **kwargs, +) -> tuple[np.ndarray, np.ndarray]: return gca().cohere( - x, y, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, - noverlap=noverlap, pad_to=pad_to, sides=sides, + x, + y, + NFFT=NFFT, + Fs=Fs, + Fc=Fc, + detrend=detrend, + window=window, + noverlap=noverlap, + pad_to=pad_to, + sides=sides, scale_by_freq=scale_by_freq, - **({"data": data} if data is not None else {}), **kwargs) + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.contour) @set_category(CATEGORY_SLICE) -def contour(*args, data=None, **kwargs): +def contour(*args, data=None, **kwargs) -> QuadContourSet: __ret = gca().contour( - *args, **({"data": data} if data is not None else {}), - **kwargs) - if __ret._A is not None: sci(__ret) # noqa + *args, **({"data": data} if data is not None else {}), **kwargs + ) + if __ret._A is not None: # type: ignore[attr-defined] + sci(__ret) return __ret # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.contourf) @set_category(CATEGORY_SLICE) -def contourf(*args, data=None, **kwargs): +def contourf(*args, data=None, **kwargs) -> QuadContourSet: __ret = gca().contourf( - *args, **({"data": data} if data is not None else {}), - **kwargs) - if __ret._A is not None: sci(__ret) # noqa + *args, **({"data": data} if data is not None else {}), **kwargs + ) + if __ret._A is not None: # type: ignore[attr-defined] + sci(__ret) return __ret @@ -2307,93 +2876,259 @@ def contourf(*args, data=None, **kwargs): @_copy_docstring_and_deprecators(Axes.csd) @set_category(CATEGORY_CUT) def csd( - x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, - noverlap=None, pad_to=None, sides=None, scale_by_freq=None, - return_line=None, *, data=None, **kwargs): + x: ArrayLike, + y: ArrayLike, + NFFT: int | None = None, + Fs: float | None = None, + Fc: int | None = None, + detrend: ( + Literal["none", "mean", "linear"] | Callable[[ArrayLike], ArrayLike] | None + ) = None, + window: Callable[[ArrayLike], ArrayLike] | ArrayLike | None = None, + noverlap: int | None = None, + pad_to: int | None = None, + sides: Literal["default", "onesided", "twosided"] | None = None, + scale_by_freq: bool | None = None, + return_line: bool | None = None, + *, + data=None, + **kwargs, +) -> tuple[np.ndarray, np.ndarray] | tuple[np.ndarray, np.ndarray, Line2D]: return gca().csd( - x, y, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, - noverlap=noverlap, pad_to=pad_to, sides=sides, - scale_by_freq=scale_by_freq, return_line=return_line, - **({"data": data} if data is not None else {}), **kwargs) + x, + y, + NFFT=NFFT, + Fs=Fs, + Fc=Fc, + detrend=detrend, + window=window, + noverlap=noverlap, + pad_to=pad_to, + sides=sides, + scale_by_freq=scale_by_freq, + return_line=return_line, + **({"data": data} if data is not None else {}), + **kwargs, + ) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.ecdf) +@set_category(CATEGORY_CUT) +def ecdf( + x: ArrayLike, + weights: ArrayLike | None = None, + *, + complementary: bool = False, + orientation: Literal["vertical", "horizonatal"] = "vertical", + compress: bool = False, + data=None, + **kwargs, +) -> Line2D: + return gca().ecdf( + x, + weights=weights, + complementary=complementary, + orientation=orientation, + compress=compress, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.errorbar) @set_category(CATEGORY_CUT) def errorbar( - x, y, yerr=None, xerr=None, fmt='', ecolor=None, - elinewidth=None, capsize=None, barsabove=False, lolims=False, - uplims=False, xlolims=False, xuplims=False, errorevery=1, - capthick=None, *, data=None, **kwargs): + x: float | ArrayLike, + y: float | ArrayLike, + yerr: float | ArrayLike | None = None, + xerr: float | ArrayLike | None = None, + fmt: str = "", + ecolor: ColorType | None = None, + elinewidth: float | None = None, + capsize: float | None = None, + barsabove: bool = False, + lolims: bool | ArrayLike = False, + uplims: bool | ArrayLike = False, + xlolims: bool | ArrayLike = False, + xuplims: bool | ArrayLike = False, + errorevery: int | tuple[int, int] = 1, + capthick: float | None = None, + *, + data=None, + **kwargs, +) -> ErrorbarContainer: return gca().errorbar( - x, y, yerr=yerr, xerr=xerr, fmt=fmt, ecolor=ecolor, - elinewidth=elinewidth, capsize=capsize, barsabove=barsabove, - lolims=lolims, uplims=uplims, xlolims=xlolims, - xuplims=xuplims, errorevery=errorevery, capthick=capthick, - **({"data": data} if data is not None else {}), **kwargs) + x, + y, + yerr=yerr, + xerr=xerr, + fmt=fmt, + ecolor=ecolor, + elinewidth=elinewidth, + capsize=capsize, + barsabove=barsabove, + lolims=lolims, + uplims=uplims, + xlolims=xlolims, + xuplims=xuplims, + errorevery=errorevery, + capthick=capthick, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.eventplot) @set_category(CATEGORY_CUT) def eventplot( - positions, orientation='horizontal', lineoffsets=1, - linelengths=1, linewidths=None, colors=None, alpha=None, - linestyles='solid', *, data=None, **kwargs): + positions: ArrayLike | Sequence[ArrayLike], + orientation: Literal["horizontal", "vertical"] = "horizontal", + lineoffsets: float | Sequence[float] = 1, + linelengths: float | Sequence[float] = 1, + linewidths: float | Sequence[float] | None = None, + colors: ColorType | Sequence[ColorType] | None = None, + alpha: float | Sequence[float] | None = None, + linestyles: LineStyleType | Sequence[LineStyleType] = "solid", + *, + data=None, + **kwargs, +) -> EventCollection: return gca().eventplot( - positions, orientation=orientation, lineoffsets=lineoffsets, - linelengths=linelengths, linewidths=linewidths, colors=colors, - alpha=alpha, linestyles=linestyles, - **({"data": data} if data is not None else {}), **kwargs) + positions, + orientation=orientation, + lineoffsets=lineoffsets, + linelengths=linelengths, + linewidths=linewidths, + colors=colors, + alpha=alpha, + linestyles=linestyles, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.fill) @set_category(CATEGORY_CUT) -def fill(*args, data=None, **kwargs): - return gca().fill( - *args, **({"data": data} if data is not None else {}), - **kwargs) +def fill(*args, data=None, **kwargs) -> list[Polygon]: + return gca().fill(*args, **({"data": data} if data is not None else {}), **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.fill_between) @set_category(CATEGORY_CUT) def fill_between( - x, y1, y2=0, where=None, interpolate=False, step=None, *, - data=None, **kwargs): + x: ArrayLike, + y1: ArrayLike | float, + y2: ArrayLike | float = 0, + where: Sequence[bool] | None = None, + interpolate: bool = False, + step: Literal["pre", "post", "mid"] | None = None, + *, + data=None, + **kwargs, +) -> PolyCollection: return gca().fill_between( - x, y1, y2=y2, where=where, interpolate=interpolate, step=step, - **({"data": data} if data is not None else {}), **kwargs) + x, + y1, + y2=y2, + where=where, + interpolate=interpolate, + step=step, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.fill_betweenx) @set_category(CATEGORY_CUT) def fill_betweenx( - y, x1, x2=0, where=None, step=None, interpolate=False, *, - data=None, **kwargs): + y: ArrayLike, + x1: ArrayLike | float, + x2: ArrayLike | float = 0, + where: Sequence[bool] | None = None, + step: Literal["pre", "post", "mid"] | None = None, + interpolate: bool = False, + *, + data=None, + **kwargs, +) -> PolyCollection: return gca().fill_betweenx( - y, x1, x2=x2, where=where, step=step, interpolate=interpolate, - **({"data": data} if data is not None else {}), **kwargs) + y, + x1, + x2=x2, + where=where, + step=step, + interpolate=interpolate, + **({"data": data} if data is not None else {}), + **kwargs, + ) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.grid) +@set_category(CATEGORY_CUT) +def grid( + visible: bool | None = None, + which: Literal["major", "minor", "both"] = "major", + axis: Literal["both", "x", "y"] = "both", + **kwargs, +) -> None: + gca().grid(visible=visible, which=which, axis=axis, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.hexbin) @set_category(CATEGORY_SLICE) def hexbin( - x, y, C=None, gridsize=100, bins=None, xscale='linear', - yscale='linear', extent=None, cmap=None, norm=None, vmin=None, - vmax=None, alpha=None, linewidths=None, edgecolors='face', - reduce_C_function=np.mean, mincnt=None, marginals=False, *, - data=None, **kwargs): + x: ArrayLike, + y: ArrayLike, + C: ArrayLike | None = None, + gridsize: int | tuple[int, int] = 100, + bins: Literal["log"] | int | Sequence[float] | None = None, + xscale: Literal["linear", "log"] = "linear", + yscale: Literal["linear", "log"] = "linear", + extent: tuple[float, float, float, float] | None = None, + cmap: str | Colormap | None = None, + norm: str | Normalize | None = None, + vmin: float | None = None, + vmax: float | None = None, + alpha: float | None = None, + linewidths: float | None = None, + edgecolors: Literal["face", "none"] | ColorType = "face", + reduce_C_function: Callable[[np.ndarray | list[float]], float] = np.mean, + mincnt: int | None = None, + marginals: bool = False, + *, + data=None, + **kwargs, +) -> PolyCollection: __ret = gca().hexbin( - x, y, C=C, gridsize=gridsize, bins=bins, xscale=xscale, - yscale=yscale, extent=extent, cmap=cmap, norm=norm, vmin=vmin, - vmax=vmax, alpha=alpha, linewidths=linewidths, - edgecolors=edgecolors, reduce_C_function=reduce_C_function, - mincnt=mincnt, marginals=marginals, - **({"data": data} if data is not None else {}), **kwargs) + x, + y, + C=C, + gridsize=gridsize, + bins=bins, + xscale=xscale, + yscale=yscale, + extent=extent, + cmap=cmap, + norm=norm, + vmin=vmin, + vmax=vmax, + alpha=alpha, + linewidths=linewidths, + edgecolors=edgecolors, + reduce_C_function=reduce_C_function, + mincnt=mincnt, + marginals=marginals, + **({"data": data} if data is not None else {}), + **kwargs, + ) sci(__ret) return __ret @@ -2402,40 +3137,102 @@ def hexbin( @_copy_docstring_and_deprecators(Axes.hist) @set_category(CATEGORY_CUT) def hist( - x, bins=None, range=None, density=False, weights=None, - cumulative=False, bottom=None, histtype='bar', align='mid', - orientation='vertical', rwidth=None, log=False, color=None, - label=None, stacked=False, *, data=None, **kwargs): + x: ArrayLike | Sequence[ArrayLike], + bins: int | Sequence[float] | str | None = None, + range: tuple[float, float] | None = None, + density: bool = False, + weights: ArrayLike | None = None, + cumulative: bool | float = False, + bottom: ArrayLike | float | None = None, + histtype: Literal["bar", "barstacked", "step", "stepfilled"] = "bar", + align: Literal["left", "mid", "right"] = "mid", + orientation: Literal["vertical", "horizontal"] = "vertical", + rwidth: float | None = None, + log: bool = False, + color: ColorType | Sequence[ColorType] | None = None, + label: str | Sequence[str] | None = None, + stacked: bool = False, + *, + data=None, + **kwargs, +) -> tuple[ + np.ndarray | list[np.ndarray], + np.ndarray, + BarContainer | Polygon | list[BarContainer | Polygon], +]: return gca().hist( - x, bins=bins, range=range, density=density, weights=weights, - cumulative=cumulative, bottom=bottom, histtype=histtype, - align=align, orientation=orientation, rwidth=rwidth, log=log, - color=color, label=label, stacked=stacked, - **({"data": data} if data is not None else {}), **kwargs) + x, + bins=bins, + range=range, + density=density, + weights=weights, + cumulative=cumulative, + bottom=bottom, + histtype=histtype, + align=align, + orientation=orientation, + rwidth=rwidth, + log=log, + color=color, + label=label, + stacked=stacked, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.stairs) @set_category(CATEGORY_CUT) def stairs( - values, edges=None, *, orientation='vertical', baseline=0, - fill=False, data=None, **kwargs): + values: ArrayLike, + edges: ArrayLike | None = None, + *, + orientation: Literal["vertical", "horizontal"] = "vertical", + baseline: float | ArrayLike | None = 0, + fill: bool = False, + data=None, + **kwargs, +) -> StepPatch: return gca().stairs( - values, edges=edges, orientation=orientation, - baseline=baseline, fill=fill, - **({"data": data} if data is not None else {}), **kwargs) + values, + edges=edges, + orientation=orientation, + baseline=baseline, + fill=fill, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.hist2d) @set_category(CATEGORY_SLICE) def hist2d( - x, y, bins=10, range=None, density=False, weights=None, - cmin=None, cmax=None, *, data=None, **kwargs): + x: ArrayLike, + y: ArrayLike, + bins: None | int | tuple[int, int] | ArrayLike | tuple[ArrayLike, ArrayLike] = 10, + range: ArrayLike | None = None, + density: bool = False, + weights: ArrayLike | None = None, + cmin: float | None = None, + cmax: float | None = None, + *, + data=None, + **kwargs, +) -> tuple[np.ndarray, np.ndarray, np.ndarray, QuadMesh]: __ret = gca().hist2d( - x, y, bins=bins, range=range, density=density, - weights=weights, cmin=cmin, cmax=cmax, - **({"data": data} if data is not None else {}), **kwargs) + x, + y, + bins=bins, + range=range, + density=density, + weights=weights, + cmin=cmin, + cmax=cmax, + **({"data": data} if data is not None else {}), + **kwargs, + ) sci(__ret[-1]) return __ret @@ -2444,38 +3241,94 @@ def hist2d( @_copy_docstring_and_deprecators(Axes.hlines) @set_category(CATEGORY_CUT) def hlines( - y, xmin, xmax, colors=None, linestyles='solid', label='', *, - data=None, **kwargs): + y: float | ArrayLike, + xmin: float | ArrayLike, + xmax: float | ArrayLike, + colors: ColorType | Sequence[ColorType] | None = None, + linestyles: LineStyleType = "solid", + label: str = "", + *, + data=None, + **kwargs, +) -> LineCollection: return gca().hlines( - y, xmin, xmax, colors=colors, linestyles=linestyles, - label=label, **({"data": data} if data is not None else {}), - **kwargs) + y, + xmin, + xmax, + colors=colors, + linestyles=linestyles, + label=label, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.imshow) @set_category(CATEGORY_SLICE) def imshow( - X, cmap=None, norm=None, *, aspect=None, interpolation=None, - alpha=None, vmin=None, vmax=None, origin=None, extent=None, - interpolation_stage=None, filternorm=True, filterrad=4.0, - resample=None, url=None, data=None, **kwargs): + X: ArrayLike | PIL.Image.Image, + cmap: str | Colormap | None = None, + norm: str | Normalize | None = None, + *, + aspect: Literal["equal", "auto"] | float | None = None, + interpolation: str | None = None, + alpha: float | ArrayLike | None = None, + vmin: float | None = None, + vmax: float | None = None, + origin: Literal["upper", "lower"] | None = None, + extent: tuple[float, float, float, float] | None = None, + interpolation_stage: Literal["data", "rgba"] | None = None, + filternorm: bool = True, + filterrad: float = 4.0, + resample: bool | None = None, + url: str | None = None, + data=None, + **kwargs, +) -> AxesImage: __ret = gca().imshow( - X, cmap=cmap, norm=norm, aspect=aspect, - interpolation=interpolation, alpha=alpha, vmin=vmin, - vmax=vmax, origin=origin, extent=extent, + X, + cmap=cmap, + norm=norm, + aspect=aspect, + interpolation=interpolation, + alpha=alpha, + vmin=vmin, + vmax=vmax, + origin=origin, + extent=extent, interpolation_stage=interpolation_stage, - filternorm=filternorm, filterrad=filterrad, resample=resample, - url=url, **({"data": data} if data is not None else {}), - **kwargs) + filternorm=filternorm, + filterrad=filterrad, + resample=resample, + url=url, + **({"data": data} if data is not None else {}), + **kwargs, + ) sci(__ret) return __ret +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.legend) +@set_category(CATEGORY_CUT) +def legend(*args, **kwargs) -> Legend: + return gca().legend(*args, **kwargs) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.locator_params) +@set_category(CATEGORY_CUT) +def locator_params( + axis: Literal["both", "x", "y"] = "both", tight: bool | None = None, **kwargs +) -> None: + gca().locator_params(axis=axis, tight=tight, **kwargs) + + # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.loglog) @set_category(CATEGORY_CUT) -def loglog(*args, **kwargs): +def loglog(*args, **kwargs) -> list[Line2D]: return gca().loglog(*args, **kwargs) @@ -2483,24 +3336,81 @@ def loglog(*args, **kwargs): @_copy_docstring_and_deprecators(Axes.magnitude_spectrum) @set_category(CATEGORY_CUT) def magnitude_spectrum( - x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, - scale=None, *, data=None, **kwargs): + x: ArrayLike, + Fs: float | None = None, + Fc: int | None = None, + window: Callable[[ArrayLike], ArrayLike] | ArrayLike | None = None, + pad_to: int | None = None, + sides: Literal["default", "onesided", "twosided"] | None = None, + scale: Literal["default", "linear", "dB"] | None = None, + *, + data=None, + **kwargs, +) -> tuple[np.ndarray, np.ndarray, Line2D]: return gca().magnitude_spectrum( - x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, sides=sides, - scale=scale, **({"data": data} if data is not None else {}), - **kwargs) + x, + Fs=Fs, + Fc=Fc, + window=window, + pad_to=pad_to, + sides=sides, + scale=scale, + **({"data": data} if data is not None else {}), + **kwargs, + ) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.margins) +@set_category(CATEGORY_CUT) +def margins( + *margins: float, + x: float | None = None, + y: float | None = None, + tight: bool | None = True, +) -> tuple[float, float] | None: + return gca().margins(*margins, x=x, y=y, tight=tight) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.minorticks_off) +@set_category(CATEGORY_CUT) +def minorticks_off() -> None: + gca().minorticks_off() + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.minorticks_on) +@set_category(CATEGORY_CUT) +def minorticks_on() -> None: + gca().minorticks_on() # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.pcolor) @set_category(CATEGORY_SLICE) def pcolor( - *args, shading=None, alpha=None, norm=None, cmap=None, - vmin=None, vmax=None, data=None, **kwargs): + *args: ArrayLike, + shading: Literal["flat", "nearest", "auto"] | None = None, + alpha: float | None = None, + norm: str | Normalize | None = None, + cmap: str | Colormap | None = None, + vmin: float | None = None, + vmax: float | None = None, + data=None, + **kwargs, +) -> Collection: __ret = gca().pcolor( - *args, shading=shading, alpha=alpha, norm=norm, cmap=cmap, - vmin=vmin, vmax=vmax, - **({"data": data} if data is not None else {}), **kwargs) + *args, + shading=shading, + alpha=alpha, + norm=norm, + cmap=cmap, + vmin=vmin, + vmax=vmax, + **({"data": data} if data is not None else {}), + **kwargs, + ) sci(__ret) return __ret @@ -2509,13 +3419,29 @@ def pcolor( @_copy_docstring_and_deprecators(Axes.pcolormesh) @set_category(CATEGORY_SLICE) def pcolormesh( - *args, alpha=None, norm=None, cmap=None, vmin=None, - vmax=None, shading=None, antialiased=False, data=None, - **kwargs): + *args: ArrayLike, + alpha: float | None = None, + norm: str | Normalize | None = None, + cmap: str | Colormap | None = None, + vmin: float | None = None, + vmax: float | None = None, + shading: Literal["flat", "nearest", "gouraud", "auto"] | None = None, + antialiased: bool = False, + data=None, + **kwargs, +) -> QuadMesh: __ret = gca().pcolormesh( - *args, alpha=alpha, norm=norm, cmap=cmap, vmin=vmin, - vmax=vmax, shading=shading, antialiased=antialiased, - **({"data": data} if data is not None else {}), **kwargs) + *args, + alpha=alpha, + norm=norm, + cmap=cmap, + vmin=vmin, + vmax=vmax, + shading=shading, + antialiased=antialiased, + **({"data": data} if data is not None else {}), + **kwargs, + ) sci(__ret) return __ret @@ -2524,73 +3450,166 @@ def pcolormesh( @_copy_docstring_and_deprecators(Axes.phase_spectrum) @set_category(CATEGORY_CUT) def phase_spectrum( - x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, *, - data=None, **kwargs): + x: ArrayLike, + Fs: float | None = None, + Fc: int | None = None, + window: Callable[[ArrayLike], ArrayLike] | ArrayLike | None = None, + pad_to: int | None = None, + sides: Literal["default", "onesided", "twosided"] | None = None, + *, + data=None, + **kwargs, +) -> tuple[np.ndarray, np.ndarray, Line2D]: return gca().phase_spectrum( - x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, sides=sides, - **({"data": data} if data is not None else {}), **kwargs) + x, + Fs=Fs, + Fc=Fc, + window=window, + pad_to=pad_to, + sides=sides, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.pie) @set_category(CATEGORY_CUT) def pie( - x, explode=None, labels=None, colors=None, autopct=None, - pctdistance=0.6, shadow=False, labeldistance=1.1, - startangle=0, radius=1, counterclock=True, wedgeprops=None, - textprops=None, center=(0, 0), frame=False, - rotatelabels=False, *, normalize=True, hatch=None, data=None): + x: ArrayLike, + explode: ArrayLike | None = None, + labels: Sequence[str] | None = None, + colors: ColorType | Sequence[ColorType] | None = None, + autopct: str | Callable[[float], str] | None = None, + pctdistance: float = 0.6, + shadow: bool = False, + labeldistance: float | None = 1.1, + startangle: float = 0, + radius: float = 1, + counterclock: bool = True, + wedgeprops: dict[str, Any] | None = None, + textprops: dict[str, Any] | None = None, + center: tuple[float, float] = (0, 0), + frame: bool = False, + rotatelabels: bool = False, + *, + normalize: bool = True, + hatch: str | Sequence[str] | None = None, + data=None, +) -> tuple[list[Wedge], list[Text]] | tuple[list[Wedge], list[Text], list[Text]]: return gca().pie( - x, explode=explode, labels=labels, colors=colors, - autopct=autopct, pctdistance=pctdistance, shadow=shadow, - labeldistance=labeldistance, startangle=startangle, - radius=radius, counterclock=counterclock, - wedgeprops=wedgeprops, textprops=textprops, center=center, - frame=frame, rotatelabels=rotatelabels, normalize=normalize, - hatch=hatch, **({"data": data} if data is not None else {})) + x, + explode=explode, + labels=labels, + colors=colors, + autopct=autopct, + pctdistance=pctdistance, + shadow=shadow, + labeldistance=labeldistance, + startangle=startangle, + radius=radius, + counterclock=counterclock, + wedgeprops=wedgeprops, + textprops=textprops, + center=center, + frame=frame, + rotatelabels=rotatelabels, + normalize=normalize, + hatch=hatch, + **({"data": data} if data is not None else {}), + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.plot) @set_category(CATEGORY_CUT) -def plot(*args, scalex=True, scaley=True, data=None, **kwargs): +def plot( + *args: float | ArrayLike | str, + scalex: bool = True, + scaley: bool = True, + data=None, + **kwargs, +) -> list[Line2D]: return gca().plot( - *args, scalex=scalex, scaley=scaley, - **({"data": data} if data is not None else {}), **kwargs) + *args, + scalex=scalex, + scaley=scaley, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.plot_date) @set_category(CATEGORY_CUT) def plot_date( - x, y, fmt='o', tz=None, xdate=True, ydate=False, *, - data=None, **kwargs): + x: ArrayLike, + y: ArrayLike, + fmt: str = "o", + tz: str | datetime.tzinfo | None = None, + xdate: bool = True, + ydate: bool = False, + *, + data=None, + **kwargs, +) -> list[Line2D]: return gca().plot_date( - x, y, fmt=fmt, tz=tz, xdate=xdate, ydate=ydate, - **({"data": data} if data is not None else {}), **kwargs) + x, + y, + fmt=fmt, + tz=tz, + xdate=xdate, + ydate=ydate, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.psd) @set_category(CATEGORY_CUT) def psd( - x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, - noverlap=None, pad_to=None, sides=None, scale_by_freq=None, - return_line=None, *, data=None, **kwargs): + x: ArrayLike, + NFFT: int | None = None, + Fs: float | None = None, + Fc: int | None = None, + detrend: ( + Literal["none", "mean", "linear"] | Callable[[ArrayLike], ArrayLike] | None + ) = None, + window: Callable[[ArrayLike], ArrayLike] | ArrayLike | None = None, + noverlap: int | None = None, + pad_to: int | None = None, + sides: Literal["default", "onesided", "twosided"] | None = None, + scale_by_freq: bool | None = None, + return_line: bool | None = None, + *, + data=None, + **kwargs, +) -> tuple[np.ndarray, np.ndarray] | tuple[np.ndarray, np.ndarray, Line2D]: return gca().psd( - x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, - noverlap=noverlap, pad_to=pad_to, sides=sides, - scale_by_freq=scale_by_freq, return_line=return_line, - **({"data": data} if data is not None else {}), **kwargs) + x, + NFFT=NFFT, + Fs=Fs, + Fc=Fc, + detrend=detrend, + window=window, + noverlap=noverlap, + pad_to=pad_to, + sides=sides, + scale_by_freq=scale_by_freq, + return_line=return_line, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.quiver) @set_category(CATEGORY_SLICE) -def quiver(*args, data=None, **kwargs): +def quiver(*args, data=None, **kwargs) -> Quiver: __ret = gca().quiver( - *args, **({"data": data} if data is not None else {}), - **kwargs) + *args, **({"data": data} if data is not None else {}), **kwargs + ) sci(__ret) return __ret @@ -2598,7 +3617,9 @@ def quiver(*args, data=None, **kwargs): # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.quiverkey) @set_category(CATEGORY_CUT) -def quiverkey(Q, X, Y, U, label, **kwargs): +def quiverkey( + Q: Quiver, X: float, Y: float, U: float, label: str, **kwargs +) -> QuiverKey: return gca().quiverkey(Q, X, Y, U, label, **kwargs) @@ -2606,14 +3627,40 @@ def quiverkey(Q, X, Y, U, label, **kwargs): @_copy_docstring_and_deprecators(Axes.scatter) @set_category(CATEGORY_SLICE) def scatter( - x, y, s=None, c=None, marker=None, cmap=None, norm=None, - vmin=None, vmax=None, alpha=None, linewidths=None, *, - edgecolors=None, plotnonfinite=False, data=None, **kwargs): + x: float | ArrayLike, + y: float | ArrayLike, + s: float | ArrayLike | None = None, + c: ArrayLike | Sequence[ColorType] | ColorType | None = None, + marker: MarkerType | None = None, + cmap: str | Colormap | None = None, + norm: str | Normalize | None = None, + vmin: float | None = None, + vmax: float | None = None, + alpha: float | None = None, + linewidths: float | Sequence[float] | None = None, + *, + edgecolors: Literal["face", "none"] | ColorType | Sequence[ColorType] | None = None, + plotnonfinite: bool = False, + data=None, + **kwargs, +) -> PathCollection: __ret = gca().scatter( - x, y, s=s, c=c, marker=marker, cmap=cmap, norm=norm, - vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, - edgecolors=edgecolors, plotnonfinite=plotnonfinite, - **({"data": data} if data is not None else {}), **kwargs) + x, + y, + s=s, + c=c, + marker=marker, + cmap=cmap, + norm=norm, + vmin=vmin, + vmax=vmax, + alpha=alpha, + linewidths=linewidths, + edgecolors=edgecolors, + plotnonfinite=plotnonfinite, + **({"data": data} if data is not None else {}), + **kwargs, + ) sci(__ret) return __ret @@ -2621,14 +3668,14 @@ def scatter( # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.semilogx) @set_category(CATEGORY_CUT) -def semilogx(*args, **kwargs): +def semilogx(*args, **kwargs) -> list[Line2D]: return gca().semilogx(*args, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.semilogy) @set_category(CATEGORY_CUT) -def semilogy(*args, **kwargs): +def semilogy(*args, **kwargs) -> list[Line2D]: return gca().semilogy(*args, **kwargs) @@ -2636,16 +3683,48 @@ def semilogy(*args, **kwargs): @_copy_docstring_and_deprecators(Axes.specgram) @set_category(CATEGORY_SLICE) def specgram( - x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, - noverlap=None, cmap=None, xextent=None, pad_to=None, - sides=None, scale_by_freq=None, mode=None, scale=None, - vmin=None, vmax=None, *, data=None, **kwargs): + x: ArrayLike, + NFFT: int | None = None, + Fs: float | None = None, + Fc: int | None = None, + detrend: ( + Literal["none", "mean", "linear"] | Callable[[ArrayLike], ArrayLike] | None + ) = None, + window: Callable[[ArrayLike], ArrayLike] | ArrayLike | None = None, + noverlap: int | None = None, + cmap: str | Colormap | None = None, + xextent: tuple[float, float] | None = None, + pad_to: int | None = None, + sides: Literal["default", "onesided", "twosided"] | None = None, + scale_by_freq: bool | None = None, + mode: Literal["default", "psd", "magnitude", "angle", "phase"] | None = None, + scale: Literal["default", "linear", "dB"] | None = None, + vmin: float | None = None, + vmax: float | None = None, + *, + data=None, + **kwargs, +) -> tuple[np.ndarray, np.ndarray, np.ndarray, AxesImage]: __ret = gca().specgram( - x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, - noverlap=noverlap, cmap=cmap, xextent=xextent, pad_to=pad_to, - sides=sides, scale_by_freq=scale_by_freq, mode=mode, - scale=scale, vmin=vmin, vmax=vmax, - **({"data": data} if data is not None else {}), **kwargs) + x, + NFFT=NFFT, + Fs=Fs, + Fc=Fc, + detrend=detrend, + window=window, + noverlap=noverlap, + cmap=cmap, + xextent=xextent, + pad_to=pad_to, + sides=sides, + scale_by_freq=scale_by_freq, + mode=mode, + scale=scale, + vmin=vmin, + vmax=vmax, + **({"data": data} if data is not None else {}), + **kwargs, + ) sci(__ret[-1]) return __ret @@ -2653,67 +3732,196 @@ def specgram( # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.stackplot) @set_category(CATEGORY_CUT) -def stackplot( - x, *args, labels=(), colors=None, baseline='zero', data=None, - **kwargs): +def stackplot(x, *args, labels=(), colors=None, baseline="zero", data=None, **kwargs): return gca().stackplot( - x, *args, labels=labels, colors=colors, baseline=baseline, - **({"data": data} if data is not None else {}), **kwargs) + x, + *args, + labels=labels, + colors=colors, + baseline=baseline, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.stem) @set_category(CATEGORY_CUT) def stem( - *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0, - label=None, - use_line_collection=_api.deprecation._deprecated_parameter, - orientation='vertical', data=None): + *args: ArrayLike | str, + linefmt: str | None = None, + markerfmt: str | None = None, + basefmt: str | None = None, + bottom: float = 0, + label: str | None = None, + orientation: Literal["vertical", "horizontal"] = "vertical", + data=None, +) -> StemContainer: return gca().stem( - *args, linefmt=linefmt, markerfmt=markerfmt, basefmt=basefmt, - bottom=bottom, label=label, - use_line_collection=use_line_collection, + *args, + linefmt=linefmt, + markerfmt=markerfmt, + basefmt=basefmt, + bottom=bottom, + label=label, orientation=orientation, - **({"data": data} if data is not None else {})) + **({"data": data} if data is not None else {}), + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.step) @set_category(CATEGORY_CUT) -def step(x, y, *args, where='pre', data=None, **kwargs): +def step( + x: ArrayLike, + y: ArrayLike, + *args, + where: Literal["pre", "post", "mid"] = "pre", + data=None, + **kwargs, +) -> list[Line2D]: return gca().step( - x, y, *args, where=where, - **({"data": data} if data is not None else {}), **kwargs) + x, + y, + *args, + where=where, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.streamplot) @set_category(CATEGORY_SLICE) def streamplot( - x, y, u, v, density=1, linewidth=None, color=None, cmap=None, - norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, - transform=None, zorder=None, start_points=None, maxlength=4.0, - integration_direction='both', broken_streamlines=True, *, - data=None): + x, + y, + u, + v, + density=1, + linewidth=None, + color=None, + cmap=None, + norm=None, + arrowsize=1, + arrowstyle="-|>", + minlength=0.1, + transform=None, + zorder=None, + start_points=None, + maxlength=4.0, + integration_direction="both", + broken_streamlines=True, + *, + data=None, +): __ret = gca().streamplot( - x, y, u, v, density=density, linewidth=linewidth, color=color, - cmap=cmap, norm=norm, arrowsize=arrowsize, - arrowstyle=arrowstyle, minlength=minlength, - transform=transform, zorder=zorder, start_points=start_points, + x, + y, + u, + v, + density=density, + linewidth=linewidth, + color=color, + cmap=cmap, + norm=norm, + arrowsize=arrowsize, + arrowstyle=arrowstyle, + minlength=minlength, + transform=transform, + zorder=zorder, + start_points=start_points, maxlength=maxlength, integration_direction=integration_direction, broken_streamlines=broken_streamlines, - **({"data": data} if data is not None else {})) + **({"data": data} if data is not None else {}), + ) sci(__ret.lines) return __ret +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.table) +@set_category(CATEGORY_CUT) +def table( + cellText=None, + cellColours=None, + cellLoc="right", + colWidths=None, + rowLabels=None, + rowColours=None, + rowLoc="left", + colLabels=None, + colColours=None, + colLoc="center", + loc="bottom", + bbox=None, + edges="closed", + **kwargs, +): + return gca().table( + cellText=cellText, + cellColours=cellColours, + cellLoc=cellLoc, + colWidths=colWidths, + rowLabels=rowLabels, + rowColours=rowColours, + rowLoc=rowLoc, + colLabels=colLabels, + colColours=colColours, + colLoc=colLoc, + loc=loc, + bbox=bbox, + edges=edges, + **kwargs, + ) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.text) +@set_category(CATEGORY_CUT) +def text( + x: float, y: float, s: str, fontdict: dict[str, Any] | None = None, **kwargs +) -> Text: + return gca().text(x, y, s, fontdict=fontdict, **kwargs) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.tick_params) +@set_category(CATEGORY_CUT) +def tick_params(axis: Literal["both", "x", "y"] = "both", **kwargs) -> None: + gca().tick_params(axis=axis, **kwargs) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.ticklabel_format) +@set_category(CATEGORY_CUT) +def ticklabel_format( + *, + axis: Literal["both", "x", "y"] = "both", + style: Literal["", "sci", "scientific", "plain"] = "", + scilimits: tuple[int, int] | None = None, + useOffset: bool | float | None = None, + useLocale: bool | None = None, + useMathText: bool | None = None, +) -> None: + gca().ticklabel_format( + axis=axis, + style=style, + scilimits=scilimits, + useOffset=useOffset, + useLocale=useLocale, + useMathText=useMathText, + ) + + # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.tricontour) @set_category(CATEGORY_SLICE) def tricontour(*args, **kwargs): __ret = gca().tricontour(*args, **kwargs) - if __ret._A is not None: sci(__ret) # noqa + if __ret._A is not None: # type: ignore[attr-defined] + sci(__ret) return __ret @@ -2722,7 +3930,8 @@ def tricontour(*args, **kwargs): @set_category(CATEGORY_SLICE) def tricontourf(*args, **kwargs): __ret = gca().tricontourf(*args, **kwargs) - if __ret._A is not None: sci(__ret) # noqa + if __ret._A is not None: # type: ignore[attr-defined] + sci(__ret) return __ret @@ -2730,11 +3939,27 @@ def tricontourf(*args, **kwargs): @_copy_docstring_and_deprecators(Axes.tripcolor) @set_category(CATEGORY_SLICE) def tripcolor( - *args, alpha=1.0, norm=None, cmap=None, vmin=None, vmax=None, - shading='flat', facecolors=None, **kwargs): + *args, + alpha=1.0, + norm=None, + cmap=None, + vmin=None, + vmax=None, + shading="flat", + facecolors=None, + **kwargs, +): __ret = gca().tripcolor( - *args, alpha=alpha, norm=norm, cmap=cmap, vmin=vmin, - vmax=vmax, shading=shading, facecolors=facecolors, **kwargs) + *args, + alpha=alpha, + norm=norm, + cmap=cmap, + vmin=vmin, + vmax=vmax, + shading=shading, + facecolors=facecolors, + **kwargs, + ) sci(__ret) return __ret @@ -2750,390 +3975,600 @@ def triplot(*args, **kwargs): @_copy_docstring_and_deprecators(Axes.violinplot) @set_category(CATEGORY_CUT) def violinplot( - dataset, positions=None, vert=True, widths=0.5, - showmeans=False, showextrema=True, showmedians=False, - quantiles=None, points=100, bw_method=None, *, data=None): + dataset: ArrayLike | Sequence[ArrayLike], + positions: ArrayLike | None = None, + vert: bool = True, + widths: float | ArrayLike = 0.5, + showmeans: bool = False, + showextrema: bool = True, + showmedians: bool = False, + quantiles: Sequence[float | Sequence[float]] | None = None, + points: int = 100, + bw_method: ( + Literal["scott", "silverman"] | float | Callable[[GaussianKDE], float] | None + ) = None, + *, + data=None, +) -> dict[str, Collection]: return gca().violinplot( - dataset, positions=positions, vert=vert, widths=widths, - showmeans=showmeans, showextrema=showextrema, - showmedians=showmedians, quantiles=quantiles, points=points, + dataset, + positions=positions, + vert=vert, + widths=widths, + showmeans=showmeans, + showextrema=showextrema, + showmedians=showmedians, + quantiles=quantiles, + points=points, bw_method=bw_method, - **({"data": data} if data is not None else {})) + **({"data": data} if data is not None else {}), + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.vlines) @set_category(CATEGORY_CUT) def vlines( - x, ymin, ymax, colors=None, linestyles='solid', label='', *, - data=None, **kwargs): + x: float | ArrayLike, + ymin: float | ArrayLike, + ymax: float | ArrayLike, + colors: ColorType | Sequence[ColorType] | None = None, + linestyles: LineStyleType = "solid", + label: str = "", + *, + data=None, + **kwargs, +) -> LineCollection: return gca().vlines( - x, ymin, ymax, colors=colors, linestyles=linestyles, - label=label, **({"data": data} if data is not None else {}), - **kwargs) + x, + ymin, + ymax, + colors=colors, + linestyles=linestyles, + label=label, + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.xcorr) @set_category(CATEGORY_CUT) def xcorr( - x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, - maxlags=10, *, data=None, **kwargs): + x: ArrayLike, + y: ArrayLike, + normed: bool = True, + detrend: Callable[[ArrayLike], ArrayLike] = mlab.detrend_none, + usevlines: bool = True, + maxlags: int = 10, + *, + data=None, + **kwargs, +) -> tuple[np.ndarray, np.ndarray, LineCollection | Line2D, Line2D | None]: return gca().xcorr( - x, y, normed=normed, detrend=detrend, usevlines=usevlines, + x, + y, + normed=normed, + detrend=detrend, + usevlines=usevlines, maxlags=maxlags, - **({"data": data} if data is not None else {}), **kwargs) + **({"data": data} if data is not None else {}), + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.cla) -def cla(): - return gca().cla() +@_copy_docstring_and_deprecators(Axes._sci) +@set_category(CATEGORY_CUT) +def sci(im: ScalarMappable) -> None: + gca()._sci(im) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.grid) -def grid(visible=None, which='major', axis='both', **kwargs): - return gca().grid(visible=visible, which=which, axis=axis, **kwargs) +@_copy_docstring_and_deprecators(Axes.set_title) +@set_category(CATEGORY_CUT) +def title( + label: str, + fontdict: dict[str, Any] | None = None, + loc: Literal["left", "center", "right"] | None = None, + pad: float | None = None, + *, + y: float | None = None, + **kwargs, +) -> Text: + return gca().set_title(label, fontdict=fontdict, loc=loc, pad=pad, y=y, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.legend) -def legend(*args, **kwargs): - return gca().legend(*args, **kwargs) +@_copy_docstring_and_deprecators(Axes.set_xlabel) +@set_category(CATEGORY_CUT) +def xlabel( + xlabel: str, + fontdict: dict[str, Any] | None = None, + labelpad: float | None = None, + *, + loc: Literal["left", "center", "right"] | None = None, + **kwargs, +) -> Text: + return gca().set_xlabel( + xlabel, fontdict=fontdict, labelpad=labelpad, loc=loc, **kwargs + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.table) -def table( - cellText=None, cellColours=None, cellLoc='right', - colWidths=None, rowLabels=None, rowColours=None, - rowLoc='left', colLabels=None, colColours=None, - colLoc='center', loc='bottom', bbox=None, edges='closed', - **kwargs): - return gca().table( - cellText=cellText, cellColours=cellColours, cellLoc=cellLoc, - colWidths=colWidths, rowLabels=rowLabels, - rowColours=rowColours, rowLoc=rowLoc, colLabels=colLabels, - colColours=colColours, colLoc=colLoc, loc=loc, bbox=bbox, - edges=edges, **kwargs) +@_copy_docstring_and_deprecators(Axes.set_ylabel) +@set_category(CATEGORY_CUT) +def ylabel( + ylabel: str, + fontdict: dict[str, Any] | None = None, + labelpad: float | None = None, + *, + loc: Literal["bottom", "center", "top"] | None = None, + **kwargs, +) -> Text: + return gca().set_ylabel( + ylabel, fontdict=fontdict, labelpad=labelpad, loc=loc, **kwargs + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.text) -def text(x, y, s, fontdict=None, **kwargs): - return gca().text(x, y, s, fontdict=fontdict, **kwargs) +@_copy_docstring_and_deprecators(Axes.set_xscale) +@set_category(CATEGORY_CUT) +def xscale(value: str | ScaleBase, **kwargs) -> None: + gca().set_xscale(value, **kwargs) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.set_yscale) +@set_category(CATEGORY_CUT) +def yscale(value: str | ScaleBase, **kwargs) -> None: + gca().set_yscale(value, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.annotate) def annotate( - text, xy, xytext=None, xycoords='data', textcoords=None, - arrowprops=None, annotation_clip=None, **kwargs): + text: str, + xy: tuple[float, float], + xytext: tuple[float, float] | None = None, + xycoords: ( + str + | Artist + | Transform + | Callable[[RendererBase], Bbox | Transform] + | tuple[float, float] + ) = "data", + textcoords: ( + str + | Artist + | Transform + | Callable[[RendererBase], Bbox | Transform] + | tuple[float, float] + | None + ) = None, + arrowprops: dict[str, Any] | None = None, + annotation_clip: bool | None = None, + **kwargs, +) -> Annotation: return gca().annotate( - text, xy, xytext=xytext, xycoords=xycoords, - textcoords=textcoords, arrowprops=arrowprops, - annotation_clip=annotation_clip, **kwargs) + text, + xy, + xytext=xytext, + xycoords=xycoords, + textcoords=textcoords, + arrowprops=arrowprops, + annotation_clip=annotation_clip, + **kwargs, + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.ticklabel_format) -def ticklabel_format( - *, axis='both', style='', scilimits=None, useOffset=None, - useLocale=None, useMathText=None): - return gca().ticklabel_format( - axis=axis, style=style, scilimits=scilimits, - useOffset=useOffset, useLocale=useLocale, - useMathText=useMathText) +@_copy_docstring_and_deprecators(Axes.autoscale) +def autoscale( + enable: bool = True, + axis: Literal["both", "x", "y"] = "both", + tight: bool | None = None, +) -> None: + gca().autoscale(enable=enable, axis=axis, tight=tight) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.locator_params) -def locator_params(axis='both', tight=None, **kwargs): - return gca().locator_params(axis=axis, tight=tight, **kwargs) +@_copy_docstring_and_deprecators(Axes.axis) +def axis( + arg: tuple[float, float, float, float] | bool | str | None = None, + /, + *, + emit: bool = True, + **kwargs, +) -> tuple[float, float, float, float]: + return gca().axis(arg, emit=emit, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.tick_params) -def tick_params(axis='both', **kwargs): - return gca().tick_params(axis=axis, **kwargs) +@_copy_docstring_and_deprecators(Axes.grid) +def grid( + visible: bool | None = None, + which: Literal["major", "minor", "both"] = "major", + axis: Literal["both", "x", "y"] = "both", + **kwargs, +) -> None: + gca().grid(visible=visible, which=which, axis=axis, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.margins) -def margins(*margins, x=None, y=None, tight=True): - return gca().margins(*margins, x=x, y=y, tight=tight) +@_copy_docstring_and_deprecators(Axes.legend) +def legend(*args, **kwargs) -> Legend: + return gca().legend(*args, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.autoscale) -def autoscale(enable=True, axis='both', tight=None): - return gca().autoscale(enable=enable, axis=axis, tight=tight) +@_copy_docstring_and_deprecators(Axes.locator_params) +def locator_params( + axis: Literal["both", "x", "y"] = "both", tight: bool | None = None, **kwargs +) -> None: + gca().locator_params(axis=axis, tight=tight, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.axis) -def axis(arg=None, /, *, emit=True, **kwargs): - return gca().axis(arg, emit=emit, **kwargs) +@_copy_docstring_and_deprecators(Axes.margins) +def margins( + *margins: float, + x: float | None = None, + y: float | None = None, + tight: bool | None = True, +) -> tuple[float, float] | None: + return gca().margins(*margins, x=x, y=y, tight=tight) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.minorticks_off) -def minorticks_off(): - return gca().minorticks_off() +def minorticks_off() -> None: + gca().minorticks_off() # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.minorticks_on) -def minorticks_on(): - return gca().minorticks_on() +def minorticks_on() -> None: + gca().minorticks_on() + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.table) +def table( + cellText=None, + cellColours=None, + cellLoc="right", + colWidths=None, + rowLabels=None, + rowColours=None, + rowLoc="left", + colLabels=None, + colColours=None, + colLoc="center", + loc="bottom", + bbox=None, + edges="closed", + **kwargs, +): + return gca().table( + cellText=cellText, + cellColours=cellColours, + cellLoc=cellLoc, + colWidths=colWidths, + rowLabels=rowLabels, + rowColours=rowColours, + rowLoc=rowLoc, + colLabels=colLabels, + colColours=colColours, + colLoc=colLoc, + loc=loc, + bbox=bbox, + edges=edges, + **kwargs, + ) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.text) +def text( + x: float, y: float, s: str, fontdict: dict[str, Any] | None = None, **kwargs +) -> Text: + return gca().text(x, y, s, fontdict=fontdict, **kwargs) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.ticklabel_format) +def ticklabel_format( + *, + axis: Literal["both", "x", "y"] = "both", + style: Literal["", "sci", "scientific", "plain"] = "", + scilimits: tuple[int, int] | None = None, + useOffset: bool | float | None = None, + useLocale: bool | None = None, + useMathText: bool | None = None, +) -> None: + gca().ticklabel_format( + axis=axis, + style=style, + scilimits=scilimits, + useOffset=useOffset, + useLocale=useLocale, + useMathText=useMathText, + ) + + +# Autogenerated by boilerplate.py. Do not edit as changes will be lost. +@_copy_docstring_and_deprecators(Axes.tick_params) +def tick_params(axis: Literal["both", "x", "y"] = "both", **kwargs) -> None: + gca().tick_params(axis=axis, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes._sci) -def sci(im): - return gca()._sci(im) +def sci(im: ScalarMappable) -> None: + gca()._sci(im) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.set_title) -def title(label, fontdict=None, loc=None, pad=None, *, y=None, **kwargs): - return gca().set_title( - label, fontdict=fontdict, loc=loc, pad=pad, y=y, **kwargs) +def title( + label: str, + fontdict: dict[str, Any] | None = None, + loc: Literal["left", "center", "right"] | None = None, + pad: float | None = None, + *, + y: float | None = None, + **kwargs, +) -> Text: + return gca().set_title(label, fontdict=fontdict, loc=loc, pad=pad, y=y, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.set_xlabel) -def xlabel(xlabel, fontdict=None, labelpad=None, *, loc=None, **kwargs): +def xlabel( + xlabel: str, + fontdict: dict[str, Any] | None = None, + labelpad: float | None = None, + *, + loc: Literal["left", "center", "right"] | None = None, + **kwargs, +) -> Text: return gca().set_xlabel( - xlabel, fontdict=fontdict, labelpad=labelpad, loc=loc, - **kwargs) + xlabel, fontdict=fontdict, labelpad=labelpad, loc=loc, **kwargs + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.set_ylabel) -def ylabel(ylabel, fontdict=None, labelpad=None, *, loc=None, **kwargs): +def ylabel( + ylabel: str, + fontdict: dict[str, Any] | None = None, + labelpad: float | None = None, + *, + loc: Literal["bottom", "center", "top"] | None = None, + **kwargs, +) -> Text: return gca().set_ylabel( - ylabel, fontdict=fontdict, labelpad=labelpad, loc=loc, - **kwargs) + ylabel, fontdict=fontdict, labelpad=labelpad, loc=loc, **kwargs + ) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.set_xscale) -def xscale(value, **kwargs): - return gca().set_xscale(value, **kwargs) +def xscale(value: str | ScaleBase, **kwargs) -> None: + gca().set_xscale(value, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. @_copy_docstring_and_deprecators(Axes.set_yscale) -def yscale(value, **kwargs): - return gca().set_yscale(value, **kwargs) +def yscale(value: str | ScaleBase, **kwargs) -> None: + gca().set_yscale(value, **kwargs) # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def autumn(): +def autumn() -> None: """ Set the colormap to 'autumn'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('autumn') + set_cmap("autumn") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def bone(): +def bone() -> None: """ Set the colormap to 'bone'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('bone') + set_cmap("bone") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def cool(): +def cool() -> None: """ Set the colormap to 'cool'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('cool') + set_cmap("cool") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def copper(): +def copper() -> None: """ Set the colormap to 'copper'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('copper') + set_cmap("copper") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def flag(): +def flag() -> None: """ Set the colormap to 'flag'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('flag') + set_cmap("flag") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def gray(): +def gray() -> None: """ Set the colormap to 'gray'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('gray') + set_cmap("gray") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def hot(): +def hot() -> None: """ Set the colormap to 'hot'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('hot') + set_cmap("hot") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def hsv(): +def hsv() -> None: """ Set the colormap to 'hsv'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('hsv') + set_cmap("hsv") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def jet(): +def jet() -> None: """ Set the colormap to 'jet'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('jet') + set_cmap("jet") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def pink(): +def pink() -> None: """ Set the colormap to 'pink'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('pink') + set_cmap("pink") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def prism(): +def prism() -> None: """ Set the colormap to 'prism'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('prism') + set_cmap("prism") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def spring(): +def spring() -> None: """ Set the colormap to 'spring'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('spring') + set_cmap("spring") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def summer(): +def summer() -> None: """ Set the colormap to 'summer'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('summer') + set_cmap("summer") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def winter(): +def winter() -> None: """ Set the colormap to 'winter'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('winter') + set_cmap("winter") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def magma(): +def magma() -> None: """ Set the colormap to 'magma'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('magma') + set_cmap("magma") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def inferno(): +def inferno() -> None: """ Set the colormap to 'inferno'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('inferno') + set_cmap("inferno") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def plasma(): +def plasma() -> None: """ Set the colormap to 'plasma'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('plasma') + set_cmap("plasma") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def viridis(): +def viridis() -> None: """ Set the colormap to 'viridis'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('viridis') + set_cmap("viridis") # Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def nipy_spectral(): +def nipy_spectral() -> None: """ Set the colormap to 'nipy_spectral'. This changes the default colormap as well as the colormap of the current image if there is one. See ``help(colormaps)`` for more information. """ - set_cmap('nipy_spectral') + set_cmap("nipy_spectral") diff --git a/tools/boilerplate.py b/tools/boilerplate.py index 8903c868..1de03376 100644 --- a/tools/boilerplate.py +++ b/tools/boilerplate.py @@ -1,8 +1,6 @@ """ Script to autogenerate pyplot wrappers. -This script copies the approach take by matplotlib.pyplot to generate the pyplot api. - When this script is run, the current contents of pyplot are split into generatable and non-generatable content (via the magic header :attr:`PYPLOT_MAGIC_HEADER`) and the generatable content is overwritten. @@ -15,12 +13,15 @@ # runtime with the proper signatures, a static pyplot.py is simpler for static # analysis tools to parse. +import ast from enum import Enum +import functools import inspect from inspect import Parameter from pathlib import Path import sys -import textwrap +import subprocess + # This line imports the installed copy of matplotlib, and not the local copy. import numpy as np @@ -74,23 +75,23 @@ def {name}{signature}: @_copy_docstring_and_deprecators(Axes.{called_name}) @set_category(CATEGORY_CUT) def {name}{signature}: - return gca().{called_name}{call} + {return_statement}gca().{called_name}{call} """ MISC_METHOD_TEMPLATE = AUTOGEN_MSG + """ @_copy_docstring_and_deprecators(Axes.{called_name}) def {name}{signature}: - return gca().{called_name}{call} + {return_statement}gca().{called_name}{call} """ FIGURE_METHOD_TEMPLATE = AUTOGEN_MSG + """ @_copy_docstring_and_deprecators(Figure.{called_name}) def {name}{signature}: - return gcf().{called_name}{call} + {return_statement}gcf().{called_name}{call} """ CMAP_TEMPLATE = ''' -def {name}(): +def {name}() -> None: """ Set the colormap to {name!r}. @@ -127,6 +128,17 @@ def __repr__(self): return self._repr +class direct_repr: + """ + A placeholder class to destringify annotations from ast + """ + def __init__(self, value): + self._repr = value + + def __repr__(self): + return self._repr + + def generate_function(name, called_fullname, template, **kwargs): """ Create a wrapper function *pyplot_name* calling *call_name*. @@ -149,10 +161,6 @@ def generate_function(name, called_fullname, template, **kwargs): **kwargs Additional parameters are passed to ``template.format()``. """ - text_wrapper = textwrap.TextWrapper( - break_long_words=False, width=70, - initial_indent=' ' * 8, subsequent_indent=' ' * 8) - # Get signature of wrapped function. class_name, called_name = called_fullname.split('.') class_ = {'Axes': Axes, 'Figure': Figure}[class_name] @@ -163,16 +171,17 @@ def generate_function(name, called_fullname, template, **kwargs): # redecorated with make_keyword_only by _copy_docstring_and_deprecators. if decorator and decorator.func is _api.make_keyword_only: meth = meth.__wrapped__ - signature = inspect.signature(meth) + + annotated_trees = get_ast_mro_trees(class_) + signature = get_matching_signature(meth, annotated_trees) + # Replace self argument. params = list(signature.parameters.values())[1:] + has_return_value = str(signature.return_annotation) != 'None' signature = str(signature.replace(parameters=[ param.replace(default=value_formatter(param.default)) if param.default is not param.empty else param for param in params])) - if len('def ' + name + signature) >= 80: - # Move opening parenthesis before newline. - signature = '(\n' + text_wrapper.fill(signature).replace('(', '', 1) # How to call the wrapped function. call = '(' + ', '.join(( # Pass "intended-as-positional" parameters positionally to avoid @@ -184,9 +193,6 @@ def generate_function(name, called_fullname, template, **kwargs): # Only pass the data kwarg if it is actually set, to avoid forcing # third-party subclasses to support it. '**({{"data": data}} if data is not None else {{}})' - # Avoid linebreaks in the middle of the expression, by using \0 as a - # placeholder that will be substituted after wrapping. - .replace(' ', '\0') if param.name == "data" else '{0}={0}' if param.kind in [ @@ -200,9 +206,7 @@ def generate_function(name, called_fullname, template, **kwargs): if param.kind is Parameter.VAR_KEYWORD else None).format(param.name) for param in params) + ')' - MAX_CALL_PREFIX = 18 # len(' __ret = gca().') - if MAX_CALL_PREFIX + max(len(name), len(called_name)) + len(call) >= 80: - call = '(\n' + text_wrapper.fill(call[1:]).replace('\0', ' ') + return_statement = 'return ' if has_return_value else '' # Bail out in case of name collision. for reserved in ('gca', 'gci', 'gcf', '__ret'): if reserved in params: @@ -214,6 +218,7 @@ def generate_function(name, called_fullname, template, **kwargs): called_name=called_name, signature=signature, call=call, + return_statement=return_statement, **kwargs) @@ -231,22 +236,20 @@ def boilerplate_gen(): 'tight_layout', 'waitforbuttonpress', ) - _misc_commands = ( - 'cla', + 'annotate', + 'autoscale', + 'axis', 'grid', 'legend', - 'table', - 'text', - 'annotate', - 'ticklabel_format', 'locator_params', - 'tick_params', 'margins', - 'autoscale', - 'axis', 'minorticks_off', 'minorticks_on', + 'table', + 'text', + 'ticklabel_format', + 'tick_params', # pyplot name : real name 'sci:_sci', 'title:set_title', @@ -255,14 +258,16 @@ def boilerplate_gen(): 'xscale:set_xscale', 'yscale:set_yscale', ) - # These methods are all simple wrappers of Axes methods by the same name. _axes_commands = ( 'acorr', 'angle_spectrum', + 'annotate', 'arrow', + 'autoscale', 'axhline', 'axhspan', + 'axis', 'axline', 'axvline', 'axvspan', @@ -277,19 +282,26 @@ def boilerplate_gen(): 'contour', 'contourf', 'csd', + 'ecdf', 'errorbar', 'eventplot', 'fill', 'fill_between', 'fill_betweenx', + 'grid', 'hexbin', 'hist', 'stairs', 'hist2d', 'hlines', 'imshow', + 'legend', + 'locator_params', 'loglog', 'magnitude_spectrum', + 'margins', + 'minorticks_off', + 'minorticks_on', 'pcolor', 'pcolormesh', 'phase_spectrum', @@ -308,6 +320,10 @@ def boilerplate_gen(): 'stem', 'step', 'streamplot', + 'table', + 'text', + 'tick_params', + 'ticklabel_format', 'tricontour', 'tricontourf', 'tripcolor', @@ -315,11 +331,24 @@ def boilerplate_gen(): 'violinplot', 'vlines', 'xcorr', + # pyplot name : real name + 'sci:_sci', + 'title:set_title', + 'xlabel:set_xlabel', + 'ylabel:set_ylabel', + 'xscale:set_xscale', + 'yscale:set_yscale', ) cmappable = { - 'contour': 'if __ret._A is not None: sci(__ret) # noqa', - 'contourf': 'if __ret._A is not None: sci(__ret) # noqa', + 'contour': ( + 'if __ret._A is not None: # type: ignore[attr-defined]\n' + ' sci(__ret)' + ), + 'contourf': ( + 'if __ret._A is not None: # type: ignore[attr-defined]\n' + ' sci(__ret)' + ), 'hexbin': 'sci(__ret)', 'scatter': 'sci(__ret)', 'pcolor': 'sci(__ret)', @@ -330,8 +359,14 @@ def boilerplate_gen(): 'quiver': 'sci(__ret)', 'specgram': 'sci(__ret[-1])', 'streamplot': 'sci(__ret.lines)', - 'tricontour': 'if __ret._A is not None: sci(__ret) # noqa', - 'tricontourf': 'if __ret._A is not None: sci(__ret) # noqa', + 'tricontour': ( + 'if __ret._A is not None: # type: ignore[attr-defined]\n' + ' sci(__ret)' + ), + 'tricontourf': ( + 'if __ret._A is not None: # type: ignore[attr-defined]\n' + ' sci(__ret)' + ), 'tripcolor': 'sci(__ret)', } @@ -401,6 +436,80 @@ def build_pyplot(pyplot_path): pyplot.writelines(pyplot_orig) pyplot.writelines(boilerplate_gen()) + # Run black to autoformat pyplot + subprocess.run( + [sys.executable, "-m", "black", "--line-length=88", pyplot_path], + check=True + ) + + +### Methods for retrieving signatures from pyi stub files + +def get_ast_tree(cls): + path = Path(inspect.getfile(cls)) + stubpath = path.with_suffix(".pyi") + path = stubpath if stubpath.exists() else path + tree = ast.parse(path.read_text()) + for item in tree.body: + if isinstance(item, ast.ClassDef) and item.name == cls.__name__: + return item + raise ValueError(f"Cannot find {cls.__name__} in ast") + + +@functools.lru_cache +def get_ast_mro_trees(cls): + return [get_ast_tree(c) for c in cls.__mro__ if c.__module__ != "builtins"] + + +def get_matching_signature(method, trees): + sig = inspect.signature(method) + for tree in trees: + for item in tree.body: + if not isinstance(item, ast.FunctionDef): + continue + if item.name == method.__name__: + return update_sig_from_node(item, sig) + # The following methods are implemented outside of the mro of Axes + # and thus do not get their annotated versions found with current code + # stackplot + # streamplot + # table + # tricontour + # tricontourf + # tripcolor + # triplot + + # import warnings + # warnings.warn(f"'{method.__name__}' not found") + return sig + + +def update_sig_from_node(node, sig): + params = dict(sig.parameters) + args = node.args + allargs = ( + *args.posonlyargs, + *args.args, + args.vararg, + *args.kwonlyargs, + args.kwarg, + ) + for param in allargs: + if param is None: + continue + if param.annotation is None: + continue + annotation = direct_repr(ast.unparse(param.annotation)) + params[param.arg] = params[param.arg].replace(annotation=annotation) + + if node.returns is not None: + return inspect.Signature( + params.values(), + return_annotation=direct_repr(ast.unparse(node.returns)) + ) + else: + return inspect.Signature(params.values()) + if __name__ == '__main__': # Write the matplotlib.pyplot file.