Skip to content

[Bug]: Type checkers not finding Axes, Figure in matplotlib.pyplot #26812

Closed as not planned

Description

@Dr-Irv

Bug summary

We had code that did imports of Axes and Figure as follows:

This used to work fine with type checkers like pyright and mypy prior to matplotlib 3.8. Now the type checkers complain.

Code for reproduction

from matplotlib import pyplot as plt
plt.Axes
plt.Figure

Actual outcome

pyright reports that Axes and Figure are not exported from the module pyplot

Expected outcome

No typing error.

Additional information

Fix would be to do something like this in pyplot.py

from matplotlib.axes import Axes as Axes
from matploblib.figure import Figure as Figure

This came up due to our CI for pandas-stubs that was testing typing of pandas against matplotlib imports

Operating system

Windows 10

Matplotlib Version

3.8

Matplotlib Backend

N/A

Python version

3.10

Jupyter version

N/A

Installation

pip

Activity

  1. timhoffm commented on Sep 18, 2023

    @timhoffm
    Member

    Thanks for the report.

    The classes Axes and Figure are not meant to be public from pyplot. Their existence is an implementation detail. Unfortunately, python does reasonably support private use of classes within modules. There's only a rather crude recommended convention in PEP-8:

    Documented interfaces are considered public, unless the documentation explicitly declares them to be provisional or internal interfaces exempt from the usual backwards compatibility guarantees. All undocumented interfaces should be assumed to be internal.

    As far as I'm aware, we have not documented Axes and Figure to be public interfaces of pyplot.

  2. ksunden commented on Sep 18, 2023

    @ksunden
    Member

    Type hints are new in Matplotlib 3.8.0, so previously type checkers would ignore (or rather, warn on import without the --ignore-missing-imports flag), but now that we have type hints, it will see more.

    That said, pyplot is not the canonical import location for Axes (matplotlib.axes.Axes) and Figure (matplotlib.figure.Figure). The fact that they are importable there at run-time is to some degree an implementation detail (as imports to most python modules are).

    Now, it is an implementation detail that has a relatively widespread use, so I will defer to @timhoffm (in his role as API lead) for the final say on whether importing Axes and Figure from pyplot should be officially supported or not. My personal opinion is that type hints can be used as a mechanism to communicate and encourage best practices, while not breaking runtime behavior. (Note, he posted while I was writing this)

    I think the only place where we depend on the behavior internally is for the (discouraged) pylab module (which just does a * import, and never uses Figure/Axes, so even there it is debatable whether Axes, Figure, etc are intended to be there).

    None of our examples use these, though there are precisely two instances of plt.Axes in test code. (and none of plt.Figure (noting the capital letter))

    xref #12743, which adds a __all__ to pyplot (though it far predates type hints)

    That PR does include Axes and Figure, though I think partly because it affects runtime time behavior of * imports.

  3. Dr-Irv commented on Sep 18, 2023

    @Dr-Irv
    Author

    Now, it is an implementation detail that has a relatively widespread use,

    Yes, I believe the person who wrote the testing code for pandas-stubs that used matplotlib.pyplot.Axes and matplotlib.pyplot.Figure did that based on his experience.

  4. timhoffm commented on Sep 18, 2023

    @timhoffm
    Member

    That PR does include Axes and Figure, though I think partly because it affects runtime time behavior of * imports.

    Just to clarify: #12743 was a very first step of collection. It explicitly states "The hard-coded part of functions in __all__ needs some cleanup. Possible some are missing, while others are not neccesary. The state of the PR does not tell anything about intended public/private convention.


    My (strong) opinion on Axes/Figure being available from pyplot (or pylab): These modules are function based interfaces. Users have no business in accessing the classes via them. I wish python had a reasonable way to mark them as private but there's officially only the crude "is-not-documented" convention. Given where we are, I propose no further action. In particular that means:

    • We continue to consider these classes private within pyplot - "communicated by not documenting"
    • Runtime usage will continue to work. (But you are relying on an implementation detail then).
    • We do not add the type information for these classes from pyplot, in line with considering them to be private.

    I agree with @ksunden's statement

    type hints can be used as a mechanism to communicate and encourage best practices, while not breaking runtime behavior.

    Before, we weren't able to communicate this reasonably. TBH this "private by not documented" claim from PEP-8 is horrific. Code and maybe convention should define the API not documentation. Unfortunately, there are no means within the Python language (except for crude import renaming like from matplotlib.axes import Axes as _Axes, which I reject for other reasons).
    The best that can come off this is that people realize through the type checker that they were using the library not as intended.

  5. Dr-Irv commented on Sep 18, 2023

    @Dr-Irv
    Author

    The best that can come off this is that people realize through the type checker that they were using the library not as intended.

    I agree with your conclusion. We've taken a similar philosophy with pandas-stubs. We are using the concept of "the typing stubs cover what is documented".

  6. added a commit that references this issue on Sep 30, 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

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions