Использование matplotlib в GAE

Мои теги и заголовок достаточно четко определяют мою проблему. Я хочу использовать matplotlib для создания графиков в реальном времени в Google App Engine. Я прочитал документацию и выполнил поиск в SO и Google. Я нашел сообщение, указав на эту рабочую демонстрацию. Но когда я это пробовал самостоятельно, это не работает для меня.

Я создал простое приложение, состоящее только из обработчика script hello_world.py

import numpy as np
import os
import sys
import cStringIO

print "Content-type: image/png\n"

os.environ["MATPLOTLIBDATA"] = os.getcwdu()  # own matplotlib data
os.environ["MPLCONFIGDIR"] = os.getcwdu()    # own matplotlibrc
import matplotlib.pyplot as plt

plt.plot(np.random.random((20))) #imshow(np.random.randint((10,10)))

sio = cStringIO.StringIO()
plt.savefig(sio, format="png")
sys.stdout.write(sio.getvalue())

и файл конфигурации app.yaml

application: helloworldtak
version: 1
runtime: python27
api_version: 1
threadsafe: no

handlers:
- url: /.*
  script: hello_world.py

libraries:
- name: numpy
  version: "latest"
- name: matplotlib
  version: "latest"

Я хочу сделать что-то, а затем вернуть содержимое в виде png-изображения. Эта процедура отлично работает для обычного веб-сервера, такого как Apache или IIS, я сделал это миллион раз.

Проблема в том, что, когда я запускаю свой script локально на сервере разработки, я получаю ошибку, которая, вероятно, связана с моей версией MPL 1.1.1, которая является только "экспериментальной" в GAE. Но когда я развертываю свое приложение в GAE, я получаю совершенно другую, некоррелированную ошибку.

При взгляде на внешний вид трассировка выглядит следующим образом:

Traceback (most recent call last):
  File "/base/data/home/apps/s~helloworldtak/1.364765672279579252/hello_world.py", line 16, in <module>
    import matplotlib.pyplot as plt
  File "/python27_runtime/python27_lib/versions/third_party/matplotlib-1.1.1/matplotlib/pyplot.py", line 23, in <module>
    from matplotlib.figure import Figure, figaspect
  File "/python27_runtime/python27_lib/versions/third_party/matplotlib-1.1.1/matplotlib/figure.py", line 18, in <module>
    from axes import Axes, SubplotBase, subplot_class_factory
  File "/python27_runtime/python27_lib/versions/third_party/matplotlib-1.1.1/matplotlib/axes.py", line 14, in <module>
    import matplotlib.axis as maxis
  File "/python27_runtime/python27_lib/versions/third_party/matplotlib-1.1.1/matplotlib/axis.py", line 10, in <module>
    import matplotlib.font_manager as font_manager
  File "/python27_runtime/python27_lib/versions/third_party/matplotlib-1.1.1/matplotlib/font_manager.py", line 1324, in <module>
    _rebuild()
  File "/python27_runtime/python27_lib/versions/third_party/matplotlib-1.1.1/matplotlib/font_manager.py", line 1278, in _rebuild
    fontManager = FontManager()
  File "/python27_runtime/python27_lib/versions/third_party/matplotlib-1.1.1/matplotlib/font_manager.py", line 995, in __init__
    self.defaultFont['ttf'] = self.ttffiles[0]
IndexError: list index out of range

Кажется, что-то нужно сделать с файловым кешем MPL. Я прочитал в документах, что кеширование и доступ к файлам - одна из проблем с MPL в GAE, но, очевидно, импорт работает для других.

Что я делаю неправильно?

Edit Основываясь на ответе ниже, я изменил свой код на

import numpy as np
import cStringIO
import matplotlib.pyplot as plt

import webapp2

class MainPage(webapp2.RequestHandler):
    def get(self):
        plt.plot(np.random.random((20)),"r-")
        sio = cStringIO.StringIO()
        plt.savefig(sio, format="png")
        self.response.headers['Content-Type'] = 'image/png'

        self.response.out.write(sio.getvalue())

app = webapp2.WSGIApplication([('/', MainPage)],
                              debug=True)

и вот так оно работает.

Ответ 1

Я не знаком с модулем sys. Чтобы ответить на вопрос, я предпочитаю использовать webapp2. Это рабочий обработчик:

import webapp2
import StringIO
import numpy as np
import matplotlib.pyplot as plt


class MainPage(webapp2.RequestHandler):
    def get(self):
        plt.plot(np.random.random((20)))
        sio = StringIO.StringIO()
        plt.savefig(sio, format="png")
        img_b64 = sio.getvalue().encode("base64").strip()
        plt.clf()
        sio.close()
        self.response.write("""<html><body>""")
        self.response.write("<img src='data:image/png;base64,%s'/>" % img_b64)
        self.response.write("""</body> </html>""")

app = webapp2.WSGIApplication([('/', MainPage)], debug=True)

В качестве альтернативы вы можете записать sio.getvalue() в blobstore с файлами api и использовать метод get_serving_url() для изображений api, чтобы избежать кодирования в base64.

Ответ 2

Проблема заключалась в том, что вы устанавливали переменные среды MATPLOTLIBDATA и MPLCONFIGDIR в каталог приложений перед импортом matplotlib. Поскольку у вас не было никаких шрифтов в каталоге приложений, он не мог загружать шрифты.