You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[MNT]: Deprecate mutability of FontProperties #22495
Currently, FontProperties (FPs) are mutable, which makes caching based on them a bit tricky (#22323, #22487). Manipulation is also not so nice; e.g. the fonts_demo.py example needs to repeatedly copy a base FP instance and call setters on the copies. Moreover, if one writes
then later mutation of fp doesn't modify the fontproperties on textobj, because set_fontproperties explicitly makes a copy of its argument. While this kind of "isolate-from-later-changes" is something we typically aim for in Matplotlib (see recent PRs likewise isolating artists from later changes in passed-in data arrays), this also means that there is basically no functionality gain coming from mutable FPs (and at least one complication, in the case of caching). (If set_fontproperties did not copy, then mutability would provide a way for two text objects to share exactly the same FP while having to mutate only one of them.)
I would therefore argue that the various property setters on FP should be deprecated to ultimately make FPs immutable; instead, we should use with_foo() methods that return new instances with just a single property being changed (similar to the pathlib API, for example). More precisely, we can either have with_family(family), with_style(style), with_variant(variant), etc., or instead we could also have a single with_props(**kwargs) (called e.g. as with_props(family="Times", ...)); I don't have a very strong opinion there.
The change to data classes is a little larger than only removing setters. But it may be simpler to understand and use in the long run. I also don't expect that getters and setters in FontProperties are widely used, so the change could be bearable.
The with_foo() pattern is definitively an option (and we do the same for colormaps). However, we should have a multi-setter.fp.with_family(name).with_size(24).with_style('italics') is hard to read and would require a copy for each update.
The multi-setter must be the basis and should be written first. Optionally, we can then create with_foo()-type functions as a convenience layer on top. Naming fp.with_properties(family=name, size=24, style='italics') would be an option; or we ammend the existing copy() with the optional arguments: fp.copy(family=name, size=24, style='italics') - Read: "copy fp and replace family by name, size by 24, style by 'italics').
The multi-setter must be the basis and should be written first. Optionally, we can then create with_foo()-type functions as a convenience layer on top. Naming fp.with_properties(family=name, size=24, style='italics') would be an option; or we ammend the existing copy() with the optional arguments: fp.copy(family=name, size=24, style='italics') - Read: "copy fp and replace family by name, size by 24, style by 'italics').
Data classes already have .replace() as their version of this.
Yes, if we can go for dataclasses the creation of modified versions is solved. I’m don’t oversee how big a jump the change to dataclasses are. My intuition is that modifying the existing class is simpler and if we want to end up with a dataclasses, a transition through a dataclass-like own class will be smoother. In that sense, we could roll our own replace function.
Activity
timhoffm commentedon Feb 19, 2022
Agree with immutability. Interface t.b.d. Could this even be a frozen dataclass?
jklymak commentedon Feb 19, 2022
I think you closed this accidentally?
anntzer commentedon Feb 19, 2022
A frozen dataclass could possibly work, indeed.
timhoffm commentedon Feb 19, 2022
Sorry for closing, I hit the wrong button.
The change to data classes is a little larger than only removing setters. But it may be simpler to understand and use in the long run. I also don't expect that getters and setters in FontProperties are widely used, so the change could be bearable.
MNT: Use __init__ parameters of font properties
timhoffm commentedon Sep 18, 2024
The
with_foo()pattern is definitively an option (and we do the same for colormaps). However, we should have a multi-setter.fp.with_family(name).with_size(24).with_style('italics')is hard to read and would require a copy for each update.The multi-setter must be the basis and should be written first. Optionally, we can then create
with_foo()-type functions as a convenience layer on top. Namingfp.with_properties(family=name, size=24, style='italics')would be an option; or we ammend the existingcopy()with the optional arguments:fp.copy(family=name, size=24, style='italics')- Read: "copy fp and replace family by name, size by 24, style by 'italics').MNT: Use __init__ parameters of font properties
QuLogic commentedon Jun 21, 2025
Data classes already have
.replace()as their version of this.timhoffm commentedon Jun 21, 2025
Yes, if we can go for dataclasses the creation of modified versions is solved. I’m don’t oversee how big a jump the change to dataclasses are. My intuition is that modifying the existing class is simpler and if we want to end up with a dataclasses, a transition through a dataclass-like own class will be smoother. In that sense, we could roll our own
replacefunction.