Skip to content
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

[ENH]: Make "No artists with labels found to put in legend" a warning #27145

Closed
Jacob-Stevens-Haas opened this issue Oct 19, 2023 · 8 comments · Fixed by #27172
Closed

[ENH]: Make "No artists with labels found to put in legend" a warning #27145

Jacob-Stevens-Haas opened this issue Oct 19, 2023 · 8 comments · Fixed by #27172
Milestone

Comments

@Jacob-Stevens-Haas
Copy link
Contributor

Jacob-Stevens-Haas commented Oct 19, 2023

Problem

When I receive this issue, it's hard to debug and find the specific call to ax.legend() if I can't raise Warnings to Exceptions. I sometimes nest plotting functions in order to draw the same plot different ways or arrange plots in a composite figure, depending upon the presentation.

MWE and Full message:

from matplotlib import pyplot as plt
import warnings

warnings.simplefilter("error", category=Warning)
plt.plot([1,2,3])
plt.legend()

(no error, plot appears)

No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.

Proposed solution

Make this a true UserWarning. Currently, it's a logging.LogRecord of level=logging.Warning.

log = logging.getLogger(__name__)
...
log.warning(<this message>)

Additional pro: In the future, it would be easier to find and work on this warning if it is, in fact, a true Warning.

Con: It won't be handled by the logging infrastructure. But legend.py uses the default module logger, and the only LogRecord that it emits is this one. The only situation I could think of where this might matter is if a user has some override of global logging setup (by calling logging.setLoggerClass()) but does not apply a similar philosophy to capturing warnings in STDERR.

@jklymak
Copy link
Member

jklymak commented Oct 19, 2023

This is related to #23422 though this one is a little more ambiguous if it should actually be a warning or not as the user can suppress the warning by either adding a label or removing the legend call.

@Jacob-Stevens-Haas
Copy link
Contributor Author

Thanks for the link :) My issue is definitely an example of @mwaskom's point that "you can convert a warning to an error and then drop into a debugger to figure out what's happening"


Also, I'm not sure I understand how its ambiguous? Your statement:

the user can suppress the warning by either adding a label or removing the legend call.

sounds like the standard, "the issue is avoidable and the client application should be modified to eliminate the warning." I can't think of a situation where a user would legitimately intend to call .legend() without labels. Matplotlib won't draw a legend when it triggers the warning.

@jklymak
Copy link
Member

jklymak commented Oct 19, 2023

I agree, but I don't remember the original rationale for a logging call versus warning, and the rule itself is a bit ambiguous.

@Jacob-Stevens-Haas
Copy link
Contributor Author

Jacob-Stevens-Haas commented Oct 20, 2023

TL;DR: logging was added to this module at a time when you were making the package-wide effort of replacing the old Verbose class with python's standard logging. I think adding this particular logging case (and perhaps others) was consumed in effort, and the warnings vs logging choice wasn't given serious discussion in the original planning & review.

Doing a bit of git blame investigation, it looks like this case was added in #9324, which sought to fix #9320, a regression in 2.1 where it seems labels/handles behaved differently as kwargs vs positional args. The PR included code to warn about missing handles, incl the case of a leading underscore in a label. Early in the PR you were using warnings.warn, but mentioned

I’d suggest we just strip the illegal underscore and warn (or logging.warning 😉) or set it to an empty string.

That was the extent of discussion I could find about logging vs warning. Another commit later amended the message (#20956) but didn't touch on logging vs warning.

A pickaxe search of the git log for "logging" around the same time finds another commit (844877c) & PR (#9313) that address a discussion (#9302) about replacing matplotlib's old Verbose class with python's standard logging. A grep search of the documentation diff explains it's better to use logging than print() and links to the logging tutorial's distinction between warnings and logging, but stops there. The issue discussion never addresses the difference between warning and logging modules, but does note that the old Verbose class did not have equivalents to logging.WARNING, logging.ERROR, or logging.CRITICAL. (comment)

@timhoffm
Copy link
Member

Thanks for the archeology 🚀! I conclude that the current state has evolved organically and there has probably not been a conscious decision between logging and warnings for this specific case.

Either way,

I can't think of a situation where a user would legitimately intend to call .legend() without labels.

plus

the user can suppress the warning by either adding a label or removing the legend call.

is a clear indication for me that this should be a UserWarning.

@jklymak
Copy link
Member

jklymak commented Oct 22, 2023

@Jacob-Stevens-Haas Did you have bandwidth to put in a PR to change this?

@Jacob-Stevens-Haas
Copy link
Contributor Author

I think so! Got the dev environment/install up and working, and all tests (at least all relevant tests) pass, so I can start a PR.

@Jacob-Stevens-Haas
Copy link
Contributor Author

Jacob-Stevens-Haas commented Oct 23, 2023

Am I supposed to add a file to matplotlib/doc/users/next_whats_new for this kind of change?

Also, should I keep the default module logger definition
log = logging.getLogger(__name__)? It no longer is accessed.

timhoffm pushed a commit that referenced this issue Dec 1, 2023
…27172)

* ENH: Change logging to warning when creating a legend with no labels

Fixes #27145

Also add test_legend_nolabels_warning()

* TST: Add mock labels to mock data.

* DOC: Note warning in legend() documentation

* DOC: Correct examples to no longer raise UserWarning

With modified explanation where appropriate

* TST: Add test for drawing empty legend()

* TST: Test legend for specific warning when no labels present

Also:
DOC: Correct Axes docstring when no labels in legend.

* TST: Filter no labels warning by message.

Also assert that a legend is at least created
@QuLogic QuLogic added this to the v3.9.0 milestone Dec 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants