|
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