|
35 | 35 | from pathlib import Path
|
36 | 36 | import re
|
37 | 37 | import subprocess
|
| 38 | +from tempfile import TemporaryDirectory |
38 | 39 |
|
39 | 40 | import numpy as np
|
40 | 41 |
|
@@ -269,12 +270,12 @@ def make_tex_preview(self, tex, fontsize):
|
269 | 270 |
|
270 | 271 | return texfile
|
271 | 272 |
|
272 |
| - def _run_checked_subprocess(self, command, tex): |
| 273 | + def _run_checked_subprocess(self, command, tex, *, cwd=None): |
273 | 274 | _log.debug(cbook._pformat_subprocess(command))
|
274 | 275 | try:
|
275 |
| - report = subprocess.check_output(command, |
276 |
| - cwd=self.texcache, |
277 |
| - stderr=subprocess.STDOUT) |
| 276 | + report = subprocess.check_output( |
| 277 | + command, cwd=cwd if cwd is not None else self.texcache, |
| 278 | + stderr=subprocess.STDOUT) |
278 | 279 | except FileNotFoundError as exc:
|
279 | 280 | raise RuntimeError(
|
280 | 281 | 'Failed to process string with tex because {} could not be '
|
@@ -305,17 +306,16 @@ def make_dvi(self, tex, fontsize):
|
305 | 306 | dvifile = '%s.dvi' % basefile
|
306 | 307 | if not os.path.exists(dvifile):
|
307 | 308 | texfile = self.make_tex(tex, fontsize)
|
308 |
| - with cbook._lock_path(texfile): |
| 309 | + # Generate the dvi in a temporary directory to avoid race |
| 310 | + # conditions e.g. if multiple processes try to process the same tex |
| 311 | + # string at the same time. Having tmpdir be a subdirectory of the |
| 312 | + # final output dir ensures that they are on the same filesystem, |
| 313 | + # and thus replace() works atomically. |
| 314 | + with TemporaryDirectory(dir=Path(dvifile).parent) as tmpdir: |
309 | 315 | self._run_checked_subprocess(
|
310 | 316 | ["latex", "-interaction=nonstopmode", "--halt-on-error",
|
311 |
| - texfile], tex) |
312 |
| - for fname in glob.glob(basefile + '*'): |
313 |
| - if not fname.endswith(('dvi', 'tex')): |
314 |
| - try: |
315 |
| - os.remove(fname) |
316 |
| - except OSError: |
317 |
| - pass |
318 |
| - |
| 317 | + texfile], tex, cwd=tmpdir) |
| 318 | + (Path(tmpdir) / Path(dvifile).name).replace(dvifile) |
319 | 319 | return dvifile
|
320 | 320 |
|
321 | 321 | @cbook.deprecated("3.3")
|
|
0 commit comments