Skip to content

Raise an exception when find_tex_file fails to find a file. #21356

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
Oct 15, 2021
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
5 changes: 5 additions & 0 deletions doc/api/next_api_changes/deprecations/21356-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
In the future, ``dviread.find_tex_file`` will raise a ``FileNotFoundError`` for missing files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Previously, it would return an empty string in such cases. Raising an
exception allows attaching a user-friendly message instead. During the
transition period, a warning is raised.
2 changes: 1 addition & 1 deletion lib/matplotlib/backends/backend_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ def dviFontName(self, dvifont):
if dvi_info is not None:
return dvi_info.pdfname

tex_font_map = dviread.PsfontsMap(dviread.find_tex_file('pdftex.map'))
tex_font_map = dviread.PsfontsMap(dviread._find_tex_file('pdftex.map'))
psfont = tex_font_map[dvifont.texname]
if psfont.filename is None:
raise ValueError(
Expand Down
116 changes: 69 additions & 47 deletions lib/matplotlib/dviread.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,13 +470,12 @@ def _fnt_def_real(self, k, c, s, d, a, l):
n = self.file.read(a + l)
fontname = n[-l:].decode('ascii')
tfm = _tfmfile(fontname)
if tfm is None:
raise FileNotFoundError("missing font metrics file: %s" % fontname)
if c != 0 and tfm.checksum != 0 and c != tfm.checksum:
raise ValueError('tfm checksum mismatch: %s' % n)

vf = _vffile(fontname)

try:
vf = _vffile(fontname)
except FileNotFoundError:
vf = None
self.fonts[k] = DviFont(scale=s, tfm=tfm, texname=n, vf=vf)

@_dispatch(247, state=_dvistate.pre, args=('u1', 'u4', 'u4', 'u4', 'u1'))
Expand Down Expand Up @@ -938,9 +937,9 @@ def _parse_and_cache_line(self, line):
if basename is None:
basename = tfmname
if encodingfile is not None:
encodingfile = find_tex_file(encodingfile)
encodingfile = _find_tex_file(encodingfile)
if fontfile is not None:
fontfile = find_tex_file(fontfile)
fontfile = _find_tex_file(fontfile)
self._parsed[tfmname] = PsFont(
texname=tfmname, psname=basename, effects=effects,
encoding=encodingfile, filename=fontfile)
Expand Down Expand Up @@ -992,21 +991,20 @@ def search(self, filename):
self._proc.stdin.write(os.fsencode(filename) + b"\n")
self._proc.stdin.flush()
out = self._proc.stdout.readline().rstrip()
return "" if out == b"nil" else os.fsdecode(out)
return None if out == b"nil" else os.fsdecode(out)


@lru_cache()
@_api.delete_parameter("3.5", "format")
def find_tex_file(filename, format=None):
def _find_tex_file(filename, format=None):
"""
Find a file in the texmf tree.
Find a file in the texmf tree using kpathsea_.

Calls :program:`kpsewhich` which is an interface to the kpathsea
library [1]_. Most existing TeX distributions on Unix-like systems use
kpathsea. It is also available as part of MikTeX, a popular
distribution on Windows.
The kpathsea library, provided by most existing TeX distributions, both
on Unix-like systems and on Windows (MikTeX), is invoked via a long-lived
luatex process if luatex is installed, or via kpsewhich otherwise.

*If the file is not found, an empty string is returned*.
.. _kpathsea: https://www.tug.org/kpathsea/

Parameters
----------
Expand All @@ -1016,10 +1014,10 @@ def find_tex_file(filename, format=None):
Could be e.g. 'tfm' or 'vf' to limit the search to that type of files.
Deprecated.

References
----------
.. [1] `Kpathsea documentation <http://www.tug.org/kpathsea/>`_
The library that :program:`kpsewhich` is part of.
Raises
------
FileNotFoundError
If the file is not found.
"""

# we expect these to always be ascii encoded, but use utf-8
Expand All @@ -1029,39 +1027,63 @@ def find_tex_file(filename, format=None):
if isinstance(format, bytes):
format = format.decode('utf-8', errors='replace')

if format is None:
try:
lk = _LuatexKpsewhich()
except FileNotFoundError:
lk = None # Fallback to directly calling kpsewhich, as below.

if lk and format is None:
path = lk.search(filename)

else:
if os.name == 'nt':
# On Windows only, kpathsea can use utf-8 for cmd args and output.
# The `command_line_encoding` environment variable is set to force
# it to always use utf-8 encoding. See Matplotlib issue #11848.
kwargs = {'env': {**os.environ, 'command_line_encoding': 'utf-8'},
'encoding': 'utf-8'}
else: # On POSIX, run through the equivalent of os.fsdecode().
kwargs = {'encoding': sys.getfilesystemencoding(),
'errors': 'surrogateescape'}

cmd = ['kpsewhich']
if format is not None:
cmd += ['--format=' + format]
cmd += [filename]
try:
lk = _LuatexKpsewhich()
except FileNotFoundError:
pass # Fallback to directly calling kpsewhich, as below.
else:
return lk.search(filename)

if os.name == 'nt':
# On Windows only, kpathsea can use utf-8 for cmd args and output.
# The `command_line_encoding` environment variable is set to force it
# to always use utf-8 encoding. See Matplotlib issue #11848.
kwargs = {'env': {**os.environ, 'command_line_encoding': 'utf-8'},
'encoding': 'utf-8'}
else: # On POSIX, run through the equivalent of os.fsdecode().
kwargs = {'encoding': sys.getfilesystemencoding(),
'errors': 'surrogatescape'}

cmd = ['kpsewhich']
if format is not None:
cmd += ['--format=' + format]
cmd += [filename]
path = (cbook._check_and_log_subprocess(cmd, _log, **kwargs)
.rstrip('\n'))
except (FileNotFoundError, RuntimeError):
path = None

if path:
return path
else:
raise FileNotFoundError(
f"Matplotlib's TeX implementation searched for a file named "
f"{filename!r} in your texmf tree, but could not find it")


# After the deprecation period elapses, delete this shim and rename
# _find_tex_file to find_tex_file everywhere.
@_api.delete_parameter("3.5", "format")
def find_tex_file(filename, format=None):
try:
result = cbook._check_and_log_subprocess(cmd, _log, **kwargs)
except (FileNotFoundError, RuntimeError):
return ''
return result.rstrip('\n')
return (_find_tex_file(filename, format) if format is not None else
_find_tex_file(filename))
except FileNotFoundError as exc:
_api.warn_deprecated(
"3.6", message=f"{exc.args[0]}; in the future, this will raise a "
f"FileNotFoundError.")
return ""


find_tex_file.__doc__ = _find_tex_file.__doc__


@lru_cache()
def _fontfile(cls, suffix, texname):
filename = find_tex_file(texname + suffix)
return cls(filename) if filename else None
return cls(_find_tex_file(texname + suffix))


_tfmfile = partial(_fontfile, Tfm, ".tfm")
Expand All @@ -1077,7 +1099,7 @@ def _fontfile(cls, suffix, texname):
parser.add_argument("dpi", nargs="?", type=float, default=None)
args = parser.parse_args()
with Dvi(args.filename, args.dpi) as dvi:
fontmap = PsfontsMap(find_tex_file('pdftex.map'))
fontmap = PsfontsMap(_find_tex_file('pdftex.map'))
for page in dvi:
print(f"=== new page === "
f"(w: {page.width}, h: {page.height}, d: {page.descent})")
Expand Down
6 changes: 5 additions & 1 deletion lib/matplotlib/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,8 @@ def _check_for_pgf(texsystem):


def _has_tex_package(package):
return bool(mpl.dviread.find_tex_file(f"{package}.sty"))
try:
mpl.dviread._find_tex_file(f"{package}.sty")
return True
except FileNotFoundError:
return False
2 changes: 1 addition & 1 deletion lib/matplotlib/tests/test_dviread.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


def test_PsfontsMap(monkeypatch):
monkeypatch.setattr(dr, 'find_tex_file', lambda x: x)
monkeypatch.setattr(dr, '_find_tex_file', lambda x: x)

filename = str(Path(__file__).parent / 'baseline_images/dviread/test.map')
fontmap = dr.PsfontsMap(filename)
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/textpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ def get_glyphs_tex(self, prop, s, glyph_map=None,
@staticmethod
@functools.lru_cache(50)
def _get_ps_font_and_encoding(texname):
tex_font_map = dviread.PsfontsMap(dviread.find_tex_file('pdftex.map'))
tex_font_map = dviread.PsfontsMap(dviread._find_tex_file('pdftex.map'))
psfont = tex_font_map[texname]
if psfont.filename is None:
raise ValueError(
Expand Down