From 9c4c9a9ddd78d11f74ab55601d2f845619edb78d Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:50:07 +0200 Subject: [PATCH] Backport PR #31920: Fix various small type hint issues --- ci/mypy-stubtest-allowlist.txt | 1 - lib/matplotlib/artist.pyi | 4 ++-- lib/matplotlib/axes/_base.pyi | 2 +- lib/matplotlib/backends/registry.pyi | 2 +- lib/matplotlib/colorbar.pyi | 7 +++--- lib/matplotlib/font_manager.pyi | 2 +- lib/matplotlib/image.pyi | 2 +- lib/matplotlib/rcsetup.pyi | 8 ++++++- lib/matplotlib/testing/compare.pyi | 5 +++-- lib/matplotlib/transforms.py | 2 +- lib/matplotlib/transforms.pyi | 30 +++++++++++++++++++++++--- lib/matplotlib/typing.py | 32 ++++++++++++++++------------ 12 files changed, 66 insertions(+), 31 deletions(-) diff --git a/ci/mypy-stubtest-allowlist.txt b/ci/mypy-stubtest-allowlist.txt index 0e199889cb07..5bb4f0d36c5e 100644 --- a/ci/mypy-stubtest-allowlist.txt +++ b/ci/mypy-stubtest-allowlist.txt @@ -3,7 +3,6 @@ matplotlib\.backends\..* matplotlib\.tests(\..*)? matplotlib\.pylab(\..*)? matplotlib\._.* -matplotlib\.rcsetup\._listify_validator matplotlib\.rcsetup\._validate_linestyle matplotlib\.ft2font\.Glyph matplotlib\.ft2font\.LayoutItem diff --git a/lib/matplotlib/artist.pyi b/lib/matplotlib/artist.pyi index 7b8b0c36be69..c70a9ac750fc 100644 --- a/lib/matplotlib/artist.pyi +++ b/lib/matplotlib/artist.pyi @@ -23,8 +23,8 @@ _T_Artist = TypeVar("_T_Artist", bound=Artist) def allow_rasterization(draw): ... class _XYPair(NamedTuple): - x: ArrayLike - y: ArrayLike + x: list[float] + y: list[float] class Artist: zorder: float diff --git a/lib/matplotlib/axes/_base.pyi b/lib/matplotlib/axes/_base.pyi index 4a70405346a5..f90ddc45f347 100644 --- a/lib/matplotlib/axes/_base.pyi +++ b/lib/matplotlib/axes/_base.pyi @@ -67,7 +67,7 @@ class _AxesBase(martist.Artist): def __init__( self, - fig: Figure, + fig: Figure | SubFigure, *args: tuple[float, float, float, float] | Bbox | int, facecolor: ColorType | None = ..., frameon: bool = ..., diff --git a/lib/matplotlib/backends/registry.pyi b/lib/matplotlib/backends/registry.pyi index 565f044bf212..d1ba09cb523b 100644 --- a/lib/matplotlib/backends/registry.pyi +++ b/lib/matplotlib/backends/registry.pyi @@ -25,7 +25,7 @@ class BackendRegistry: def backend_for_gui_framework(self, framework: str) -> str | None: ... def is_valid_backend(self, backend: str) -> bool: ... def list_all(self) -> list[str]: ... - def list_builtin(self, filter_: BackendFilter | None) -> list[str]: ... + def list_builtin(self, filter_: BackendFilter | None = ...) -> list[str]: ... def list_gui_frameworks(self) -> list[str]: ... def load_backend_module(self, backend: str) -> ModuleType: ... def resolve_backend(self, backend: str | None) -> tuple[str, str | None]: ... diff --git a/lib/matplotlib/colorbar.pyi b/lib/matplotlib/colorbar.pyi index 07467ca74f3d..33a63ddd5335 100644 --- a/lib/matplotlib/colorbar.pyi +++ b/lib/matplotlib/colorbar.pyi @@ -13,11 +13,12 @@ from collections.abc import Sequence from typing import Any, Literal, overload from .typing import ColorType -class _ColorbarSpine(mspines.Spines): + +class _ColorbarSpine(mspines.Spine): def __init__(self, axes: Axes): ... - def get_window_extent(self, renderer: RendererBase | None = ...) -> Bbox:... + def get_window_extent(self, renderer: RendererBase | None = ...) -> Bbox: ... def set_xy(self, xy: ArrayLike) -> None: ... - def draw(self, renderer: RendererBase | None) -> None:... + def draw(self, renderer: RendererBase | None) -> None: ... class Colorbar: diff --git a/lib/matplotlib/font_manager.pyi b/lib/matplotlib/font_manager.pyi index 4bb1a8bae2a9..45cafeb23e3f 100644 --- a/lib/matplotlib/font_manager.pyi +++ b/lib/matplotlib/font_manager.pyi @@ -147,7 +147,7 @@ class FontManager: rebuild_if_missing: bool = ..., ) -> list[FontPath]: ... -def is_opentype_cff_font(filename: str) -> bool: ... +def is_opentype_cff_font(filename: str | os.PathLike) -> bool: ... def get_font( font_filepaths: Iterable[str | bytes | os.PathLike | FontPath] | str | bytes | os.PathLike | FontPath, ) -> ft2font.FT2Font: ... diff --git a/lib/matplotlib/image.pyi b/lib/matplotlib/image.pyi index 1fcc1a710bfd..4c2a8fbdb0e6 100644 --- a/lib/matplotlib/image.pyi +++ b/lib/matplotlib/image.pyi @@ -84,7 +84,7 @@ class _ImageBase(colorizer.ColorizingArtist): self, renderer: RendererBase, magnification: float = ..., unsampled: bool = ... ) -> tuple[np.ndarray, float, float, Affine2D]: ... def draw(self, renderer: RendererBase) -> None: ... - def write_png(self, fname: str | pathlib.Path | BinaryIO) -> None: ... + def write_png(self, fname: str | os.PathLike | BinaryIO) -> None: ... def set_data(self, A: ArrayLike | None) -> None: ... def set_array(self, A: ArrayLike | None) -> None: ... def get_shape(self) -> tuple[int, int, int]: ... diff --git a/lib/matplotlib/rcsetup.pyi b/lib/matplotlib/rcsetup.pyi index 120c0c93bec9..79f96430107a 100644 --- a/lib/matplotlib/rcsetup.pyi +++ b/lib/matplotlib/rcsetup.pyi @@ -7,7 +7,13 @@ from matplotlib.typing import ColorType, LineStyleType, MarkEveryType _T = TypeVar("_T") -def _listify_validator(s: Callable[[Any], _T]) -> Callable[[Any], list[_T]]: ... +def _listify_validator( + scalar_validator: Callable[[Any], _T], + allow_stringlist: bool = ..., + *, + n: int | None = ..., + doc: str | None = ..., +) -> Callable[[Any], list[_T]]: ... class ValidateInStrings: key: str diff --git a/lib/matplotlib/testing/compare.pyi b/lib/matplotlib/testing/compare.pyi index 8f11b3bebc1a..c673bdad6231 100644 --- a/lib/matplotlib/testing/compare.pyi +++ b/lib/matplotlib/testing/compare.pyi @@ -1,18 +1,19 @@ from collections.abc import Callable +from os import PathLike from typing import Literal, overload from numpy.typing import NDArray __all__ = ["calculate_rms", "comparable_formats", "compare_images"] -def make_test_filename(fname: str, purpose: str) -> str: ... +def make_test_filename(fname: str | PathLike, purpose: str) -> str: ... def get_cache_dir() -> str: ... def get_file_hash(path: str, block_size: int = ...) -> str: ... converter: dict[str, Callable[[str, str], None]] = {} def comparable_formats() -> list[str]: ... -def convert(filename: str, cache: bool) -> str: ... +def convert(filename: str | PathLike, cache: bool) -> str: ... def crop_to_same( actual_path: str, actual_image: NDArray, expected_path: str, expected_image: NDArray ) -> tuple[NDArray, NDArray]: ... diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index a279de0dfd8b..2b46deae43dd 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -156,7 +156,7 @@ def invalidate(self): Invalidate this `TransformNode` and triggers an invalidation of its ancestors. Should be called any time the transform changes. """ - return self._invalidate_internal( + self._invalidate_internal( level=self._INVALID_AFFINE_ONLY if self.is_affine else self._INVALID_FULL, invalidating_node=self) diff --git a/lib/matplotlib/transforms.pyi b/lib/matplotlib/transforms.pyi index ebee3954a3a7..d3cd5f4ab692 100644 --- a/lib/matplotlib/transforms.pyi +++ b/lib/matplotlib/transforms.pyi @@ -9,9 +9,6 @@ from typing import Literal DEBUG: bool class TransformNode: - INVALID_NON_AFFINE: int - INVALID_AFFINE: int - INVALID: int # Implemented as a standard attr in base class, but functionally readonly and some subclasses implement as such @property def is_affine(self) -> bool: ... @@ -130,12 +127,39 @@ class Bbox(BboxBase): updatex: bool = ..., updatey: bool = ..., ) -> None: ... + # Access to the parent class property is broken in mypy right now, so ignore that + # check for the following read/write properties. + # https://github.com/python/mypy/issues/5936 + @BboxBase.x0.setter # type: ignore[attr-defined,misc] + def x0(self, val: float) -> None: ... + @BboxBase.y0.setter # type: ignore[attr-defined,misc] + def y0(self, val: float) -> None: ... + @BboxBase.x1.setter # type: ignore[attr-defined,misc] + def x1(self, val: float) -> None: ... + @BboxBase.y1.setter # type: ignore[attr-defined,misc] + def y1(self, val: float) -> None: ... + @BboxBase.p0.setter # type: ignore[attr-defined,misc] + def p0(self, val: tuple[float, float]) -> None: ... + @BboxBase.p1.setter # type: ignore[attr-defined,misc] + def p1(self, val: tuple[float, float]) -> None: ... + @BboxBase.intervalx.setter # type: ignore[attr-defined,misc] + def intervalx(self, interval: tuple[float, float]) -> None: ... + @BboxBase.intervaly.setter # type: ignore[attr-defined,misc] + def intervaly(self, interval: tuple[float, float]) -> None: ... + @BboxBase.bounds.setter # type: ignore[attr-defined,misc] + def bounds(self, bounds: tuple[float, float, float, float]) -> None: ... @property def minpos(self) -> float: ... + @minpos.setter + def minpos(self, val: float) -> None: ... @property def minposx(self) -> float: ... + @minposx.setter + def minposx(self, val: float) -> None: ... @property def minposy(self) -> float: ... + @minposy.setter + def minposy(self, val: float) -> None: ... def get_points(self) -> np.ndarray: ... def set_points(self, points: ArrayLike) -> None: ... def set(self, other: Bbox) -> None: ... diff --git a/lib/matplotlib/typing.py b/lib/matplotlib/typing.py index 76b5f2b642f3..68325fe284e2 100644 --- a/lib/matplotlib/typing.py +++ b/lib/matplotlib/typing.py @@ -12,7 +12,7 @@ """ from collections.abc import Hashable, Sequence import pathlib -from typing import Any, Literal, TypeAlias, TypeVar, Union +from typing import Any, Literal, TypeAlias, TypeVar from collections.abc import Callable, Mapping from . import path @@ -99,19 +99,14 @@ LogLevel: TypeAlias = Literal["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] """Literal type for valid logging levels accepted by `matplotlib.set_loglevel()`.""" -CoordsBaseType = Union[ - str, - Artist, - Transform, - Callable[ - [RendererBase], - Union[Bbox, Transform] - ] -] -CoordsType = Union[ - CoordsBaseType, - tuple[CoordsBaseType, CoordsBaseType] -] +CoordsBaseType: TypeAlias = ( + str | + Artist | + Transform | + Callable[[RendererBase], Bbox | Transform] +) +CoordsType: TypeAlias = CoordsBaseType | tuple[CoordsBaseType, CoordsBaseType] +"""Annotation coordinate systems. See :doc:`/users/explain/text/annotations`.""" RcStyleType: TypeAlias = ( str | @@ -138,16 +133,22 @@ "axes_enter_event", "axes_leave_event", ] +"""Literal type for valid `.MouseEvent` names.""" KeyEventType: TypeAlias = Literal[ "key_press_event", "key_release_event" ] +"""Literal type for valid `.KeyEvent` names.""" DrawEventType: TypeAlias = Literal["draw_event"] +"""Literal type for valid `.DrawEvent` names.""" PickEventType: TypeAlias = Literal["pick_event"] +"""Literal type for valid `.PickEvent` names.""" ResizeEventType: TypeAlias = Literal["resize_event"] +"""Literal type for valid `.ResizeEvent` names.""" CloseEventType: TypeAlias = Literal["close_event"] +"""Literal type for valid `.CloseEvent` names.""" EventType: TypeAlias = Literal[ MouseEventType, @@ -157,6 +158,7 @@ ResizeEventType, CloseEventType, ] +"""Literal type for all valid events.""" LegendLocType: TypeAlias = ( Literal[ @@ -524,6 +526,7 @@ "ytick.minor.width", "ytick.right", ] +"""Valid specifiers for keys in `matplotlib.rcParams` and `matplotlib.rc_context`.""" RcGroupKeyType: TypeAlias = Literal[ "agg", @@ -590,3 +593,4 @@ "ytick.major", "ytick.minor", ] +"""Literal type for valid groups accepted by `matplotlib.rc`."""