Skip to content

Commit 8f2e158

Browse files
committed
Use pathlib in texmanager.
Only a few changes are needed. Note that make_png returns an absolute path so path joining is not even needed here. Also restore the recently incorrectly removed usage of dpi in generating the png filename. Also drop an unrelated but outdated comment re: background handling, which is obsolete since 8940c66 (look for `if hack: ...`).
1 parent cae6696 commit 8f2e158

File tree

1 file changed

+34
-30
lines changed

1 file changed

+34
-30
lines changed

lib/matplotlib/texmanager.py

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import functools
2424
import hashlib
2525
import logging
26-
import os
2726
from pathlib import Path
2827
import subprocess
2928
from tempfile import TemporaryDirectory
@@ -63,7 +62,7 @@ class TexManager:
6362
Repeated calls to this constructor always return the same instance.
6463
"""
6564

66-
_texcache = os.path.join(mpl.get_cachedir(), 'tex.cache')
65+
_cache_dir = Path(mpl.get_cachedir(), 'tex.cache')
6766
_grey_arrayd = {}
6867

6968
_font_families = ('serif', 'sans-serif', 'cursive', 'monospace')
@@ -109,7 +108,7 @@ class TexManager:
109108

110109
@functools.lru_cache # Always return the same instance.
111110
def __new__(cls):
112-
Path(cls._texcache).mkdir(parents=True, exist_ok=True)
111+
cls._cache_dir.mkdir(parents=True, exist_ok=True)
113112
return object.__new__(cls)
114113

115114
@classmethod
@@ -167,23 +166,30 @@ def _get_font_preamble_and_command(cls):
167166
return preamble, fontcmd
168167

169168
@classmethod
170-
def get_basefile(cls, tex, fontsize, dpi=None):
169+
def _get_base_path(cls, tex, fontsize, dpi=None):
171170
"""
172-
Return a filename based on a hash of the string, fontsize, and dpi.
171+
Return a file path based on a hash of the string, fontsize, and dpi.
173172
"""
174173
src = cls._get_tex_source(tex, fontsize) + str(dpi)
175174
filehash = hashlib.sha256(
176175
src.encode('utf-8'),
177176
usedforsecurity=False
178177
).hexdigest()
179-
filepath = Path(cls._texcache)
178+
filepath = cls._cache_dir
180179

181180
num_letters, num_levels = 2, 2
182181
for i in range(0, num_letters*num_levels, num_letters):
183-
filepath = filepath / Path(filehash[i:i+2])
182+
filepath = filepath / filehash[i:i+2]
184183

185184
filepath.mkdir(parents=True, exist_ok=True)
186-
return os.path.join(filepath, filehash)
185+
return filepath / filehash
186+
187+
@classmethod
188+
def get_basefile(cls, tex, fontsize, dpi=None): # Kept for backcompat.
189+
"""
190+
Return a filename based on a hash of the string, fontsize, and dpi.
191+
"""
192+
return str(cls._get_base_path(tex, fontsize, dpi))
187193

188194
@classmethod
189195
def get_font_preamble(cls):
@@ -243,17 +249,16 @@ def make_tex(cls, tex, fontsize):
243249
244250
Return the file name.
245251
"""
246-
texfile = cls.get_basefile(tex, fontsize) + ".tex"
247-
Path(texfile).write_text(cls._get_tex_source(tex, fontsize),
248-
encoding='utf-8')
249-
return texfile
252+
texpath = cls._get_base_path(tex, fontsize).with_suffix(".tex")
253+
texpath.write_text(cls._get_tex_source(tex, fontsize), encoding='utf-8')
254+
return str(texpath)
250255

251256
@classmethod
252257
def _run_checked_subprocess(cls, command, tex, *, cwd=None):
253258
_log.debug(cbook._pformat_subprocess(command))
254259
try:
255260
report = subprocess.check_output(
256-
command, cwd=cwd if cwd is not None else cls._texcache,
261+
command, cwd=cwd if cwd is not None else cls._cache_dir,
257262
stderr=subprocess.STDOUT)
258263
except FileNotFoundError as exc:
259264
raise RuntimeError(
@@ -281,8 +286,8 @@ def make_dvi(cls, tex, fontsize):
281286
282287
Return the file name.
283288
"""
284-
dvifile = Path(cls.get_basefile(tex, fontsize)).with_suffix(".dvi")
285-
if not dvifile.exists():
289+
dvipath = cls._get_base_path(tex, fontsize).with_suffix(".dvi")
290+
if not dvipath.exists():
286291
# Generate the tex and dvi in a temporary directory to avoid race
287292
# conditions e.g. if multiple processes try to process the same tex
288293
# string at the same time. Having tmpdir be a subdirectory of the
@@ -292,17 +297,17 @@ def make_dvi(cls, tex, fontsize):
292297
# the absolute path may contain characters (e.g. ~) that TeX does
293298
# not support; n.b. relative paths cannot traverse parents, or it
294299
# will be blocked when `openin_any = p` in texmf.cnf).
295-
with TemporaryDirectory(dir=dvifile.parent) as tmpdir:
300+
with TemporaryDirectory(dir=dvipath.parent) as tmpdir:
296301
Path(tmpdir, "file.tex").write_text(
297302
cls._get_tex_source(tex, fontsize), encoding='utf-8')
298303
cls._run_checked_subprocess(
299304
["latex", "-interaction=nonstopmode", "--halt-on-error",
300305
"file.tex"], tex, cwd=tmpdir)
301-
Path(tmpdir, "file.dvi").replace(dvifile)
306+
Path(tmpdir, "file.dvi").replace(dvipath)
302307
# Also move the tex source to the main cache directory, but
303308
# only for backcompat.
304-
Path(tmpdir, "file.tex").replace(dvifile.with_suffix(".tex"))
305-
return str(dvifile)
309+
Path(tmpdir, "file.tex").replace(dvipath.with_suffix(".tex"))
310+
return str(dvipath)
306311

307312
@classmethod
308313
def make_png(cls, tex, fontsize, dpi):
@@ -311,13 +316,12 @@ def make_png(cls, tex, fontsize, dpi):
311316
312317
Return the file name.
313318
"""
314-
pngfile = Path(cls.get_basefile(tex, fontsize)).with_suffix(".png")
315-
# see get_rgba for a discussion of the background
316-
if not pngfile.exists():
317-
dvifile = cls.make_dvi(tex, fontsize)
318-
with TemporaryDirectory(dir=pngfile.parent) as tmpdir:
319+
pngpath = cls._get_base_path(tex, fontsize, dpi).with_suffix(".png")
320+
if not pngpath.exists():
321+
dvipath = cls.make_dvi(tex, fontsize)
322+
with TemporaryDirectory(dir=pngpath.parent) as tmpdir:
319323
cmd = ["dvipng", "-bg", "Transparent", "-D", str(dpi),
320-
"-T", "tight", "-o", "file.png", dvifile]
324+
"-T", "tight", "-o", "file.png", dvipath]
321325
# When testing, disable FreeType rendering for reproducibility;
322326
# but dvipng 1.16 has a bug (fixed in f3ff241) that breaks
323327
# --freetype0 mode, so for it we keep FreeType enabled; the
@@ -326,8 +330,8 @@ def make_png(cls, tex, fontsize, dpi):
326330
mpl._get_executable_info("dvipng").raw_version != "1.16"):
327331
cmd.insert(1, "--freetype0")
328332
cls._run_checked_subprocess(cmd, tex, cwd=tmpdir)
329-
Path(tmpdir, "file.png").replace(pngfile)
330-
return str(pngfile)
333+
Path(tmpdir, "file.png").replace(pngpath)
334+
return str(pngpath)
331335

332336
@classmethod
333337
def get_grey(cls, tex, fontsize=None, dpi=None):
@@ -338,7 +342,7 @@ def get_grey(cls, tex, fontsize=None, dpi=None):
338342
alpha = cls._grey_arrayd.get(key)
339343
if alpha is None:
340344
pngfile = cls.make_png(tex, fontsize, dpi)
341-
rgba = mpl.image.imread(os.path.join(cls._texcache, pngfile))
345+
rgba = mpl.image.imread(pngfile)
342346
cls._grey_arrayd[key] = alpha = rgba[:, :, -1]
343347
return alpha
344348

@@ -364,9 +368,9 @@ def get_text_width_height_descent(cls, tex, fontsize, renderer=None):
364368
"""Return width, height and descent of the text."""
365369
if tex.strip() == '':
366370
return 0, 0, 0
367-
dvifile = cls.make_dvi(tex, fontsize)
371+
dvipath = cls.make_dvi(tex, fontsize)
368372
dpi_fraction = renderer.points_to_pixels(1.) if renderer else 1
369-
with dviread.Dvi(dvifile, 72 * dpi_fraction) as dvi:
373+
with dviread.Dvi(dvipath, 72 * dpi_fraction) as dvi:
370374
page, = dvi
371375
# A total height (including the descent) needs to be returned.
372376
return page.width, page.height + page.descent, page.descent

0 commit comments

Comments
 (0)