we generate some plots on the fly for some of the pages on our platform. The site is written in django and the gunicorn server use gthreads (I suspect it might be relevant). The code generating the graph is something like this:
figure = get_figure(request.data)
response = http.HttpResponse(content_type="image/png")
figure.savefig(response, format="png", bbox_inches="tight")
and the get_figure is the “usual” graph of something like:
figure, axis = plt.subplots(figsize=(6, 2.2), dpi=200, num=getrandbits(32))
and nothing “weird” is happening inside it but regular matplotlib stuff (not that I don’t want to share it, it’s just not relevant I believe - it’s normal plotting and it works in 99% of cases). It works for all our manual tests (including stress tests) and answers correctly to tens of thousands requests, but from time to time, it throws these errors (which we cannot replicate on the very same data):
AttributeError: 'NoneType' object has no attribute 'canvas'
'NoneType' object has no attribute 'dpi_scale_trans'
We suspect it might be something caused by threading (but don’t know what or how to prevent that from happening). Any ideas what we could try or what we are doing wrong?
If you are already using the “explict” API (and not using
plt.XYZ in the rest of the code I suggest you change to
from matplotlib.figure import Figure
figure = Figure(figsize=(6, 2.2), dpi=200)
axis = figure.subplots() # really hard not to rename this to ax
Which will not tell
pyplot about the
Figure so you can just let it go out of scope and get garbage collected like any other Python object.
I see, good idea. We are using explicit API up to a few instances. I have now changed all of them (but
plt.close, for which I cannot find non-pyplot equivalent and
rcParams for font settings) to use explicit API. Will try! Thank you
If you are not using
pyplot to create the
Figures , you also do not need it to close the figures so there in so direct replacement.
I also hope you are using
mpl.use('agg') or the equivalent already as well or you are creating a bunch of GUI objects for no reason.
rcParams actually live at the top level (
matplotlib.rcParams) and is imported to
Given that this is running in a webserver, you have very tight control of the life cycle, and there are threads I also suggest doing
sys.modules['matplotlib.pyplot'] = None
which will prevent pyplot (and it’s global state) from being imported!
Thanks for the tips! Applied!
I also hope you are using mpl.use(‘agg’) or the equivalent already as well or you are creating a bunch of GUI objects for no reason.
I used that up until now, but now switched to just using
from matplotlib.backends.backend_agg import FigureCanvasAgg (new for every request/figure). Is that OK?
I think so (but depends one exactly what you are doing).
Hey. I confirm that removing all
plt code helped!