Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР 130, ОКТЯБРЬ 2009 г.

Web через Zope. Обзор питоновского web-фреймворка Zope

Вадим Шпаковский (shpak.vadim@gmail.com)

В предыдущем номере журнала был сделан обзор самых популярных питоновских web-фреймворков. Продолжая тему, сегодня мы подробно рассмотрим один из них, а именно - Zope.

Zope2

В 1998 году сотруднику компании Digital Creations, Python-специалисту, Джиму Фултону было предложено прочитать лекцию по CGI (стандарт интерфейса, используемого для связи внешней программы с веб-сервером – в то время был стандартом для Питона в вебе). Согласно легенде, он ничего о CGI не знал, и его начальнику, пока они летели в самолете, пришлось рассказать ему основы CGI. С этой лекцией Фултон и выступил на конференции. Говорят, что протокол ужаснул его своей чрезмерной простотой, и он решил создать что-нибудь объектно-ориентированное. Так началась история Zope. Вскоре компания Digital Creations переименовалась в Zope Corporation и начала выпускать свой продукт под собственной Open Source лицензией ZPL.

Деньги же они зарабатывают за счет создания сайтов и консультаций. В 2000 году в ее состав вошли создатели языка Python, которых возглавлял Гвидо ван Россум (правда, сам Гвидо пробыл в Zope Corporation только до 2003 года, после чего уволился и сейчас благополучно работает в Google). Zope2 приобрел большую популярность благодаря своим особенностям. Во-первых, данные представлялись в виде объектов (что было нетипично для веба), а во-вторых, появилась возможность настраивать компоненты напрямую через браузер. Для написания простого приложения стало требоваться гораздо меньше знаний и сил. Но переход к большим серьезным проектам уже был не так-то прост. Появился даже термин «Z-образная кривая обучения», который означает, что поначалу Zope сильно облегчает жизнь, но затем требует больших усилий для понимания модели разработки. После прохождения этого этапа работать с Zope снова становится легче.

Zope3

Проект Zope3 начался в 2001 году, когда Zope Corporation экспериментировала с компонентной архитектурой. Главной целью было разбиение объектов Zope2, которые сильно разрастались в объеме, на более мелкие - компоненты. Другой целью стало сглаживание кривой обучения: Zope2 был слишком сложным, чтобы внедрить в него компонентную архитектуру. Поэтому приняли решение полностью его переписать, включив в новый проект компонентную архитектуру и все сильные стороны Zope2. Так появился Zope3, образовав две ветки продуктов Zope, которые не обладали обратной совместимостью.

Однако было уже написано слишком много кода под Zope2, чтобы забросить эту ветку. В результате, обе ветки разрабатываются параллельно (на сентябрь 2009 года последними стабильными версиями являлись 3.4.0 и 2.11.3). Нововведения в Zope3 слишком заманчивы, чтобы их игнорировать. Поэтому был запущен проект Five, который позволял использовать некоторые технологии Zope3 в Zope2. Five был интегрирован в Zope2, начиная с версии 2.8. С каждой новой версией в Zope2 становится возможным использовать все больше и больше фишек Zope3.
Далее я буду рассматривать именно Zope3.

Основные концепции

В основе многих особенностей Zope лежит его объектно-ориентированность:

  • Zope-приложение представляет собой коллекцию компонентов - объектов, с четко заданной функциональностью, которая описывается с помощью интерфейсов.
  • Каждый компонент можно заменить на любой другой с таким же интерфейсом (можно провести аналогию с моделью провайдеров в ASP.NET). Таким образом, компоненты выполняют один и тот же функционал, но разными способами. А программист выбирает из них наиболее подходящий.
  • Данные также представляются в виде объектов, которые хранятся в ZOBD (Zope Object Database).
  • Мощный механизм HTML/XML-шаблонов.
  • Широкие возможности для тестирования.
  • Многие из фишек Zope можно использовать и вне его.

Компонент контента

Zope-приложение состоит из компонентов. Один из типов компонентов - это компонент контента. Он предназначен для хранения данных. Отвечает за хранение и апдейт, но не за представление данных или их обработку. С его помощью данные отделяются от логики и представления. Но также есть возможность хранить данные прежним способом, например в реляционной базе данных. В этом случае Zope предоставляет механизм ORM, с помощью которого происходит конвертация реляционных данных в объекты и наоборот.

Адаптеры и утилиты

Адаптер - это объект, который служит своего рода «переходником» между двумя другими объектами, которые из-за разных интерфейсов не могут взаимодействовать напрямую. По-сути, адаптер превращает вызовы внешнего объекта в вызовы методов внутреннего.

Утилита отличается от адаптера тем, что не взаимодействует с другими компонентами, а просто предоставляет некоторую функциональность.

Конфиги

Все компоненты регистрируются в специальном реестре компонентов. Кроме информации о зарегистрированных компонентах, там также хранятся настройки приложения - конфиги. Для регистрации используется специальный XML-подобный язык разметки ZCML (Zope Configuration Markup Language). Файл конфигурации имеет имя и расширение configure.zcml и располагается в папке проекта. Для включения проекта в состав действующих доступных пакетов Zope необходимо дополнительно в папку etcpackage-includes экземпляра сервера поместить файл с именем my_project_name-configure.zcml, содержащий строку <include package="my_project_name"/>.

DTML

Document Template Markup Language (DTML) - средство создания страничных шаблонов, которые поддерживают динамический контент. Он основан на тегах и скриптовом языке, и используется на стороне сервера (в отличие от JavaScript, который работает на стороне клиента).
DTML-теги поддерживают два формата: Extended Python format strings (EPFS) и HTML. Формат EPFS основан на заключении питоновских строк текста в круглые скобки для задания границ блоков кода. Дополнительный параметр форматирования позволяет указать детали преобразования данных; например: %(date fmt=DayOfWeek upper)s позволяет преобразовать дату как день недели заглавными буквами. Формат HTML использует синтаксис HTML на стороне сервера для кодирования команд вставки текстов в формируемый документ. Как и HTML-теги, тег DTML может содержать список атрибутов с присвоением значений (имя атрибута можно не указывать).

Для примера, получим информацию о текущем запросе клиента с помощью объекта REQUEST. Для этого необходимо создать страницу DTML следующего вида:

<html>
<body>
<dtml-var REQUEST html_quote>
</body>
</html>

ZPT

DTML-страницы имеют ряд недостатков, главным из которых является то, что они не предназначены для дизайнеров, работающих с HTML. Как только на HTML-странице появляется код DTML, то результат обычно становится непригодным для редакторов и браузеров. В DTML не совсем удачно разделены представление информации, логика формирования документа и контент, из которого документ формируется. Это может затруднить масштабирование содержимого и разработку самого сайта. Наконец, модель пространства имен в DTML имеет слишком много скрытых нюансов при работе с объектами и не допускает полного программного управления поиском. По прогнозам авторов Zope3, язык DTML лишен в будущем перспектив и будет удален. Подходящей альтернативой являются страничные шаблоны ZPT (Zope Page Template), при работе с которыми приходится иметь дело с языком TAL – расширением языка HTML/XML. Отличие от XML в том, что атрибут тега начинается с ключевого слова tal и отделяется от имени оператора двоеточием. Значение атрибута заключается в кавычки. Включение текста может производиться двумя способами: заменой тега и заменой содержимого тега. Замена тега на значение производится оператором tal:replace = выражение. Если необходимо включить текст внутри тега, но оставить сам тег, то используется оператор tal:content = выражение. Например, во фрагменте:

<head>
<title tal:content="template/title">
The Title
</title>
</head>

– блочный тег title будет оставлен, но его содержимое «The Title» будет заменено значением атрибута «template/title». Кроме того, существует много других полезных операторов (например, repeat, для циклов, или condition, для проверки условий).

Интерфейсы

Интерфейс в Zope выполняет такие же функции, как интерфейсы Java или C#. Интерфейс определяется как питоновский класс, наследуемый от специального класса Interface. Предположим, что нам надо создать проект книги рецептов. Вот как будет выглядеть интерфейс для получения информации о рецепте:

from zope.interface import Interface
class IRecipeInfo(Interface):
"""Give information about a recipe."""
def getName():
"""Return the name of the dish."""
def getIngredients():
"""Return a list of ingredients."""

Интерфейсы, как и классы, поддерживают наследование. Если требуется изменять содержимое рецепта, можно определить следующий интерфейс, который будет включать методы интерфейса IRecipeInfo:

class IRecipe(IRecipeInfo):
"""Give and store recipe's information."""
def setName(name):
"""Set the name of the dish."""
def setIngredients(ingredients):
"""Set the recipe's ingredients."""

Если компонент хочет реализовать определенный интерфейс, следует использовать такой синтаксис (возможна реализация нескольких интерфейсов):

from zope.interface import implements
from worldcookery.interfaces import IRecipeInfo
class RecipeInfo(object): implements(IRecipeInfo)
def __init__(self, name=u'', ingredients=[]):
self.name = name
self.ingredients = ingredients
def getName(self):
return self.name
def getIngredients(self):
return self.ingredients

В отличие от Java или C# интерфейсов, класс, который реализует Zope-интерфейс, не обязан определять все методы этого интерфейса. Ошибка возникнет только во время выполнения, когда произойдет обращение к неопределенному методу.

В модуле zope.interface есть несколько полезных методов. Например, метод alsoProvides(obj, IRecipe) подписывает объект obj (и только объект, а не класс!) на выполнение интерфейса IRecipe. Чтобы сделать то же самое для всего класса Recipe, необходимо вызвать метод classImplements(Recipe, IRecipe). Метод verifyObject(IRecipe, obj) из модуля zope.interface.verify проверяет, реализует ли объект интерфейс полностью. Метод verifyClass выполняет такую же функцию для классов.

Схемы

Описанный выше подход с определением интерфейсов с get- и set- методами не очень питоничен, особенно для простых объектов, которые просто хранят данные. Для питоновских объектов характерна работа с аттрибутами напрямую. Реализуется с помощью механизма схем следующим образом:

from zope.interface import Interface
from zope.schema import List, TextLine
class IRecipe(Interface):
name = TextLine(
title=u"Name",
description=u"Name of the dish",
required=True)
ingredients = List(title=u"Ingredients",
description=u"List of ingredients.",
required=True,
value_type=TextLine(title=u"Ingredient"))

В действительности, схемы являются теми же самыми интерфейсами, поэтому отличие между ними только внешнее.

Установка

  1. Установить Питон (если вдруг он все еще не стоит).
  2. Установить Zope (установщик можно скачать с официального сайта или взять на диске ][).
  3. Чтобы начать работать с Zope, необходимо создать, по крайней мере, один экземпляр Zope-сервера, который кроме сервера также содержит экземпляр базы данных (никто не мешает создать несколько таких экземпляров). Для этого нужно запустить mkzopeinstance.bat, который находится в папке <Python_dir>/Scripts, указать директорию экземпляра сервера, имя администратора и пароль. Скрипт создаст в указанной директории необходимые для функционирования сервера файлы и папки.
  4. Для запуска сервера надо запустить скрипт bin/runzope (или runzope.bat в Windows). Способ хорош при разработке приложения и не подходит для запуска готового продукта, так как он связан с терминалом. Чтобы сервер работал и при закрытии терминала, его необходимо запускать с помощью скрипта bin/zopectl с параметром start. Для остановки запусти тот же скрипт с параметром stop. Но работает только для Unix-систем (под Windows нужно провести более сложные махинации).
  5. Для создания нового проекта необходимо создать директорию <Zope_server_dir>/lib/python/<app_name>.
  6. Одна из особенностей Zope - его интерфейс управления (ZMI), который доступен после запуска сервера по адресу http://localhost:8080/manage.

Основные файлы

Zope3 устанавливается, как и все прикладные пакеты Питона, в папку <Python_dir>Libsite-packages, – часть инсталляционных файлов располагается в папке <Python_dir>Scripts. В директории экземпляра сайта имеются папки bin, etc, lib. log и var.

Папка bin сайта содержит код и командные файлы для запуска системы на исполнение (в частности, runzope.bat).

Папка etc содержит конфигурационные файла сайта. Например, в файле etc/zope.conf хранятся все настройки сервера и базы данных. Секция server определяет порты протокола TCP/IP 8080 для http-сервера и 8021 для FTP-сервера. Секция zodb определяет расположение файла объектной базы данных. Секции accesslog и eventlog определяют местоположение журналов сайта для регистрации событий. Файлы principals.zcml и securitypolicy.zcml конфигурируют параметры допущенных пользователей сайта (принципалов) и их роли. Файл site.zcml – главный конфигурационный файл сайта, с ссылками на отдельные разделы, определяющие детали настроек системы.

Папка etcpackage-includes содержит небольшие файлы на языке ZCML. Они определяют включение различных пакетов в текущую конфигурацию сайта. Эта папка пополняется администраторами или разработчиками новых компонент при необходимости расширить функциональность сайта.

Папка libpython предназначена для размещения новых пакетов, определяющих особенности данного сайта. Для подключения пакета необходимо добавить ссылку на него в папку etcpackage-includes.

Папка log содержит файлы журналов, просмотр которых позволяет администратору или программисту проанализировать последовательность событий и провести диагностику ошибок, возникающих при отладке новых пакетов.

Папка var содержит файлы ZODB.

Zope Management Interface (ZMI)

Среда Zope3 является одновременно сетевым сервером – публикатором хранимых на сервере в объектной базе данных экземпляров компонент и средством разработки сетевых приложений. Сетевые приложения могут разрабатываться как в режиме TTW (через сеть) с использованием вышеупомянутого ZMI, так и в режиме создания файлов проекта средствами файловой системы.

Уже упоминалось, что ZMI - это интерфейс управления, который позволяет управлять содержимым Zope. Кроме того, он дает возможность динамически управлять настройками сервера во время его работы (закладка «Сервер»). В левом верхнем углу находится навигатор - иерархическая структура объектной базы данных. В нем представлены две категории объектов – простые объекты и контейнеры. Ниже расположены другие блоки, включая меню «Добавить». Остальную часть экрана занимает рабочая область, в которой производятся различные операции над объектами.

Hello, world!

В качестве демонстрационного примера напишем страницу, которая выводит надпись «Hello, world!».

1. Создаем директорию для пакета: /lib/python/helloworld.

2. В ней создаем пустой файл __init__.py (чтобы Питон трактовал ее как пакет).

3. Тут же создай файл browser.py со следующим содержимым:

from zope.publisher.browser import BrowserView
class HelloView(BrowserView):
def __call__(self):
return """
<html>
<head>
<title>Hello World</title>
</head>
<body>
Hello, world!
</body>
</html>"""

4. Регистрируем пакет helloworld. Для этого в директории etc/package-includes создадим файл helloworld-configure.zcml, содержащий строки:

<configure
xmlns="http://namespaces.zope.org/zope">
<include package="helloworld" />
</configure>

5. Последний шаг - написание конфига configure.zcml, который нужно разместить в директории только что созданного пакета lib/python/helloworld. Этот конфиг регистрирует представление страницы с именем helloworld и открытыми правами доступа, использующей объявленный нами класс HelloView:

<configure
xmlns="http://namespaces.zope.org/browser">
<page
for="*"
name="helloworld"
permission="zope.Public"
class=".browser.HelloView"
/>
</configure>

6. Запускаем сервер и переходим по адресу http://localhost:8080/helloworld. Должна загрузиться страница с надписью «Hello, world!».

Заключение

Zope - очень солидный фреймворк, и в рамках одной статьи трудно упомянуть все его возможности, не говоря о том, чтобы рассмотреть их подробно. Хочешь подробностей? Советую посетить ресурсы, указанные на врезке. Там можно найти пару неплохих статей для начинающих. Для серьезного изучения я рекомендую книгу Суханова «Введение в Zope3». При небольшом объеме в ней рассматриваются все основные концепции. Книгу можно найти на диске ][ в разделе «Литература». Удачи!

Полезные ресурсы.

http://zope.org – официальный сайт.

http://wiki.zope.org/zope3/Zope3Wiki – если нет проблем с английским, то здесь можно найти много полезного.

http://zopelada.ru, http://zope3.ru, http://zope.net.ru – русскоязычные ресурсы.

INFO

Код Zope3 содержит более 5000 автоматических тестов, что значительно увеличивает его стабильность, особенно при внесении крупных изменений.

В 2006 году появился Grok - идейный продолжатель Zope3!

Содержание
ttfb: 7.9789161682129 ms