Уже не раз писал о том, что мы используем в качестве основы Tipfy. И я думаю остаются вопросы прочему и в чем особенности.
Прежде всего tipfy состоит из нескольких частей, которые в целом определили наш выбор. Поскольку zc.buildout это необходимое условие для работы над любым python проектом, в котором участвует несколько человек, возникла задача найти нормальный рецепт для разворачивания GAE SDK. Такой рецепт был найден, заодно захотелось узнать, чем занимается автор и, оказалось, что он еще и делает Tipfy. Поскольку нам хотелось отказаться от всего мусора, который предлагает Django, то Tipfy быстро занял нужное место.
Вот краткий список того, что нужно было именно нам:
Внимание
Это важная оговорка, многие могут подумать, что Tipfy — это то, что им надо для начала работы над новым GAE проектом. Хочу предостеречь этих людей. Tipfy прежде всего ваш выбор тогда, когда вы точно уверены, что код фреймворка будет только мешать.
Убедитесь, что версия python, которую вы используете, является версией 2.5.x. Надеюсь, что скоро это требование устареет, в связи с планами Google добавить поддержку 2.7.
Мой пример будет немного отличаться от рекомендуемого пути, но приведет к тому же результату.
Создайте папку проекта:
$ mkdir demo
$ cd demo
Теперь можно создать buildout.cfg, все его содержимое можно взять на странице документации по рецепту appfy.recipe.gae:
[buildout]
parts =
gae_sdk
gae_tools
app_lib
unzip = true
relative-paths = true
download-cache = etc/downloads
# eggs-directory = etc/eggs
develop-eggs-directory = etc/develop-eggs
parts-directory = etc/parts
#
[gae_sdk]
recipe = appfy.recipe.gae:sdk
url = http://googleappengine.googlecode.com/files/google_appengine_1.4.3.zip
destination = ${buildout:parts-directory}
hash-name = false
clear-destination = true
#
[gae_tools]
recipe = appfy.recipe.gae:tools
sdk-directory = ${gae_sdk:destination}/google_appengine
[app_lib]
# Sets the library dependencies for the app.
recipe = appfy.recipe.gae:app_lib
lib-directory = src/distlib
use-zipimport = false
#
eggs =
tipfy
tipfy.ext.debugger
tipfy.ext.jinja2
tipfy.ext.wtforms
tipfy.ext.db
# Остальные необходимые модули, например:
# pygments
Конечно выполнение этого сценария потребует создания структуры папок:
etc/downloads — кеш, в который скачиваются egg файлыetc/develop-eggs — папка, в которую можно будет положить яйца, которые будут разрабатыватьсяetc/parts — рабочая директория рецептов, которым нужны дополнительные данные, в нашем случае сюда скачается GAE SDKsrc — собственно данные проекта и его исходный кодsrc/distlib — ключевое место проекта, вся магия для того, чтобы наполнить эту папку зависимыми модулями, которые потом уйдут на хостинг вместе с кодомПодробнее о работе переменных, которым присвоены значения с путями, можно прочитать в справке по buildout.
Создаем папки:
demo $ mkdir etc etc/downloads etc/develop-eggs etc/parts
demo $ mkdir src src/distlib
Самое время развернуть окружение:
demo $ python2.5 <(curl http://python-distribute.org/bootstrap.py) --distribute
Команда создает папку bin и скачивает современный аналог старых setuptools distribute. Поскольку выполнять требуется один раз, предлагаю такой краткий вариант. А теперь главная часть:
demo $ bin/buildout
Выполнение команды займет некоторое время, но результат того стоит. Вы получите полностью готовое окружение.
Теперь дело за малым, начать писать код своего проекта. Он будет расположен в папке src. Структура tipfy проекта достаточно простая:
app.yaml — уже знакомый по документации конфигурационный файл Google App Engine проектаmain.py — в принципе, этот файл нужно будет изменять достаточно редкоconfig.py — файл конфигурации, тоже должен быть знаком по многим другим проектамurls.py — правила роутинга адресов страниц к хендлерам их обрабатывающимАвторы рекомендуют так же следующие папки:
lib — ваши библиотекиtemplates — папка с шаблонами, если ваш проект совсем маленький, то возможно будет удобно размещать все шаблоны в одной папкеstatic — место для статических файлов, часто можно встретить следующую структуру внутри:
js — javascriptimages — картинкиcss — таблицы стилейrobots.txt, favicon.icoapp.yamlФайл, необходимый для работы любого GAE проекта. Минимальное содержимое для tipfy получится следующим:
application: my-app # тот id который вы выбрали в консоли GAE
version: dev # версия
runtime: python
api_version: 1
derived_file_type:
- python_precompiled
handlers:
- url: /(robots\.txt|favicon\.ico)
static_files: static/\1
upload: static/(.*)
- url: /static # та самая папка static
static_dir: static
- url: /remote_api
script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
login: admin
- url: /_ah/queue/deferred
script: main.py
login: admin
- url: /.*
script: main.py
Обратите внимание на последний блок, он указывает, что любые адреса страниц, кроме тех, которые были указаны выше (порядок имеет значение) передаются модулю main.py.
main.pyГлавный загрузочный файл проекта тоже достаточно простой:
# -*- coding: utf-8 -*-
import os
import sys
if 'lib' not in sys.path:
# Add /lib as primary libraries directory, with fallback to /distlib
# and optionally to distlib loaded using zipimport.
sys.path[0:0] = ['lib', 'distlib', 'distlib.zip']
import config
import tipfy
# Is this the development server?
debug = os.environ.get('SERVER_SOFTWARE', '').startswith('Dev')
# Instantiate the application.
app = tipfy.make_wsgi_app(config=config.config, debug=debug)
def main():
app.run()
if __name__ == '__main__':
main()
Строка if 'lib' not in sys.path: проверяет добавлены ли модули, которые лежат в папке lib, в путях, по которым ищет python. Из-за особенности устройства инстанций GAE путь уже может быть изменен (модулем, который загрузился до этого или warmup хендлером), поэтому проводится проверка.
Рецепт appfy.recipe.gae после запуска bin/buildout заполняет папку src/distlib теми продуктами, которые перечислены в директиве ${app_lib:eggs} в buildout.cfg. Поэтому, после перезапуска buildout'а мы получаем актуальное содержимое папки, включая изменения из яиц, которые разрабатываются и находятся в режиме для разработки.
Редактировать файл main.py требуется крайне редко, разве что вам надо загрузить какие-то модули до обработки конфигурации и того, как начнут обрабатываться middleware самим tipfy (а точнее werkzeug вокруг которого tipfy на самом деле является оберткой).
config.pyКонфигурационный файл тоже имеет достаточно простую архитектуру, из обязательных полей, по сути только перечень middleware (который может быть пустым) и сам перечень приложений:
# -*- coding: utf-8 -*-
config = {}
# Configurations for the 'tipfy' module.
config['tipfy'] = {
# Enable debugger. It will be loaded only in development.
'middleware': [
'tipfy.ext.debugger.DebuggerMiddleware',
],
# Перечень ваших приложений
'apps_installed': [
'demo',
# другие приложения
],
}
# Остальные настройки
urls.pyХотя описание путей само по себе нельзя назвать простым занятием, но файл urls.py в данном случае является предельно простым и прозначным:
# -*- coding: utf-8 -*-
from tipfy import get_config, import_string, Rule
import logging
def get_rules():
rules = []
for app_module in get_config('tipfy', 'apps_installed'):
try:
# Load the urls module from the app and extend our rules.
app_rules = import_string('%s.urls' % app_module)
rules.extend(app_rules.get_rules())
except (AttributeError, ImportError):
logging.error("Ouch! Misconfigured modules list, cant use %r" %
app_module)
return rules
Задача файла обойти все приложения, зарегистрированные в конфигурационном файле config.py (директива apps_installed) и объединить их в единое правило.
Теперь время написать хоть что-то, что сможет вывести на экран вожделенный Fuck world!. Создайте папку demo, со следующей структурой:
__init__.py — пустой файл, который говорит, что текущая папка является python модулемhandlers.py — непосредственно код модуляurls.py — правила обработки страниц сайтаСодержимое src\demo\handlers.py:
from tipfy import RequestHandler, Response
class HelloWorldHandler(RequestHandler):
def get(self):
return Response('Hello, World!')
Содержимое src\demo\urls.py:
from tipfy import Rule
def get_rules(app):
rules = [
Rule('/', endpoint='hello-world', handler='demo.handlers.HelloWorldHandler'),
]
Когда весь гениальный код написан, хочется его проверить в деле. Рецепт appfy.recipe.gae услужливо сделал для этого все нужные скрипты и разместил их в папке bin. Поскольку нас интересует запуск, выполните следующую команду:
demo $ bin/dev_appserver src
Если все было сделано правильно, у вас должны были появиться логи проверки версии SDK и уведомление о том, на каком порту запущен веб сервер. Адрес можно скопировать в браузер и попытаться зайти, где с некоторой вероятностью получить приветствие.
Скрипт dev_appserver может получать все те параметы, которые описаны в документации, но для удобства можно их перечислить в специальном конфигурационном файле gaetools.cfg и положить рядом с buildout.cfg. Пример файла:
[dev_appserver]
defaults =
--datastore_path=var/data.store
--history_path=var/history.store
--blobstore_path=var/blob.store
src
Последняя строка указывает на папку, в которой расположен проект, что упростит команду запуска сервера до:
demo $ bin/dev_appserver
Не забудьте создать папку var если воспользуетесь указанным файлом.
Остался последний шаг - отправить проект на сервера App Engine и проверить работу там. Для этого в папке bin есть скрипт appfg, который так же входит в состав стандартного SDK, и имеет тот же самый набор команд. Заливка на сервер происходит достаточно просто:
demo $ bin/appcfg --no_cookies --email=your_email@gmail.com --passin update src
В качестве email'а укажите свой. Выполнение команды может занять минуты.
Все описаные шаги в будущем можно сократить, скачав с сайта tipfy архив типового проекта www.tipfy.org/tipfy.build.tar.gz, в котором уже есть необходимая структура папок и файлов.
Дополнительные ссылки: