Skip to content

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

Closed

Description

@Jacob-Stevens-Haas

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.

Activity

  1. jklymak commented on Oct 19, 2023

    @jklymak
    Member

    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.

  2. Jacob-Stevens-Haas commented on Oct 19, 2023

    @Jacob-Stevens-Haas
    ContributorAuthor

    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.

  3. jklymak commented on Oct 19, 2023

    @jklymak
    Member

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

  4. Jacob-Stevens-Haas commented on Oct 20, 2023

    @Jacob-Stevens-Haas
    ContributorAuthor

    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)

  5. timhoffm commented on Oct 20, 2023

    @timhoffm
    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.

  6. jklymak commented on Oct 22, 2023

    @jklymak
    Member

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

  7. Jacob-Stevens-Haas commented on Oct 23, 2023

    @Jacob-Stevens-Haas
    ContributorAuthor

    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.

  8. Jacob-Stevens-Haas commented on Oct 23, 2023

    @Jacob-Stevens-Haas
    ContributorAuthor

    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.

  9. added a commit that references this issue on Dec 1, 2023
    ee241ad
  10. added this to the v3.9.0 milestone on Dec 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions