Spec-Zone.ru › Bottle 0.12

Рецепты

Это коллекция фрагментов кода и примеров для распространённых случаев использования.

Отслеживание сессий

Встроенной поддержки сессий нет, потому что нет правильного способа сделать это (в микрофреймворке). В зависимости от требований и среды можно использовать middleware beaker с подходящим бэкендом или реализовать его самостоятельно. Вот пример сессий beaker с файловым бэкендом:

import bottle
from beaker.middleware import SessionMiddleware

session_opts = {
    'session.type': 'file',
    'session.cookie_expires': 300,
    'session.data_dir': './data',
    'session.auto': True
}
app = SessionMiddleware(bottle.app(), session_opts)

@bottle.route('/test')
def test():
  s = bottle.request.environ.get('beaker.session')
  s['test'] = s.get('test',0) + 1
  s.save()
  return 'Test counter: %d' % s['test']

bottle.run(app=app)

Отладка со стилем: Отладочный middleware

Bottle перехватывает все исключения, поднятые в коде приложения, чтобы предотвратить сбой вашего WSGI-сервера. Если встроенный режим debug() недостаточен, и вам нужно, чтобы исключения распространялись на отладочный middleware, вы можете отключить это поведение:

import bottle
app = bottle.app()
app.catchall = False #Now most exceptions are re-raised within bottle.
myapp = DebuggingMiddleware(app) #Replace this with a middleware of your choice (see below)
bottle.run(app=myapp)

Теперь Bottle перехватывает только свои собственные исключения (HTTPError, HTTPResponse и BottleException), а ваш middleware может обрабатывать остальное.

Библиотеки werkzeug и paste поставляются с очень мощным отладочным WSGI-middleware. Посмотрите на werkzeug.debug.DebuggedApplication для werkzeug и paste.evalexception.middleware.EvalException для paste. Они оба позволяют инспектировать стек и даже выполнять код Python в контексте стека, поэтому **не используйте их в рабочей среде**.

Тестирование Bottle-приложений на единицу

Тестирование на единицу обычно выполняется для методов, определённых в вашем веб-приложении, без запуска WSGI-окружения.

Простой пример с использованием Nose:

import bottle

@bottle.route('/')
def index():
    return 'Hi!'

if __name__ == '__main__':
    bottle.run()

Сценарий теста:

import mywebapp

def test_webapp_index():
    assert mywebapp.index() == 'Hi!'

В примере метод Bottle route() никогда не выполняется — тестируется только index().

Функциональное тестирование Bottle-приложений

Любая система тестирования на основе HTTP может использоваться с работающим WSGI-сервером, но некоторые фреймворки тестирования работают более тесно с WSGI и предоставляют возможность вызывать WSGI-приложения в контролируемой среде, с трассировками и полным использованием отладочных инструментов. Инструменты тестирования для WSGI — хорошая отправная точка.

Пример с использованием WebTest и Nose:

from webtest import TestApp
import mywebapp

def test_functional_login_logout():
    app = TestApp(mywebapp.app)

    app.post('/login', {'user': 'foo', 'pass': 'bar'}) # log in and get a cookie

    assert app.get('/admin').status == '200 OK'        # fetch a page successfully

    app.get('/logout')                                 # log out
    app.reset()                                        # drop the cookie

    # fetch the same page, unsuccessfully
    assert app.get('/admin').status == '401 Unauthorized'

Встраивание других WSGI-приложений

Это не рекомендуемый способ (следует использовать middleware перед bottle для этого), но вы можете вызвать другие WSGI-приложения из своего приложения bottle и позволить bottle действовать как псевдо-middleware. Вот пример:

from bottle import request, response, route
subproject = SomeWSGIApplication()

@route('/subproject/:subpath#.*#', method='ANY')
def call_wsgi(subpath):
    new_environ = request.environ.copy()
    new_environ['SCRIPT_NAME'] = new_environ.get('SCRIPT_NAME','') + '/subproject'
    new_environ['PATH_INFO'] = '/' + subpath
    def start_response(status, headerlist):
        response.status = int(status.split()[0])
        for key, value in headerlist:
            response.add_header(key, value)
    return app(new_environ, start_response)

Опять же, это не рекомендуемый способ реализации подпроектов. Он здесь только потому, что многие люди об этом просили и для демонстрации того, как bottle соотносится с WSGI.

Игнорирование конечных слэшей

Для Bottle, /example и /example/ — это две разные маршрутизации [1]. Чтобы обработать оба URL одинаково, можно добавить два @route декоратора:

@route('/test')
@route('/test/')
def test(): return 'Slash? no?'

или добавить WSGI-middleware, который удаляет конечные слэши из всех URL:

class StripPathMiddleware(object):
  def __init__(self, app):
    self.app = app
  def __call__(self, e, h):
    e['PATH_INFO'] = e['PATH_INFO'].rstrip('/')
    return self.app(e,h)

app = bottle.app()
myapp = StripPathMiddleware(app)
bottle.run(app=myapp)

Примечания

[1]Потому что они таковы. См. <http://www.ietf.org/rfc/rfc3986.txt>

Keep-alive запросы

Примечание

Для более подробного объяснения см. Введение в асинхронные приложения.

Несколько «push» механизмов, таких как XHR multipart, требуют возможности записи данных ответа без закрытия соединения в сочетании с заголовком ответа «Connection: keep-alive». WSGI не легко поддается этому поведению, но всё же можно сделать это в Bottle, используя асинхронный фреймворк gevent. Вот пример, который работает как с HTTP-сервером gevent, так и с HTTP-сервером paste (он может работать и с другими, но я не пробовал). Просто измените server='gevent' на server='paste' для использования сервера paste:

from gevent import monkey; monkey.patch_all()

import time
from bottle import route, run

@route('/stream')
def stream():
    yield 'START'
    time.sleep(3)
    yield 'MIDDLE'
    time.sleep(5)
    yield 'END'

run(host='0.0.0.0', port=8080, server='gevent')

Если вы перейдёте по ссылке http://localhost:8080/stream, вы должны увидеть «START», «MIDDLE» и «END», появляющиеся по одному (а не ждать 8 секунд, чтобы увидеть их все сразу).

Сжатие gzip в Bottle

Примечание

Для подробного обсуждения см. сжатие

Частым запросом является поддержка Bottle сжатия gzip, что ускоряет сайты путём сжатия статических ресурсов (таких как файлы CSS и JS) во время запроса.

Поддержка сжатия gzip — не простая задача, из-за ряда часто возникающих граничных случаев. Правильная реализация gzip должна:

  • Сжимать на лету и делать это быстро.
  • Не сжимать для браузеров, которые его не поддерживают.
  • Не сжимать уже сжатые файлы (изображения, видео).
  • Не сжимать динамические файлы.
  • Поддерживать два различных алгоритма сжатия (gzip и deflate).
  • Кэшировать сжатые файлы, которые редко меняются.
  • Деактивировать кэш, если любой из файлов изменился.
  • Убедиться, что кэш не слишком большой.
  • Не кэшировать небольшие файлы, потому что поиск по диску займёт больше времени, чем сжатие на лету.

Из-за этих требований проект Bottle рекомендует, что сжатие gzip лучше всего обрабатывается WSGI-сервером, на котором работает Bottle. WSGI-серверы, такие как cherrypy, предоставляют middleware GzipFilter, который можно использовать для этого.

Использование плагина hooks

Например, если вы хотите разрешить Cross-Origin Resource Sharing для содержимого, возвращаемого всеми вашими URL, вы можете использовать декоратор hook и установить функцию обратного вызова:

from bottle import hook, response, route

@hook('after_request')
def enable_cors():
    response.headers['Access-Control-Allow-Origin'] = '*'

@route('/foo')
def say_foo():
    return 'foo!'

@route('/bar')
def say_bar():
    return {'type': 'friendly', 'content': 'Hi!'}

Вы также можете использовать before_request для выполнения действия перед вызовом каждой функции.

Использование Bottle с Heroku

Heroku, популярная облачная платформа приложений, теперь поддерживает запуск приложений Python на своей инфраструктуре.

Этот рецепт основан на Heroku Quickstart, с заменой кода Bottle на Write Your App раздел Getting Started with Python on Heroku/Cedar руководства:

import os
from bottle import route, run

@route("/")
def hello_world():
        return "Hello World!"

run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))

Стек приложения Heroku передает порт, на котором приложение должно прослушивать запросы, используя словарь os.environ.

© 2009–2017 Marcel Hellkamp
Licensed under the MIT License.
https://bottlepy.org/docs/0.12/recipes.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API