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:
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?
The stacktraces: image720×768 69.8 KBimage727×888 78.7 KB
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
def get_figure(data):
figure = Figure(figsize=(6, 2.2), dpi=200)
axis = figure.subplots() # really hard not to rename this to ax
return figure
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.
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 plt.
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
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?