I originally posted this to the user’s list but got no response there. As I think there’s a bug in matplotlib here, I’m re-trying on the development list. Here’s what I sent to -users back on Mar 13:
I am using matplotlib’s object-oriented API to dynamically generate some graphs served by a web site. The web site is built with Django and I have generally followed the cookbook example I found here: http://www.scipy.org/Cookbook/Matplotlib/Django
for serving matplotlib figures under Django. Specifically my code looks like this:
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
def generate_png(request, f, year, cid, pid, ic):
# ...snipped code that generates the data to graph... fig = Figure() ax = fig.add_subplot(111) ax.set_title(fig_title) ax.set_xlabel("Score") ax.set_ylabel("Frequency") n, bins2, patches = ax.hist(vals, bins, facecolor='blue', edgecolor='blue') if x is not None: patches[x].set_facecolor('red') patches[x].set_edgecolor('red') fig.legend((patches[x],), ('%s (%d)' % (cname, cval),), 'lower left') canvas = FigureCanvas(fig) canvas.print_png(f) # ... snip remainder ...
This works fine, except when I run it under a multi-threaded web server (Apache with mod_wsgi in daemon mode with multi-threaded processes) it sometimes (not always) fails with this traceback:
File “/home/kmt/django/Django-1.1-alpha-1/django/core/handlers/base.py”, line 86, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File “/home/kmt/software/web/xword/acpt/views.py”, line 321, in get_png
response = generate_png(request, f, year, cid, pid, ic)
File “/home/kmt/software/web/xword/acpt/views.py”, line 308, in generate_png
File “/usr/lib/python2.5/site-packages/matplotlib/backends/backend_agg.py”, line 305, in print_png
File “/usr/lib/python2.5/site-packages/matplotlib/backends/backend_agg.py”, line 261, in draw
File “/usr/lib/python2.5/site-packages/matplotlib/figure.py”, line 765, in draw
File “/usr/lib/python2.5/site-packages/matplotlib/legend.py”, line 215, in draw
File “/usr/lib/python2.5/site-packages/matplotlib/text.py”, line 329, in draw
File “/usr/lib/python2.5/site-packages/matplotlib/backends/backend_agg.py”, line 113, in draw_text
self._renderer.draw_text_image(font.get_image(), int(x), int(y) + 1, angle, gc)
RuntimeError: You must call .set_text() before .get_image()
I’m not at all familiar with the internals (truly I’m barely familiar with the public APIs) of matplotlib but it appears from this exception that internally there’s a ‘font’ object being shared between threads here, such that one thread can come in and change the font state resulting in a subsequent error in a different thread that was also in the middle of using that font object? If I protect that block of code above with a thread lock so that only one thread is allowed in at a time, the problem goes away.
For reference I’m using the latest matplotlib available in the Ubuntu Intrepid (8.10) repositories, which looks to be 0.98.3. In a brief scan I didn’t see anything relevant listed in the “what’s new” page for 0.98.4 (and can’t find a “what’s new in 0.98.5” on the matplotlib web site though that is what is listed as most recent?). Nor can I find anything that looks similar logged as a bug in the tracker.
Is there something (besides bracketing all access to the matplotlib code with a thread mutex) that I should be doing to make my use of matplotlib thread safe or does it seem like there’s a multi-threading bug in matplotlib here?
Apologies if this is the wrong list and there is in fact something I ought to be doing in my code (other than using a mutex) to prevent this – I haven’t been able to find anything. My impression from various doc I’ve read is the object-oriented API is supposed to be thread-safe. Is that true? If so should I file a ticket for this?
Thanks for any feedback,