;--Doctest--

Providers Index

A providers index indexes the interfaces provided by objects. The index can then be queried by a specification to retrieve the objects providing that specification.

Create an interface for object to provide:

>>> from zope import interface
>>> import zope.component.interface
>>> class IFoo(interface.Interface): pass
>>> zope.component.interface.provideInterface('', IFoo)

Create an object to index:

>>> from zope import component
>>> import zope.app.intid.interfaces
>>> class Content(object):
...     def __init__(self, id): self.id = id
>>> foo = Content('foo')
>>> intids = component.getUtility(
...     zope.app.intid.interfaces.IIntIds)
>>> foo_intid = intids.register(foo)

Before the object is indexed, the providers index is empty:

>>> from grouparchy.index.provider import interfaces
>>> providers = component.getUtility(interfaces.IProvidersIndex)
>>> providers.documentCount()
0
>>> providers.wordCount()
0

Index the object:

>>> providers.index_doc(foo_intid, foo)
>>> providers.documentCount()
1

zope.interface.Interface is always included:

>>> providers.wordCount()
1

The index is queried with a zope.interface.Declaration:

>>> (foo_intid, ) == tuple(providers.apply(
...     interface.Declaration(interface.Interface)))
True

Before the object provides an interface, querying on that interface returns an empty set:

>>> len(providers.apply(interface.Declaration(IFoo)))
0

Apply an interface to the object:

>>> import grouparchy.schema.interface
>>> foo_provided = (
...     grouparchy.schema.interface.IDirectlyProvided(foo))
>>> foo_provided.directlyProvided = (IFoo,)

Events update the index when changes are made:

>>> providers.wordCount()
2
>>> (foo_intid, ) == tuple(
...     providers.apply(interface.Declaration(IFoo)))
True

Index another object providing the interface:

>>> bar = Content('bar')
>>> bar_intid = intids.register(bar)
>>> bar_provided = (
...     grouparchy.schema.interface.IDirectlyProvided(bar))
>>> bar_provided.directlyProvided = (IFoo,)

>>> providers.index_doc(bar_intid, bar)

>>> providers.documentCount()
2
>>> providers.wordCount()
2
>>> sorted([foo_intid, bar_intid]) == sorted(
...     providers.apply(interface.Declaration(IFoo)))
True

The index is updated if objects no longer provide the interface:

>>> foo_provided.directlyProvided = ()

>>> providers.wordCount()
2
>>> (bar_intid, ) == tuple(
...     providers.apply(interface.Declaration(IFoo)))
True

>>> bar_provided.directlyProvided = ()

>>> providers.wordCount()
1
>>> len(providers.apply(interface.Declaration(IFoo)))
0

A set also includes objects that provide interfaces that extend the interface:

>>> class IFooFoo(IFoo): pass
>>> zope.component.interface.provideInterface('', IFooFoo)

>>> foo_provided.directlyProvided = (IFooFoo,)

>>> providers.wordCount()
3
>>> (foo_intid, ) == tuple(providers.apply(interface.Declaration(IFoo)))
True

IProviders returns a set which only contains a qualifying provider once if it has multiple declarations which provide the interface:

>>> foo_provided.directlyProvided = (IFoo, IFooFoo)

>>> (foo_intid, ) == tuple(providers.apply(interface.Declaration(IFoo)))
True

Classes

The interfaces implemented by a class behave similarly as long as the events are triggered when changes are made.

Make a new interface that the class implements:

>>> class IBar(interface.Interface): pass
>>> interface.classImplements(Content, IBar)

Before the event is notified, the index doesn't include the class interfaces:

>>> providers.wordCount()
3
>>> len(providers.apply(interface.Declaration(IBar)))
0

Notify the event:

>>> from zope import event
>>> event.notify(
...     grouparchy.schema.interface.InterfacesAdded(
...         context=foo,
...         added=interface.Declaration(IBar)))

Now the index includes the class interfaces:

>>> providers.wordCount()
4
>>> (foo_intid, ) == tuple(
...     providers.apply(interface.Declaration(IBar)))
True

Similarly, if a class no longer implements an interface, the interfaces included in the index are only removed when the event is triggered.

Remove the interface implemented by the class:

>>> interface.classImplementsOnly(Content)

Before the event is notified, the index doesn't include the class interfaces:

>>> providers.wordCount()
4
>>> (foo_intid, ) == tuple(
...     providers.apply(interface.Declaration(IBar)))
True

Notify the event:

>>> event.notify(
...     grouparchy.schema.interface.InterfacesRemoved(
...         context=foo,
...         removed=interface.Declaration(IBar)))

Now the index is updated:

>>> providers.wordCount()
3
>>> len(providers.apply(interface.Declaration(IBar)))
0