From 91c0f4869d5c527dbef387f72e9aa041ceabebb7 Mon Sep 17 00:00:00 2001 From: N R Navaneet Date: Sat, 12 Jul 2025 21:45:55 +0530 Subject: [PATCH 1/6] `Fix labelcolor='linecolor' handling for artists with facecolor=None` --- lib/matplotlib/legend.py | 10 ++++++---- lib/matplotlib/tests/test_legend.py | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 2fb14e52c58c..91c6b27b9550 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -596,10 +596,12 @@ def __init__( try: color = getattr(handle, getter_name)() if isinstance(color, np.ndarray): - if ( - color.shape[0] == 1 - or np.isclose(color, color[0]).all() - ): + if color.size == 0: + continue + elif ( + color.shape[0] == 1 + or np.isclose(color, color[0]).all() + ): text.set_color(color[0]) else: pass diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 9b100037cc41..21d06ef97592 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -1472,3 +1472,28 @@ def test_boxplot_legend_labels(): bp4 = axs[3].boxplot(data, label='box A') assert bp4['medians'][0].get_label() == 'box A' assert all(x.get_label().startswith("_") for x in bp4['medians'][1:]) + + +def test_labelcolor_linecolor_with_none_facecolor(): + # This test ensures that labelcolor='linecolor' works even when the + # artist has a facecolor of None (fixes issue #30298). + + np.random.seed(0) + x, y = np.random.randn(2, 100) + + fig, axes = plt.subplots(2, 2) + + # Step histogram + axes[0, 0].hist(x, histtype='bar', label="bar") + axes[0, 1].hist(x, histtype='step', label="step") + + # Scatter with default and empty facecolor + axes[1, 0].scatter(x, y, label="scatter-filled") + axes[1, 1].scatter(x, y, ec='C0', fc='None', label="scatter-empty") + + # The test will fail with IndexError unless the bug is fixed + for ax in axes.ravel(): + try: + ax.legend(labelcolor='linecolor') + except Exception as e: + pytest.fail(f"Legend with labelcolor='linecolor' failed: {e}") From 14e0fe04eb0a8e3aab753b3c367cb3b3dbed5379 Mon Sep 17 00:00:00 2001 From: N R Navaneet Date: Sun, 13 Jul 2025 00:29:14 +0530 Subject: [PATCH 2/6] Fix: Improve labelcolor='linecolor' handling for transparent legend colors --- lib/matplotlib/legend.py | 43 +++++++++++++++---------- lib/matplotlib/tests/test_legend.py | 50 ++++++++++++++++------------- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 91c6b27b9550..07df7a5dfb0d 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -576,7 +576,7 @@ def __init__( # set the text color color_getters = { # getter function depends on line or patch - 'linecolor': ['get_color', 'get_facecolor'], + 'linecolor': ['get_facecolor', 'get_edgecolor', 'get_color'], 'markerfacecolor': ['get_markerfacecolor', 'get_facecolor'], 'mfc': ['get_markerfacecolor', 'get_facecolor'], 'markeredgecolor': ['get_markeredgecolor', 'get_edgecolor'], @@ -592,34 +592,43 @@ def __init__( continue except AttributeError: pass + for getter_name in getter_names: try: color = getattr(handle, getter_name)() + if isinstance(color, np.ndarray): if color.size == 0: continue - elif ( - color.shape[0] == 1 - or np.isclose(color, color[0]).all() - ): - text.set_color(color[0]) + if color.shape[0] == 1 or np.isclose(color, color[0]).all(): + rgba = color[0] else: - pass + continue # Gradient or ambiguous color else: - text.set_color(color) + rgba = color + + # Skip if fully transparent (invisible legend text) + if ( + hasattr(rgba, '__getitem__') + and len(rgba) == 4 and rgba[3] == 0 + ): + continue + + text.set_color(rgba) break except AttributeError: pass - elif cbook._str_equal(labelcolor, 'none'): - for text in self.texts: - text.set_color(labelcolor) - elif np.iterable(labelcolor): - for text, color in zip(self.texts, - itertools.cycle( - colors.to_rgba_array(labelcolor))): - text.set_color(color) else: - raise ValueError(f"Invalid labelcolor: {labelcolor!r}") + if cbook._str_equal(labelcolor, 'none'): + for text in self.texts: + text.set_color(labelcolor) + elif np.iterable(labelcolor): + for text, color in zip(self.texts, + itertools.cycle(colors.to_rgba_array(labelcolor))): + text.set_color(color) + else: + for text in self.texts: + text.set_color(labelcolor) def _set_artist_props(self, a): """ diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 21d06ef97592..c25f112d134f 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -22,6 +22,9 @@ import matplotlib.legend as mlegend from matplotlib import rc_context from matplotlib.font_manager import FontProperties +import matplotlib.colors as mcolors + + def test_legend_ordereddict(): @@ -1474,26 +1477,29 @@ def test_boxplot_legend_labels(): assert all(x.get_label().startswith("_") for x in bp4['medians'][1:]) -def test_labelcolor_linecolor_with_none_facecolor(): - # This test ensures that labelcolor='linecolor' works even when the - # artist has a facecolor of None (fixes issue #30298). - - np.random.seed(0) - x, y = np.random.randn(2, 100) - +def test_legend_labelcolor_linecolor_default_artists(): fig, axes = plt.subplots(2, 2) - - # Step histogram - axes[0, 0].hist(x, histtype='bar', label="bar") - axes[0, 1].hist(x, histtype='step', label="step") - - # Scatter with default and empty facecolor - axes[1, 0].scatter(x, y, label="scatter-filled") - axes[1, 1].scatter(x, y, ec='C0', fc='None', label="scatter-empty") - - # The test will fail with IndexError unless the bug is fixed - for ax in axes.ravel(): - try: - ax.legend(labelcolor='linecolor') - except Exception as e: - pytest.fail(f"Legend with labelcolor='linecolor' failed: {e}") + x = np.random.randn(1000) + y = np.random.randn(1000) + + # Top Left: Filled Histogram (default color C0) + axes[0, 0].hist(x, histtype='bar', label="spam") + leg00 = axes[0, 0].legend(labelcolor='linecolor') + assert np.allclose(leg00.get_texts()[0].get_color()[:3], mcolors.to_rgb('C0')) + + # Top Right: Step Histogram (default color C0) + axes[0, 1].hist(x, histtype='step', label="spam") + leg01 = axes[0, 1].legend(labelcolor='linecolor') + assert np.allclose(leg01.get_texts()[0].get_color()[:3], mcolors.to_rgb('C0')) + + # Bottom Left: Scatter (filled, default color C0) + axes[1, 0].scatter(x, y, label="spam") + leg10 = axes[1, 0].legend(labelcolor='linecolor') + assert np.allclose(leg10.get_texts()[0].get_color()[:3], mcolors.to_rgb('C0')) + + # Bottom Right: Scatter (outline, default edge color C0) + axes[1, 1].scatter(x, y, label="spam") + leg11 = axes[1, 1].legend(labelcolor='linecolor') + assert np.allclose(leg11.get_texts()[0].get_color()[:3], mcolors.to_rgb('C0')) + + plt.close(fig) From adf6f860e75df38d566de5389b0e7d7d010722f7 Mon Sep 17 00:00:00 2001 From: N R Navaneet Date: Sun, 13 Jul 2025 03:46:42 +0530 Subject: [PATCH 3/6] `Update matplotlib typing and rcParams for improved type hinting` `Added type hints for LogLevel, RcGroupType, and RcParamKeyType in matplotlib/typing.py` `Updated type hints for set_loglevel, rc, and rcParams in matplotlib --- lib/matplotlib/__init__.pyi | 8 ++- lib/matplotlib/pyplot.py | 4 +- lib/matplotlib/typing.py | 24 +++++++++ tools/update_rcparam_typing.py | 89 ++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 tools/update_rcparam_typing.py diff --git a/lib/matplotlib/__init__.pyi b/lib/matplotlib/__init__.pyi index 07019109f406..b7f55f560fd1 100644 --- a/lib/matplotlib/__init__.pyi +++ b/lib/matplotlib/__init__.pyi @@ -39,8 +39,10 @@ import contextlib from packaging.version import Version from matplotlib._api import MatplotlibDeprecationWarning +from matplotlib.typing import LogLevel, RcGroupType, RcParamKeyType from typing import Any, Literal, NamedTuple, overload + class _VersionInfo(NamedTuple): major: int minor: int @@ -52,7 +54,7 @@ __bibtex__: str __version__: str __version_info__: _VersionInfo -def set_loglevel(level: str) -> None: ... +def set_loglevel(level: LogLevel) -> None: ... class _ExecInfo(NamedTuple): executable: str @@ -78,6 +80,8 @@ class RcParams(dict[str, Any]): def _ensure_has_backend(self) -> None: ... def __setitem__(self, key: str, val: Any) -> None: ... def __getitem__(self, key: str) -> Any: ... + def get_typed(self, key: RcParamKeyType) -> Any: ... + def set_typed(self, key: RcParamKeyType, val: Any) -> None: ... def __iter__(self) -> Generator[str, None, None]: ... def __len__(self) -> int: ... def find_all(self, pattern: str) -> RcParams: ... @@ -95,7 +99,7 @@ rcParams: RcParams rcParamsOrig: RcParams defaultParams: dict[str, Any] -def rc(group: str, **kwargs) -> None: ... +def rc(group: RcGroupType, **kwargs) -> None: ... def rcdefaults() -> None: ... def rc_file_defaults() -> None: ... def rc_file( diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index d77b06115268..09c279ad8a49 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -131,6 +131,7 @@ HashableList, LineStyleType, MarkerType, + RcGroupType ) from matplotlib.widgets import SubplotTool @@ -773,10 +774,9 @@ def pause(interval: float) -> None: @_copy_docstring_and_deprecators(matplotlib.rc) -def rc(group: str, **kwargs) -> None: +def rc(group: RcGroupType, **kwargs) -> None: matplotlib.rc(group, **kwargs) - @_copy_docstring_and_deprecators(matplotlib.rc_context) def rc_context( rc: dict[str, Any] | None = None, diff --git a/lib/matplotlib/typing.py b/lib/matplotlib/typing.py index df192df76b33..feed42b74bd8 100644 --- a/lib/matplotlib/typing.py +++ b/lib/matplotlib/typing.py @@ -77,6 +77,30 @@ FillStyleType: TypeAlias = Literal["full", "left", "right", "bottom", "top", "none"] """Marker fill styles. See :doc:`/gallery/lines_bars_and_markers/marker_reference`.""" +LogLevel: TypeAlias = Literal["notset", "debug", "info", "warning", "error", "critical"] +"""Valid string values for logging levels used in `set_loglevel`.""" + +RcGroupType: TypeAlias = Literal[ + "animation", "axes", "axes3d", "backend", "boxplot", "contour", "date", + "errorbar", "figure", "font", "grid", "hatch", "image", "interactive", + "keymap", "legend", "lines", "markers", "patch", "path", "pdf", "pgf", "ps", + "savefig", "scatter", "text", "tk", "toolbar", "xtick", "ytick" +] +"""Valid values for the `group` parameter in `rc(group=...)`.""" + +# --- START GENERATED RcParamKeyType --- +RcParamKeyType: TypeAlias = Literal[ + "_internal.classic_mode", + +] +# --- END GENERATED RcParamKeyType --- +"""The keys are extracted directly from the matplotlib runtime via: + `generate_rc_keys_once.py` +To regenerate this block: +1. Run `python tools/update_rc_keys_once.py` +2. Replace the content between the START and END markers below +""" + JoinStyleType: TypeAlias = JoinStyle | Literal["miter", "round", "bevel"] """Line join styles. See :doc:`/gallery/lines_bars_and_markers/joinstyle`.""" diff --git a/tools/update_rcparam_typing.py b/tools/update_rcparam_typing.py new file mode 100644 index 000000000000..9f6bef1299b4 --- /dev/null +++ b/tools/update_rcparam_typing.py @@ -0,0 +1,89 @@ +""" +Script to generate and update the `RcParamKeyType` Literal type alias in +the `matplotlib.typing` module based on the current set of rcParams keys. + +This automates keeping the typing definitions up to date with Matplotlib's +runtime rcParams keys, improving type safety and IDE autocomplete. + +Note: + This script overwrites the block between markers: + # --- START GENERATED RcParamKeyType --- + and + # --- END GENERATED RcParamKeyType --- + in the typing file with the newly generated Literal block. +""" + +import matplotlib +matplotlib.use("Agg") # Use non-GUI backend to avoid GUI errors during import +import re + +# Path to the typing file to update — adjust if your directory structure differs +typing_file = "lib/matplotlib/typing.py" + +def generate_rcparamkeytype_literal(keys: list[str]) -> str: + """ + Generate the Literal block string for RcParamKeyType with the given keys. + + Parameters + ---------- + keys : list[str] + Sorted list of rcParams keys as strings. + + Returns + ------- + str + A formatted string defining RcParamKeyType as a typing.Literal of keys, + wrapped with start and end generation markers. + """ + keys_str = ",\n ".join(f'"{key}"' for key in keys) + return ( + "# --- START GENERATED RcParamKeyType ---\n" + "RcParamKeyType: TypeAlias = Literal[\n" + f" {keys_str},\n" + "]\n" + "# --- END GENERATED RcParamKeyType ---" + ) + +def update_typing_file(path: str, new_block: str) -> None: + """ + Replace the existing RcParamKeyType block in the typing file with new_block. + + Parameters + ---------- + path : str + File path of the typing file to update. + + new_block : str + The new Literal block string to insert. + + Raises + ------ + RuntimeError + If the start and end markers are not found in the file. + """ + with open(path, "r", encoding="utf-8") as f: + content = f.read() + + pattern = re.compile( + r"# --- START GENERATED RcParamKeyType ---\n.*?# --- END GENERATED RcParamKeyType ---", + re.DOTALL, + ) + + new_content, count = pattern.subn(new_block, content) + if count == 0: + raise RuntimeError(f"Markers not found in {path}. Please ensure the markers exist.") + + with open(path, "w", encoding="utf-8") as f: + f.write(new_content) + +if __name__ == "__main__": + # Retrieve and sort rcParams keys from Matplotlib runtime + rc_keys = sorted(matplotlib.rcParams.keys()) + + # Generate new Literal block + new_block = generate_rcparamkeytype_literal(rc_keys) + + # Update typing.py file in place + update_typing_file(typing_file, new_block) + + print(f"Updated {typing_file} with {len(rc_keys)} RcParamKeyType keys.") From 7dc5eaa358f8fbe1a7954b17a10ebe363b48d843 Mon Sep 17 00:00:00 2001 From: N R Navaneet Date: Sun, 13 Jul 2025 04:05:41 +0530 Subject: [PATCH 4/6] `Update matplotlib typing definitions to remove RcGroupType and RcParamKeyType` `Remove tools/update_rcparam_typing.py script` `Update matplotlib rc function to accept string group instead of RcGroupType` `Remove unused imports and docstrings` --- lib/matplotlib/__init__.pyi | 6 +-- lib/matplotlib/pyplot.py | 55 ++++----------------- lib/matplotlib/typing.py | 20 -------- tools/update_rcparam_typing.py | 89 ---------------------------------- 4 files changed, 11 insertions(+), 159 deletions(-) delete mode 100644 tools/update_rcparam_typing.py diff --git a/lib/matplotlib/__init__.pyi b/lib/matplotlib/__init__.pyi index b7f55f560fd1..0baf9ed18639 100644 --- a/lib/matplotlib/__init__.pyi +++ b/lib/matplotlib/__init__.pyi @@ -39,7 +39,7 @@ import contextlib from packaging.version import Version from matplotlib._api import MatplotlibDeprecationWarning -from matplotlib.typing import LogLevel, RcGroupType, RcParamKeyType +from matplotlib.typing import LogLevel from typing import Any, Literal, NamedTuple, overload @@ -80,8 +80,6 @@ class RcParams(dict[str, Any]): def _ensure_has_backend(self) -> None: ... def __setitem__(self, key: str, val: Any) -> None: ... def __getitem__(self, key: str) -> Any: ... - def get_typed(self, key: RcParamKeyType) -> Any: ... - def set_typed(self, key: RcParamKeyType, val: Any) -> None: ... def __iter__(self) -> Generator[str, None, None]: ... def __len__(self) -> int: ... def find_all(self, pattern: str) -> RcParams: ... @@ -99,7 +97,7 @@ rcParams: RcParams rcParamsOrig: RcParams defaultParams: dict[str, Any] -def rc(group: RcGroupType, **kwargs) -> None: ... +def rc(group: str, **kwargs) -> None: ... def rcdefaults() -> None: ... def rc_file_defaults() -> None: ... def rc_file( diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index afe9060f7817..d77b06115268 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -50,7 +50,7 @@ import sys import threading import time -from typing import IO, TYPE_CHECKING, cast, overload +from typing import TYPE_CHECKING, cast, overload from cycler import cycler # noqa: F401 import matplotlib @@ -100,14 +100,7 @@ import matplotlib.backend_bases from matplotlib.axis import Tick from matplotlib.axes._base import _AxesBase - from matplotlib.backend_bases import ( - CloseEvent, - DrawEvent, - KeyEvent, - MouseEvent, - PickEvent, - ResizeEvent, - ) + from matplotlib.backend_bases import Event from matplotlib.cm import ScalarMappable from matplotlib.contour import ContourSet, QuadContourSet from matplotlib.collections import ( @@ -133,18 +126,11 @@ from matplotlib.quiver import Barbs, Quiver, QuiverKey from matplotlib.scale import ScaleBase from matplotlib.typing import ( - CloseEventType, ColorType, CoordsType, - DrawEventType, HashableList, - KeyEventType, LineStyleType, MarkerType, - MouseEventType, - PickEventType, - ResizeEventType, - RcGroupType, ) from matplotlib.widgets import SubplotTool @@ -352,8 +338,8 @@ def uninstall_repl_displayhook() -> None: # Ensure this appears in the pyplot docs. @_copy_docstring_and_deprecators(matplotlib.set_loglevel) -def set_loglevel(level: str) -> None: - return matplotlib.set_loglevel(level) +def set_loglevel(*args, **kwargs) -> None: + return matplotlib.set_loglevel(*args, **kwargs) @_copy_docstring_and_deprecators(Artist.findobj) @@ -787,9 +773,10 @@ def pause(interval: float) -> None: @_copy_docstring_and_deprecators(matplotlib.rc) -def rc(group: RcGroupType, **kwargs) -> None: +def rc(group: str, **kwargs) -> None: matplotlib.rc(group, **kwargs) + @_copy_docstring_and_deprecators(matplotlib.rc_context) def rc_context( rc: dict[str, Any] | None = None, @@ -1188,32 +1175,8 @@ def get_current_fig_manager() -> FigureManagerBase | None: return gcf().canvas.manager -@overload -def connect(s: MouseEventType, func: Callable[[MouseEvent], Any]) -> int: ... - - -@overload -def connect(s: KeyEventType, func: Callable[[KeyEvent], Any]) -> int: ... - - -@overload -def connect(s: PickEventType, func: Callable[[PickEvent], Any]) -> int: ... - - -@overload -def connect(s: ResizeEventType, func: Callable[[ResizeEvent], Any]) -> int: ... - - -@overload -def connect(s: CloseEventType, func: Callable[[CloseEvent], Any]) -> int: ... - - -@overload -def connect(s: DrawEventType, func: Callable[[DrawEvent], Any]) -> int: ... - - @_copy_docstring_and_deprecators(FigureCanvasBase.mpl_connect) -def connect(s, func) -> int: +def connect(s: str, func: Callable[[Event], Any]) -> int: return gcf().canvas.mpl_connect(s, func) @@ -1296,11 +1259,11 @@ def draw() -> None: @_copy_docstring_and_deprecators(Figure.savefig) -def savefig(fname: str | os.PathLike | IO, **kwargs) -> None: +def savefig(*args, **kwargs) -> None: fig = gcf() # savefig default implementation has no return, so mypy is unhappy # presumably this is here because subclasses can return? - res = fig.savefig(fname, **kwargs) # type: ignore[func-returns-value] + res = fig.savefig(*args, **kwargs) # type: ignore[func-returns-value] fig.canvas.draw_idle() # Need this if 'transparent=True', to reset colors. return res diff --git a/lib/matplotlib/typing.py b/lib/matplotlib/typing.py index 213dd3b92f9b..f4f7395482bd 100644 --- a/lib/matplotlib/typing.py +++ b/lib/matplotlib/typing.py @@ -80,26 +80,6 @@ LogLevel: TypeAlias = Literal["notset", "debug", "info", "warning", "error", "critical"] """Valid string values for logging levels used in `set_loglevel`.""" -RcGroupType: TypeAlias = Literal[ - "animation", "axes", "axes3d", "backend", "boxplot", "contour", "date", - "errorbar", "figure", "font", "grid", "hatch", "image", "interactive", - "keymap", "legend", "lines", "markers", "patch", "path", "pdf", "pgf", "ps", - "savefig", "scatter", "text", "tk", "toolbar", "xtick", "ytick" -] -"""Valid values for the `group` parameter in `rc(group=...)`.""" - -# --- START GENERATED RcParamKeyType --- -RcParamKeyType: TypeAlias = Literal[ - "_internal.classic_mode", - -] -# --- END GENERATED RcParamKeyType --- -"""The keys are extracted directly from the matplotlib runtime via: - `generate_rc_keys_once.py` -To regenerate this block: -1. Run `python tools/update_rc_keys_once.py` -2. Replace the content between the START and END markers below -""" JoinStyleType: TypeAlias = JoinStyle | Literal["miter", "round", "bevel"] """Line join styles. See :doc:`/gallery/lines_bars_and_markers/joinstyle`.""" diff --git a/tools/update_rcparam_typing.py b/tools/update_rcparam_typing.py deleted file mode 100644 index 9f6bef1299b4..000000000000 --- a/tools/update_rcparam_typing.py +++ /dev/null @@ -1,89 +0,0 @@ -""" -Script to generate and update the `RcParamKeyType` Literal type alias in -the `matplotlib.typing` module based on the current set of rcParams keys. - -This automates keeping the typing definitions up to date with Matplotlib's -runtime rcParams keys, improving type safety and IDE autocomplete. - -Note: - This script overwrites the block between markers: - # --- START GENERATED RcParamKeyType --- - and - # --- END GENERATED RcParamKeyType --- - in the typing file with the newly generated Literal block. -""" - -import matplotlib -matplotlib.use("Agg") # Use non-GUI backend to avoid GUI errors during import -import re - -# Path to the typing file to update — adjust if your directory structure differs -typing_file = "lib/matplotlib/typing.py" - -def generate_rcparamkeytype_literal(keys: list[str]) -> str: - """ - Generate the Literal block string for RcParamKeyType with the given keys. - - Parameters - ---------- - keys : list[str] - Sorted list of rcParams keys as strings. - - Returns - ------- - str - A formatted string defining RcParamKeyType as a typing.Literal of keys, - wrapped with start and end generation markers. - """ - keys_str = ",\n ".join(f'"{key}"' for key in keys) - return ( - "# --- START GENERATED RcParamKeyType ---\n" - "RcParamKeyType: TypeAlias = Literal[\n" - f" {keys_str},\n" - "]\n" - "# --- END GENERATED RcParamKeyType ---" - ) - -def update_typing_file(path: str, new_block: str) -> None: - """ - Replace the existing RcParamKeyType block in the typing file with new_block. - - Parameters - ---------- - path : str - File path of the typing file to update. - - new_block : str - The new Literal block string to insert. - - Raises - ------ - RuntimeError - If the start and end markers are not found in the file. - """ - with open(path, "r", encoding="utf-8") as f: - content = f.read() - - pattern = re.compile( - r"# --- START GENERATED RcParamKeyType ---\n.*?# --- END GENERATED RcParamKeyType ---", - re.DOTALL, - ) - - new_content, count = pattern.subn(new_block, content) - if count == 0: - raise RuntimeError(f"Markers not found in {path}. Please ensure the markers exist.") - - with open(path, "w", encoding="utf-8") as f: - f.write(new_content) - -if __name__ == "__main__": - # Retrieve and sort rcParams keys from Matplotlib runtime - rc_keys = sorted(matplotlib.rcParams.keys()) - - # Generate new Literal block - new_block = generate_rcparamkeytype_literal(rc_keys) - - # Update typing.py file in place - update_typing_file(typing_file, new_block) - - print(f"Updated {typing_file} with {len(rc_keys)} RcParamKeyType keys.") From cf1a70cd981ba7305d025247f7581db8f30a9716 Mon Sep 17 00:00:00 2001 From: N R Navaneet Date: Sun, 13 Jul 2025 04:13:47 +0530 Subject: [PATCH 5/6] Minor pyplot error fixed --- lib/matplotlib/pyplot.py | 54 ++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index d77b06115268..2ff742167db9 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -50,7 +50,7 @@ import sys import threading import time -from typing import TYPE_CHECKING, cast, overload +from typing import IO, TYPE_CHECKING, cast, overload from cycler import cycler # noqa: F401 import matplotlib @@ -100,7 +100,14 @@ import matplotlib.backend_bases from matplotlib.axis import Tick from matplotlib.axes._base import _AxesBase - from matplotlib.backend_bases import Event + from matplotlib.backend_bases import ( + CloseEvent, + DrawEvent, + KeyEvent, + MouseEvent, + PickEvent, + ResizeEvent, + ) from matplotlib.cm import ScalarMappable from matplotlib.contour import ContourSet, QuadContourSet from matplotlib.collections import ( @@ -126,11 +133,18 @@ from matplotlib.quiver import Barbs, Quiver, QuiverKey from matplotlib.scale import ScaleBase from matplotlib.typing import ( + CloseEventType, ColorType, CoordsType, + DrawEventType, HashableList, + KeyEventType, LineStyleType, MarkerType, + MouseEventType, + PickEventType, + ResizeEventType, + LogLevel ) from matplotlib.widgets import SubplotTool @@ -338,8 +352,8 @@ def uninstall_repl_displayhook() -> None: # Ensure this appears in the pyplot docs. @_copy_docstring_and_deprecators(matplotlib.set_loglevel) -def set_loglevel(*args, **kwargs) -> None: - return matplotlib.set_loglevel(*args, **kwargs) +def set_loglevel(level: LogLevel) -> None: + return matplotlib.set_loglevel(level) @_copy_docstring_and_deprecators(Artist.findobj) @@ -1175,8 +1189,32 @@ def get_current_fig_manager() -> FigureManagerBase | None: return gcf().canvas.manager +@overload +def connect(s: MouseEventType, func: Callable[[MouseEvent], Any]) -> int: ... + + +@overload +def connect(s: KeyEventType, func: Callable[[KeyEvent], Any]) -> int: ... + + +@overload +def connect(s: PickEventType, func: Callable[[PickEvent], Any]) -> int: ... + + +@overload +def connect(s: ResizeEventType, func: Callable[[ResizeEvent], Any]) -> int: ... + + +@overload +def connect(s: CloseEventType, func: Callable[[CloseEvent], Any]) -> int: ... + + +@overload +def connect(s: DrawEventType, func: Callable[[DrawEvent], Any]) -> int: ... + + @_copy_docstring_and_deprecators(FigureCanvasBase.mpl_connect) -def connect(s: str, func: Callable[[Event], Any]) -> int: +def connect(s, func) -> int: return gcf().canvas.mpl_connect(s, func) @@ -1259,11 +1297,11 @@ def draw() -> None: @_copy_docstring_and_deprecators(Figure.savefig) -def savefig(*args, **kwargs) -> None: +def savefig(fname: str | os.PathLike | IO, **kwargs) -> None: fig = gcf() # 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] + res = fig.savefig(fname, **kwargs) # type: ignore[func-returns-value] fig.canvas.draw_idle() # Need this if 'transparent=True', to reset colors. return res @@ -4718,4 +4756,4 @@ def nipy_spectral() -> None: 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") \ No newline at end of file From 92b69f72bfc660527ba7f511eaff84fa4d983813 Mon Sep 17 00:00:00 2001 From: N R Navaneet Date: Sun, 13 Jul 2025 04:23:17 +0530 Subject: [PATCH 6/6] pre-commit fixes --- lib/matplotlib/pyplot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 2ff742167db9..e916d57f8871 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -4756,4 +4756,4 @@ def nipy_spectral() -> None: 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") \ No newline at end of file + set_cmap("nipy_spectral")