-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Inset axes bug and docs fix #14278
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
Inset axes bug and docs fix #14278
Conversation
This is new and experimental so I think breaking changes are still fair game. Thanks for looking at this! |
Should it go in as is (with the docs and implementation both using a list, either 4-length or empty), or should I revert the docs to the optional 4-tuple and then match the implementation to that? I think the optional 4-tuple is more true to what it's representing (index has semantic meaning, and it's either 4-length or nothing), although it's arguable that the list is more pythonic (empty lists being falsey anyway, and still behaving in an unsurprising way for iteration, concatenation etc). |
Lastly, as this is new-ish API surface for versions of matplotlib which will only be targeting python >=3.6, is there any appetite for PEP-484 type annotations? |
5bac860
to
23f56c8
Compare
New commits implement the documented tuple rather than documenting the implemented list. Also adds type annotations. |
23f56c8
to
63904bf
Compare
lib/matplotlib/tests/test_axes.py
Outdated
@@ -5935,6 +5935,33 @@ def test_tick_padding_tightbbox(): | |||
assert bb.y0 < bb2.y0 | |||
|
|||
|
|||
def test_inset(): | |||
"""Regression test: failure described in for issue #14275""" |
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.
Please don't reference issues in the comments.. Every commit adds a notification message in the issue ;-)
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.
Yes, I noticed that! Sorry. I think it's valuable to reference the issue in regression tests, but next time I won't include the #
, to stop github picking it up.
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.
#12475 mentions a doc problem, so the test can't really be testing for that. I'd rather just explain what you're testing in the docstring.
Also "Regression test" basically adds no information: most tests are regression tests...
lib/matplotlib/axes/_axes.py
Outdated
edgecolor='0.5', | ||
alpha: float = 0.5, | ||
zorder: float = 4.99, **kwargs | ||
) -> Tuple[ |
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.
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.
These are PEP-484 type hints; they're immensely helpful to downstream users (in my opinion). Matplotlib which has a deep, nested class structure, and the docs can be tricky to navigate: knowing exactly what you're getting out of a function call, and your IDE being able to tell you if you're using it wrong, increases discoverability of the library and productivity of the user. A systematic, well-specified, core python way of documenting types in signatures are also just better than the somewhat vague numpydoc conventions for docstrings. Sphinx can pick it up with an extension, the whole module path is necessarily resolved, and the IDE support is much broader. We've been messing around with rst, numpydoc, googledoc for years: now we have one obvious way to do it.
It might be nice if eventually most of matplotlib's API surface was covered, but there's no need to do it all in one go - because it's not checked at runtime and not enforced they can be phased in gradually. As this is new API surface which will only ever be used in python 3.6+, it seemed a good corner of the code to start!
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.
Given that @jklymak asked me for my opinion... I personally think the design of PEP484 (and followup PEPs, especially 560, 561, 563) type hints is poor and am not particularly looking forward to adding type hints everywhere (no, I don't have any concrete alternative proposal).
On the other hand, I guess type hints are going one day or another to be everywhere :/, so I'm not going to block their addition either. (I guess some here may be a bit surprised to hear that there actually are some new and shiny Python features that I don't like :p)
(FWIW I would have been more interested by work on type inference (à la C++ "almost always auto" style).)
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.
I think we need an internal decision if and how we want to add type hints. See also @tacaswell's comment: #13798 (comment).
`matplotlib.axes.Axes.indicate_inset_zoom` were documented as returning | ||
a 4-tuple of `matplotlib.patches.ConnectionPatch`es, where in fact they | ||
returned a 4-length list. | ||
|
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.
I guess I'm not seeing the difference here. What is the advantage of the change below? If its just because we don't think a list is a tuple, then I'd argue just changing the docs to say it is a list. But practically, what is the difference?
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.
I'm not sure I understand. Lists and tuples are different. They do different things. They have different methods. At the very least, the documentation should match the implementation.
If you tell someone you're giving them a duck and then show up with a cat, they're going to be annoyed that they've spent some time digging a pond.
I'd argue just changing the docs to say it is a list
This is what I did initially. However, in this case, the return value has a fixed length and the indices are meaningful, so an optional tuple is closer to the meaning of what's being represented. I suggested the change in an earlier comment and said my only reservation was about it being a breaking API change, and you responded that breaking changes were fair game as it's an experimental feature.
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.
Ok I was looking at the wrong change. In general it’s better not to have too many style changes unless they help pass PEP8 with fewer exceptions.
I’m not clear if a tuple or list is particularly better here. I guess the fact that it’s always a four tuple would argue for that. My apologies that my previous comment was taken to mean I thought it should be changed, just that it could if there is a good reason.
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.
Returning a tuple makes sense semantically.
lib/matplotlib/axes/_axes.py
Outdated
@@ -31,9 +28,12 @@ | |||
import matplotlib.ticker as mticker | |||
import matplotlib.transforms as mtransforms | |||
import matplotlib.tri as mtri | |||
from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer | |||
import numpy as np |
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.
please leave the numpy imports at the top (between the stdlib imports and the matplotlib imports).
lib/matplotlib/axes/_axes.py
Outdated
Optional[ | ||
Tuple[ | ||
mpatches.ConnectionPatch, mpatches.ConnectionPatch, | ||
mpatches.ConnectionPatch, mpatches.ConnectionPatch |
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.
If you're going to go to the bother of having closing brackets each on their own line (not really sure it's worth it in this case), I would suggest adding trailing commas as well (minimizing diffs, yada yada).
lib/matplotlib/tests/test_axes.py
Outdated
fig.canvas.draw() | ||
xx = np.array([[1.5, 2.], | ||
[2.15, 2.5]]) | ||
assert (np.all(rec.get_bbox().get_points() == xx)) |
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.
extra parentheses
63904bf
to
cc1f9f8
Compare
Rebased on master and addressed @anntzer 's style comments. I'll squash down the commits before it goes in. Happy to polish this off in whichever way you want, just give me a directive for
Also, the docs build is failing and I don't know why - any pointers? Locally, I get
|
I added whether to add type hints on the agenda of the weekly dev call (next week). |
Thanks! To be clear, I don't think that it should be a priority to sweep through the entire codebase adding them - nor does that need to be the case for them to be added in this PR. They can be added incrementally wherever it's helpful, if we happen to be in that area of the code doing other things. It's mainly a question of whether the core maintainers prefer to affirmatively avoid them. For what it's worth, we are gradually adding them to our own project and have found a lot of dead code, bugs, inaccurate documentation etc. because of it; it's been very valuable from a maintainability perspective. |
I think that introducing type annotations now would be a mistake. They add yet another level of complexity and bulk to a codebase that is already hard to master, especially considering that it remains a volunteer project. |
IMHO, they could make the codebase considerably easier to master. Right now, IDEs struggle to autocomplete ambiguous return types (and/or they incorrectly infer them, leading to the dreaded yellow squigglies), and a lot of effort is put into explaining types in docstrings which are finnicky to make machine-readable. #14275, which precipitated this PR, is a mismatch in docs and implementation which would have been caught in a snap given type annotations. There are definitely places where the return type would have to be very complex because it returns different things in different situations - most of these won't change due for compatibility reasons, but encouraging type annotations in new code would dissuade people from making similar ambiguous API designs in the future, which is a positive. Annotations shouldn't have to change with any regularity, and there's no hurry or even necessity to tackle the older code. |
I think the controversial nature of the type hints are holding this up, so I'll strip out that commit. However, I think it should be discussed further and so would appreciate it if anyone with opinions on the matter would check in at #13798 ! Unless anyone gets back to me with a decision to the contrary, I'll keep the fix here as an optional 4-tuple (as originally documented) rather than a zero-or-4-length-list (as originally implemented). Also rebased on master and squashed review-fix commits. |
81399c6
to
11b3f94
Compare
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.
Still approving after all changes. 😄
Seems fine to me, but must pass CI? |
See matplotlib#14275 inset_ax argument is meant to be optional, but currently fails if not given.
11b3f94
to
c5c1b2a
Compare
See matplotlib#14275 1. Methods now return a tuple as documented, instead of a list. 2. indicate_inset() now does not error when optional inset_ax is not passed, but returns None instead of a tuple.
c5c1b2a
to
86f5fb0
Compare
Done, I'd written some markdown in a ReST file and it broke the docs build. |
Thanks a lot @clbarnes ! |
See #14275
Axes.indicate_inset
would fail if the optionalinset_ax
argument was not given; now it doesn't. Test included.Axes.indicate_inset*
methods' docstring incorrectly stated that they returned a tuple; now they correctly state that they return a list (which is empty ifinset_ax
isNone
).Ideally, I would have preferred them to return a 4-tuple as documented (or
None
, in the case ofindicate_inset
with noinset_ax
). However, that would technically be a breaking change: bringing the docs in line with the implementation seemed a less contentious idea that bringing the code in line with the docs.That said, these methods are documented as experimental, so if others agree with my API preference, it may be worth raising a new issue to use tuples instead.