Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Update type hints to fix #30160 #30193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
190 changes: 109 additions & 81 deletions lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -338,8 +338,8 @@

# 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: str) -> None:
return matplotlib.set_loglevel(level)

Check warning on line 342 in lib/matplotlib/pyplot.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/pyplot.py#L342

Added line #L342 was not covered by tests


@_copy_docstring_and_deprecators(Artist.findobj)
Expand Down Expand Up @@ -569,6 +569,14 @@
return _get_backend_mod().draw_if_interactive(*args, **kwargs)


@overload
def show() -> None: ...


@overload
def show(block: bool) -> None: ...


# This function's signature is rewritten upon backend-load by switch_backend.
def show(*args, **kwargs) -> None:
"""
Expand Down Expand Up @@ -1251,11 +1259,11 @@


@_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

Expand Down Expand Up @@ -1393,6 +1401,22 @@

## More ways of creating Axes ##

@overload
def subplot(nrows: int, ncols: int, index: int, /, **kwargs): ...


@overload
def subplot(pos: int | SubplotSpec, /, **kwargs): ...


@overload
def subplot(ax: Axes, /): ...


@overload
def subplot(**kwargs): ...


@_docstring.interpd
def subplot(*args, **kwargs) -> Axes:
"""
Expand Down Expand Up @@ -2096,80 +2120,6 @@
## Axis ##


def xlim(*args, **kwargs) -> tuple[float, float]:
"""
Get or set the x limits of the current Axes.

Call signatures::

left, right = xlim() # return the current xlim
xlim((left, right)) # set the xlim to left, right
xlim(left, right) # set the xlim to left, right

If you do not specify args, you can pass *left* or *right* as kwargs,
i.e.::

xlim(right=3) # adjust the right leaving left unchanged
xlim(left=1) # adjust the left leaving right unchanged

Setting limits turns autoscaling off for the x-axis.

Returns
-------
left, right
A tuple of the new x-axis limits.

Notes
-----
Calling this function with no arguments (e.g. ``xlim()``) is the pyplot
equivalent of calling `~.Axes.get_xlim` on the current Axes.
Calling this function with arguments is the pyplot equivalent of calling
`~.Axes.set_xlim` on the current Axes. All arguments are passed though.
"""
ax = gca()
if not args and not kwargs:
return ax.get_xlim()
ret = ax.set_xlim(*args, **kwargs)
return ret


def ylim(*args, **kwargs) -> tuple[float, float]:
"""
Get or set the y-limits of the current Axes.

Call signatures::

bottom, top = ylim() # return the current ylim
ylim((bottom, top)) # set the ylim to bottom, top
ylim(bottom, top) # set the ylim to bottom, top

If you do not specify args, you can alternatively pass *bottom* or
*top* as kwargs, i.e.::

ylim(top=3) # adjust the top leaving bottom unchanged
ylim(bottom=1) # adjust the bottom leaving top unchanged

Setting limits turns autoscaling off for the y-axis.

Returns
-------
bottom, top
A tuple of the new y-axis limits.

Notes
-----
Calling this function with no arguments (e.g. ``ylim()``) is the pyplot
equivalent of calling `~.Axes.get_ylim` on the current Axes.
Calling this function with arguments is the pyplot equivalent of calling
`~.Axes.set_ylim` on the current Axes. All arguments are passed though.
"""
ax = gca()
if not args and not kwargs:
return ax.get_ylim()
ret = ax.set_ylim(*args, **kwargs)
return ret


def xticks(
ticks: ArrayLike | None = None,
labels: Sequence[str] | None = None,
Expand Down Expand Up @@ -2690,7 +2640,13 @@
return im


def polar(*args, **kwargs) -> list[Line2D]:
def polar(
*args: float | ArrayLike | str,
scalex: bool = True,
scaley: bool = True,
data=None,
**kwargs
) -> list[Line2D]:
"""
Make a polar plot.

Expand Down Expand Up @@ -2724,7 +2680,13 @@
)
else:
ax = axes(projection="polar")
return ax.plot(*args, **kwargs)
return ax.plot(
*args,
scalex=scalex,
scaley=scaley,
data=data,
**kwargs
)


# If rcParams['backend_fallback'] is true, and an interactive backend is
Expand Down Expand Up @@ -4457,6 +4419,72 @@
gca().set_yscale(value, **kwargs)


# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
@overload
@_copy_docstring_and_deprecators(Axes.get_xlim)
def xlim() -> tuple[float, float]:
...

Check warning on line 4426 in lib/matplotlib/pyplot.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/pyplot.py#L4426

Added line #L4426 was not covered by tests


# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
@overload
@_copy_docstring_and_deprecators(Axes.set_xlim)
def xlim(
left: float | tuple[float, float] | None = None,
right: float | None = None,
*,
emit: bool = True,
auto: bool | None = False,
xmin: float | None = None,
xmax: float | None = None,
) -> tuple[float, float]:
...

Check warning on line 4441 in lib/matplotlib/pyplot.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/pyplot.py#L4441

Added line #L4441 was not covered by tests


# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
@_copy_docstring_and_deprecators(Axes.get_xlim)
def xlim(*args, **kwargs):
ax = gca()
if not args and not kwargs:
return ax.get_xlim()

Check warning on line 4449 in lib/matplotlib/pyplot.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/pyplot.py#L4449

Added line #L4449 was not covered by tests

ret = ax.set_xlim(*args, **kwargs)
return ret


# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
@overload
@_copy_docstring_and_deprecators(Axes.get_ylim)
def ylim() -> tuple[float, float]:
...

Check warning on line 4459 in lib/matplotlib/pyplot.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/pyplot.py#L4459

Added line #L4459 was not covered by tests


# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
@overload
@_copy_docstring_and_deprecators(Axes.set_ylim)
def ylim(
bottom: float | tuple[float, float] | None = None,
top: float | None = None,
*,
emit: bool = True,
auto: bool | None = False,
ymin: float | None = None,
ymax: float | None = None,
) -> tuple[float, float]:
...

Check warning on line 4474 in lib/matplotlib/pyplot.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/pyplot.py#L4474

Added line #L4474 was not covered by tests


# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
@_copy_docstring_and_deprecators(Axes.get_ylim)
def ylim(*args, **kwargs):
ax = gca()
if not args and not kwargs:
return ax.get_ylim()

Check warning on line 4482 in lib/matplotlib/pyplot.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/pyplot.py#L4482

Added line #L4482 was not covered by tests

ret = ax.set_ylim(*args, **kwargs)
return ret


# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
def autumn() -> None:
"""
Expand Down
75 changes: 74 additions & 1 deletion lib/matplotlib/tests/test_pyplot.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import ast
import difflib
import inspect

import numpy as np
import sys
Expand Down Expand Up @@ -449,7 +451,6 @@


def test_figure_hook():

test_rc = {
'figure.hooks': ['matplotlib.tests.test_pyplot:figure_hook_example']
}
Expand Down Expand Up @@ -484,3 +485,75 @@

# 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):
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")

Check warning on line 504 in lib/matplotlib/tests/test_pyplot.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/tests/test_pyplot.py#L504

Added line #L504 was not covered by tests

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
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 = 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())

Check warning on line 536 in lib/matplotlib/tests/test_pyplot.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/tests/test_pyplot.py#L536

Added line #L536 was not covered by tests

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 ValueError(f"{original_sig} is not an instance method")

Check warning on line 546 in lib/matplotlib/tests/test_pyplot.py

View check run for this annotation

Codecov / codecov/patch

lib/matplotlib/tests/test_pyplot.py#L546

Added line #L546 was not covered by tests

original_params = original_params.copy()
del original_params["self"]

assert plt_sig.parameters == 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)
Loading
Loading