API работы с реестрами.txt
2008-01-24 13:30API работы с реестрами: Реестр - основной строительный инструмент 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'



