-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
FIX: Allow different colormap name from registered name #25479
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
Conversation
lib/matplotlib/pyplot.py
Outdated
# The name of the colormap may be different than the registered name. | ||
# We want the registered name for the rc setting | ||
name = getattr(cmap, "name", cmap) | ||
# Run this through get_cmap to confirm this is a valid cmap before |
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.
Can this be clearer which "this" is meant here? I almost commented based on this comment that the next line should be cmap = get_cmap(name)
, but am no longer sure which was intended.
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.
I think we want to use get_cmap(cmap)
here in case someone didn't register the colormap? Although that would also mean the rc paramter wouldn't work then either...
4fbc57d
to
16a8477
Compare
lib/matplotlib/pyplot.py
Outdated
# We want the registered name for the rc setting | ||
name = getattr(cmap, "name", cmap) | ||
# Run the cmap (str or Colormap) through get_cmap to validate | ||
# the cmap before updating the rc parameters | ||
cmap = get_cmap(cmap) |
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.
Perhaps clearer to return a nonesense variable here? Even with he comments, I thought the next line was a typo...
cmap = get_cmap(cmap) | |
_ = get_cmap(cmap) |
lib/matplotlib/pyplot.py
Outdated
cmap = get_cmap(cmap) | ||
|
||
rc('image', cmap=cmap.name) | ||
rc('image', cmap=name) |
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.
If I do:
my_cmap = mpl.colormaps["viridis"].copy()
my_cmap.set_bad("FF00FF")
mpl.colormaps.register("my-cmap", my_cmap) # note, did not update cmap.name
plt.set_cmap(my_cmap) # note, passing instance, not name
Won't this get "viridis"
from the getattr
, and thus set that as the rc cmap? (It will apply the "correct" cmap to gci
, but not to future mappables)
Not sure how to square that without a) doing a reverse lookup in the registry or b) requiring that the names match when registered.
Perhaps this change is still better than no change, but not sure it's without its rough edges still... certainly it makes it possible to use a mismatched cmap, but you do need to get it by the registry name, not passing the cmap object.
Of course, a bug that has been hanging around for a while isn't super easy to fix. @ksunden, good catch and explanation about that edge-case. You said it much better than me in one of the other comments:
Question: Is it OK to assign a colormap object to the I just pushed up a commit with that, which I think is the more robust way to do this, but all other rcParams I think are strings, so I'm not sure if this is OK during runtime. |
lib/matplotlib/pyplot.py
Outdated
cmap = get_cmap(cmap) | ||
|
||
rc('image', cmap=cmap.name) | ||
rc('image', cmap=cmap) |
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.
See here for changing the string name to the object itself.
I was about to say I was wary of just putting the colormap object in rcparams....but the validator allows it from bb2de5e so we support that from 3.4 on. Maybe the logic should be if the named lookup is itself then use the string otherwise the object? |
I think we'd need to add a bit of unsightly code and try/except for this case because the name of the cmap may not be in the lookup table if it was registered under a different name, thus throwing a ValueError cmap is not a valid name. It seems like quite a bit of complication to add and I'm not sure how much the string nature matters here since we are only doing this during runtime? Unless someone is exporting the rcParams to a file and hoping to reimport them later with a specific string... |
Since I changed this to updating the rcParams with an object instead of string, I've remilestoned to 3.8 instead because some people may be relying on rcParams of a certain type and it doesn't seem quite right for a patch release and this bug has been around for a while already. Happy to change back if people think otherwise. |
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.
I'm 👎 on registering a colormap with a name different from its original name. This is confusing up to dangerous.
By default colormaps.register(cmap)
uses cmap.name
. The only reason to use register(cmap, name=other_name)
is that the user intentionally wants to register the colormap under a different name. We can interpret this as authorization to rename the colormap itself as well.
Note that ColormapRegistry.register()
stores a copy of the given colormap to avoid any later modifcations. Since renaming would only apply to this copy, it cannot have unintended side-effects.
I regard this as a bug-fix. It can be applied immediately.
I'm somewhat sceptical assigning complex objects to rcParams
. Even though this is technically supported through the validator, I would not go down this road further without need (and I think with the above renaming, there is no need to actively set image.cmap
to a Colormap
from our side).
40a321f
to
e327961
Compare
@timhoffm, thank you for taking a look at this and coming up with that suggestion! I like the idea of setting the name on the object that gets registered to keep it consistent and I think that flows nicely with expectations from an end-user perspective (I forgot we make a copy upon registering now). I've updated the code accordingly. Note that I also removed |
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.
Modulo, restoring the equal test.
lib/matplotlib/tests/test_colors.py
Outdated
# Test different names are not equal | ||
cm_copy = cmap.copy() | ||
cm_copy.name = "Test" | ||
assert cm_copy != cmap |
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.
I suggest to keep this and test that colormaps with different names are still equal.
When registering a colormap you can use a different name than the "cmap.name" attribute now. This will set the colormap based on the registered name rather than cmap.name, updating the copy of the object that gets registered. Additionally, the equals method of Colormaps shouldn't care about the name of the object, we should only worry about whether the values are the same in the lookup tables and let someone use object identity if they are worried about the name of the object.
e327961
to
cc977dc
Compare
PR Summary
When registering a colormap you can use a different name than the
cmap.name
attribute now. This will set the colormap based on the registered name rather thancmap.name
.closes #5087
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