2008-01-24

API работы с реестрами.txt

  2008-01-24 13:30

API работы с реестрами: Реестр - основной строительный инструмент Zope, который в настоящее время находится в состоянии интенсивного изменения. object is valid. ...

API работы с реестрами:

Реестр - основной строительный инструмент Zope, который в настоящее время находится в состоянии интенсивного изменения. Некоторые соображения Джима Фултона на этот счет можно прочитать здесь

Настоящий текст есть вольный перевод краткой документации из zope.component.

Утилита:

Утилита - это такой компонент, который предоставляет интерфейс и может быть найден по этому интерфейсу и имени. Рассмотрим простейший пример:

                from zope import interface

                class IGreeter(interface.Interface):
                    def greet():
                        "say hello"

                class Greeter:

                    interface.implements(IGreeter)

                    def __init__(self, other="world"):
                        self.other = other

                    def greet(self):
                        print "Hello", self.other

Экземпляр этого класса может быть зарегистрирован используя provideUtility:

                from zope import component
                greet = Greeter('bob')
                component.provideUtility(greet, IGreeter, 'robert')

В этом примере утилита зарегистрирована как предоставляющая интерфейс IGreeter и параметризованная именем bob. Утилита может быть локализована (найдена) при помощи queryUtility или getUtility:

                >>> component.queryUtility(IGreeter, 'robert').greet()
                Hello bob

                >>> component.getUtility(IGreeter, 'robert').greet()
                Hello bob

Различие между queryUtility и getUtility в поведении при неудачном поиске. queryUtility возвращает значение по умолчанию:

                >>> component.queryUtility(IGreeter, 'ted')
                >>> component.queryUtility(IGreeter, 'ted', 42)
                42

Тогда как getUtility порождает исключение:

                >>> component.getUtility(IGreeter, 'ted')
                ... 
                Traceback (most recent call last):
                ...
                ComponentLookupError: (<InterfaceClass ...IGreeter>, 'ted')

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

                >>> ted = Greeter('ted')
                >>> component.provideUtility(ted, name='ted')
                >>> component.queryUtility(IGreeter, 'ted').greet()
                Hello ted

По умолчанию, имя это пустая строка:

                >>> world = Greeter()
                >>> component.provideUtility(world)
                >>> component.queryUtility(IGreeter).greet()
                Hello world

Адаптер:

Адаптер это компонент выполняющий адаптацию компонентов к интерфейсам: т.е. выражающий отсутствующие у компонентов интерфейсы через интерфейсы, которые компонент предоставляет. В чем-то понятие адаптера аналогично понятию прокси, да и устроен адаптер обычно также.

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

Так как инициирование работы адаптера требует связывания его с другими объектами, то удобным решением оказалось реализовывать адаптеры как фабрики (обычно, классы). В следующем примере приведен персонализированный класс, который будет адаптировать различных людей (IPerson) к поздравленям (IGreater), давая возможность получать персонализированные поздравления:

                >>> class IPerson(interface.Interface):
                ...     name = interface.Attribute("Name")

                >>> class PersonGreeter:
                ...
                ...     component.adapts(IPerson)
                ...     interface.implements(IGreeter)
                ...
                ...     def __init__(self, person):
                ...         self.person = person
                ...
                ...     def greet(self):
                ...         print "Hello", self.person.name

Этот класс имеет конструктор, аргумент которого - адаптируемый объект.

Функция component.adapts использована для того, чтобы декларировать то, что адаптирует адаптер. По адаптеру можно узнать, что именно он адаптирует:

                >>> list(component.adaptedBy(PersonGreeter)) == [IPerson]
                True

Если при декларации адаптера не был декларирован адаптируемый интерфейс, то возвращается None:

                >>> list(component.adaptedBy(PersonGreeter))
                None

Если адаптируемый и предоставляемый интерфейс были декларированы, то регистрация адаптера упрощается:

                >>> component.provideAdapter(PersonGreeter)

Для адаптеров, которые адаптируют единственный интерфейс к единственному интерфейсу без имени, адаптер можно получить просто вызвав интерфейс:

                >>> class Person:
                ...     interface.implements(IPerson)
                ...
                ...     def __init__(self, name):
                ...         self.name = name

                >>> IGreeter(Person("Sally")).greet()
                Hello Sally

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

                >>> class BobPersonGreeter(PersonGreeter):
                ...     name = 'Bob'
                ...     def greet(self):
                ...         print "Hello", self.person.name, "my name is", self.name

                >>> component.provideAdapter(
                ...     BobPersonGreeter, [IPerson], IGreeter, 'bob')

Возможен вызов provideAdapter с указанием ключевых аргументов, вместо позиционных:

                >>> class TedPersonGreeter(BobPersonGreeter):
                ...     name = "Ted"

                >>> component.provideAdapter(
                ...     factory=TedPersonGreeter, adapts=[IPerson],
                ...     provides=IGreeter, name='ted')

Чтобы получить именованный адаптер с необходимыми параметрами, можно использовать queryAdapter или getAdapter :

                >>> component.queryAdapter(Person("Sally"), IGreeter, 'bob').greet()
                Hello Sally my name is Bob

                >>> component.getAdapter(Person("Sally"), IGreeter, 'ted').greet()
                Hello Sally my name is Ted

Если адаптер не может быть найден, queryAdapter возвращает значение по умолчанию:

                >>> component.queryAdapter(Person("Sally"), IGreeter, 'frank')
                >>> component.queryAdapter(Person("Sally"), IGreeter, 'frank', 42)
                42

А getAdapter порождает исключение :

                >>> component.getAdapter(Person("Sally"), IGreeter, 'frank')
                ... # doctest: +ELLIPSIS
                Traceback (most recent call last):
                ...
                ComponentLookupError: (...Person...>, <...IGreeter>, 'frank')

Адаптеры могут адаптировать несколько объектов сразу (такой адаптер называется мультиадаптер) :

                >>> class TwoPersonGreeter:
                ...
                ...     component.adapts(IPerson, IPerson)
                ...     interface.implements(IGreeter)
                ...
                ...     def __init__(self, person, greeter):
                ...         self.person = person
                ...         self.greeter = greeter
                ...
                ...     def greet(self):
                ...         print "Hello", self.person.name
                ...         print "my name is", self.greeter.name

                >>> component.provideAdapter(TwoPersonGreeter)

Мультиадаптер может быть получен при помощи функций queryMultiAdapter или getMultiAdapter:

                >>> component.queryMultiAdapter((Person("Sally"), Person("Bob")),
                ...                                  IGreeter).greet()
                Hello Sally
                my name is Bob

Подписные адаптеры:

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

В качестве примера рассмотрим проблему проверки (валидации). Пусть даны объекты, которые нужно оценить на то, удовлетворяют ли какие-либо из них некоторому стандарту. Определим интерфейс валидатора:

                >>> class IValidate(interface.Interface):
                ...     def validate(ob):
                ...         """Determine whether the object is valid
                ...
                ...         Return a string describing a validation problem.
                ...         An empty string is returned to indicate that the
                ...         object is valid.
                ...         """

Пусть даны документы:

                >>> class IDocument(interface.Interface):
                ...     summary = interface.Attribute("Document summary")
                ...     body = interface.Attribute("Document text")

                >>> class Document:
                ...     interface.implements(IDocument)
                ...     def __init__(self, summary, body):
                ...         self.summary, self.body = summary, body

Cпецифицируем правила проверки документов. Потребуем, чтобы аннотация (summary) состояла из единственной строки:

                >>> class SingleLineSummary:
                ...     component.adapts(IDocument)
                ...     interface.implements(IValidate)

                ...     def __init__(self, doc):
                ...         self.doc = doc
                ...
                ...     def validate(self):
                ...         if '\n' in self.doc.summary:
                ...             return 'Summary should only have one line'
                ...         else:
                ...             return ''

Потребуем, чтобы текст документа (body) был минимум 1000 символов длиной:

                >>> class AdequateLength:
                ...     component.adapts(IDocument)
                ...     interface.implements(IValidate)
                ...
                ...     def __init__(self, doc):
                ...         self.doc = doc
                ...
                ...     def validate(self):
                ...         if len(self.doc.body) < 1000:
                ...             return 'too short'
                ...         else:
                ...             return ''

Зарегистрировать подписной адаптер (или, что то же самое, подписать документ на этот адаптер) можно так:

                >>> component.provideSubscriptionAdapter(SingleLineSummary)
                >>> component.provideSubscriptionAdapter(AdequateLength)

Документ проверяется подписными адаптерами так:

                >>> doc = Document("A\nDocument", "blah")
                >>> [adapter.validate()
                ...  for adapter in component.subscribers([doc], IValidate)
                ...  if adapter.validate()]
                ['Summary should only have one line', 'too short']

                >>> doc = Document("A\nDocument", "blah" * 1000)
                >>> [adapter.validate()
                ...  for adapter in component.subscribers([doc], IValidate)
                ...  if adapter.validate()]
                ['Summary should only have one line']

                >>> doc = Document("A Document", "blah")
                >>> [adapter.validate()
                ...  for adapter in component.subscribers([doc], IValidate)
                ...  if adapter.validate()]
                ['too short']

Обработчики:

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

Например, чтобы в системе управления документами реализовать запись времени создания документа, можно использовать такой обработчик события создания:

                >>> import datetime

                >>> def documentCreated(event):
                ...     event.doc.created = datetime.datetime.utcnow()

Функция documentCreated в этом примере принимает событие, выполняет некоторую обработку и не возвращает ничего. Это и есть специальный случай подписного адаптера, называемый обработчиком.

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

                >>> class IDocumentCreated(interface.Interface):
                ...     doc = interface.Attribute("The document that was created")

                >>> class DocumentCreated:
                ...     interface.implements(IDocumentCreated)
                ...
                ...     def __init__(self, doc):
                ...         self.doc = doc

Декларируем обработчик как адаптер IDocumentCreated:

                >>> def documentCreated(event):
                ...     event.doc.created = datetime.datetime.utcnow()

                >>> documentCreated = component.adapter(IDocumentCreated)(documentCreated)

Теперь регистрируем обработчик:

                >>> component.provideHandler(documentCreated)

Для вызова обработчика используется функция handle:

                >>> component.handle(DocumentCreated(doc))
                >>> doc.created.__class__.__name__
                'datetime'

Официальный сайт Zope3 Московская группа изучения реактивного движения The Dream Bot Site noooxml