Pyramid использует реестр компонентов фреймворка Zope Component Architecture в качестве своего реестра приложения. В разговорной речи Zope Component Architecture обозначается как "ZCA".
API zope.component, используемый для доступа к данным в традиционных приложениях Zope, может быть непрозрачным. Например, это обычный поиск "безымянной утилиты" с использованием глобального API zope.component.getUtility(), как это может быть в традиционном приложении Zope:
from pyramid.interfaces import ISettings
from zope.component import getUtility
settings = getUtility(ISettings)
После запуска этого кода, переменная settings будет содержать Python-словарь. Но маловероятно, что непосвященный человек будет способен понять это, только поверхностно взглянув на код. Когда API zope.component.getUtility используется разработчиком, создается большая умозрительная нагрузка на того, кто читает этот код непостоянно.
В то время как ZCA отличный инструмент для тех, кто создает такой фреймворк как Pyramid, это не всегда лучший инструмент для тех, кто создает обычное приложение, из-за непрозрачности API zope.component. Соответственно, Pyramid, как правило, скрывает наличие ZCA от разработчиков приложений. Вам необязательно понимание ZCA для создания приложений Pyramid; фактически, ZCA используется только для деталей реализации фреймворка.
Однако, разработчики, которые уже занимались написанием приложений Zope, часто хотят использовать ZCA и при создании приложений Pyramid. Pyramid позволяет это сделать.
Использование глобального API ZCA в Pyramid-приложении
Zope использует единый реестр ZCA - "глобальный" реестр ZCA - для всех приложений Zope, запущенных в одном Python-процессе, фактически, делая невозможным запустить более одного приложения Zope в одном процессе.
Однако, для простоты развертывания, часто бывает полезным иметь возможность запустить больше одного приложения на процесс. Например, использование "компоновщика" PasteDeploy позволяет запускать отдельные WSGI-приложения в одном процессе, каждое из которых будет отвечать на запросы для определенного URL-префикса. Это, например, делает возможным запустить приложение TurboGears по адресу /turbogears и приложение Pyramid по адресу /pyramid, обслуживаемые одним WSGI-сервером внутри одного Python-процесса.
Большинство реальных приложений Zope относительно большие, что делает непрактичным запуск более одного Zope-приложения на один Python-процесс в связи с ограничениями памяти. Однако, приложение Pyramid может быть очень маленьким и потреблять очень мало памяти, поэтому обоснованной целью является возможность запускать более одного Pyramid-приложения на процесс.
Для того чтобы сделать возможным запуск более одного Pyramid-приложения в одном процессе, Pyramid использует отдельный ZCA-реестр для каждого приложения.
Хотя это позволяет достичь цели, это создает некоторые проблемы, если попытаться использовать в Pyramid-приложении паттерны, которые вы можете использовать при создании обычного Zope-приложения. Без специальной помощи "глобальные" ZCA-API, такие как zope.component.getUtility() и zope.component.getSiteManager(), будут использовать "глобальный" ZCA-реестр. Поэтому, эти API ожидаемо провалятся при использовании в Pyramid-приложении, потому что "глобальный" реестр ZCA будет принят во внимание раньше, чем реестр компонентов, связанный с вашим Pyramid-приложением.
Есть три пути исправить это: полностью отказавшись от глобального ZCA-API, используя pyramid.config.Configurator.hook_zca() или передав глобальный реестр ZCA в конструктор класса Configurator во время старта. Опишем все три метода.
Отказ от использования глобального ZCA-API
"Глобальные" функции ZCA-API, такие как zope.component.getSiteManager, zope.component.getUtility, zope.component.getAdapter() и zope.component.getMultiAdapter() требуются не всегда. Каждый реестр компонентов имеет API методов, которые дают похожую функциональность и могут буть использованы взамен. Например, в предположении, что значением переменной registry является реестр компонентов Zope Component Architecture, следующий кусок кода эквивалентен zope.component.getUtility(IFoo):
registry.getUtility(IFoo)
Полный API методов задокументирован в пакете zope.component, но он в значительной степени отражает "глобальный" API.
Если вы готовы отказаться от использования "глобального" ZCA-API и использовать вместо этого интерфейс методов реестра, вы должны знать, как получить реестр компонентов Pyramid.
Есть два пути сделать это:
- использовать функцию pyramid.threadlocal.get_current_registry() внутри view или ресурса Pyramid.
- использовать атрибут объекта request с именем registry внутри кода Pyramid-view, например, request.registry. Это реестр компонентов ZCA, связанный с текущим Pyramid-приложением.
Посмотрите Thread Locals для того чтобы узнать больше информации о pyramid.threadlocal.get_current_registry().
Включение глобального API ZCA с использованием hook_zca
Рассмотрим следующий фрагмент идиоматического кода запуска Pyramid:
from pyramid.config import Configurator
def app(global_settings, **settings):
config = Configurator(settings=settings)
config.include('some.other.package')
return config.make_wsgi_app()
Когда запускается показанная выше функция run, создается Configurator. Когда конфигуратор создан, он создает новый реестр приложения (реестр компонентов ZCA). Новый реестр создается всякий раз, когда аргумент registry пропущен при вызове конструктора класса Configurator, или когда аргумент registry передан в конструктор класса Configurator со значением None.
Во время запроса, реестр приложения, созданный конфигуратором, является созданным для текущего контекста. Это означает, что вызовы get_current_registry() в потоке, обрабатывающем запрос, вернут реестр компонентов, связанный с приложением.
В результате, разработчики приложений могут использовать get_current_registry для получения реестра, и, таким образом, получать доступ к утилитам и т.д., как при отказе от использования глобального ZCA-API. Но они все еще не могут использовать глобальный API ZCA. Без специальной обработки, методы глобального API ZCA будут всегда возвращать глобальный реестр ZCA (находящийся в zope.component.globalregistry.base).
Для того чтобы исправить это и заставить методы глобального API ZCA использовать "текущий" реестр Pyramid, нужно вызвать hook_zca() внутри стартового кода. Например:
from pyramid.config import Configurator
def app(global_settings, **settings):
config = Configurator(settings=settings)
config.hook_zca()
config.include('some.other.application')
return config.make_wsgi_app()
Мы добавили строку в наш оригинальный стартовый код (строка номер 6), которая вызывает config.hook_zca(). Эффект этой строки под капотом это аналог выполнения следующего кода:
from zope.component import getSiteManager
from pyramid.threadlocal import get_current_registry
getSiteManager.sethook(get_current_registry)
Это заставляет глобальный API ZCA использовать реестр приложения Pyramid в потоках, обрабатывающих запрос Pyramid.
Вызова hook_zca обычно достаточно для решения проблемы использования глобального API ZCA внутри приложения Pyramid. Однако, это также означает, что приложение Zope, запущенное в том же процессе, может начать использовать глобальный реестр Pyramid вместо глобального реестра Zope, фактически, инвертируя исходную проблему. В таком случае, следуйте шагам в следующей секции.
Включение глобального API ZCA, используя глобальный реестр ZCA
Вы можете указать приложению Pyramid использовать глобальный реестр ZCA во время старта вместо создания нового:
from zope.component import getGlobalSiteManager
from pyramid.config import Configurator
def app(global_settings, **settings):
globalreg = getGlobalSiteManager()
config = Configurator(registry=globalreg)
config.setup_registry(settings=settings)
config.include('some.other.application')
return config.make_wsgi_app()
Интересны строки 5, 6 и 7, показанные выше. Строка 5 извлекает глобальный реестр компонентов ZCA. Строка 6 создает конфигуратор, передавая глобальный реестр ZCA в его конструктор в качестве значения аргумента registry. Строка 7 производит в глобальном реестре регистрации, специфичные для Pyramid; это код, который нормально выполняется, когда реестр создан заранее, но мы можем сделать это вручную, когда явно передаем реестр.
В этот момент Pyramid будет использовать глобальный реестр ZCA вместо создания нового реестра специально для приложения; поскольку по умолчанию глобальный API ZCA будет использовать этот реестр, приложение Zope будет работать так, как вы и ожидаете при использовании глобального API ZCA.
*Статья является переводом http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/zca.html