| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958 |
- ==========
- Interfaces
- ==========
- Interfaces are objects that specify (document) the external behavior
- of objects that "provide" them. An interface specifies behavior
- through:
- - Informal documentation in a doc string
- - Attribute definitions
- - Invariants, which are conditions that must hold for objects that
- provide the interface
- Attribute definitions specify specific attributes. They define the
- attribute name and provide documentation and constraints of attribute
- values. Attribute definitions can take a number of forms, as we'll
- see below.
- Defining interfaces
- ===================
- Interfaces are defined using Python ``class`` statements:
- .. doctest::
- >>> import zope.interface
- >>> class IFoo(zope.interface.Interface):
- ... """Foo blah blah"""
- ...
- ... x = zope.interface.Attribute("""X blah blah""")
- ...
- ... def bar(q, r=None):
- ... """bar blah blah"""
- In the example above, we've created an interface, :class:`IFoo`. We
- subclassed :class:`zope.interface.Interface`, which is an ancestor interface for
- all interfaces, much as ``object`` is an ancestor of all new-style
- classes [#create]_. The interface is not a class, it's an Interface,
- an instance of :class:`zope.interface.interface.InterfaceClass`:
- .. doctest::
- >>> type(IFoo)
- <class 'zope.interface.interface.InterfaceClass'>
- We can ask for the interface's documentation:
- .. doctest::
- >>> IFoo.__doc__
- 'Foo blah blah'
- and its name:
- .. doctest::
- >>> IFoo.__name__
- 'IFoo'
- and even its module:
- .. doctest::
- >>> IFoo.__module__
- '__builtin__'
- The interface defined two attributes:
- ``x``
- This is the simplest form of attribute definition. It has a name
- and a doc string. It doesn't formally specify anything else.
- ``bar``
- This is a method. A method is defined via a function definition. A
- method is simply an attribute constrained to be a callable with a
- particular signature, as provided by the function definition.
- Note that ``bar`` doesn't take a ``self`` argument. Interfaces document
- how an object is *used*. When calling instance methods, you don't
- pass a ``self`` argument, so a ``self`` argument isn't included in the
- interface signature. The ``self`` argument in instance methods is
- really an implementation detail of Python instances. Other objects,
- besides instances can provide interfaces and their methods might not
- be instance methods. For example, modules can provide interfaces and
- their methods are usually just functions. Even instances can have
- methods that are not instance methods.
- You can access the attributes defined by an interface using mapping
- syntax:
- .. doctest::
- >>> x = IFoo['x']
- >>> type(x)
- <class 'zope.interface.interface.Attribute'>
- >>> x.__name__
- 'x'
- >>> x.__doc__
- 'X blah blah'
- >>> IFoo.get('x').__name__
- 'x'
- >>> IFoo.get('y')
- You can use ``in`` to determine if an interface defines a name:
- .. doctest::
- >>> 'x' in IFoo
- True
- You can iterate over interfaces to get the names they define:
- .. doctest::
- >>> names = list(IFoo)
- >>> names.sort()
- >>> names
- ['bar', 'x']
- Remember that interfaces aren't classes. You can't access attribute
- definitions as attributes of interfaces:
- .. doctest::
- >>> IFoo.x
- Traceback (most recent call last):
- File "<stdin>", line 1, in ?
- AttributeError: 'InterfaceClass' object has no attribute 'x'
- Methods provide access to the method signature:
- .. doctest::
- >>> bar = IFoo['bar']
- >>> bar.getSignatureString()
- '(q, r=None)'
- TODO
- Methods really should have a better API. This is something that
- needs to be improved.
- Declaring interfaces
- ====================
- Having defined interfaces, we can *declare* that objects provide
- them. Before we describe the details, lets define some terms:
- *provide*
- We say that objects *provide* interfaces. If an object provides an
- interface, then the interface specifies the behavior of the
- object. In other words, interfaces specify the behavior of the
- objects that provide them.
- *implement*
- We normally say that classes *implement* interfaces. If a class
- implements an interface, then the instances of the class provide
- the interface. Objects provide interfaces that their classes
- implement [#factory]_. (Objects can provide interfaces directly,
- in addition to what their classes implement.)
- It is important to note that classes don't usually provide the
- interfaces that they implement.
- We can generalize this to factories. For any callable object we
- can declare that it produces objects that provide some interfaces
- by saying that the factory implements the interfaces.
- Now that we've defined these terms, we can talk about the API for
- declaring interfaces.
- Declaring implemented interfaces
- --------------------------------
- The most common way to declare interfaces is using the implements
- function in a class statement:
- .. doctest::
- >>> class Foo:
- ... zope.interface.implements(IFoo)
- ...
- ... def __init__(self, x=None):
- ... self.x = x
- ...
- ... def bar(self, q, r=None):
- ... return q, r, self.x
- ...
- ... def __repr__(self):
- ... return "Foo(%s)" % self.x
- In this example, we declared that ``Foo`` implements ``IFoo``. This means
- that instances of ``Foo`` provide ``IFoo``. Having made this declaration,
- there are several ways we can introspect the declarations. First, we
- can ask an interface whether it is implemented by a class:
- .. doctest::
- >>> IFoo.implementedBy(Foo)
- True
- And we can ask whether an interface is provided by an object:
- .. doctest::
- >>> foo = Foo()
- >>> IFoo.providedBy(foo)
- True
- Of course, ``Foo`` doesn't *provide* ``IFoo``, it *implements* it:
- .. doctest::
- >>> IFoo.providedBy(Foo)
- False
- We can also ask what interfaces are implemented by a class:
- .. doctest::
- >>> list(zope.interface.implementedBy(Foo))
- [<InterfaceClass __builtin__.IFoo>]
- It's an error to ask for interfaces implemented by a non-callable
- object:
- .. doctest::
- >>> IFoo.implementedBy(foo)
- Traceback (most recent call last):
- ...
- TypeError: ('ImplementedBy called for non-factory', Foo(None))
- >>> list(zope.interface.implementedBy(foo))
- Traceback (most recent call last):
- ...
- TypeError: ('ImplementedBy called for non-factory', Foo(None))
- Similarly, we can ask what interfaces are provided by an object:
- .. doctest::
- >>> list(zope.interface.providedBy(foo))
- [<InterfaceClass __builtin__.IFoo>]
- >>> list(zope.interface.providedBy(Foo))
- []
- We can declare interfaces implemented by other factories (besides
- classes). We do this using a Python-2.4-style decorator named
- `implementer`. In versions of Python before 2.4, this looks like:
- .. doctest::
- >>> def yfoo(y):
- ... foo = Foo()
- ... foo.y = y
- ... return foo
- >>> yfoo = zope.interface.implementer(IFoo)(yfoo)
- >>> list(zope.interface.implementedBy(yfoo))
- [<InterfaceClass __builtin__.IFoo>]
- Note that the implementer decorator may modify its argument. Callers
- should not assume that a new object is created.
- Using implementer also works on callable objects. This is used by
- :py:mod:`zope.formlib`, as an example:
- .. doctest::
- >>> class yfactory:
- ... def __call__(self, y):
- ... foo = Foo()
- ... foo.y = y
- ... return foo
- >>> yfoo = yfactory()
- >>> yfoo = zope.interface.implementer(IFoo)(yfoo)
- >>> list(zope.interface.implementedBy(yfoo))
- [<InterfaceClass __builtin__.IFoo>]
- XXX: Double check and update these version numbers:
- In :py:mod:`zope.interface` 3.5.2 and lower, the implementer decorator can not
- be used for classes, but in 3.6.0 and higher it can:
- .. doctest::
- >>> Foo = zope.interface.implementer(IFoo)(Foo)
- >>> list(zope.interface.providedBy(Foo()))
- [<InterfaceClass __builtin__.IFoo>]
-
- Note that class decorators using the ``@implementer(IFoo)`` syntax are only
- supported in Python 2.6 and later.
- Declaring provided interfaces
- -----------------------------
- We can declare interfaces directly provided by objects. Suppose that
- we want to document what the ``__init__`` method of the ``Foo`` class
- does. It's not *really* part of ``IFoo``. You wouldn't normally call
- the ``__init__`` method on Foo instances. Rather, the ``__init__`` method
- is part of ``Foo``'s ``__call__`` method:
- .. doctest::
- >>> class IFooFactory(zope.interface.Interface):
- ... """Create foos"""
- ...
- ... def __call__(x=None):
- ... """Create a foo
- ...
- ... The argument provides the initial value for x ...
- ... """
- It's the class that provides this interface, so we declare the
- interface on the class:
- .. doctest::
- >>> zope.interface.directlyProvides(Foo, IFooFactory)
- And then, we'll see that Foo provides some interfaces:
- .. doctest::
- >>> list(zope.interface.providedBy(Foo))
- [<InterfaceClass __builtin__.IFooFactory>]
- >>> IFooFactory.providedBy(Foo)
- True
- Declaring class interfaces is common enough that there's a special
- declaration function for it, `classProvides`, that allows the
- declaration from within a class statement:
- .. doctest::
- >>> class Foo2:
- ... zope.interface.implements(IFoo)
- ... zope.interface.classProvides(IFooFactory)
- ...
- ... def __init__(self, x=None):
- ... self.x = x
- ...
- ... def bar(self, q, r=None):
- ... return q, r, self.x
- ...
- ... def __repr__(self):
- ... return "Foo(%s)" % self.x
- >>> list(zope.interface.providedBy(Foo2))
- [<InterfaceClass __builtin__.IFooFactory>]
- >>> IFooFactory.providedBy(Foo2)
- True
- There's a similar function, ``moduleProvides``, that supports interface
- declarations from within module definitions. For example, see the use
- of ``moduleProvides`` call in ``zope.interface.__init__``, which declares that
- the package ``zope.interface`` provides ``IInterfaceDeclaration``.
- Sometimes, we want to declare interfaces on instances, even though
- those instances get interfaces from their classes. Suppose we create
- a new interface, ``ISpecial``:
- .. doctest::
- >>> class ISpecial(zope.interface.Interface):
- ... reason = zope.interface.Attribute("Reason why we're special")
- ... def brag():
- ... "Brag about being special"
- We can make an existing foo instance special by providing ``reason``
- and ``brag`` attributes:
- .. doctest::
- >>> foo.reason = 'I just am'
- >>> def brag():
- ... return "I'm special!"
- >>> foo.brag = brag
- >>> foo.reason
- 'I just am'
- >>> foo.brag()
- "I'm special!"
- and by declaring the interface:
- .. doctest::
- >>> zope.interface.directlyProvides(foo, ISpecial)
- then the new interface is included in the provided interfaces:
- .. doctest::
- >>> ISpecial.providedBy(foo)
- True
- >>> list(zope.interface.providedBy(foo))
- [<InterfaceClass __builtin__.ISpecial>, <InterfaceClass __builtin__.IFoo>]
- We can find out what interfaces are directly provided by an object:
- .. doctest::
- >>> list(zope.interface.directlyProvidedBy(foo))
- [<InterfaceClass __builtin__.ISpecial>]
- >>> newfoo = Foo()
- >>> list(zope.interface.directlyProvidedBy(newfoo))
- []
- Inherited declarations
- ----------------------
- Normally, declarations are inherited:
- .. doctest::
- >>> class SpecialFoo(Foo):
- ... zope.interface.implements(ISpecial)
- ... reason = 'I just am'
- ... def brag(self):
- ... return "I'm special because %s" % self.reason
- >>> list(zope.interface.implementedBy(SpecialFoo))
- [<InterfaceClass __builtin__.ISpecial>, <InterfaceClass __builtin__.IFoo>]
- >>> list(zope.interface.providedBy(SpecialFoo()))
- [<InterfaceClass __builtin__.ISpecial>, <InterfaceClass __builtin__.IFoo>]
- Sometimes, you don't want to inherit declarations. In that case, you
- can use ``implementsOnly``, instead of ``implements``:
- .. doctest::
- >>> class Special(Foo):
- ... zope.interface.implementsOnly(ISpecial)
- ... reason = 'I just am'
- ... def brag(self):
- ... return "I'm special because %s" % self.reason
- >>> list(zope.interface.implementedBy(Special))
- [<InterfaceClass __builtin__.ISpecial>]
- >>> list(zope.interface.providedBy(Special()))
- [<InterfaceClass __builtin__.ISpecial>]
- External declarations
- ---------------------
- Normally, we make implementation declarations as part of a class
- definition. Sometimes, we may want to make declarations from outside
- the class definition. For example, we might want to declare interfaces
- for classes that we didn't write. The function ``classImplements`` can
- be used for this purpose:
- .. doctest::
- >>> class C:
- ... pass
- >>> zope.interface.classImplements(C, IFoo)
- >>> list(zope.interface.implementedBy(C))
- [<InterfaceClass __builtin__.IFoo>]
- We can use ``classImplementsOnly`` to exclude inherited interfaces:
- .. doctest::
- >>> class C(Foo):
- ... pass
- >>> zope.interface.classImplementsOnly(C, ISpecial)
- >>> list(zope.interface.implementedBy(C))
- [<InterfaceClass __builtin__.ISpecial>]
- Declaration Objects
- -------------------
- When we declare interfaces, we create *declaration* objects. When we
- query declarations, declaration objects are returned:
- .. doctest::
- >>> type(zope.interface.implementedBy(Special))
- <class 'zope.interface.declarations.Implements'>
- Declaration objects and interface objects are similar in many ways. In
- fact, they share a common base class. The important thing to realize
- about them is that they can be used where interfaces are expected in
- declarations. Here's a silly example:
- .. doctest::
- >>> class Special2(Foo):
- ... zope.interface.implementsOnly(
- ... zope.interface.implementedBy(Foo),
- ... ISpecial,
- ... )
- ... reason = 'I just am'
- ... def brag(self):
- ... return "I'm special because %s" % self.reason
- The declaration here is almost the same as
- ``zope.interface.implements(ISpecial)``, except that the order of
- interfaces in the resulting declaration is different:
- .. doctest::
- >>> list(zope.interface.implementedBy(Special2))
- [<InterfaceClass __builtin__.IFoo>, <InterfaceClass __builtin__.ISpecial>]
- Interface Inheritance
- =====================
- Interfaces can extend other interfaces. They do this simply by listing
- the other interfaces as base interfaces:
- .. doctest::
- >>> class IBlat(zope.interface.Interface):
- ... """Blat blah blah"""
- ...
- ... y = zope.interface.Attribute("y blah blah")
- ... def eek():
- ... """eek blah blah"""
- >>> IBlat.__bases__
- (<InterfaceClass zope.interface.Interface>,)
- >>> class IBaz(IFoo, IBlat):
- ... """Baz blah"""
- ... def eek(a=1):
- ... """eek in baz blah"""
- ...
- >>> IBaz.__bases__
- (<InterfaceClass __builtin__.IFoo>, <InterfaceClass __builtin__.IBlat>)
- >>> names = list(IBaz)
- >>> names.sort()
- >>> names
- ['bar', 'eek', 'x', 'y']
- Note that ``IBaz`` overrides ``eek``:
- .. doctest::
- >>> IBlat['eek'].__doc__
- 'eek blah blah'
- >>> IBaz['eek'].__doc__
- 'eek in baz blah'
- We were careful to override ``eek`` in a compatible way. When extending
- an interface, the extending interface should be compatible [#compat]_
- with the extended interfaces.
- We can ask whether one interface extends another:
- .. doctest::
- >>> IBaz.extends(IFoo)
- True
- >>> IBlat.extends(IFoo)
- False
- Note that interfaces don't extend themselves:
- .. doctest::
- >>> IBaz.extends(IBaz)
- False
- Sometimes we wish they did, but we can instead use ``isOrExtends``:
- .. doctest::
- >>> IBaz.isOrExtends(IBaz)
- True
- >>> IBaz.isOrExtends(IFoo)
- True
- >>> IFoo.isOrExtends(IBaz)
- False
- When we iterate over an interface, we get all of the names it defines,
- including names defined by base interfaces. Sometimes, we want *just*
- the names defined by the interface directly. We can use the ``names``
- method for that:
- .. doctest::
- >>> list(IBaz.names())
- ['eek']
- Inheritance of attribute specifications
- ---------------------------------------
- An interface may override attribute definitions from base interfaces.
- If two base interfaces define the same attribute, the attribute is
- inherited from the most specific interface. For example, with:
- .. doctest::
- >>> class IBase(zope.interface.Interface):
- ...
- ... def foo():
- ... "base foo doc"
- >>> class IBase1(IBase):
- ... pass
- >>> class IBase2(IBase):
- ...
- ... def foo():
- ... "base2 foo doc"
- >>> class ISub(IBase1, IBase2):
- ... pass
- ``ISub``'s definition of ``foo`` is the one from ``IBase2``, since ``IBase2`` is more
- specific than ``IBase``:
- .. doctest::
- >>> ISub['foo'].__doc__
- 'base2 foo doc'
- Note that this differs from a depth-first search.
- Sometimes, it's useful to ask whether an interface defines an
- attribute directly. You can use the direct method to get a directly
- defined definitions:
- .. doctest::
- >>> IBase.direct('foo').__doc__
- 'base foo doc'
- >>> ISub.direct('foo')
- Specifications
- --------------
- Interfaces and declarations are both special cases of specifications.
- What we described above for interface inheritance applies to both
- declarations and specifications. Declarations actually extend the
- interfaces that they declare:
- .. doctest::
- >>> class Baz(object):
- ... zope.interface.implements(IBaz)
- >>> baz_implements = zope.interface.implementedBy(Baz)
- >>> baz_implements.__bases__
- (<InterfaceClass __builtin__.IBaz>, <implementedBy ...object>)
- >>> baz_implements.extends(IFoo)
- True
- >>> baz_implements.isOrExtends(IFoo)
- True
- >>> baz_implements.isOrExtends(baz_implements)
- True
- Specifications (interfaces and declarations) provide an ``__sro__``
- that lists the specification and all of it's ancestors:
- .. doctest::
- >>> from pprint import pprint
- >>> pprint(baz_implements.__sro__)
- (<implementedBy __builtin__.Baz>,
- <InterfaceClass __builtin__.IBaz>,
- <InterfaceClass __builtin__.IFoo>,
- <InterfaceClass __builtin__.IBlat>,
- <InterfaceClass zope.interface.Interface>,
- <implementedBy ...object>)
- Tagged Values
- =============
- Interfaces and attribute descriptions support an extension mechanism,
- borrowed from UML, called "tagged values" that lets us store extra
- data:
- .. doctest::
- >>> IFoo.setTaggedValue('date-modified', '2004-04-01')
- >>> IFoo.setTaggedValue('author', 'Jim Fulton')
- >>> IFoo.getTaggedValue('date-modified')
- '2004-04-01'
- >>> IFoo.queryTaggedValue('date-modified')
- '2004-04-01'
- >>> IFoo.queryTaggedValue('datemodified')
- >>> tags = list(IFoo.getTaggedValueTags())
- >>> tags.sort()
- >>> tags
- ['author', 'date-modified']
- Function attributes are converted to tagged values when method
- attribute definitions are created:
- .. doctest::
- >>> class IBazFactory(zope.interface.Interface):
- ... def __call__():
- ... "create one"
- ... __call__.return_type = IBaz
- >>> IBazFactory['__call__'].getTaggedValue('return_type')
- <InterfaceClass __builtin__.IBaz>
- Tagged values can also be defined from within an interface definition:
- .. doctest::
- >>> class IWithTaggedValues(zope.interface.Interface):
- ... zope.interface.taggedValue('squish', 'squash')
- >>> IWithTaggedValues.getTaggedValue('squish')
- 'squash'
- Invariants
- ==========
- Interfaces can express conditions that must hold for objects that
- provide them. These conditions are expressed using one or more
- invariants. Invariants are callable objects that will be called with
- an object that provides an interface. An invariant raises an ``Invalid``
- exception if the condition doesn't hold. Here's an example:
- .. doctest::
- >>> class RangeError(zope.interface.Invalid):
- ... """A range has invalid limits"""
- ... def __repr__(self):
- ... return "RangeError(%r)" % self.args
- >>> def range_invariant(ob):
- ... if ob.max < ob.min:
- ... raise RangeError(ob)
- Given this invariant, we can use it in an interface definition:
- .. doctest::
- >>> class IRange(zope.interface.Interface):
- ... min = zope.interface.Attribute("Lower bound")
- ... max = zope.interface.Attribute("Upper bound")
- ...
- ... zope.interface.invariant(range_invariant)
- Interfaces have a method for checking their invariants:
- .. doctest::
- >>> class Range(object):
- ... zope.interface.implements(IRange)
- ...
- ... def __init__(self, min, max):
- ... self.min, self.max = min, max
- ...
- ... def __repr__(self):
- ... return "Range(%s, %s)" % (self.min, self.max)
- >>> IRange.validateInvariants(Range(1,2))
- >>> IRange.validateInvariants(Range(1,1))
- >>> IRange.validateInvariants(Range(2,1))
- Traceback (most recent call last):
- ...
- RangeError: Range(2, 1)
- If you have multiple invariants, you may not want to stop checking
- after the first error. If you pass a list to ``validateInvariants``,
- then a single ``Invalid`` exception will be raised with the list of
- exceptions as its argument:
- .. doctest::
- >>> from zope.interface.exceptions import Invalid
- >>> errors = []
- >>> try:
- ... IRange.validateInvariants(Range(2,1), errors)
- ... except Invalid, e:
- ... str(e)
- '[RangeError(Range(2, 1))]'
-
- And the list will be filled with the individual exceptions:
- .. doctest::
- >>> errors
- [RangeError(Range(2, 1))]
- >>> del errors[:]
- Adaptation
- ==========
- Interfaces can be called to perform adaptation.
- The semantics are based on those of the PEP 246 ``adapt`` function.
- If an object cannot be adapted, then a ``TypeError`` is raised:
- .. doctest::
- >>> class I(zope.interface.Interface):
- ... pass
- >>> I(0)
- Traceback (most recent call last):
- ...
- TypeError: ('Could not adapt', 0, <InterfaceClass __builtin__.I>)
- unless an alternate value is provided as a second positional argument:
- .. doctest::
- >>> I(0, 'bob')
- 'bob'
- If an object already implements the interface, then it will be returned:
- .. doctest::
- >>> class C(object):
- ... zope.interface.implements(I)
- >>> obj = C()
- >>> I(obj) is obj
- True
- If an object implements ``__conform__``, then it will be used:
- .. doctest::
- >>> class C(object):
- ... zope.interface.implements(I)
- ... def __conform__(self, proto):
- ... return 0
- >>> I(C())
- 0
- Adapter hooks (see ``__adapt__``) will also be used, if present:
- .. doctest::
- >>> from zope.interface.interface import adapter_hooks
- >>> def adapt_0_to_42(iface, obj):
- ... if obj == 0:
- ... return 42
- >>> adapter_hooks.append(adapt_0_to_42)
- >>> I(0)
- 42
- >>> adapter_hooks.remove(adapt_0_to_42)
- >>> I(0)
- Traceback (most recent call last):
- ...
- TypeError: ('Could not adapt', 0, <InterfaceClass __builtin__.I>)
- ``__adapt__``
- -------------
- .. doctest::
- >>> class I(zope.interface.Interface):
- ... pass
- Interfaces implement the PEP 246 ``__adapt__`` method.
- This method is normally not called directly. It is called by the PEP
- 246 adapt framework and by the interface ``__call__`` operator.
- The ``adapt`` method is responsible for adapting an object to the
- reciever.
- The default version returns ``None``:
- .. doctest::
- >>> I.__adapt__(0)
- unless the object given provides the interface:
- .. doctest::
- >>> class C(object):
- ... zope.interface.implements(I)
- >>> obj = C()
- >>> I.__adapt__(obj) is obj
- True
- Adapter hooks can be provided (or removed) to provide custom
- adaptation. We'll install a silly hook that adapts 0 to 42.
- We install a hook by simply adding it to the ``adapter_hooks``
- list:
- .. doctest::
- >>> from zope.interface.interface import adapter_hooks
- >>> def adapt_0_to_42(iface, obj):
- ... if obj == 0:
- ... return 42
- >>> adapter_hooks.append(adapt_0_to_42)
- >>> I.__adapt__(0)
- 42
- Hooks must either return an adapter, or ``None`` if no adapter can
- be found.
- Hooks can be uninstalled by removing them from the list:
- .. doctest::
- >>> adapter_hooks.remove(adapt_0_to_42)
- >>> I.__adapt__(0)
- .. [#create] The main reason we subclass ``Interface`` is to cause the
- Python class statement to create an interface, rather
- than a class.
- It's possible to create interfaces by calling a special
- interface class directly. Doing this, it's possible
- (and, on rare occasions, useful) to create interfaces
- that don't descend from ``Interface``. Using this
- technique is beyond the scope of this document.
- .. [#factory] Classes are factories. They can be called to create
- their instances. We expect that we will eventually
- extend the concept of implementation to other kinds of
- factories, so that we can declare the interfaces
- provided by the objects created.
- .. [#compat] The goal is substitutability. An object that provides an
- extending interface should be substitutable for an object
- that provides the extended interface. In our example, an
- object that provides ``IBaz`` should be usable wherever an
- object that provides ``IBlat`` is expected.
- The interface implementation doesn't enforce this,
- but maybe it should do some checks.
|