From 1abf8565decfb438e958d83f51e359630e06d451 Mon Sep 17 00:00:00 2001 From: Corenthin ZOZOR Date: Thu, 19 Jun 2025 16:00:21 +0200 Subject: [PATCH 1/3] Create tests (cherry picked from commit 608b51fd6321ac07133b8d66a14e15a906e21169) --- lib/matplotlib/tests/test_pyplot.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_pyplot.py b/lib/matplotlib/tests/test_pyplot.py index ab713707bace..193635973733 100644 --- a/lib/matplotlib/tests/test_pyplot.py +++ b/lib/matplotlib/tests/test_pyplot.py @@ -1,4 +1,5 @@ import difflib +import inspect import numpy as np import sys @@ -449,7 +450,6 @@ def figure_hook_example(figure): def test_figure_hook(): - test_rc = { 'figure.hooks': ['matplotlib.tests.test_pyplot:figure_hook_example'] } @@ -484,3 +484,24 @@ def test_matshow(): # Smoke test that matshow does not ask for a new figsize on the existing figure plt.matshow(arr, fignum=fig.number) + + +def assert_signatures_identical(plt_meth, original_meth, remove_self_param=False): + plt_params = inspect.signature(plt_meth).parameters + original_params = inspect.signature(original_meth).parameters + if remove_self_param: + if next(iter(original_params)) not in ["self"]: + raise AssertionError(f"{original_params} is not an instance method") + + original_params = dict(original_params) + del original_params["self"] + + assert plt_params == original_params + + +def test_setloglevel_signature(): + assert_signatures_identical(plt.set_loglevel, mpl.set_loglevel) + + +def test_polar_signature(): + assert_signatures_identical(plt.polar, plt.Axes.plot, True) From 054077fccbebf4bcc99918fec6dce024d862b5c3 Mon Sep 17 00:00:00 2001 From: Corenthin ZOZOR Date: Fri, 20 Jun 2025 19:36:15 +0200 Subject: [PATCH 2/3] Update test_pyplot.py to include type on signature (cherry picked from commit 4ea0ff8e50f3a2460d18694aa1d58e7757c8726a) --- lib/matplotlib/tests/test_pyplot.py | 55 ++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/tests/test_pyplot.py b/lib/matplotlib/tests/test_pyplot.py index 193635973733..8bfc1951f4bb 100644 --- a/lib/matplotlib/tests/test_pyplot.py +++ b/lib/matplotlib/tests/test_pyplot.py @@ -1,3 +1,4 @@ +import ast import difflib import inspect @@ -487,16 +488,60 @@ def test_matshow(): def assert_signatures_identical(plt_meth, original_meth, remove_self_param=False): - plt_params = inspect.signature(plt_meth).parameters - original_params = inspect.signature(original_meth).parameters + def get_src(meth): + meth_src = Path(inspect.getfile(meth)) + meth_stub = meth_src.with_suffix(".pyi") + return meth_stub if meth_stub.exists() else meth_src + + def tree_loop(tree, name, class_): + for item in tree.body: + if class_ and isinstance(item, ast.ClassDef) and item.name == class_: + return tree_loop(item, name, None) + + if isinstance(item, ast.FunctionDef) and item.name == name: + return item + + raise ValueError(f"Cannot find {class_}.{name} in ast") + + def get_signature(meth): + qualname = meth.__qualname__ + class_ = None if "." not in qualname else qualname.split(".")[-2] + path = get_src(meth) + tree = ast.parse(path.read_text()) + node = tree_loop(tree, meth.__name__, class_) + + params = dict(inspect.signature(meth).parameters) + args = node.args + for param in (*args.posonlyargs, *args.args, args.vararg, *args.kwonlyargs, args.kwarg): + if param is None: + continue + if param.annotation is None: + continue + annotation = 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=ast.unparse(node.returns) + ) + else: + return inspect.Signature(params.values()) + + plt_sig = get_signature(plt_meth) + original_sig = get_signature(original_meth) + + assert plt_sig.return_annotation == original_sig.return_annotation + + original_params = original_sig.parameters if remove_self_param: if next(iter(original_params)) not in ["self"]: - raise AssertionError(f"{original_params} is not an instance method") + raise ValueError(f"{original_sig} is not an instance method") - original_params = dict(original_params) + original_params = original_params.copy() del original_params["self"] - assert plt_params == original_params + assert plt_sig.parameters == original_params def test_setloglevel_signature(): From 2c9a9eda59ee465f79edcb2cb554a943ae348a82 Mon Sep 17 00:00:00 2001 From: Corenthin ZOZOR Date: Fri, 20 Jun 2025 21:12:24 +0200 Subject: [PATCH 3/3] Format with ruff (cherry picked from commit 64e7921b0b3f56c88c1f449a4f2081e862289279) --- lib/matplotlib/tests/test_pyplot.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_pyplot.py b/lib/matplotlib/tests/test_pyplot.py index 8bfc1951f4bb..80b4bfd3f9c3 100644 --- a/lib/matplotlib/tests/test_pyplot.py +++ b/lib/matplotlib/tests/test_pyplot.py @@ -451,6 +451,7 @@ def figure_hook_example(figure): def test_figure_hook(): + test_rc = { 'figure.hooks': ['matplotlib.tests.test_pyplot:figure_hook_example'] } @@ -512,7 +513,14 @@ def get_signature(meth): params = dict(inspect.signature(meth).parameters) args = node.args - for param in (*args.posonlyargs, *args.args, args.vararg, *args.kwonlyargs, args.kwarg): + 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: