.. -*-doctest-*-

==========================
Broken Interfaces Handling
==========================

Start with a ZODB root with a basic object in it which provides an
interface.

    >>> from ZODB.DemoStorage import DemoStorage
    >>> from ZODB.DB import DB
    >>> storage = DemoStorage()
    >>> db = DB(storage)

    >>> from experimental.broken import tests
    >>> db.open().root()['foo'] = foo_one = tests.Foo()
    >>> foo_one.bar = 'bar'

    >>> import zope.interface
    >>> IFoo = tests.IFoo
    >>> zope.interface.alsoProvides(foo_one, IFoo)

    >>> from ZODB import interfaces
    >>> foo_one.bar
    'bar'
    >>> list(zope.interface.directlyProvidedBy(foo_one))
    [<InterfaceClass experimental.broken.tests.IFoo>]
    >>> IFoo.providedBy(foo_one)
    True
    >>> interfaces.IBroken.providedBy(foo_one)
    False

    >>> import transaction
    >>> transaction.commit()

When the code that an interface originally came from has been removed,
zope.interface operations on objects in the ZODB which directly
provide that interface will fail.

    >>> del tests.IFoo
    >>> tests.reset()

    >>> foo_two = db.open().root()['foo']
    >>> foo_two.bar
    Traceback (most recent call last):
    ...
    TypeError: ...'type' object is not iterable...
    >>> list(zope.interface.directlyProvidedBy(foo_two))
    Traceback (most recent call last):
    ...
    TypeError: ...'type' object is not iterable...
    >>> IFoo.providedBy(foo_two)
    False
    >>> interfaces.IBroken.providedBy(foo_two)
    False

When the patches are applied, the object behaves properly.

    >>> from zope.interface.interface import InterfaceClass
    >>> from zope.interface import declarations
    >>> from experimental.broken import interface
    >>> InterfaceClass.__reduce__ = interface.interface_reduce
    >>> declarations.ProvidesClass.__init__ = interface.provides_init
    >>> tests.reset()

    >>> foo_three = db.open().root()['foo']
    >>> foo_three.bar
    'bar'
    >>> foo_three_provided = list(
    ...     zope.interface.directlyProvidedBy(foo_three))
    >>> foo_three_provided
    [<InterfaceClass experimental.broken.tests.IFoo>]
    >>> foo_three_provided[0].providedBy(foo_three)
    True
    >>> interfaces.IBroken.providedBy(foo_three)
    True

The object can be committed back to the database when the code is
missing and the patches are applied.

    >>> foo_three._p_changed = True
    >>> transaction.commit()

If the code is restored after the object has previously been committed
to the ZODB without the code, the object behaves as it did before the
code was removed in the first place.

    >>> tests.tearDown()
    >>> foo_four = db.open().root()['foo']

    >>> foo_four.bar
    'bar'
    >>> list(zope.interface.directlyProvidedBy(foo_four))
    [<InterfaceClass experimental.broken.tests.IFoo>]
    >>> IFoo.providedBy(foo_four)
    True
    >>> interfaces.IBroken.providedBy(foo_four)
    False

The interface can be removed even when the code is not available.

    >>> del tests.IFoo
    >>> InterfaceClass.__reduce__ = interface.interface_reduce
    >>> declarations.ProvidesClass.__init__ = interface.provides_init
    >>> tests.reset()

    >>> foo_five = db.open().root()['foo']
    >>> zope.interface.noLongerProvides(foo_five, interfaces.IBroken)
    >>> transaction.commit()

    >>> foo_six = db.open().root()['foo']
    >>> foo_six.bar
    'bar'
    >>> list(zope.interface.directlyProvidedBy(foo_six))
    []
    >>> IFoo.providedBy(foo_six)
    False
    >>> interfaces.IBroken.providedBy(foo_six)
    False
