8000 Update type hints for font manager and extension by QuLogic · Pull Request #26403 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Update type hints for font manager and extension #26403

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

Merged
merged 1 commit into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ci/mypy-stubtest-allowlist.txt
8000
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ matplotlib.pylab.*
matplotlib._.*
matplotlib.rcsetup._listify_validator
matplotlib.rcsetup._validate_linestyle
matplotlib.ft2font.*
matplotlib.ft2font.Glyph
matplotlib.testing.jpl_units.*
matplotlib.sphinxext.*

Expand Down
5 changes: 4 additions & 1 deletion lib/matplotlib/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import subprocess
import sys
import threading
from typing import Union

import matplotlib as mpl
from matplotlib import _api, _afm, cbook, ft2font
Expand Down Expand Up @@ -315,7 +316,7 @@ def _fontentry_helper_repr_html(fontent):
('name', str, dataclasses.field(default='')),
('style', str, dataclasses.field(default='normal')),
('variant', str, dataclasses.field(default='normal')),
('weight', str, dataclasses.field(default='normal')),
('weight', Union[str, int], dataclasses.field(default='normal')),
('stretch', str, dataclasses.field(default='normal')),
('size', str, dataclasses.field(default='medium')),
],
Expand Down Expand Up @@ -464,6 +465,8 @@ def afmFontProperty(fontpath, font):

Parameters
----------
fontpath : str
The filename corresponding to *font*.
font : AFM
The AFM font file from which information will be extracted.

Expand Down
37 changes: 20 additions & 17 deletions lib/matplotlib/font_manager.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ X11FontDirectories: list[str]
OSXFontDirectories: list[str]

def get_fontext_synonyms(fontext: str) -> list[str]: ...
def list_fonts(directory: str, extensions: Iterable[str]): ...
def list_fonts(directory: str, extensions: Iterable[str]) -> list[str]: ...
def win32FontDirectory() -> str: ...
def _get_fontconfig_fonts() -> list[Path]: ...
def findSystemFonts(
fontpaths: Iterable[str] | None = ..., fontext: str = ...
fontpaths: Iterable[str | os.PathLike | Path] | None = ..., fontext: str = ...
) -> list[str]: ...
@dataclass
class FontEntry:
fname: str = ...
name: str = ...
style: str = ...
variant: str = ...
weight: str = ...
weight: str | int = ...
stretch: str = ...
size: str = ...

Expand All @@ -42,41 +42,44 @@ def afmFontProperty(fontpath: str, font: AFM) -> FontEntry: ...
class FontProperties:
def __init__(
self,
family: str | None = ...,
family: str | Iterable[str] | None = ...,
style: Literal["normal", "italic", "oblique"] | None = ...,
variant: Literal["normal", "small-caps"] | None = ...,
weight: int | str | None = ...,
stretch: int | str | None = ...,
size: float | str | None = ...,
fname: str | None = ...,
fname: str | os.PathLike | Path | None = ...,
math_fontfamily: str | None = ...,
) -> None: ...
def __hash__(self) -> int: ...
def __eq__(self, other: object) -> bool: ...
def get_family(self) -> str: ...
def get_family(self) -> list[str]: ...
def get_name(self) -> str: ...
def get_style(self) -> Literal["normal", "italic", "oblique"]: ...
def get_variant(self) -> Literal["normal", "small-caps"]: ...
def get_weight(self) -> int | str: ...
def get_stretch(self) -> int | str: ...
def get_size(self) -> float: ...
def get_file(self) -> str: ...
def get_file(self) -> str | bytes | None: ...
def get_fontconfig_pattern(self) -> dict[str, list[Any]]: ...
def set_family(self, family: str | Iterable[str]) -> None: ...
def set_style(self, style: Literal["normal", "italic", "oblique"]) -> None: ...
def set_variant(self, variant: Literal["normal", "small-caps"]) -> None: ...
def set_weight(self, weight: int | str) -> None: ...
def set_stretch(self, stretch: int | str) -> None: ...
def set_size(self, size: float | str) -> None: ...
def set_family(self, family: str | Iterable[str] | None) -> None: ...
def set_style(
self, style: Literal["normal", "italic", "oblique"] | None
) -> None: ...
def set_variant(self, variant: Literal["normal", "small-caps"] | None) -> None: ...
def set_weight(self, weight: int | str | None) -> None: ...
def set_stretch(self, stretch: int | str | None) -> None: ...
def set_size(self, size: float | str | None) -> None: ...
def set_file(self, file: str | os.PathLike | Path | None) -> None: ...
def set_fontconfig_pattern(self, pattern: str) -> None: ...
def get_math_fontfamily(self) -> str: ...
def set_math_fontfamily(self, fontfamily: str | None) -> None: ...
def copy(self) -> FontProperties: ...
def set_name(self, family: str) -> None: ...
def get_slant(self) -> Literal["normal", "italic", "oblique"]: ...
def set_slant(self, style: Literal["normal", "italic", "oblique"]) -> None: ...
def get_size_in_points(self) -> float: ...
# Aliases
set_name = set_family
get_slant = get_style
set_slant = set_style
get_size_in_points = get_size

def json_dump(data: FontManager, filename: str | Path | os.PathLike) -> None: ...
def json_load(filename: str | Path | os.PathLike) -> FontManager: ...
Expand Down
137 changes: 84 additions & 53 deletions lib/matplotlib/ft2font.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# This is generated from a compiled module, and as such is very generic
# This could be more specific. Docstrings for this module are light
from typing import BinaryIO, Literal

from typing import Any
import numpy as np
from numpy.typing import NDArray

__freetype_build_type__: str
__freetype_version__: str
BOLD: int
EXTERNAL_STREAM: int
FAST_GLYPHS: int
Expand Down Expand Up @@ -41,54 +43,83 @@ SFNT: int
VERTICAL: int

class FT2Font:
ascender: Any
bbox: Any
descender: Any
face_flags: Any
family_name: Any
fname: Any
height: Any
max_advance_height: Any
max_advance_width: Any
num_charmaps: Any
num_faces: Any
num_fixed_sizes: Any
num_glyphs: Any
postscript_name: Any
scalable: Any
style_flags: Any
style_name: Any
underline_position: Any
underline_thickness: Any
units_per_EM: Any
def __init__(self, *args, **kwargs) -> None: ...
def _get_fontmap(self, *args, **kwargs) -> Any: ...
def clear(self, *args, **kwargs) -> Any: ...
def draw_glyph_to_bitmap(self, *args, **kwargs) -> Any: ...
def draw_glyphs_to_bitmap(self, *args, **kwargs) -> Any: ...
def get_bitmap_offset(self, *args, **kwargs) -> Any: ...
def get_char_index(self, *args, **kwargs) -> Any: ...
def get_charmap(self, *args, **kwargs) -> Any: ...
def get_descent(self, *args, **kwargs) -> Any: ...
def get_glyph_name(self, *args, **kwargs) -> Any: ...
def get_image(self, *args, **kwargs) -> Any: ...
def get_kerning(self, *args, **kwargs) -> Any: ...
def get_name_index(self, *args, **kwargs) -> Any: ...
def get_num_glyphs(self, *args, **kwargs) -> Any: ...
def get_path(self, *args, **kwargs) -> Any: ...
def get_ps_font_info(self, *args, **kwargs) -> Any: ...
def get_sfnt(self, *args, **kwargs) -> Any: ...
def get_sfnt_table(self, *args, **kwargs) -> Any: ...
def get_width_height(self, *args, **kwargs) -> Any: ...
def get_xys(self, *args, **kwargs) -> Any: ...
def load_char(self, *args, **kwargs) -> Any: ...
def load_glyph(self, *args, **kwargs) -> Any: ...
def select_charmap(self, *args, **kwargs) -> Any: ...
def set_charmap(self, *args, **kwargs) -> Any: ...
def set_size(self, *args, **kwargs) -> Any: ...
def set_text(self, *args, **kwargs) -> Any: ...
ascender: int
bbox: tuple[int, int, int, int]
descender: int
face_flags: int
family_name: str
fname: str
height: int
max_advance_height: int
max_advance_width: int
num_charmaps: int
num_faces: int
num_fixed_sizes: int
num_glyphs: int
postscript_name: str
scalable: bool
style_flags: int
style_name: str
underline_position: int
underline_thickness: int
units_per_EM: int

class FT2Image:
def __init__(self, *args, **kwargs) -> None: ...
def draw_rect(self, *args, **kwargs) -> Any: ...
def draw_rect_filled(self, *args, **kwargs) -> Any: ...
def __init__(
self,
filename: str | BinaryIO,
hinting_factor: int = ...,
*,
_fallback_list: list[FT2Font] | None = ...,
_kerning_factor: int = ...
) -> None: ...
def _get_fontmap(self, string: str) -> dict[str, FT2Font]: ...
def clear(self) -> None: ...
def draw_glyph_to_bitmap(
9E88 self, image: FT2Image, x: float, y: float, glyph: Glyph, antialiased: bool = ...
) -> None: ...
def draw_glyphs_to_bitmap(self, antialiased: bool = ...) -> None: ...
def get_bitmap_offset(self) -> tuple[int, int]: ...
def get_char_index(self, codepoint: int) -> int: ...
def get_charmap(self) -> dict[int, int]: ...
def get_descent(self) -> int: ...
def get_glyph_name(self, index: int) -> str: ...
def get_image(self) -> NDArray[np.uint8]: ...
def get_kerning(self, left: int, right: int, mode: int) -> int: ...
def get_name_index(self, name: str) -> int: ...
def get_num_glyphs(self) -> int: ...
def get_path(self) -> tuple[NDArray[np.float64], NDArray[np.int8]]: ...
def get_ps_font_info(
self,
) -> tuple[str, str, str, str, str, int, int, int, int]: ...
def get_sfnt(self) -> dict[tuple[int, int, int, int], bytes]: ...
def get_sfnt_table(
self, name: Literal["head", "maxp", "OS/2", "hhea", "vhea", "post", "pclt"]
) -> dict[str, tuple[int, int, int, int] | tuple[int, int] | int | bytes]: ...
def get_width_height(self) -> tuple[int, int]: ...
def get_xys(self, antialiased: bool = ...) -> NDArray[np.float64]: ...
def load_char(self, charcode: int, flags: int = ...) -> Glyph: ...
def load_glyph(self, glyphindex: int, flags: int = ...) -> Glyph: ...
def select_charmap(self, i: int) -> None: ...
def set_charmap(self, i: int) -> None: ...
def set_size(self, ptsize: float, dpi: float) -> None: ...
def set_text(
self, string: str, angle: float = ..., flags: int = ...
) -> NDArray[np.float64]: ...

class FT2Image: # TODO: When updating mypy>=1.4, subclass from Buffer.
def __init__(self, width: float, height: float) -> None: ...
def draw_rect(self, x0: float, y0: float, x1: float, y1: float) -> None: ...
def draw_rect_filled(self, x0: float, y0: float, x1: float, y1: float) -> None: ...

class Glyph:
width: int
height: int
horiBearingX: int
horiBearingY: int
horiAdvance: int
linearHoriAdvance: int
vertBearingX: int
vertBearingY: int
vertAdvance: int

def bbox(self) -> tuple[int, int, int, int]: ...
8 changes: 4 additions & 4 deletions lib/matplotlib/tests/test_font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ def test_font_priority():
with rc_context(rc={
'font.sans-serif':
['cmmi10', 'Bitstream Vera Sans']}):
font = findfont(FontProperties(family=["sans-serif"]))
assert Path(font).name == 'cmmi10.ttf'
fontfile = findfont(FontProperties(family=["sans-serif"]))
assert Path(fontfile).name == 'cmmi10.ttf'

# Smoketest get_charmap, which isn't used internally anymore
font = get_font(font)
font = get_font(fontfile)
cmap = font.get_charmap()
assert len(cmap) == 131
assert cmap[8729] == 30
Expand Down Expand Up @@ -148,7 +148,7 @@ def test_find_invalid(tmpdir):
# Not really public, but get_font doesn't expose non-filename constructor.
from matplotlib.ft2font import FT2Font
with pytest.raises(TypeError, match='font file or a binary-mode file'):
FT2Font(StringIO())
FT2Font(StringIO()) # type: ignore[arg-type]


@pytest.mark.skipif(sys.platform != 'linux' or not has_fclist,
Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/tests/test_ft2font.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ def test_fallback_errors():

with pytest.raises(TypeError, match="Fallback list must be a list"):
# failing to be a list will fail before the 0
ft2font.FT2Font(file_name, _fallback_list=(0,))
ft2font.FT2Font(file_name, _fallback_list=(0,)) # type: ignore[arg-type]

with pytest.raises(
TypeError, match="Fallback fonts must be FT2Font objects."
):
ft2font.FT2Font(file_name, _fallback_list=[0])
ft2font.FT2Font(file_name, _fallback_list=[0]) # type: ignore[list-item]


def test_ft2font_positive_hinting_factor():
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/tests/test_mathtext.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@
font_tests: list[None | str] = []
for fonts, chars in font_test_specs:
if fonts is None:
font_tests.extend([None] * chars) # type: ignore
font_tests.extend([None] * chars)
else:
wrapper = ''.join([
' '.join(fonts),
Expand Down
41 changes: 23 additions & 18 deletions src/ft2font_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,46 +331,51 @@ const char *PyFT2Font_init__doc__ =
"Parameters\n"
"----------\n"
"filename : str or file-like\n"
" The source of the font data in a format (ttf or ttc) that FreeType can read\n\n"
" The source of the font data in a format (ttf or ttc) that FreeType can read\n"
"\n"
"hinting_factor : int, optional\n"
" Must be positive. Used to scale the hinting in the x-direction\n"
"_fallback_list : list of FT2Font, optional\n"
" A list of FT2Font objects used to find missing glyphs.\n\n"
" A list of FT2Font objects used to find missing glyphs.\n"
"\n"
" .. warning::\n"
" This API is both private and provisional: do not use it directly\n\n"
" This API is both private and provisional: do not use it directly\n"
"\n"
"_kerning_factor : int, optional\n"
" Used to adjust the degree of kerning.\n\n"
" Used to adjust the degree of kerning.\n"
"\n"
" .. warning::\n"
" This API is private: do not use it directly\n\n"
" This API is private: do not use it directly\n"
"\n"
"Attributes\n"
"----------\n"
"num_faces\n"
"num_faces : int\n"
" Number of faces in file.\n"
"face_flags, style_flags : int\n"
" Face and style flags; see the ft2font constants.\n"
"num_glyphs\n"
"num_glyphs : int\n"
" Number of glyphs in the face.\n"
"family_name, style_name\n"
"family_name, style_name : str\n"
" Face family and style name.\n"
"num_fixed_sizes\n"
"num_fixed_sizes : int\n"
" Number of bitmap in the face.\n"
"scalable\n"
"scalable : bool\n"
" Whether face is scalable; attributes after this one are only\n"
" defined for scalable faces.\n"
"bbox\n"
"bbox : tuple[int, int, int, int]\n"
" Face global bounding box (xmin, ymin, xmax, ymax).\n"
"units_per_EM\n"
"units_per_EM : int\n"
" Number of font units covered by the EM.\n"
"ascender, descender\n"
"ascender, descender : int\n"
" Ascender and descender in 26.6 units.\n"
"height\n"
"height : int\n"
" Height in 26.6 units; used to compute a default line spacing\n"
" (baseline-to-baseline distance).\n"
"max_advance_width, max_advance_height\n"
"max_advance_width, max_advance_height : int\n"
" Maximum horizontal and vertical cursor advance for all glyphs.\n"
"underline_position, underline_thickness\n"
"underline_position, underline_thickness : int\n"
" Vertical position and thickness of the underline bar.\n"
"postscript_name\n"
"postscript_name : str\n"
" PostScript name of the font.\n";

static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds)
Expand Down Expand Up @@ -627,7 +632,7 @@ static PyObject *PyFT2Font_get_fontmap(PyFT2Font *self, PyObject *args, PyObject


const char *PyFT2Font_set_text__doc__ =
"set_text(self, string, angle, flags=32)\n"
"set_text(self, string, angle=0.0, flags=32)\n"
"--\n\n"
"Set the text *string* and *angle*.\n"
"*flags* can be a bitwise-or of the LOAD_XXX constants;\n"
Expand Down
0