-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Missing return type hints for Figure #25607
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
Conversation
diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py
index 926944e5a5..c698443e88 100644
--- a/lib/matplotlib/figure.py
+++ b/lib/matplotlib/figure.py
@@ -188,7 +188,6 @@ class FigureBase(Artist):
# axis._get_tick_boxes_siblings
self._align_label_groups = {"x": cbook.Grouper(), "y": cbook.Grouper()}
- self.figure = self
self._localaxes = [] # track all axes
self.artists = []
self.lines = []
@@ -2454,6 +2453,7 @@ None}, default: None
%(Figure:kwdoc)s
"""
super().__init__(**kwargs)
+ self.figure = self
self._layout_engine = None
if layout is not None: Should we add this diff to this commit as well? I think I agree |
There is a slight complexity with that doesn't actually come to light in the current code: I did figure out that the I was worried that that would mean our num: int | str | Figure | SubFigure | None = None, So the only valid types that pass that isinstance are It is still a little tricky in ways I don't fully like, but in ways that can likely be mostly ignored for user code. The full diff (including stubs) is then: diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py
index 926944e5a5..c698443e88 100644
--- a/lib/matplotlib/figure.py
+++ b/lib/matplotlib/figure.py
@@ -188,7 +188,6 @@ class FigureBase(Artist):
# axis._get_tick_boxes_siblings
self._align_label_groups = {"x": cbook.Grouper(), "y": cbook.Grouper()}
- self.figure = self
self._localaxes = [] # track all axes
self.artists = []
self.lines = []
@@ -2454,6 +2453,7 @@ None}, default: None
%(Figure:kwdoc)s
"""
super().__init__(**kwargs)
+ self.figure = self
self._layout_engine = None
if layout is not None:
diff --git a/lib/matplotlib/figure.pyi b/lib/matplotlib/figure.pyi
index 2b9f4584e4..15299c2966 100644
--- a/lib/matplotlib/figure.pyi
+++ b/lib/matplotlib/figure.pyi
@@ -70,10 +70,6 @@ class SubplotParams:
) -> None: ...
class FigureBase(Artist):
- # Small slight-of-hand as FigureBase is not instantiated directly
- # hinting as Figure means e.g. plt.figure can be type hinted more easily
- # FigureBase is never instantiated directly in internal code at the very least
- figure: Figure
artists: list[Artist]
lines: list[Line2D]
patches: list[Patch]
@@ -296,6 +292,7 @@ class SubFigure(FigureBase):
def get_axes(self) -> list[Axes]: ...
class Figure(FigureBase):
+ figure: Figure
bbox_inches: Bbox
dpi_scale_trans: Affine2D
bbox: Bbox
diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py
index 2933fa5d4c..ace36725e3 100644
--- a/lib/matplotlib/pyplot.py
+++ b/lib/matplotlib/pyplot.py
@@ -858,6 +858,7 @@ default: None
in the matplotlibrc file.
"""
if isinstance(num, FigureBase):
+ # type narrowed to `Figure | SubFigure` by combination of input and isinstance
if num.canvas.manager is None:
raise ValueError("The passed figure is not managed by pyplot")
_pylab_helpers.Gcf.set_active(num.canvas.manager) I think I agree, and will add it.
|
Cycling to restart CI, as it was failing for unrelated reasons (jupyter) when this was opened. If it is still affected, I'll rebase |
Can we adjust the type on |
Required changes for Figure.figure type hint
Rebased to (not quite, dodging #25782) main, updated |
@@ -154,15 +153,15 @@ class FigureBase(Artist): | |||
*, | |||
sharex: bool | Literal["none", "all", "row", "col"] = ..., | |||
sharey: bool | Literal["none", "all", "row", "col"] = ..., | |||
squeeze: Literal[True] = ..., | |||
squeeze: bool = ..., |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a particular case of a potentially more general issue, which was closely related enough to this change that I decided to make the change for this one here.
Essentially while it seems like Literal[True]
and Literal[False]
should actually span bool
, they technically do not, as they only handle the case of literals being passed, not a variable with type bool
.
That said, in many cases it doesn't really make sense to pass anything other than a literal there, so the "passed an unknown bool" case is pretty unexpected.
e.g. the cbook.load_data
method has a couple booleans that we would never expect to be passed as an unknown bool (in part because that is a largely for internal use method).
In this particular case a more complete answer would be to try and do our best with overloads
to mete out the case which is passed sqeeze=True
with Literal[1]
for nrows/ncols and then have a fallback for this version which can't necessarily differentiate what the return will be.
In fact, unions in returns are generally discouraged and the prevailing wisdom is to just use Any
(if overloads cannot properly distinguish the return type). This is actually what I've already done with pyplot.subplots
, which calls this method.
Removing such unions is a change I've been leaning towards doing at some point, but made a minimal change here as the return union actually encompasses all possible returns regardless of the value of squeeze
here.
PR Summary
Required changes for Figure.figure type hint
The core change here, which enables the
plt.figure
type hint is thatFigureBase.figure
(and subclasses) is now type hinted asFigure
instead ofFigureBase
.This is itself predicated on the fact that
FigureBase
is never directly instantiated.Internally, this holds true, and so I'm inclined to think that is the case unless
proven otherwise.
Even our docs for Custom Figure subclasses
subclass from
Figure
, notFigureBase
.I will note that FigureBase refers to attributes only defined in the two subclasses:
Figure
andSubFigure
. (Namely at leastself.patch
andself.bbox
, may be others)This to me begs the question of whether
FigureBase
should be an Abstract Base Class,thus defining methods that any valid subclass should have (including those attributes,
which may be required to become properties if so, as I don't think ABC can mark
pure attributes as abstract)
See discussion: #24976 (comment)
Figure
instead ofFigureBase
FigureBase
never instantiated directly (If this assumption is not true, please speak up)SubFigure.figure
is alwaysparent.figure
Figure.figure
is alwaysself
SubFigure.figure
is alwaysFigure
(GivenFigureBase
not instantiated directly)None
was me copying theaxes.Figure
was not actually possiblePR Checklist
Documentation and Tests
pytest
passes)Release Notes
.. versionadded::
directive in the docstring and documented indoc/users/next_whats_new/
.. versionchanged::
directive in the docstring and documented indoc/api/next_api_changes/
next_whats_new/README.rst
ornext_api_changes/README.rst