-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
FIX: behaviour of legend labels with leading underscores did not adhere to the API documentation #24713
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
FIX: behaviour of legend labels with leading underscores did not adhere to the API documentation #24713
Conversation
…es and labels parameters to adhere to its documentaion.
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.
Thank you for opening your first PR into Matplotlib!
If you have not heard from us in a while, please feel free to ping @matplotlib/developers
or anyone who has commented on the PR. Most of our reviewers are volunteers and sometimes things fall through the cracks.
You can also join us on gitter for real-time discussion.
For details on testing, writing docs, and our review process, please see the developer guide
We strive to be a welcoming and open project. Please follow our Code of Conduct.
Interesting proposal. I think this is probably the correct solution, but wonder if folks use the |
Perhaps, but they would be using undocumented behaviour. In this case, this change would make those entries appear in the legend, yes. However, they are already passing a list explicitly (something the documentation treats separately from passing no arguments). It should be fairly straightforward to filter the list manually. On the other hand, the API currently doesn't allow for labels with leading underscores at all, even though it should be possible according to the documentation. This is how we arrived at this PR after all. |
So it seems an example is failing due to the change in logging method. Reviewing the contribution guidelines, it is not entirely clear to me which warning should be emitted with the logging module and which should use _api.warn_external. If I understand correctly, I should not have replaced logging with _api.warn_external, because we want (I am assuming) a warning for every invocation of legend on a particular line in the source when no labels are found, not only one (implying that something is wrong with that one line of code, where in reality it also depends on how plot was called - with or without labels). Can I get confirmation on this? |
I very strongly prefer that we update the documentation to match the behavior rather than changing the behavior. In general, when the code and the docs conflict we prefer correcting the documentation If we do want to change this behavior, then I think that this should be treated as an API change (not a bug fix) and would need to go through the deprecation cycle. It looks like this code ended up in |
One a more fundamental level, hiding labels with a leading underscore at all is a questionable design decision. If the only purpose of a label is to display an element in the legend, we could perfectly live with empty (or The distinction between logging and warnings is https://docs.python.org/3/howto/logging.html#logging-basic-tutorial
The fundamental idea is that any good code should be warnings-free, so use warnings if you want the user to modify their code. |
To me, the documentation make sense. When automatic element detection is used, additional processing is performed to extract labels following some set of rules. However, when passing the elements explicitly, this step is skipped, and so the user can pass any label they wish, even one with a leading underscore. Updating the documentation to match the behaviour seems like we are removing functionality (that is in fact documented, but not implemented). I would very much like to be able to use labels with leading underscores, but as it stands this is not possible at all. Even if I were to try and use the Legend class manually - since this is where the piece of code in question resides. Perhaps a middle ground would be to move this code to matplotlib.legend._parse_legend_args (where it should be anyway), without altering the API behaviour. This way one would not need to create a new class that inherits from Legend and overrides its constructor to use such labels. Instead, one could instantiate Legend and give it to Axes as in Axes.legend.
Here is my example use case for leading underscores. I am making a plotting utility that uses a dictionary of simulation parameters to determine parameter labels, units, symbols, names, etc. It uses some default labels with leading underscores to handle missing parameter attributes. This way the user can still see the label, and it is also obvious that it is a default-generated label, as it is using the leading underscore convention that has special meaning in python. |
The analysis that needs to be done is to sort out exactly when we started to filter the explicit labels, I think that will influence if we think going through the deprecation dance is worth it or not. I think the proposed refactor is OK. |
Suggestion: add a kwarg, "exclude_with_underscore=True" (default for current behavior) or "False" for the @Klayto-235 use case. Lot's of possibilities for the kwarg name, e.g., "drop_leading_underscore", etc. |
Apologies for missing the discussion on the call, but I would think that if the user explicitly passes a list of artists then we should indeed use them as is without dropping underscored labels; I would also not bother with adding an option as Note that a standard deprecation path is available:
For reference, I suspect(?) the current behavior came in via #2442 (mpl 1.4.0), though bisecting to such an old version of matplotlib would be somewhat tricky. |
Sorry for not being there as well. I follow @anntzer's arguments. |
It looks like this one needs devs' consensus on the way forward. FWIW, @anntzer's solution makes sense to me. @Klayto-235 are you still interested in working on this if we find that consensus? |
I'm fine with @anntzer's recommendation. |
PR Summary
This PR fixes an inconsistency between API documentation and actual behaviour. As per the documentation, when matplotlib.axes.Axes.legend or matplotlib.figure.Figure.legend is called without any arguments, artists whose labels begin with an underscore are not included in the legend. This is stated for the first of four ways to invoke the method, however, it currently applies to all four.
This fix changes the current behaviour of legend-related API to adhere to the documentation, only filtering labels with leading underscores when using the first way of invoking the method (Automatic detection of elements to be shown in the legend), allowing the user to include labels with leading underscores by passing them to the legend method explicitly - using one of the remaining three options (note that in two of these, the user passes an iterable of labels, and in the remaining one, the user only explicitly passes the artist handles, not the labels).
Furthermore, this feature (exclusion of labels with leading underscores) is only documented in the two methods mentioned above, not in the matplotlib.legend.Legend class itself, where it was partly implemented prior to this PR (probably an artefact due to earlier developments).
PR 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