Description
Bug report
Bug summary
When storing multiple PDFs using parallel processes (not threads!) a RuntimeError occurs. This happens regardless of the library used for parallelization, i.e. concurrent
, multiprocessing
or joblib
. The error is either RuntimeError: In set_text: could not load glyph
(generated by the code below) or RuntimeError: In load_char: Could not load charcode
(which I can't seem to reproduce yet, but maybe you have a better idea of the link behind the two).
Code for reproduction
from multiprocessing import Pool
from joblib import Parallel, delayed
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed
def model_handler(n):
import matplotlib.pyplot as plt
fig_out = plt.figure(figsize=(1, 1))
axes = fig_out.add_subplot(1,1,1)
axes.scatter(range(1000), range(1000))
fig_out.savefig('dummyfig{}.pdf'.format(n), format='pdf')
plt.close()
return n
n_jobs = 2
n_workers = 2
print('List comprehension (serial)... ', end='')
try:
[model_handler(j) for j in range(n_jobs)]
print('SUCCESS')
except:
print('FAILURE')
print('Parallel processes via multiprocessing package... ', end='')
try:
Pool(processes=n_workers).map(model_handler, range(n_jobs))
print('SUCCESS')
except:
print('FAILURE')
print('Parallel processes via joblib package (loky backend)... ', end='')
try:
Parallel(n_jobs=n_workers, backend='loky')(delayed(model_handler)(i) for i in range(n_jobs))
print('SUCCESS')
except:
print('FAILURE')
print('Parallel threads via joblib package... ', end='')
try:
Parallel(n_jobs=n_workers, backend='threading')(delayed(model_handler)(i) for i in range(n_jobs))
print('SUCCESS')
except:
print('FAILURE')
print('Parallel processes via joblib package... ', end='')
try:
Parallel(n_jobs=n_workers, backend='multiprocessing')(delayed(model_handler)(i) for i in range(n_jobs))
print('SUCCESS')
except:
print('FAILURE')
print('Parallel threads via concurrent.futures package... ', end='')
with ThreadPoolExecutor(max_workers=n_workers) as pool:
futures = [pool.submit(model_handler, i) for i in range(n_jobs)]
exceptions = [x.exception() for x in as_completed(futures)]
if all(exception is None for exception in exceptions):
print('SUCCESS')
else:
print('FAILURE')
print('Parallel processes via concurrent.futures package... ', end='')
with ProcessPoolExecutor(max_workers=n_workers) as pool:
futures = [pool.submit(model_handler, i) for i in range(n_jobs)]
exceptions = [x.exception() for x in as_completed(futures)]
if all(exception is None for exception in exceptions):
print('SUCCESS')
else:
print('FAILURE')
Actual outcome
List comprehension (serial)... SUCCESS
Parallel processes via multiprocessing package... FAILURE
Parallel processes via joblib package (loky backend)... SUCCESS
Parallel threads via joblib package... SUCCESS
Parallel processes via joblib package... FAILURE
Parallel threads via concurrent.futures package... SUCCESS
Parallel processes via concurrent.futures package... FAILURE
Traceback
"""
Traceback (most recent call last):
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/multiprocessing/pool.py", line 121, in worker
result = (True, func(*args, **kwds))
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/multiprocessing/pool.py", line 44, in mapstar
return list(map(*args))
File "<ipython-input-16-c44ed7855af1>", line 13, in model_handler
fig_out.savefig('dummyfig{}.pdf'.format(n), format='pdf')
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/figure.py", line 2035, in savefig
self.canvas.print_figure(fname, **kwargs)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/backend_bases.py", line 2263, in print_figure
**kwargs)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/backends/backend_pdf.py", line 2586, in print_pdf
self.figure.draw(renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/figure.py", line 1475, in draw
renderer, self, artists, self.suppressComposite)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/axes/_base.py", line 2607, in draw
mimage._draw_list_compositing_images(renderer, self, artists)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/axis.py", line 1192, in draw
renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/axis.py", line 1130, in _get_tick_bboxes
extent = tick.label1.get_window_extent(renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/text.py", line 922, in get_window_extent
bbox, info, descent = self._get_layout(self._renderer)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/text.py", line 300, in _get_layout
ismath=False)
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/backends/backend_pdf.py", line 2164, in get_text_width_height_descent
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
RuntimeError: In set_text: could not load glyph
"""
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3267, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-18-426f53ccd8fe>", line 3, in <module>
Pool(processes=n_workers).map(model_handler, range(n_jobs))
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/multiprocessing/pool.py", line 268, in map
return self._map_async(func, iterable, mapstar, chunksize).get()
File "/Users/marcotama/anaconda3/envs/py37/lib/python3.7/multiprocessing/pool.py", line 657, in get
raise self._value
RuntimeError: In set_text: could not load glyph
Expected outcome
The code should simply succeed. Notice that the loky
backend of joblib
succeeds even though it uses parallel processes. I am not sure why this is.
Matplotlib version
- Operating system: MacOS High Sierra (10.13.6)
- Matplotlib version: 2.2.2
- Matplotlib backend (
print(matplotlib.get_backend())
):module://backend_interagg
- Python version: 3.7.2
- Jupyter version (if applicable): N/A
- Other libraries: joblib==0.12.2
All packages were installed via pip
inside a conda
environment.