Merge pull request #8338 from yngvem/improve-error-messages

This commit is contained in:
Hugo van Kemenade 2024-09-18 18:22:04 +03:00 committed by GitHub
commit 743ddc7a58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 45 additions and 10 deletions

View File

@ -5,6 +5,7 @@ import os
import re import re
import shutil import shutil
import sys import sys
import tempfile
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from typing import Any, BinaryIO from typing import Any, BinaryIO
@ -460,17 +461,43 @@ def test_free_type_font_get_mask(font: ImageFont.FreeTypeFont) -> None:
assert mask.size == (108, 13) assert mask.size == (108, 13)
def test_load_when_image_not_found() -> None:
with tempfile.NamedTemporaryFile(delete=False) as tmp:
pass
with pytest.raises(OSError) as e:
ImageFont.load(tmp.name)
os.unlink(tmp.name)
root = os.path.splitext(tmp.name)[0]
assert str(e.value) == f"cannot find glyph data file {root}.{{gif|pbm|png}}"
def test_load_path_not_found() -> None: def test_load_path_not_found() -> None:
# Arrange # Arrange
filename = "somefilenamethatdoesntexist.ttf" filename = "somefilenamethatdoesntexist.ttf"
# Act/Assert # Act/Assert
with pytest.raises(OSError): with pytest.raises(OSError) as e:
ImageFont.load_path(filename) ImageFont.load_path(filename)
# The file doesn't exist, so don't suggest `load`
assert filename in str(e.value)
assert "did you mean" not in str(e.value)
with pytest.raises(OSError): with pytest.raises(OSError):
ImageFont.truetype(filename) ImageFont.truetype(filename)
def test_load_path_existing_path() -> None:
with tempfile.NamedTemporaryFile() as tmp:
with pytest.raises(OSError) as e:
ImageFont.load_path(tmp.name)
# The file exists, so the error message suggests to use `load` instead
assert tmp.name in str(e.value)
assert " did you mean" in str(e.value)
def test_load_non_font_bytes() -> None: def test_load_non_font_bytes() -> None:
with open("Tests/images/hopper.jpg", "rb") as f: with open("Tests/images/hopper.jpg", "rb") as f:
with pytest.raises(OSError): with pytest.raises(OSError):

View File

@ -98,11 +98,13 @@ class ImageFont:
def _load_pilfont(self, filename: str) -> None: def _load_pilfont(self, filename: str) -> None:
with open(filename, "rb") as fp: with open(filename, "rb") as fp:
image: ImageFile.ImageFile | None = None image: ImageFile.ImageFile | None = None
root = os.path.splitext(filename)[0]
for ext in (".png", ".gif", ".pbm"): for ext in (".png", ".gif", ".pbm"):
if image: if image:
image.close() image.close()
try: try:
fullname = os.path.splitext(filename)[0] + ext fullname = root + ext
image = Image.open(fullname) image = Image.open(fullname)
except Exception: except Exception:
pass pass
@ -112,7 +114,8 @@ class ImageFont:
else: else:
if image: if image:
image.close() image.close()
msg = "cannot find glyph data file"
msg = f"cannot find glyph data file {root}.{{gif|pbm|png}}"
raise OSError(msg) raise OSError(msg)
self.file = fullname self.file = fullname
@ -224,7 +227,7 @@ class FreeTypeFont:
raise core.ex raise core.ex
if size <= 0: if size <= 0:
msg = "font size must be greater than 0" msg = f"font size must be greater than 0, not {size}"
raise ValueError(msg) raise ValueError(msg)
self.path = font self.path = font
@ -784,7 +787,8 @@ class TransposedFont:
def load(filename: str) -> ImageFont: def load(filename: str) -> ImageFont:
""" """
Load a font file. This function loads a font object from the given Load a font file. This function loads a font object from the given
bitmap font file, and returns the corresponding font object. bitmap font file, and returns the corresponding font object. For loading TrueType
or OpenType fonts instead, see :py:func:`~PIL.ImageFont.truetype`.
:param filename: Name of font file. :param filename: Name of font file.
:return: A font object. :return: A font object.
@ -804,9 +808,10 @@ def truetype(
) -> FreeTypeFont: ) -> FreeTypeFont:
""" """
Load a TrueType or OpenType font from a file or file-like object, Load a TrueType or OpenType font from a file or file-like object,
and create a font object. and create a font object. This function loads a font object from the given
This function loads a font object from the given file or file-like file or file-like object, and creates a font object for a font of the given
object, and creates a font object for a font of the given size. size. For loading bitmap fonts instead, see :py:func:`~PIL.ImageFont.load`
and :py:func:`~PIL.ImageFont.load_path`.
Pillow uses FreeType to open font files. On Windows, be aware that FreeType Pillow uses FreeType to open font files. On Windows, be aware that FreeType
will keep the file open as long as the FreeTypeFont object exists. Windows will keep the file open as long as the FreeTypeFont object exists. Windows
@ -942,7 +947,10 @@ def load_path(filename: str | bytes) -> ImageFont:
return load(os.path.join(directory, filename)) return load(os.path.join(directory, filename))
except OSError: except OSError:
pass pass
msg = "cannot find font file" msg = f'cannot find font file "{filename}" in sys.path'
if os.path.exists(filename):
msg += f', did you mean ImageFont.load("{filename}") instead?'
raise OSError(msg) raise OSError(msg)