From f63e4386d2ad8d006a5b1b827dfabd02e1e8c143 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:04:02 +0200 Subject: [PATCH] MNT: Deprecate FontProperties(pattern) ... and use `FontProperties.from_pattern()` instead. `FontProperties(pattern)` was an overloaded API that easily conflicts with `FontProperties(family)`. The parameter was assumed to be a pattern if it was a string and the only parameter. --- .../deprecations/28837-TH.rst | 5 ++ lib/matplotlib/font_manager.py | 54 ++++++++++++++----- lib/matplotlib/font_manager.pyi | 2 + .../tests/test_fontconfig_pattern.py | 34 +++++++++--- 4 files changed, 77 insertions(+), 18 deletions(-) create mode 100644 doc/api/next_api_changes/deprecations/28837-TH.rst diff --git a/doc/api/next_api_changes/deprecations/28837-TH.rst b/doc/api/next_api_changes/deprecations/28837-TH.rst new file mode 100644 index 000000000000..b61a6dd7aa45 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/28837-TH.rst @@ -0,0 +1,5 @@ +FontProperties(pattern) +~~~~~~~~~~~~~~~~~~~~~~~ + +Passing a single string parameter as fontconfig pattern to `.FontProperties` is +deprecated. Use `.FontProperties.from_pattern` instead. diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index d9560ec0cc0f..fcebbcebd244 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -585,18 +585,22 @@ class FontProperties: approach allows all text sizes to be made larger or smaller based on the font manager's default font size. - This class will also accept a fontconfig_ pattern_, if it is the only - argument provided. This support does not depend on fontconfig; we are - merely borrowing its pattern syntax for use here. - - .. _fontconfig: https://www.freedesktop.org/wiki/Software/fontconfig/ - .. _pattern: - https://www.freedesktop.org/software/fontconfig/fontconfig-user.html - - Note that Matplotlib's internal font manager and fontconfig use a - different algorithm to lookup fonts, so the results of the same pattern - may be different in Matplotlib than in other applications that use - fontconfig. + .. deprecated:: 3.10 + + **Use `.FontProperties.from_pattern` instead.** + + This class will also accept a fontconfig_ pattern_, if it is the only + argument provided. This support does not depend on fontconfig; we are + merely borrowing its pattern syntax for use here. + + .. _fontconfig: https://www.freedesktop.org/wiki/Software/fontconfig/ + .. _pattern: + https://www.freedesktop.org/software/fontconfig/fontconfig-user.html + + Note that Matplotlib's internal font manager and fontconfig use a + different algorithm to lookup fonts, so the results of the same pattern + may be different in Matplotlib than in other applications that use + fontconfig. """ def __init__(self, family=None, style=None, variant=None, weight=None, @@ -617,8 +621,34 @@ def __init__(self, family=None, style=None, variant=None, weight=None, if (isinstance(family, str) and style is None and variant is None and weight is None and stretch is None and size is None and fname is None): + _api.warn_deprecated( + "3.10", + message="Passing a single string as fontconfig pattern to " + "FontProperties is deprecated. Use " + "FontProperties.from_pattern() instead.") self.set_fontconfig_pattern(family) + @classmethod + def from_pattern(cls, pattern): + """ + Create FontProperties from a fontconfig_ pattern_. + + .. _fontconfig: https://www.freedesktop.org/wiki/Software/fontconfig/ + .. _pattern: + https://www.freedesktop.org/software/fontconfig/fontconfig-user.html + + This support does not depend on fontconfig; we are merely borrowing its + pattern syntax for use here. + + Note that Matplotlib's internal font manager and fontconfig use a + different algorithm to lookup fonts, so the results of the same pattern + may be different in Matplotlib than in other applications that use + fontconfig. + """ + prop = FontProperties() + prop.set_fontconfig_pattern(pattern) + return prop + @classmethod def _from_any(cls, arg): """ diff --git a/lib/matplotlib/font_manager.pyi b/lib/matplotlib/font_manager.pyi index 48d0e362d599..d99cc85b4b2a 100644 --- a/lib/matplotlib/font_manager.pyi +++ b/lib/matplotlib/font_manager.pyi @@ -53,6 +53,8 @@ class FontProperties: fname: str | os.PathLike | Path | None = ..., math_fontfamily: str | None = ..., ) -> None: ... + @classmethod + def from_pattern(cls, pattern: str) -> FontProperties: ... def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... def get_family(self) -> list[str]: ... diff --git a/lib/matplotlib/tests/test_fontconfig_pattern.py b/lib/matplotlib/tests/test_fontconfig_pattern.py index 496a35b95cf2..cf3ae8ba61aa 100644 --- a/lib/matplotlib/tests/test_fontconfig_pattern.py +++ b/lib/matplotlib/tests/test_fontconfig_pattern.py @@ -1,5 +1,6 @@ import pytest +from matplotlib import _api from matplotlib.font_manager import FontProperties @@ -21,7 +22,7 @@ def test_fontconfig_pattern(): f1 = FontProperties() s = str(f1) - f2 = FontProperties(s) + f2 = FontProperties.from_pattern(s) for k in keys: assert getattr(f1, k)() == getattr(f2, k)(), test + k @@ -30,7 +31,7 @@ def test_fontconfig_pattern(): f1 = FontProperties(family="serif", size=20, style="italic") s = str(f1) - f2 = FontProperties(s) + f2 = FontProperties.from_pattern(s) for k in keys: assert getattr(f1, k)() == getattr(f2, k)(), test + k @@ -41,11 +42,26 @@ def test_fontconfig_pattern(): stretch="expanded") s = str(f1) - f2 = FontProperties(s) + f2 = FontProperties.from_pattern(s) for k in keys: assert getattr(f1, k)() == getattr(f2, k)(), test + k +def test_fontconfig_pattern_deprecated(): + """ + Make sure (at one example) that the deprecated API still works. + + Same test as test_fontconfig_pattern test="basic", but with the deprecate API. + """ + f1 = FontProperties(family="serif", size=20, style="italic") + s = str(f1) + + with _api.suppress_matplotlib_deprecation_warning: + f2 = FontProperties(s) + for k in keys: + assert getattr(f1, k)() == getattr(f2, k)() + + def test_fontconfig_str(): """Test FontProperties string conversions for correctness.""" @@ -56,7 +72,7 @@ def test_fontconfig_str(): test = "defaults " s = ("sans\\-serif:style=normal:variant=normal:weight=normal" ":stretch=normal:size=12.0") - font = FontProperties(s) + font = FontProperties.from_pattern(s) right = FontProperties() for k in keys: assert getattr(font, k)() == getattr(right, k)(), test + k @@ -64,7 +80,7 @@ def test_fontconfig_str(): test = "full " s = ("serif-24:style=oblique:variant=small-caps:weight=bold" ":stretch=expanded") - font = FontProperties(s) + font = FontProperties.from_pattern(s) right = FontProperties(family="serif", size=24, weight="bold", style="oblique", variant="small-caps", stretch="expanded") @@ -74,4 +90,10 @@ def test_fontconfig_str(): def test_fontconfig_unknown_constant(): with pytest.raises(ValueError, match="ParseException"): - FontProperties(":unknown") + FontProperties.from_pattern(":unknown") + + +def test_fontconfig_unknown_constant_depreacted(): + with _api.suppress_matplotlib_warnings(): + with pytest.raises(ValueError, match="ParseException"): + FontProperties(":unknown")