Как работает SiteManager что бы обеспечить доступ к локальным утилитам
Как работает SiteManager что бы обеспечить доступ к локальным утилитам
Забавно устроен Zope. Есть глобальный SiteManager, есть куча локальных SiteManager-ов. Естественно, ряд запросов должен обрабатываться локальным SiteManager. В теории (т.е. если посмотреть написанные коды), что бы получить его, нужно дернуть что-то типа getSiteManager(), у которого есть специальный аргумент context.
Тем не менее, по неясным для меня причинам, такой способ не используется. getSiteManager() (см. zope.component._api) сделан hookable (это такой способ "законно" делать то, что когда-то в Zope2 называлось "Monkey Patch", подробности см. в zope.hookable), и фактически не используется. Так как в zope.app.component.hooks перекрывается забавным getSiteManager, который обрабатывает свой единственный аргумент несколько иначе.
Итак, если аргумент равняется None (т.е. фактически не указан), то вместо того, что бы вернуть глобальный SiteManager, возвращается содержимое siteinfo.sm, которая определена, как понимаете, там же, в zope.app.component.hooks.
Переменная siteinfo это экземпляр класса SiteInfo, который является подклассом zope.thread.local. А вот zope.thread.local это такая забавная штука, которая позволяет иметь в треде локальные (в рамках треда) переменные: вы просто порождаете класс от zope.thread.local и можете пользоваться его атрибутами как локальными переменными треда. Соответственно, атрибут sm содержит SiteManager и инициализируется глобальным SiteManager'ом. Вроде бы пока ничего не изменилось - вызов getSiteManager() по-прежнему возвращает глобальный SiteManager.
А теперь заглянем в zope.app.component.site :), в котором определена замечательная функция threadSiteSubscriber, которая в zope.app.component.configure прописана как обработчик события zope.app.publication.interfaces.IBeforeTraverseEvent при траверсе SiteManager (точнее, объекта с интерфейсом ISite). Назначение этого хендлера - прописать в siteinfo.sm пройденный SiteManager. Именно поэтому siteinfo.sm всегда содержит последний пройденный SiteManager, и именно поэтому вызов getSiteManager() (без аргументов) возвращает __последний_пройденный__ SiteManager.
Зачем так сделано - не до конца понимаю. Ну, видимо, так сложилось.
Сухой остаток отсюда, на самом деле не SiteManager:
- zope.thread.local
- работа с локальными переменными треда, я против таких методов :), но хорошо, что такая возможность есть.
- zope.hookable
- стандартный способ перегружать ключевые функции Zope.
А SiteManager и связанные с ним реестры находятся сейчас в состоянии интенсивного переписывания, и в конце концов не так уж важно, как там они работают сейчас.