Fork me on GitHub
16/11/2007

PasteDeploy: введение для разработчиков

Вторая статья про PasteDeploy, теперь уже для разработчика. Крайне рекомендую предварительно ознакомиться с первой статьей.

Итак, вы решили, что в вашем приложение неплохо бы использовать PasteDeploy. Это сделать очень просто, что я вам и продемонстрирую.

Теория

Сегодня мы рассмотрим способ, который не требует особой модификации уже написанного приложения и не привязывает новое приложение к PasteDeploy.

Достигается это при помощи системы плагинов eggs. Для подключения PasteDeploy к приложению, вы определяете точку входа paste.app_factory. Фабрика, приоритетная для данного egg, должна иметь имя main. Фабрик может быть несколько. Фабрика -- это callable-объект со следующей сигнатурой: factory(global_conf, **local_conf), т.е. глобальные настройки передаются словарем, а локальные -- как опциональные именованные параметры. При вызове этого объекта должно возвращаться WSGI-приложение.

Практика

Полагаю, что у вас есть некое WSGI-приложение, поведение которого задается параметрами конструктора, например так:

class DemoApp(object):
    """
    Demo WSGI app
    """
    def __init__(self, foo, bar, baz, author, description):
        self.foo = foo
        self.bar = bar
        self.baz = baz
        self.author = author
        self.description = description

    def __call__(self, environ, start_response):
        start_response('200 Ok', [('Content-Type', 'text/plain')])
        params = ['\t%s (%s): %r\n' % (p, type(getattr(self, p)).__name__, getattr(self, p)) 
                  for p in ('foo', 'bar', 'baz')]
        env = ['\t%s ==> %s\n' % (i, environ[i]) for i in sorted(environ.keys())]
        return [
            ' ==== Demo app by %s ==== \n' % self.author,
            ' .:[ %s ]:.\n' % self.description,
            '\nHere the params:\n',
        '-=-=-=-=-=-=-=-=-=-=-=-=-=-\n' ] + params + [
            '\nHere the environ:\n',
        '-=-=-=-=-=-=-=-=-=-=-=-=-=-\n'] + env

Приложение простое, показывает свои параметры (и типы значений этих параметров) и переменные окружения.

Пишем для этого приложения фабрику:

def demo_app_factory(global_conf, **local_conf):
    """
    Example of PasteDeploy app factory using DemoApp as target WSGI application
    """
    conf = global_conf.copy()
    conf.update(local_conf)
    return DemoApp(
        conf.get('foo') or '--absent--',
        conf.get('bar') or '--absent--',
        conf.get('baz'),
        conf.get('author') or 'N/A',
        conf.get('description') or '--absent--')

и указываем в setup.py пакета эту фабрику с именем main как точку входа paste.app_factory:

setup( ...
   entry_points="""
    # -*- Entry points: -*-

    [paste.app_factory]
    main = pastedeploysimplestexample:demo_app_factory
  """,
  ...)

Устанавливаем это яйцо, и пробуем такой конфиг:

[DEFAULT]
author = Yury Yurevich
description = The first config for PasteDeploy-enabled app

[app:main]
use = egg:PasteDeploySimplestExample
# однострочное значение
param_foo = It works!
# многострочное значение
param_bar = Multiline is 
    supported. Just 
    indent it.
# boolean значение, можно использовать 1/0, True/False, true/false
param_baz = 1

[server:main]
use = egg:Paste#http
host = 127.0.0.1
port = 8080

Всё бы ничего, но опция baz по логике должна быть boolean, а в приложение передается строка. Можно, конечно, это сделать руками, но у PasteDeploy уже есть на это дело маленькие помощники: paste.deploy.converters.asbool, paste.deploy.converters.asint, paste.deploy.converters.aslist.

Добавление asbool в фабрику оставлю читателям, а особо нетерпеливые могут посмотреть уже готовый код примера.

Дополнительно

Не думаю, что вам понадобится писать собственные сервера на Python, тем не менее может пригодиться. Для серверов используется точка входа paste.server_factory, сигнатура такая же как и у фабрики для приложения. Callable-объект, соответствующий этой точке должен возвращать сервер в виде callable-объекта с сигнатурой serve(wsgi_app). Пример можно посмотреть всё в том же PasteDeploySimplestExample.

Комментарии

Все статьи