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.
