添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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:

def image_view(request):
    figure = get_figure(request.data)
    response = http.HttpResponse(content_type="image/png")
    figure.savefig(response, format="png", bbox_inches="tight")
    plt.close(figure)
    return response

and the get_figure is the “usual” graph of something like:

matplotlib.use("AGG")
def get_figure(data):
  figure, axis = plt.subplots(figsize=(6, 2.2), dpi=200, num=getrandbits(32))
  return figure

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 KB image727×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

import sys
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?