Skip to content

Commit a22417e

Browse files
committed
MNT: Remove font-related deprecations from 3.10
1 parent fe78309 commit a22417e

7 files changed

Lines changed: 108 additions & 328 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
ft2font module-level constants replaced by enums
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The `.ft2font`-level constants have been converted to `enum` classes, and all API using
5+
them now take/return the new types. Any access to the old module-level names has been
6+
removed.
7+
8+
The following constants are now part of `.ft2font.Kerning` (without the ``KERNING_``
9+
prefix):
10+
11+
- ``KERNING_DEFAULT``
12+
- ``KERNING_UNFITTED``
13+
- ``KERNING_UNSCALED``
14+
15+
The following constants are now part of `.ft2font.LoadFlags` (without the ``LOAD_``
16+
prefix):
17+
18+
- ``LOAD_DEFAULT``
19+
- ``LOAD_NO_SCALE``
20+
- ``LOAD_NO_HINTING``
21+
- ``LOAD_RENDER``
22+
- ``LOAD_NO_BITMAP``
23+
- ``LOAD_VERTICAL_LAYOUT``
24+
- ``LOAD_FORCE_AUTOHINT``
25+
- ``LOAD_CROP_BITMAP``
26+
- ``LOAD_PEDANTIC``
27+
- ``LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH``
28+
- ``LOAD_NO_RECURSE``
29+
- ``LOAD_IGNORE_TRANSFORM``
30+
- ``LOAD_MONOCHROME``
31+
- ``LOAD_LINEAR_DESIGN``
32+
- ``LOAD_NO_AUTOHINT``
33+
- ``LOAD_TARGET_NORMAL``
34+
- ``LOAD_TARGET_LIGHT``
35+
- ``LOAD_TARGET_MONO``
36+
- ``LOAD_TARGET_LCD``
37+
- ``LOAD_TARGET_LCD_V``
38+
39+
The following constants are now part of `.ft2font.FaceFlags`:
40+
41+
- ``EXTERNAL_STREAM``
42+
- ``FAST_GLYPHS``
43+
- ``FIXED_SIZES``
44+
- ``FIXED_WIDTH``
45+
- ``GLYPH_NAMES``
46+
- ``HORIZONTAL``
47+
- ``KERNING``
48+
- ``MULTIPLE_MASTERS``
49+
- ``SCALABLE``
50+
- ``SFNT``
51+
- ``VERTICAL``
52+
53+
The following constants are now part of `.ft2font.StyleFlags`:
54+
55+
- ``ITALIC``
56+
- ``BOLD``
57+
58+
FontProperties initialization
59+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60+
61+
`.FontProperties` initialization is limited to the two call patterns:
62+
63+
- single positional parameter, interpreted as fontconfig pattern
64+
- only keyword parameters for setting individual properties
65+
66+
All other previously supported call patterns are no longer supported.
67+
68+
Passing floating-point values to ``RendererAgg.draw_text_image``
69+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
70+
71+
Any floating-point values passed to the *x* and *y* parameters were truncated to integers
72+
silently. This behaviour is no longer allowed, and only `int` values should be used.
73+
74+
Passing floating-point values to ``FT2Image``
75+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
76+
77+
Any floating-point values passed to the `.FT2Image` constructor, or the *x0*, *y0*, *x1*,
78+
and *y1* parameters of `.FT2Image.draw_rect_filled` were truncated to integers silently.
79+
This behaviour is no longer allowed, and only `int` values should be used.

lib/matplotlib/font_manager.py

Lines changed: 12 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
from base64 import b64encode
3131
import dataclasses
3232
from functools import cache, lru_cache
33-
import functools
3433
from io import BytesIO
3534
import json
3635
import logging
@@ -695,57 +694,6 @@ def afmFontProperty(fontpath, font):
695694
return FontEntry(fontpath, 0, name, style, variant, weight, stretch, size)
696695

697696

698-
def _cleanup_fontproperties_init(init_method):
699-
"""
700-
A decorator to limit the call signature to a single positional argument
701-
or alternatively only keyword arguments.
702-
703-
We still accept but deprecate all other call signatures.
704-
705-
When the deprecation expires we can switch the signature to::
706-
707-
__init__(self, pattern=None, /, *, family=None, style=None, ...)
708-
709-
plus a runtime check that pattern is not used alongside with the
710-
keyword arguments. This results eventually in the two possible
711-
call signatures::
712-
713-
FontProperties(pattern)
714-
FontProperties(family=..., size=..., ...)
715-
716-
"""
717-
@functools.wraps(init_method)
718-
def wrapper(self, *args, **kwargs):
719-
# multiple args with at least some positional ones
720-
if len(args) > 1 or len(args) == 1 and kwargs:
721-
# Note: Both cases were previously handled as individual properties.
722-
# Therefore, we do not mention the case of font properties here.
723-
_api.warn_deprecated(
724-
"3.10",
725-
message="Passing individual properties to FontProperties() "
726-
"positionally was deprecated in Matplotlib %(since)s and "
727-
"will be removed in %(removal)s. Please pass all properties "
728-
"via keyword arguments."
729-
)
730-
# single non-string arg -> clearly a family not a pattern
731-
if len(args) == 1 and not kwargs and not cbook.is_scalar_or_string(args[0]):
732-
# Case font-family list passed as single argument
733-
_api.warn_deprecated(
734-
"3.10",
735-
message="Passing family as positional argument to FontProperties() "
736-
"was deprecated in Matplotlib %(since)s and will be removed "
737-
"in %(removal)s. Please pass family names as keyword"
738-
"argument."
739-
)
740-
# Note on single string arg:
741-
# This has been interpreted as pattern so far. We are already raising if a
742-
# non-pattern compatible family string was given. Therefore, we do not need
743-
# to warn for this case.
744-
return init_method(self, *args, **kwargs)
745-
746-
return wrapper
747-
748-
749697
class FontProperties:
750698
"""
751699
A class for storing and manipulating font properties.
@@ -814,11 +762,17 @@ class FontProperties:
814762
fontconfig.
815763
"""
816764

817-
@_cleanup_fontproperties_init
818-
def __init__(self, family=None, style=None, variant=None, weight=None,
765+
def __init__(self, pattern=None, /, *,
766+
family=None, style=None, variant=None, weight=None,
819767
stretch=None, size=None,
820768
fname=None, # if set, it's a hardcoded filename to use
821769
math_fontfamily=None):
770+
if pattern is not None:
771+
if not (family is None and style is None and variant is None and
772+
weight is None and stretch is None and size is None and
773+
fname is None):
774+
raise TypeError("Passing both a fontconfig pattern and individual "
775+
"properties to FontProperties() is invalid")
822776
self.set_family(family)
823777
self.set_style(style)
824778
self.set_variant(variant)
@@ -827,13 +781,10 @@ def __init__(self, family=None, style=None, variant=None, weight=None,
827781
self.set_file(fname)
828782
self.set_size(size)
829783
self.set_math_fontfamily(math_fontfamily)
830-
# Treat family as a fontconfig pattern if it is the only parameter
831-
# provided. Even in that case, call the other setters first to set
832-
# attributes not specified by the pattern to the rcParams defaults.
833-
if (isinstance(family, str)
834-
and style is None and variant is None and weight is None
835-
and stretch is None and size is None and fname is None):
836-
self.set_fontconfig_pattern(family)
784+
# Even in the case a fontconfig pattern is provided, call the other setters
785+
# first to set attributes not specified by the pattern to the rcParams defaults.
786+
if pattern is not None:
787+
self.set_fontconfig_pattern(pattern)
837788

838789
@classmethod
839790
def _from_any(cls, arg):

lib/matplotlib/font_manager.pyi

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ from dataclasses import dataclass
33
from numbers import Integral
44
import os
55
from pathlib import Path
6-
from typing import Any, Final, Literal
6+
from typing import Any, Final, Literal, overload
77

88
from matplotlib._afm import AFM
99
from matplotlib import ft2font
@@ -62,8 +62,11 @@ def ttfFontProperty(font: ft2font.FT2Font) -> FontEntry: ...
6262
def afmFontProperty(fontpath: str, font: AFM) -> FontEntry: ...
6363

6464
class FontProperties:
65+
@overload
66+
def __init__(self, pattern: str, /) -> None: ...
67+
@overload
6568
def __init__(
66-
self,
69+
self, *,
6770
family: str | Iterable[str] | None = ...,
6871
style: Literal["normal", "italic", "oblique"] | None = ...,
6972
variant: Literal["normal", "small-caps"] | None = ...,

lib/matplotlib/tests/test_font_manager.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -564,35 +564,23 @@ def test_fontproperties_init_deprecation():
564564
which calls do and do not issue deprecation warnings. Behavior is still
565565
tested via the existing regular tests.
566566
"""
567-
with pytest.warns(mpl.MatplotlibDeprecationWarning):
567+
with pytest.raises(TypeError):
568568
# multiple positional arguments
569569
FontProperties("Times", "italic")
570570

571-
with pytest.warns(mpl.MatplotlibDeprecationWarning):
571+
with pytest.raises(TypeError):
572572
# Mixed positional and keyword arguments
573573
FontProperties("Times", size=10)
574574

575-
with pytest.warns(mpl.MatplotlibDeprecationWarning):
575+
with pytest.raises(TypeError):
576576
# passing a family list positionally
577577
FontProperties(["Times"])
578578

579579
# still accepted:
580580
FontProperties(family="Times", style="italic")
581581
FontProperties(family="Times")
582-
FontProperties("Times") # works as pattern and family
583582
FontProperties("serif-24:style=oblique:weight=bold") # pattern
584583

585-
# also still accepted:
586-
# passing as pattern via family kwarg was not covered by the docs but
587-
# historically worked. This is left unchanged for now.
588-
# AFAICT, we cannot detect this: We can determine whether a string
589-
# works as pattern, but that doesn't help, because there are strings
590-
# that are both pattern and family. We would need to identify, whether
591-
# a string is *not* a valid family.
592-
# Since this case is not covered by docs, I've refrained from jumping
593-
# extra hoops to detect this possible API misuse.
594-
FontProperties(family="serif-24:style=oblique:weight=bold")
595-
596584

597585
def test_normalize_weights():
598586
assert _normalize_weight(300) == 300 # passthrough

lib/matplotlib/tests/test_ft2font.py

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -814,30 +814,6 @@ def test_ft2font_get_kerning(left, right, unscaled, unfitted, default):
814814
assert font.get_kerning(font.get_char_index(ord(left)),
815815
font.get_char_index(ord(right)),
816816
ft2font.Kerning.DEFAULT) == default
817-
with pytest.warns(mpl.MatplotlibDeprecationWarning,
818-
match='Use Kerning.UNSCALED instead'):
819-
k = ft2font.KERNING_UNSCALED
820-
with pytest.warns(mpl.MatplotlibDeprecationWarning,
821-
match='Use Kerning enum values instead'):
822-
assert font.get_kerning(font.get_char_index(ord(left)),
823-
font.get_char_index(ord(right)),
824-
int(k)) == unscaled
825-
with pytest.warns(mpl.MatplotlibDeprecationWarning,
826-
match='Use Kerning.UNFITTED instead'):
827-
k = ft2font.KERNING_UNFITTED
828-
with pytest.warns(mpl.MatplotlibDeprecationWarning,
829-
match='Use Kerning enum values instead'):
830-
assert font.get_kerning(font.get_char_index(ord(left)),
831-
font.get_char_index(ord(right)),
832-
int(k)) == unfitted
833-
with pytest.warns(mpl.MatplotlibDeprecationWarning,
834-
match='Use Kerning.DEFAULT instead'):
835-
k = ft2font.KERNING_DEFAULT
836-
with pytest.warns(mpl.MatplotlibDeprecationWarning,
837-
match='Use Kerning enum values instead'):
838-
assert font.get_kerning(font.get_char_index(ord(left)),
839-
font.get_char_index(ord(right)),
840-
int(k)) == default
841817

842818

843819
def test_ft2font_set_text():

src/_backend_agg_wrapper.cpp

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -58,37 +58,8 @@ PyRendererAgg_draw_path(RendererAgg *self,
5858
static void
5959
PyRendererAgg_draw_text_image(RendererAgg *self,
6060
py::array_t<agg::int8u, py::array::c_style | py::array::forcecast> image_obj,
61-
std::variant<int, double> vx,
62-
std::variant<int, double> vy,
63-
double angle,
64-
GCAgg &gc)
61+
int x, int y, double angle, GCAgg &gc)
6562
{
66-
int x, y;
67-
68-
if (auto value = std::get_if<double>(&vx)) {
69-
auto api = py::module_::import("matplotlib._api");
70-
auto warn = api.attr("warn_deprecated");
71-
warn("since"_a="3.10", "name"_a="x", "obj_type"_a="parameter as float",
72-
"alternative"_a="int(x)");
73-
x = static_cast<int>(*value);
74-
} else if (auto value = std::get_if<int>(&vx)) {
75-
x = *value;
76-
} else {
77-
throw std::runtime_error("Should not happen");
78-
}
79-
80-
if (auto value = std::get_if<double>(&vy)) {
81-
auto api = py::module_::import("matplotlib._api");
82-
auto warn = api.attr("warn_deprecated");
83-
warn("since"_a="3.10", "name"_a="y", "obj_type"_a="parameter as float",
84-
"alternative"_a="int(y)");
85-
y = static_cast<int>(*value);
86-
} else if (auto value = std::get_if<int>(&vy)) {
87-
y = *value;
88-
} else {
89-
throw std::runtime_error("Should not happen");
90-
}
91-
9263
// TODO: This really shouldn't be mutable, but Agg's renderer buffers aren't const.
9364
auto image = image_obj.mutable_unchecked<2>();
9465

0 commit comments

Comments
 (0)