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
[Bug]: Argument types for handles
and labels
are too strict for method Axes.legend
#27507
Comments
handles
and labels are too strict for method
Axes.legend`handles
and labels
are too strict for method Axes.legend
The docstring does call out "sequence", which matches the type hint, at least for the parameter description of That said, earlier in the body of the docstring, it does in fact say "iterable", so I think we should be consistent... Speaking more generally, I'd argue that just because the current implementation works doesn't mean we must guarantee it going forward. The type hints are describing (to the best of our ability, note that they are still provisional) what we consider to be the formal API, deviating (well, at least narrowing), would be considered an API change. |
Note that we do not systematically make a distinction between, list, sequence and iterable in the docstring type https://matplotlib.org/devdocs/devel/document.html#parameter-type-descriptions
We use this simplification to make the docs easier to read for less experienced python users. This does not hold for type hints, which should be rather exact. IMO the problem with iterations us that they can be infinite, which of course is not a valid input. AFAIK, the type system cannot distinguish between finite and infinite iterators. So Sequence is too strict and Iterator would be too loose. |
I think the guiding principle for typing Python code should generally be the same as for good API design, namely the robustness principle "be conservative in what you send, be liberal in what you accept". My main motivation for this change is memory efficiency, if I want to e.g. reverse the order of labels/handles in the legend I can currently do the following, if I ignore the type annotations: handles, labels = ax.get_legend_handles_labels()
ax.legend(reversed(handles), reversed(labels)) If I follow the restriction I have to do: handles, labels = ax.get_legend_handles_labels()
ax.legend(list(reversed(handles)), list(reversed(labels))) Which creates two unnecessary lists. Another use-case would be aggregating the legend from multiple subplots into a shared legend. Rather than creating a new list, I would like to be able to use Annotations imho should generally be guided by implementation first, and documentation second, especially if the terminology that was used is vague. I don't think the argument of guaranteeing forward compatibility through type annotations holds any water, if it worked previously, you will break people's code if you decide to go with a more narrow implementation and you will get bug reports. So I think it's generally more productive for the type annotations on parameters to be as broad as possible and reflect reality. That way, if you do, for whatever reason, decide to make a breaking change, people that do use type checkers will know whether or not they are affected, and where they need to make changes. |
At #27004 we added a test to ensure iterators would work… |
I'm not sure this should be a problem. If we have a parameter that has to be a float between 0 and 1, we would still type it as I would vote for changing the hints to matplotlib/lib/matplotlib/legend.py Lines 1349 to 1358 in cf60835
@Daverball seems to have a reasonable use-case, and we have a test so we will not accidentally break it. |
Bug summary
Axes.legend
is annotated too strictly and incorrectly necessitatesSequence
forhandles
andlabels
, even though anyIterable
works. This also contradicts the docstring on the method.Code for reproduction
Actual outcome
No overload variant of "legend" of "Axes" matches argument types "Iterator[Artist]", "Iterator[Any]" [call-overload]
Expected outcome
Passes without type error
Additional information
No response
Operating system
No response
Matplotlib Version
3.8.2
Matplotlib Backend
No response
Python version
3.10
Jupyter version
No response
Installation
pip
The text was updated successfully, but these errors were encountered: