Блог экспериментатора инженера-разработчика: Infanty.
Я пишу how-to статьи на редкие темы или статьи обзоры - для себя и тех кто со мной работает.
Блог существует при поддержке: "Оккупационных сил Марса".

CherryPy — объектно-ориентированный веб-фреймворк, написанный на языке программирования Python. Спроектирован для быстрой разработки веб-приложений для сети Интернет. Представляет собой надстройку над HTTP-протоколом, но остаётся на низком уровне и не выходит за рамки требований RFC 2616. Может выступать в качестве самостоятельного веб-сервера или работать под управлением другого серверного приложения, поддерживающего протокол WSGI.

Но мало кто знает как запустить этот маленький и быстрый фреймворк используя uWSGI, без использования встроенного WSGI сервера.

Вариант 1: менее правильный

Для начала создадим файл cherrypy_uwsgi.py содержащий:

  • два (для примера) приложения CherryPy;
  • функцию именованную по спецификации PEP-3333;
  • в функции зарегистрируем пути (URL) для приложений CherryPy;
  • после чего передадим управление CherryPy для обработки запросов к зарегистрированным URL.
import cherrypy

# Первое CherryPy приложение.
class One:
  @cherrypy.expose
  def index(self):
    r = cherrypy.response
    r.headers['Content-Type'] = 'text/plain'
    return "Hello World" 

# Второе CherryPy приложение.
class Two:
  @cherrypy.expose
  def default(self, *args, **kwargs):
    r = cherrypy.response
    r.headers['Content-Type'] = 'text/plain'
    content = "Positional arguments\n\n"
    for k in args:
      content += k + "\n"
    content += "\nKeyword arguments\n\n"
    for k in kwargs:
      content += k + ": " + kwargs[k] + "\n"
    return content

# Функция именованная по спецификации PEP-3333.
def application(environ, start_response):
  # Первое CherryPy приложение будет доступно по URL: "/".
  cherrypy.tree.mount(One(), '/', None)
  # Второе CherryPy приложение будет доступно по URL: "/cherrypy-two".
  cherrypy.tree.mount(Two(), '/cherrypy-two', None)
  # Передача управления в CherryPy.
  return cherrypy.tree(environ, start_response) 

После чего исправляем конфиг uWSGI описанный в статье: Веб-интерфейс для Python в Ubuntu используя Nginx и uWSGI. В файле "uwsgiapp.ini" расположенном в папке "/home/example.com" удаляем строки:

mount = /testuwsgiapp=wsgi.py
manage-script-name = true 

и добавляем строку с именем файла содержащего приложения CherryPy:

wsgi-file = cherrypy_uwsgi.py 

После чего перезапустим uWSGI:

sudo stop uwsgiapp
sudo start uwsgiapp 

И проверим работу приложения, набрав в браузере URL: "http://example.com/" и "http://example.com/cherrypy-two".

Вариант 2.1: более правильный

В этом варианте также создадим файл cherrypy_uwsgi.py следующего содержания:

import cherrypy

class Root(object):
    @cherrypy.expose
    def index(self):
        return "hello world"
		
# Отключаем функцию автоперезагрузки приложения при изменении файлов.
cherrypy.config.update({'engine.autoreload.on': False})
# Отключаем встроенный в CherryPy - HTTP-сервер, так как он не будет использоваться.
cherrypy.server.unsubscribe()
# Запускаем CherryPy.
cherrypy.engine.start()

# Запускаем Python приложение на основе CherryPy
# работающее с использованием WSGI стандарта.
wsgiapp = cherrypy.tree.mount(Root()) 

После чего исправляем конфиг uWSGI как в варианте 1. Кроме того при редактировании файла "uwsgiapp.ini" добавим дополнительные строки для запуска приложения:

# Указывает uWSGI о необходимости взаимодействия 
# с Python приложением на основе CherryPy
# используя WSGI стандарт.
callable = wsgiapp 

И удалим строки:

# Указывает uWSGI о необходимости взаимодействия 
# с Python приложением используя WSGI стандарт.
module = wsgi:application 

Вариант 2.2: дополнительный

Рассмотрим другой возможный вариант содержимого файла cherrypy_uwsgi.py реализующего урок из документации CherryPy: "Tutorial 5 - Track my end-user’s activity". В нём не только показана регистрацая нескольких путей (URL) на сайте, но и работа с формой, сессией (папка: "/home/session" должна существавать с правами на запись) и конфигурацией CherryPy:

import random
import string

import cherrypy

class StringGenerator(object):
    @cherrypy.expose
    def index(self):
        return """<html>
          <head></head>
          <body>
            <form method="get" action="generate">
              <input type="text" value="8" name="length" />
              <button type="submit">Give it now!</button>
            </form>
          </body>
        </html>"""

    @cherrypy.expose
    def generate(self, length=8):
        some_string = ''.join(random.sample(string.hexdigits, int(length)))
        cherrypy.session['mystring'] = some_string
        return some_string
        
    @cherrypy.expose
    def display(self):
        return cherrypy.session['mystring']
         
# Отключаем функцию автоперезагрузки приложения при изменении файлов.
cherrypy.config.update({'engine.autoreload.on': False})
# Отключаем встроенный в CherryPy - HTTP-сервер, так как он не будет использоваться.
cherrypy.server.unsubscribe()
# Запускаем CherryPy.
cherrypy.engine.start()

conf = {
    '/': {
        'tools.sessions.on': True,
        'tools.sessions.storage_type': "file",
        'tools.sessions.storage_path': "/home/session"
    }
}

# Запускаем Python приложение на основе CherryPy 
# работающее с использованием WSGI стандарта.
wsgiapp = cherrypy.tree.mount(StringGenerator(), '/', conf) 

Конфиг uWSGI приложения при этом останется таким же как в варианте 2.1.