Current File : //proc/self/root/usr/lib/python3/dist-packages/zope/interface/tests/test_adapter.py
##############################################################################
#
# Copyright (c) 2003 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Adapter registry tests
"""
import unittest

from zope.interface.tests import OptimizationTestMixin

# pylint:disable=inherit-non-class,protected-access,too-many-lines
# pylint:disable=attribute-defined-outside-init,blacklisted-name

def _makeInterfaces():
    from zope.interface import Interface

    class IB0(Interface):
        pass
    class IB1(IB0):
        pass
    class IB2(IB0):
        pass
    class IB3(IB2, IB1):
        pass
    class IB4(IB1, IB2):
        pass

    class IF0(Interface):
        pass
    class IF1(IF0):
        pass

    class IR0(Interface):
        pass
    class IR1(IR0):
        pass

    return IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1


# Custom types to use as part of the AdapterRegistry data structures.
# Our custom types do strict type checking to make sure
# types propagate through the data tree as expected.
class CustomDataTypeBase:
    _data = None
    def __getitem__(self, name):
        return self._data[name]

    def __setitem__(self, name, value):
        self._data[name] = value

    def __delitem__(self, name):
        del self._data[name]

    def __len__(self):
        return len(self._data)

    def __contains__(self, name):
        return name in self._data

    def __eq__(self, other):
        if other is self:
            return True
        # pylint:disable=unidiomatic-typecheck
        if type(other) != type(self):
            return False
        return other._data == self._data

    def __repr__(self):
        return repr(self._data)

class CustomMapping(CustomDataTypeBase):
    def __init__(self, other=None):
        self._data = {}
        if other:
            self._data.update(other)
        self.get = self._data.get
        self.items = self._data.items


class CustomSequence(CustomDataTypeBase):
    def __init__(self, other=None):
        self._data = []
        if other:
            self._data.extend(other)
        self.append = self._data.append

class CustomLeafSequence(CustomSequence):
    pass

class CustomProvided(CustomMapping):
    pass


class BaseAdapterRegistryTests(unittest.TestCase):

    maxDiff = None

    def _getBaseAdapterRegistry(self):
        from zope.interface.adapter import BaseAdapterRegistry
        return BaseAdapterRegistry

    def _getTargetClass(self):
        BaseAdapterRegistry = self._getBaseAdapterRegistry()
        class _CUT(BaseAdapterRegistry):
            class LookupClass:
                _changed = _extendors = ()
                def __init__(self, reg):
                    pass
                def changed(self, orig):
                    self._changed += (orig,)
                def add_extendor(self, provided):
                    self._extendors += (provided,)
                def remove_extendor(self, provided):
                    self._extendors = tuple([x for x in self._extendors
                                             if x != provided])
        for name in BaseAdapterRegistry._delegated:
            setattr(_CUT.LookupClass, name, object())
        return _CUT

    def _makeOne(self):
        return self._getTargetClass()()

    def _getMappingType(self):
        return dict

    def _getProvidedType(self):
        return dict

    def _getMutableListType(self):
        return list

    def _getLeafSequenceType(self):
        return tuple

    def test_lookup_delegation(self):
        CUT = self._getTargetClass()
        registry = CUT()
        for name in CUT._delegated:
            self.assertIs(getattr(registry, name), getattr(registry._v_lookup, name))

    def test__generation_on_first_creation(self):
        registry = self._makeOne()
        # Bumped to 1 in BaseAdapterRegistry.__init__
        self.assertEqual(registry._generation, 1)

    def test__generation_after_calling_changed(self):
        registry = self._makeOne()
        orig = object()
        registry.changed(orig)
        # Bumped to 1 in BaseAdapterRegistry.__init__
        self.assertEqual(registry._generation, 2)
        self.assertEqual(registry._v_lookup._changed, (registry, orig,))

    def test__generation_after_changing___bases__(self):
        class _Base:
            pass
        registry = self._makeOne()
        registry.__bases__ = (_Base,)
        self.assertEqual(registry._generation, 2)

    def _check_basic_types_of_adapters(self, registry, expected_order=2):
        self.assertEqual(len(registry._adapters), expected_order) # order 0 and order 1
        self.assertIsInstance(registry._adapters, self._getMutableListType())
        MT = self._getMappingType()
        for mapping in registry._adapters:
            self.assertIsInstance(mapping, MT)
        self.assertEqual(registry._adapters[0], MT())
        self.assertIsInstance(registry._adapters[1], MT)
        self.assertEqual(len(registry._adapters[expected_order - 1]), 1)

    def _check_basic_types_of_subscribers(self, registry, expected_order=2):
        self.assertEqual(len(registry._subscribers), expected_order) # order 0 and order 1
        self.assertIsInstance(registry._subscribers, self._getMutableListType())
        MT = self._getMappingType()
        for mapping in registry._subscribers:
            self.assertIsInstance(mapping, MT)
        if expected_order:
            self.assertEqual(registry._subscribers[0], MT())
            self.assertIsInstance(registry._subscribers[1], MT)
            self.assertEqual(len(registry._subscribers[expected_order - 1]), 1)

    def test_register(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        registry.register([IB0], IR0, '', 'A1')
        self.assertEqual(registry.registered([IB0], IR0, ''), 'A1')
        self.assertEqual(registry._generation, 2)
        self._check_basic_types_of_adapters(registry)
        MT = self._getMappingType()
        self.assertEqual(registry._adapters[1], MT({
            IB0: MT({
                IR0: MT({'': 'A1'})
            })
        }))
        PT = self._getProvidedType()
        self.assertEqual(registry._provided, PT({
            IR0: 1
        }))

        registered = list(registry.allRegistrations())
        self.assertEqual(registered, [(
            (IB0,), # required
            IR0, # provided
            '', # name
            'A1' # value
        )])

    def test_register_multiple_allRegistrations(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        # Use several different depths and several different names
        registry.register([], IR0, '', 'A1')
        registry.register([], IR0, 'name1', 'A2')

        registry.register([IB0], IR0, '', 'A1')
        registry.register([IB0], IR0, 'name2', 'A2')
        registry.register([IB0], IR1, '', 'A3')
        registry.register([IB0], IR1, 'name3', 'A4')

        registry.register([IB0, IB1], IR0, '', 'A1')
        registry.register([IB0, IB2], IR0, 'name2', 'A2')
        registry.register([IB0, IB2], IR1, 'name4', 'A4')
        registry.register([IB0, IB3], IR1, '', 'A3')

        def build_adapters(L, MT):
            return L([
                # 0
                MT({
                    IR0: MT({
                        '': 'A1',
                        'name1': 'A2'
                    })
                }),
                # 1
                MT({
                    IB0: MT({
                        IR0: MT({
                            '': 'A1',
                            'name2': 'A2'
                        }),
                        IR1: MT({
                            '': 'A3',
                            'name3': 'A4'
                        })
                    })
                }),
                # 3
                MT({
                    IB0: MT({
                        IB1: MT({
                            IR0: MT({'': 'A1'})
                        }),
                        IB2: MT({
                            IR0: MT({'name2': 'A2'}),
                            IR1: MT({'name4': 'A4'}),
                        }),
                        IB3: MT({
                            IR1: MT({'': 'A3'})
                        })
                    }),
                }),
            ])

        self.assertEqual(registry._adapters,
                         build_adapters(L=self._getMutableListType(),
                                        MT=self._getMappingType()))

        registered = sorted(registry.allRegistrations())
        self.assertEqual(registered, [
            ((), IR0, '', 'A1'),
            ((), IR0, 'name1', 'A2'),
            ((IB0,), IR0, '', 'A1'),
            ((IB0,), IR0, 'name2', 'A2'),
            ((IB0,), IR1, '', 'A3'),
            ((IB0,), IR1, 'name3', 'A4'),
            ((IB0, IB1), IR0, '', 'A1'),
            ((IB0, IB2), IR0, 'name2', 'A2'),
            ((IB0, IB2), IR1, 'name4', 'A4'),
            ((IB0, IB3), IR1, '', 'A3')
        ])

        # We can duplicate to another object.
        registry2 = self._makeOne()
        for args in registered:
            registry2.register(*args)

        self.assertEqual(registry2._adapters, registry._adapters)
        self.assertEqual(registry2._provided, registry._provided)

        # We can change the types and rebuild the data structures.
        registry._mappingType = CustomMapping
        registry._leafSequenceType = CustomLeafSequence
        registry._sequenceType = CustomSequence
        registry._providedType = CustomProvided
        def addValue(existing, new):
            existing = existing if existing is not None else CustomLeafSequence()
            existing.append(new)
            return existing
        registry._addValueToLeaf = addValue

        registry.rebuild()

        self.assertEqual(registry._adapters,
                         build_adapters(
                             L=CustomSequence,
                             MT=CustomMapping
                         ))

    def test_register_with_invalid_name(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        with self.assertRaises(ValueError):
            registry.register([IB0], IR0, object(), 'A1')

    def test_register_with_value_None_unregisters(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        registry.register([None], IR0, '', 'A1')
        registry.register([None], IR0, '', None)
        self.assertEqual(len(registry._adapters), 0)
        self.assertIsInstance(registry._adapters, self._getMutableListType())
        registered = list(registry.allRegistrations())
        self.assertEqual(registered, [])

    def test_register_with_same_value(self):
        from zope.interface import Interface
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        _value = object()
        registry.register([None], IR0, '', _value)
        _before = registry._generation
        registry.register([None], IR0, '', _value)
        self.assertEqual(registry._generation, _before) # skipped changed()
        self._check_basic_types_of_adapters(registry)
        MT = self._getMappingType()
        self.assertEqual(registry._adapters[1], MT(
            {
                Interface: MT(
                    {
                        IR0: MT({'': _value})
                    }
                )
            }
        ))
        registered = list(registry.allRegistrations())
        self.assertEqual(registered, [(
            (Interface,), # required
            IR0, # provided
            '', # name
            _value # value
        )])


    def test_registered_empty(self):
        registry = self._makeOne()
        self.assertEqual(registry.registered([None], None, ''), None)
        registered = list(registry.allRegistrations())
        self.assertEqual(registered, [])

    def test_registered_non_empty_miss(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        registry.register([IB1], None, '', 'A1')
        self.assertEqual(registry.registered([IB2], None, ''), None)

    def test_registered_non_empty_hit(self):
        registry = self._makeOne()
        registry.register([None], None, '', 'A1')
        self.assertEqual(registry.registered([None], None, ''), 'A1')

    def test_unregister_empty(self):
        registry = self._makeOne()
        registry.unregister([None], None, '') # doesn't raise
        self.assertEqual(registry.registered([None], None, ''), None)
        self.assertEqual(len(registry._provided), 0)

    def test_unregister_non_empty_miss_on_required(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        registry.register([IB1], None, '', 'A1')
        registry.unregister([IB2], None, '') # doesn't raise
        self.assertEqual(registry.registered([IB1], None, ''), 'A1')
        self._check_basic_types_of_adapters(registry)
        MT = self._getMappingType()
        self.assertEqual(registry._adapters[1], MT(
            {
                IB1: MT(
                    {
                        None: MT({'': 'A1'})
                    }
                )
            }
        ))
        PT = self._getProvidedType()
        self.assertEqual(registry._provided, PT({
            None: 1
        }))

    def test_unregister_non_empty_miss_on_name(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        registry.register([IB1], None, '', 'A1')
        registry.unregister([IB1], None, 'nonesuch') # doesn't raise
        self.assertEqual(registry.registered([IB1], None, ''), 'A1')
        self._check_basic_types_of_adapters(registry)
        MT = self._getMappingType()
        self.assertEqual(registry._adapters[1], MT(
            {
                IB1: MT(
                    {
                        None: MT({'': 'A1'})
                    }
                )
            }
        ))
        PT = self._getProvidedType()
        self.assertEqual(registry._provided, PT({
            None: 1
        }))

    def test_unregister_with_value_not_None_miss(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        orig = object()
        nomatch = object()
        registry.register([IB1], None, '', orig)
        registry.unregister([IB1], None, '', nomatch) #doesn't raise
        self.assertIs(registry.registered([IB1], None, ''), orig)

    def test_unregister_hit_clears_empty_subcomponents(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        one = object()
        another = object()
        registry.register([IB1, IB2], None, '', one)
        registry.register([IB1, IB3], None, '', another)
        self._check_basic_types_of_adapters(registry, expected_order=3)
        self.assertIn(IB2, registry._adapters[2][IB1])
        self.assertIn(IB3, registry._adapters[2][IB1])
        MT = self._getMappingType()
        self.assertEqual(registry._adapters[2], MT(
            {
                IB1: MT(
                    {
                        IB2: MT({None: MT({'': one})}),
                        IB3: MT({None: MT({'': another})})
                    }
                )
            }
        ))
        PT = self._getProvidedType()
        self.assertEqual(registry._provided, PT({
            None: 2
        }))

        registry.unregister([IB1, IB3], None, '', another)
        self.assertIn(IB2, registry._adapters[2][IB1])
        self.assertNotIn(IB3, registry._adapters[2][IB1])
        self.assertEqual(registry._adapters[2], MT(
            {
                IB1: MT(
                    {
                        IB2: MT({None: MT({'': one})}),
                    }
                )
            }
        ))
        self.assertEqual(registry._provided, PT({
            None: 1
        }))

    def test_unsubscribe_empty(self):
        registry = self._makeOne()
        registry.unsubscribe([None], None, '') #doesn't raise
        self.assertEqual(registry.registered([None], None, ''), None)
        self._check_basic_types_of_subscribers(registry, expected_order=0)

    def test_unsubscribe_hit(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        orig = object()
        registry.subscribe([IB1], None, orig)
        MT = self._getMappingType()
        L = self._getLeafSequenceType()
        PT = self._getProvidedType()
        self._check_basic_types_of_subscribers(registry)
        self.assertEqual(registry._subscribers[1], MT({
            IB1: MT({
                None: MT({
                    '': L((orig,))
                })
            })
        }))
        self.assertEqual(registry._provided, PT({}))
        registry.unsubscribe([IB1], None, orig) #doesn't raise
        self.assertEqual(len(registry._subscribers), 0)
        self.assertEqual(registry._provided, PT({}))

    def assertLeafIdentity(self, leaf1, leaf2):
        """
        Implementations may choose to use new, immutable objects
        instead of mutating existing subscriber leaf objects, or vice versa.

        The default implementation uses immutable tuples, so they are never
        the same. Other implementations may use persistent lists so they should be
        the same and mutated in place. Subclasses testing this behaviour need to
        override this method.
        """
        self.assertIsNot(leaf1, leaf2)

    def test_unsubscribe_after_multiple(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        first = object()
        second = object()
        third = object()
        fourth = object()
        registry.subscribe([IB1], None, first)
        registry.subscribe([IB1], None, second)
        registry.subscribe([IB1], IR0, third)
        registry.subscribe([IB1], IR0, fourth)
        self._check_basic_types_of_subscribers(registry, expected_order=2)
        MT = self._getMappingType()
        L = self._getLeafSequenceType()
        PT = self._getProvidedType()
        self.assertEqual(registry._subscribers[1], MT({
            IB1: MT({
                None: MT({'': L((first, second))}),
                IR0: MT({'': L((third, fourth))}),
            })
        }))
        self.assertEqual(registry._provided, PT({
            IR0: 2
        }))
        # The leaf objects may or may not stay the same as they are unsubscribed,
        # depending on the implementation
        IR0_leaf_orig = registry._subscribers[1][IB1][IR0]['']
        Non_leaf_orig = registry._subscribers[1][IB1][None]['']

        registry.unsubscribe([IB1], None, first)
        registry.unsubscribe([IB1], IR0, third)

        self.assertEqual(registry._subscribers[1], MT({
            IB1: MT({
                None: MT({'': L((second,))}),
                IR0: MT({'': L((fourth,))}),
            })
        }))
        self.assertEqual(registry._provided, PT({
            IR0: 1
        }))
        IR0_leaf_new = registry._subscribers[1][IB1][IR0]['']
        Non_leaf_new = registry._subscribers[1][IB1][None]['']

        self.assertLeafIdentity(IR0_leaf_orig, IR0_leaf_new)
        self.assertLeafIdentity(Non_leaf_orig, Non_leaf_new)

        registry.unsubscribe([IB1], None, second)
        registry.unsubscribe([IB1], IR0, fourth)
        self.assertEqual(len(registry._subscribers), 0)
        self.assertEqual(len(registry._provided), 0)

    def test_subscribe_unsubscribe_identical_objects_provided(self):
        # https://github.com/zopefoundation/zope.interface/issues/227
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        first = object()
        registry.subscribe([IB1], IR0, first)
        registry.subscribe([IB1], IR0, first)

        MT = self._getMappingType()
        L = self._getLeafSequenceType()
        PT = self._getProvidedType()
        self.assertEqual(registry._subscribers[1], MT({
            IB1: MT({
                IR0: MT({'': L((first, first))}),
            })
        }))
        self.assertEqual(registry._provided, PT({
            IR0: 2
        }))

        registry.unsubscribe([IB1], IR0, first)
        registry.unsubscribe([IB1], IR0, first)
        self.assertEqual(len(registry._subscribers), 0)
        self.assertEqual(registry._provided, PT())

    def test_subscribe_unsubscribe_nonequal_objects_provided(self):
        # https://github.com/zopefoundation/zope.interface/issues/227
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        first = object()
        second = object()
        registry.subscribe([IB1], IR0, first)
        registry.subscribe([IB1], IR0, second)

        MT = self._getMappingType()
        L = self._getLeafSequenceType()
        PT = self._getProvidedType()
        self.assertEqual(registry._subscribers[1], MT({
            IB1: MT({
                IR0: MT({'': L((first, second))}),
            })
        }))
        self.assertEqual(registry._provided, PT({
            IR0: 2
        }))

        registry.unsubscribe([IB1], IR0, first)
        registry.unsubscribe([IB1], IR0, second)
        self.assertEqual(len(registry._subscribers), 0)
        self.assertEqual(registry._provided, PT())

    def test_subscribed_empty(self):
        registry = self._makeOne()
        self.assertIsNone(registry.subscribed([None], None, ''))
        subscribed = list(registry.allSubscriptions())
        self.assertEqual(subscribed, [])

    def test_subscribed_non_empty_miss(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        registry.subscribe([IB1], IF0, 'A1')
        # Mismatch required
        self.assertIsNone(registry.subscribed([IB2], IF0, ''))
        # Mismatch provided
        self.assertIsNone(registry.subscribed([IB1], IF1, ''))
        # Mismatch value
        self.assertIsNone(registry.subscribed([IB1], IF0, ''))

    def test_subscribed_non_empty_hit(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        registry.subscribe([IB0], IF0, 'A1')
        self.assertEqual(registry.subscribed([IB0], IF0, 'A1'), 'A1')

    def test_unsubscribe_w_None_after_multiple(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        first = object()
        second = object()

        registry.subscribe([IB1], None, first)
        registry.subscribe([IB1], None, second)
        self._check_basic_types_of_subscribers(registry, expected_order=2)
        registry.unsubscribe([IB1], None)
        self.assertEqual(len(registry._subscribers), 0)

    def test_unsubscribe_non_empty_miss_on_required(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        registry.subscribe([IB1], None, 'A1')
        self._check_basic_types_of_subscribers(registry, expected_order=2)
        registry.unsubscribe([IB2], None, '') # doesn't raise
        self.assertEqual(len(registry._subscribers), 2)
        MT = self._getMappingType()
        L = self._getLeafSequenceType()
        self.assertEqual(registry._subscribers[1], MT({
            IB1: MT({
                None: MT({'': L(('A1',))}),
            })
        }))

    def test_unsubscribe_non_empty_miss_on_value(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        registry.subscribe([IB1], None, 'A1')
        self._check_basic_types_of_subscribers(registry, expected_order=2)
        registry.unsubscribe([IB1], None, 'A2') # doesn't raise
        self.assertEqual(len(registry._subscribers), 2)
        MT = self._getMappingType()
        L = self._getLeafSequenceType()
        self.assertEqual(registry._subscribers[1], MT({
            IB1: MT({
                None: MT({'': L(('A1',))}),
            })
        }))

    def test_unsubscribe_with_value_not_None_miss(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        orig = object()
        nomatch = object()
        registry.subscribe([IB1], None, orig)
        registry.unsubscribe([IB1], None, nomatch) #doesn't raise
        self.assertEqual(len(registry._subscribers), 2)

    def _instance_method_notify_target(self):
        self.fail("Example method, not intended to be called.")

    def test_unsubscribe_instance_method(self):
        # Checking that the values are compared by equality, not identity
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        self.assertEqual(len(registry._subscribers), 0)
        registry.subscribe([IB1], None, self._instance_method_notify_target)
        registry.unsubscribe([IB1], None, self._instance_method_notify_target)
        self.assertEqual(len(registry._subscribers), 0)

    def test_subscribe_multiple_allRegistrations(self):
        IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
        registry = self._makeOne()
        # Use several different depths and several different values
        registry.subscribe([], IR0, 'A1')
        registry.subscribe([], IR0, 'A2')

        registry.subscribe([IB0], IR0, 'A1')
        registry.subscribe([IB0], IR0, 'A2')
        registry.subscribe([IB0], IR1, 'A3')
        registry.subscribe([IB0], IR1, 'A4')

        registry.subscribe([IB0, IB1], IR0, 'A1')
        registry.subscribe([IB0, IB2], IR0, 'A2')
        registry.subscribe([IB0, IB2], IR1, 'A4')
        registry.subscribe([IB0, IB3], IR1, 'A3')


        def build_subscribers(L, F, MT):
            return L([
                # 0
                MT({
                    IR0: MT({
                        '': F(['A1', 'A2'])
                    })
                }),
                # 1
                MT({
                    IB0: MT({
                        IR0: MT({
                            '': F(['A1', 'A2'])
                        }),
                        IR1: MT({
                            '': F(['A3', 'A4'])
                        })
                    })
                }),
                # 3
                MT({
                    IB0: MT({
                        IB1: MT({
                            IR0: MT({'': F(['A1'])})
                        }),
                        IB2: MT({
                            IR0: MT({'': F(['A2'])}),
                            IR1: MT({'': F(['A4'])}),
                        }),
                        IB3: MT({
                            IR1: MT({'': F(['A3'])})
                        })
                    }),
                }),
            ])

        self.assertEqual(registry._subscribers,
                         build_subscribers(
                             L=self._getMutableListType(),
                             F=self._getLeafSequenceType(),
                             MT=self._getMappingType()
                         ))

        def build_provided(P):
            return P({
                IR0: 6,
                IR1: 4,
            })


        self.assertEqual(registry._provided,
                         build_provided(P=self._getProvidedType()))

        registered = sorted(registry.allSubscriptions())
        self.assertEqual(registered, [
            ((), IR0, 'A1'),
            ((), IR0, 'A2'),
            ((IB0,), IR0, 'A1'),
            ((IB0,), IR0, 'A2'),
            ((IB0,), IR1, 'A3'),
            ((IB0,), IR1, 'A4'),
            ((IB0, IB1), IR0, 'A1'),
            ((IB0, IB2), IR0, 'A2'),
            ((IB0, IB2), IR1, 'A4'),
            ((IB0, IB3), IR1, 'A3')
        ])

        # We can duplicate this to another object
        registry2 = self._makeOne()
        for args in registered:
            registry2.subscribe(*args)

        self.assertEqual(registry2._subscribers, registry._subscribers)
        self.assertEqual(registry2._provided, registry._provided)

        # We can change the types and rebuild the data structures.
        registry._mappingType = CustomMapping
        registry._leafSequenceType = CustomLeafSequence
        registry._sequenceType = CustomSequence
        registry._providedType = CustomProvided
        def addValue(existing, new):
            existing = existing if existing is not None else CustomLeafSequence()
            existing.append(new)
            return existing
        registry._addValueToLeaf = addValue

        registry.rebuild()

        self.assertEqual(registry._subscribers,
                         build_subscribers(
                             L=CustomSequence,
                             F=CustomLeafSequence,
                             MT=CustomMapping
                         ))


class CustomTypesBaseAdapterRegistryTests(BaseAdapterRegistryTests):
    """
    This class may be extended by other packages to test their own
    adapter registries that use custom types. (So be cautious about
    breaking changes.)

    One known user is ``zope.component.persistentregistry``.
    """

    def _getMappingType(self):
        return CustomMapping

    def _getProvidedType(self):
        return CustomProvided

    def _getMutableListType(self):
        return CustomSequence

    def _getLeafSequenceType(self):
        return CustomLeafSequence

    def _getBaseAdapterRegistry(self):
        from zope.interface.adapter import BaseAdapterRegistry
        class CustomAdapterRegistry(BaseAdapterRegistry):
            _mappingType = self._getMappingType()
            _sequenceType = self._getMutableListType()
            _leafSequenceType = self._getLeafSequenceType()
            _providedType = self._getProvidedType()

            def _addValueToLeaf(self, existing_leaf_sequence, new_item):
                if not existing_leaf_sequence:
                    existing_leaf_sequence = self._leafSequenceType()
                existing_leaf_sequence.append(new_item)
                return existing_leaf_sequence

            def _removeValueFromLeaf(self, existing_leaf_sequence, to_remove):
                without_removed = BaseAdapterRegistry._removeValueFromLeaf(
                    self,
                    existing_leaf_sequence,
                    to_remove)
                existing_leaf_sequence[:] = without_removed
                assert to_remove not in existing_leaf_sequence
                return existing_leaf_sequence

        return CustomAdapterRegistry

    def assertLeafIdentity(self, leaf1, leaf2):
        self.assertIs(leaf1, leaf2)


class LookupBaseFallbackTests(unittest.TestCase):

    def _getFallbackClass(self):
        from zope.interface.adapter import LookupBaseFallback # pylint:disable=no-name-in-module
        return LookupBaseFallback

    _getTargetClass = _getFallbackClass

    def _makeOne(self, uc_lookup=None, uc_lookupAll=None,
                 uc_subscriptions=None):
        # pylint:disable=function-redefined
        if uc_lookup is None:
            def uc_lookup(self, required, provided, name):
                pass
        if uc_lookupAll is None:
            def uc_lookupAll(self, required, provided):
                raise NotImplementedError()
        if uc_subscriptions is None:
            def uc_subscriptions(self, required, provided):
                raise NotImplementedError()
        class Derived(self._getTargetClass()):
            _uncached_lookup = uc_lookup
            _uncached_lookupAll = uc_lookupAll
            _uncached_subscriptions = uc_subscriptions
        return Derived()

    def test_lookup_w_invalid_name(self):
        def _lookup(self, required, provided, name):
            self.fail("This should never be called")
        lb = self._makeOne(uc_lookup=_lookup)
        with self.assertRaises(ValueError):
            lb.lookup(('A',), 'B', object())

    def test_lookup_miss_no_default(self):
        _called_with = []
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))

        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup(('A',), 'B', 'C')
        self.assertIsNone(found)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])

    def test_lookup_miss_w_default(self):
        _called_with = []
        _default = object()
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))

        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup(('A',), 'B', 'C', _default)
        self.assertIs(found, _default)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])

    def test_lookup_not_cached(self):
        _called_with = []
        a, b, c = object(), object(), object()
        _results = [a, b, c]
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))
            return _results.pop(0)
        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup(('A',), 'B', 'C')
        self.assertIs(found, a)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])
        self.assertEqual(_results, [b, c])

    def test_lookup_cached(self):
        _called_with = []
        a, b, c = object(), object(), object()
        _results = [a, b, c]
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))
            return _results.pop(0)
        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup(('A',), 'B', 'C')
        found = lb.lookup(('A',), 'B', 'C')
        self.assertIs(found, a)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])
        self.assertEqual(_results, [b, c])

    def test_lookup_not_cached_multi_required(self):
        _called_with = []
        a, b, c = object(), object(), object()
        _results = [a, b, c]
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))
            return _results.pop(0)
        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup(('A', 'D'), 'B', 'C')
        self.assertIs(found, a)
        self.assertEqual(_called_with, [(('A', 'D'), 'B', 'C')])
        self.assertEqual(_results, [b, c])

    def test_lookup_cached_multi_required(self):
        _called_with = []
        a, b, c = object(), object(), object()
        _results = [a, b, c]
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))
            return _results.pop(0)
        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup(('A', 'D'), 'B', 'C')
        found = lb.lookup(('A', 'D'), 'B', 'C')
        self.assertIs(found, a)
        self.assertEqual(_called_with, [(('A', 'D'), 'B', 'C')])
        self.assertEqual(_results, [b, c])

    def test_lookup_not_cached_after_changed(self):
        _called_with = []
        a, b, c = object(), object(), object()
        _results = [a, b, c]
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))
            return _results.pop(0)
        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup(('A',), 'B', 'C')
        lb.changed(lb)
        found = lb.lookup(('A',), 'B', 'C')
        self.assertIs(found, b)
        self.assertEqual(_called_with,
                         [(('A',), 'B', 'C'), (('A',), 'B', 'C')])
        self.assertEqual(_results, [c])

    def test_lookup1_w_invalid_name(self):
        def _lookup(self, required, provided, name):
            self.fail("This should never be called")

        lb = self._makeOne(uc_lookup=_lookup)
        with self.assertRaises(ValueError):
            lb.lookup1('A', 'B', object())

    def test_lookup1_miss_no_default(self):
        _called_with = []
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))

        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup1('A', 'B', 'C')
        self.assertIsNone(found)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])

    def test_lookup1_miss_w_default(self):
        _called_with = []
        _default = object()
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))

        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup1('A', 'B', 'C', _default)
        self.assertIs(found, _default)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])

    def test_lookup1_miss_w_default_negative_cache(self):
        _called_with = []
        _default = object()
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))

        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup1('A', 'B', 'C', _default)
        self.assertIs(found, _default)
        found = lb.lookup1('A', 'B', 'C', _default)
        self.assertIs(found, _default)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])

    def test_lookup1_not_cached(self):
        _called_with = []
        a, b, c = object(), object(), object()
        _results = [a, b, c]
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))
            return _results.pop(0)
        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup1('A', 'B', 'C')
        self.assertIs(found, a)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])
        self.assertEqual(_results, [b, c])

    def test_lookup1_cached(self):
        _called_with = []
        a, b, c = object(), object(), object()
        _results = [a, b, c]
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))
            return _results.pop(0)
        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup1('A', 'B', 'C')
        found = lb.lookup1('A', 'B', 'C')
        self.assertIs(found, a)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])
        self.assertEqual(_results, [b, c])

    def test_lookup1_not_cached_after_changed(self):
        _called_with = []
        a, b, c = object(), object(), object()
        _results = [a, b, c]
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))
            return _results.pop(0)
        lb = self._makeOne(uc_lookup=_lookup)
        found = lb.lookup1('A', 'B', 'C')
        lb.changed(lb)
        found = lb.lookup1('A', 'B', 'C')
        self.assertIs(found, b)
        self.assertEqual(_called_with,
                         [(('A',), 'B', 'C'), (('A',), 'B', 'C')])
        self.assertEqual(_results, [c])

    def test_adapter_hook_w_invalid_name(self):
        req, prv = object(), object()
        lb = self._makeOne()
        with self.assertRaises(ValueError):
            lb.adapter_hook(prv, req, object())

    def test_adapter_hook_miss_no_default(self):
        req, prv = object(), object()
        lb = self._makeOne()
        found = lb.adapter_hook(prv, req, '')
        self.assertIsNone(found)

    def test_adapter_hook_miss_w_default(self):
        req, prv, _default = object(), object(), object()
        lb = self._makeOne()
        found = lb.adapter_hook(prv, req, '', _default)
        self.assertIs(found, _default)

    def test_adapter_hook_hit_factory_returns_None(self):
        _f_called_with = []
        def _factory(context):
            _f_called_with.append(context)

        def _lookup(self, required, provided, name):
            return _factory
        req, prv, _default = object(), object(), object()
        lb = self._makeOne(uc_lookup=_lookup)
        adapted = lb.adapter_hook(prv, req, 'C', _default)
        self.assertIs(adapted, _default)
        self.assertEqual(_f_called_with, [req])

    def test_adapter_hook_hit_factory_returns_adapter(self):
        _f_called_with = []
        _adapter = object()
        def _factory(context):
            _f_called_with.append(context)
            return _adapter
        def _lookup(self, required, provided, name):
            return _factory
        req, prv, _default = object(), object(), object()
        lb = self._makeOne(uc_lookup=_lookup)
        adapted = lb.adapter_hook(prv, req, 'C', _default)
        self.assertIs(adapted, _adapter)
        self.assertEqual(_f_called_with, [req])

    def test_adapter_hook_super_unwraps(self):
        _f_called_with = []
        def _factory(context):
            _f_called_with.append(context)
            return context
        def _lookup(self, required, provided, name=''):
            return _factory
        required = super()
        provided = object()
        lb = self._makeOne(uc_lookup=_lookup)
        adapted = lb.adapter_hook(provided, required)
        self.assertIs(adapted, self)
        self.assertEqual(_f_called_with, [self])

    def test_queryAdapter(self):
        _f_called_with = []
        _adapter = object()
        def _factory(context):
            _f_called_with.append(context)
            return _adapter
        def _lookup(self, required, provided, name):
            return _factory
        req, prv, _default = object(), object(), object()
        lb = self._makeOne(uc_lookup=_lookup)
        adapted = lb.queryAdapter(req, prv, 'C', _default)
        self.assertIs(adapted, _adapter)
        self.assertEqual(_f_called_with, [req])

    def test_lookupAll_uncached(self):
        _called_with = []
        _results = [object(), object(), object()]
        def _lookupAll(self, required, provided):
            _called_with.append((required, provided))
            return tuple(_results)
        lb = self._makeOne(uc_lookupAll=_lookupAll)
        found = lb.lookupAll('A', 'B')
        self.assertEqual(found, tuple(_results))
        self.assertEqual(_called_with, [(('A',), 'B')])

    def test_lookupAll_cached(self):
        _called_with = []
        _results = [object(), object(), object()]
        def _lookupAll(self, required, provided):
            _called_with.append((required, provided))
            return tuple(_results)
        lb = self._makeOne(uc_lookupAll=_lookupAll)
        found = lb.lookupAll('A', 'B')
        found = lb.lookupAll('A', 'B')
        self.assertEqual(found, tuple(_results))
        self.assertEqual(_called_with, [(('A',), 'B')])

    def test_subscriptions_uncached(self):
        _called_with = []
        _results = [object(), object(), object()]
        def _subscriptions(self, required, provided):
            _called_with.append((required, provided))
            return tuple(_results)
        lb = self._makeOne(uc_subscriptions=_subscriptions)
        found = lb.subscriptions('A', 'B')
        self.assertEqual(found, tuple(_results))
        self.assertEqual(_called_with, [(('A',), 'B')])

    def test_subscriptions_cached(self):
        _called_with = []
        _results = [object(), object(), object()]
        def _subscriptions(self, required, provided):
            _called_with.append((required, provided))
            return tuple(_results)
        lb = self._makeOne(uc_subscriptions=_subscriptions)
        found = lb.subscriptions('A', 'B')
        found = lb.subscriptions('A', 'B')
        self.assertEqual(found, tuple(_results))
        self.assertEqual(_called_with, [(('A',), 'B')])


class LookupBaseTests(LookupBaseFallbackTests,
                      OptimizationTestMixin):

    def _getTargetClass(self):
        from zope.interface.adapter import LookupBase
        return LookupBase


class VerifyingBaseFallbackTests(unittest.TestCase):

    def _getFallbackClass(self):
        from zope.interface.adapter import VerifyingBaseFallback # pylint:disable=no-name-in-module
        return VerifyingBaseFallback

    _getTargetClass = _getFallbackClass

    def _makeOne(self, registry, uc_lookup=None, uc_lookupAll=None,
                 uc_subscriptions=None):
        # pylint:disable=function-redefined
        if uc_lookup is None:
            def uc_lookup(self, required, provided, name):
                raise NotImplementedError()
        if uc_lookupAll is None:
            def uc_lookupAll(self, required, provided):
                raise NotImplementedError()
        if uc_subscriptions is None:
            def uc_subscriptions(self, required, provided):
                raise NotImplementedError()
        class Derived(self._getTargetClass()):
            _uncached_lookup = uc_lookup
            _uncached_lookupAll = uc_lookupAll
            _uncached_subscriptions = uc_subscriptions
            def __init__(self, registry):
                super().__init__()
                self._registry = registry
        derived = Derived(registry)
        derived.changed(derived) # init. '_verify_ro' / '_verify_generations'
        return derived

    def _makeRegistry(self, depth):
        class WithGeneration:
            _generation = 1
        class Registry:
            def __init__(self, depth):
                self.ro = [WithGeneration() for i in range(depth)]
        return Registry(depth)

    def test_lookup(self):
        _called_with = []
        a, b, c = object(), object(), object()
        _results = [a, b, c]
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))
            return _results.pop(0)
        reg = self._makeRegistry(3)
        lb = self._makeOne(reg, uc_lookup=_lookup)
        found = lb.lookup(('A',), 'B', 'C')
        found = lb.lookup(('A',), 'B', 'C')
        self.assertIs(found, a)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])
        self.assertEqual(_results, [b, c])
        reg.ro[1]._generation += 1
        found = lb.lookup(('A',), 'B', 'C')
        self.assertIs(found, b)
        self.assertEqual(_called_with,
                         [(('A',), 'B', 'C'), (('A',), 'B', 'C')])
        self.assertEqual(_results, [c])

    def test_lookup1(self):
        _called_with = []
        a, b, c = object(), object(), object()
        _results = [a, b, c]
        def _lookup(self, required, provided, name):
            _called_with.append((required, provided, name))
            return _results.pop(0)
        reg = self._makeRegistry(3)
        lb = self._makeOne(reg, uc_lookup=_lookup)
        found = lb.lookup1('A', 'B', 'C')
        found = lb.lookup1('A', 'B', 'C')
        self.assertIs(found, a)
        self.assertEqual(_called_with, [(('A',), 'B', 'C')])
        self.assertEqual(_results, [b, c])
        reg.ro[1]._generation += 1
        found = lb.lookup1('A', 'B', 'C')
        self.assertIs(found, b)
        self.assertEqual(_called_with,
                         [(('A',), 'B', 'C'), (('A',), 'B', 'C')])
        self.assertEqual(_results, [c])

    def test_adapter_hook(self):
        a, b, _c = [object(), object(), object()]
        def _factory1(context):
            return a
        def _factory2(context):
            return b
        def _factory3(context):
            self.fail("This should never be called")
        _factories = [_factory1, _factory2, _factory3]
        def _lookup(self, required, provided, name):
            return _factories.pop(0)
        req, prv, _default = object(), object(), object()
        reg = self._makeRegistry(3)
        lb = self._makeOne(reg, uc_lookup=_lookup)
        adapted = lb.adapter_hook(prv, req, 'C', _default)
        self.assertIs(adapted, a)
        adapted = lb.adapter_hook(prv, req, 'C', _default)
        self.assertIs(adapted, a)
        reg.ro[1]._generation += 1
        adapted = lb.adapter_hook(prv, req, 'C', _default)
        self.assertIs(adapted, b)

    def test_queryAdapter(self):
        a, b, _c = [object(), object(), object()]
        def _factory1(context):
            return a
        def _factory2(context):
            return b
        def _factory3(context):
            self.fail("This should never be called")
        _factories = [_factory1, _factory2, _factory3]
        def _lookup(self, required, provided, name):
            return _factories.pop(0)
        req, prv, _default = object(), object(), object()
        reg = self._makeRegistry(3)
        lb = self._makeOne(reg, uc_lookup=_lookup)
        adapted = lb.queryAdapter(req, prv, 'C', _default)
        self.assertIs(adapted, a)
        adapted = lb.queryAdapter(req, prv, 'C', _default)
        self.assertIs(adapted, a)
        reg.ro[1]._generation += 1
        adapted = lb.adapter_hook(prv, req, 'C', _default)
        self.assertIs(adapted, b)

    def test_lookupAll(self):
        _results_1 = [object(), object(), object()]
        _results_2 = [object(), object(), object()]
        _results = [_results_1, _results_2]
        def _lookupAll(self, required, provided):
            return tuple(_results.pop(0))
        reg = self._makeRegistry(3)
        lb = self._makeOne(reg, uc_lookupAll=_lookupAll)
        found = lb.lookupAll('A', 'B')
        self.assertEqual(found, tuple(_results_1))
        found = lb.lookupAll('A', 'B')
        self.assertEqual(found, tuple(_results_1))
        reg.ro[1]._generation += 1
        found = lb.lookupAll('A', 'B')
        self.assertEqual(found, tuple(_results_2))

    def test_subscriptions(self):
        _results_1 = [object(), object(), object()]
        _results_2 = [object(), object(), object()]
        _results = [_results_1, _results_2]
        def _subscriptions(self, required, provided):
            return tuple(_results.pop(0))
        reg = self._makeRegistry(3)
        lb = self._makeOne(reg, uc_subscriptions=_subscriptions)
        found = lb.subscriptions('A', 'B')
        self.assertEqual(found, tuple(_results_1))
        found = lb.subscriptions('A', 'B')
        self.assertEqual(found, tuple(_results_1))
        reg.ro[1]._generation += 1
        found = lb.subscriptions('A', 'B')
        self.assertEqual(found, tuple(_results_2))


class VerifyingBaseTests(VerifyingBaseFallbackTests,
                         OptimizationTestMixin):

    def _getTargetClass(self):
        from zope.interface.adapter import VerifyingBase
        return VerifyingBase


class AdapterLookupBaseTests(unittest.TestCase):

    def _getTargetClass(self):
        from zope.interface.adapter import AdapterLookupBase
        return AdapterLookupBase

    def _makeOne(self, registry):
        return self._getTargetClass()(registry)

    def _makeSubregistry(self, *provided):
        class Subregistry:
            def __init__(self):
                self._adapters = []
                self._subscribers = []
        return Subregistry()

    def _makeRegistry(self, *provided):
        class Registry:
            def __init__(self, provided):
                self._provided = provided
                self.ro = []
        return Registry(provided)

    def test_ctor_empty_registry(self):
        registry = self._makeRegistry()
        alb = self._makeOne(registry)
        self.assertEqual(alb._extendors, {})

    def test_ctor_w_registry_provided(self):
        from zope.interface import Interface
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        alb = self._makeOne(registry)
        self.assertEqual(sorted(alb._extendors.keys()),
                         sorted([IBar, IFoo, Interface]))
        self.assertEqual(alb._extendors[IFoo], [IFoo, IBar])
        self.assertEqual(alb._extendors[IBar], [IBar])
        self.assertEqual(sorted(alb._extendors[Interface]),
                         sorted([IFoo, IBar]))

    def test_changed_empty_required(self):
        # ALB.changed expects to call a mixed in changed.
        class Mixin:
            def changed(self, *other):
                pass
        class Derived(self._getTargetClass(), Mixin):
            pass
        registry = self._makeRegistry()
        alb = Derived(registry)
        alb.changed(alb)

    def test_changed_w_required(self):
        # ALB.changed expects to call a mixed in changed.
        class Mixin:
            def changed(self, *other):
                pass
        class Derived(self._getTargetClass(), Mixin):
            pass
        class FauxWeakref:
            _unsub = None
            def __init__(self, here):
                self._here = here
            def __call__(self):
                return self if self._here else None
            def unsubscribe(self, target):
                self._unsub = target
        gone = FauxWeakref(False)
        here = FauxWeakref(True)
        registry = self._makeRegistry()
        alb = Derived(registry)
        alb._required[gone] = 1
        alb._required[here] = 1
        alb.changed(alb)
        self.assertEqual(len(alb._required), 0)
        self.assertEqual(gone._unsub, None)
        self.assertEqual(here._unsub, alb)

    def test_init_extendors_after_registry_update(self):
        from zope.interface import Interface
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry()
        alb = self._makeOne(registry)
        registry._provided = [IFoo, IBar]
        alb.init_extendors()
        self.assertEqual(sorted(alb._extendors.keys()),
                         sorted([IBar, IFoo, Interface]))
        self.assertEqual(alb._extendors[IFoo], [IFoo, IBar])
        self.assertEqual(alb._extendors[IBar], [IBar])
        self.assertEqual(sorted(alb._extendors[Interface]),
                         sorted([IFoo, IBar]))

    def test_add_extendor(self):
        from zope.interface import Interface
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry()
        alb = self._makeOne(registry)
        alb.add_extendor(IFoo)
        alb.add_extendor(IBar)
        self.assertEqual(sorted(alb._extendors.keys()),
                         sorted([IBar, IFoo, Interface]))
        self.assertEqual(alb._extendors[IFoo], [IFoo, IBar])
        self.assertEqual(alb._extendors[IBar], [IBar])
        self.assertEqual(sorted(alb._extendors[Interface]),
                         sorted([IFoo, IBar]))

    def test_remove_extendor(self):
        from zope.interface import Interface
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        alb = self._makeOne(registry)
        alb.remove_extendor(IFoo)
        self.assertEqual(sorted(alb._extendors.keys()),
                         sorted([IFoo, IBar, Interface]))
        self.assertEqual(alb._extendors[IFoo], [IBar])
        self.assertEqual(alb._extendors[IBar], [IBar])
        self.assertEqual(sorted(alb._extendors[Interface]),
                         sorted([IBar]))

    # test '_subscribe' via its callers, '_uncached_lookup', etc.

    def test__uncached_lookup_empty_ro(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry()
        alb = self._makeOne(registry)
        result = alb._uncached_lookup((IFoo,), IBar)
        self.assertEqual(result, None)
        self.assertEqual(len(alb._required), 1)
        self.assertIn(IFoo.weakref(), alb._required)

    def test__uncached_lookup_order_miss(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        result = alb._uncached_lookup((IFoo,), IBar)
        self.assertEqual(result, None)

    def test__uncached_lookup_extendors_miss(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry()
        subr = self._makeSubregistry()
        subr._adapters = [{}, {}] #utilities, single adapters
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_lookup((IFoo,), IBar)
        self.assertEqual(result, None)

    def test__uncached_lookup_components_miss_wrong_iface(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        IQux = InterfaceClass('IQux')
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        irrelevant = object()
        subr._adapters = [ #utilities, single adapters
            {},
            {IFoo: {IQux: {'': irrelevant},
                   }},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_lookup((IFoo,), IBar)
        self.assertEqual(result, None)

    def test__uncached_lookup_components_miss_wrong_name(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()

        wrongname = object()
        subr._adapters = [ #utilities, single adapters
            {},
            {IFoo: {IBar: {'wrongname': wrongname},
                   }},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_lookup((IFoo,), IBar)
        self.assertEqual(result, None)

    def test__uncached_lookup_simple_hit(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        _expected = object()
        subr._adapters = [ #utilities, single adapters
            {},
            {IFoo: {IBar: {'': _expected}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_lookup((IFoo,), IBar)
        self.assertIs(result, _expected)

    def test__uncached_lookup_repeated_hit(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        _expected = object()
        subr._adapters = [ #utilities, single adapters
            {},
            {IFoo: {IBar: {'': _expected}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_lookup((IFoo,), IBar)
        result2 = alb._uncached_lookup((IFoo,), IBar)
        self.assertIs(result, _expected)
        self.assertIs(result2, _expected)

    def test_queryMultiAdaptor_lookup_miss(self):
        from zope.interface.declarations import implementer
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        @implementer(IFoo)
        class Foo:
            pass
        foo = Foo()
        registry = self._makeRegistry()
        subr = self._makeSubregistry()
        subr._adapters = [ #utilities, single adapters
            {},
            {},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        alb.lookup = alb._uncached_lookup # provided by derived
        subr._v_lookup = alb
        _default = object()
        result = alb.queryMultiAdapter((foo,), IBar, default=_default)
        self.assertIs(result, _default)

    def test_queryMultiAdapter_errors_on_attribute_access(self):
        # Any error on attribute access previously lead to using the _empty singleton as "requires"
        # argument (See https://github.com/zopefoundation/zope.interface/issues/162)
        # but after https://github.com/zopefoundation/zope.interface/issues/200
        # they get propagated.
        from zope.interface.interface import InterfaceClass
        from zope.interface.tests import MissingSomeAttrs

        IFoo = InterfaceClass('IFoo')
        registry = self._makeRegistry()
        alb = self._makeOne(registry)
        alb.lookup = alb._uncached_lookup

        def test(ob):
            return alb.queryMultiAdapter(
                (ob,),
                IFoo,
            )

        MissingSomeAttrs.test_raises(self, test, expected_missing='__class__')

    def test_queryMultiAdaptor_factory_miss(self):
        from zope.interface.declarations import implementer
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        @implementer(IFoo)
        class Foo:
            pass
        foo = Foo()
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        _expected = object()
        _called_with = []
        def _factory(context):
            _called_with.append(context)

        subr._adapters = [ #utilities, single adapters
            {},
            {IFoo: {IBar: {'': _factory}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        alb.lookup = alb._uncached_lookup # provided by derived
        subr._v_lookup = alb
        _default = object()
        result = alb.queryMultiAdapter((foo,), IBar, default=_default)
        self.assertIs(result, _default)
        self.assertEqual(_called_with, [foo])

    def test_queryMultiAdaptor_factory_hit(self):
        from zope.interface.declarations import implementer
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        @implementer(IFoo)
        class Foo:
            pass
        foo = Foo()
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        _expected = object()
        _called_with = []
        def _factory(context):
            _called_with.append(context)
            return _expected
        subr._adapters = [ #utilities, single adapters
            {},
            {IFoo: {IBar: {'': _factory}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        alb.lookup = alb._uncached_lookup # provided by derived
        subr._v_lookup = alb
        _default = object()
        result = alb.queryMultiAdapter((foo,), IBar, default=_default)
        self.assertIs(result, _expected)
        self.assertEqual(_called_with, [foo])

    def test_queryMultiAdapter_super_unwraps(self):
        alb = self._makeOne(self._makeRegistry())
        def lookup(*args):
            return factory
        def factory(*args):
            return args
        alb.lookup = lookup

        objects = [
            super(),
            42,
            "abc",
            super(),
        ]

        result = alb.queryMultiAdapter(objects, None)
        self.assertEqual(result, (
            self,
            42,
            "abc",
            self,
        ))

    def test__uncached_lookupAll_empty_ro(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry()
        alb = self._makeOne(registry)
        result = alb._uncached_lookupAll((IFoo,), IBar)
        self.assertEqual(result, ())
        self.assertEqual(len(alb._required), 1)
        self.assertIn(IFoo.weakref(), alb._required)

    def test__uncached_lookupAll_order_miss(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_lookupAll((IFoo,), IBar)
        self.assertEqual(result, ())

    def test__uncached_lookupAll_extendors_miss(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry()
        subr = self._makeSubregistry()
        subr._adapters = [{}, {}] #utilities, single adapters
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_lookupAll((IFoo,), IBar)
        self.assertEqual(result, ())

    def test__uncached_lookupAll_components_miss(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        IQux = InterfaceClass('IQux')
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        irrelevant = object()
        subr._adapters = [ #utilities, single adapters
            {},
            {IFoo: {IQux: {'': irrelevant}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_lookupAll((IFoo,), IBar)
        self.assertEqual(result, ())

    def test__uncached_lookupAll_simple_hit(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        _expected = object()
        _named = object()
        subr._adapters = [ #utilities, single adapters
            {},
            {IFoo: {IBar: {'': _expected, 'named': _named}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_lookupAll((IFoo,), IBar)
        self.assertEqual(sorted(result), [('', _expected), ('named', _named)])

    def test_names(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        _expected = object()
        _named = object()
        subr._adapters = [ #utilities, single adapters
            {},
            {IFoo: {IBar: {'': _expected, 'named': _named}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        alb.lookupAll = alb._uncached_lookupAll
        subr._v_lookup = alb
        result = alb.names((IFoo,), IBar)
        self.assertEqual(sorted(result), ['', 'named'])

    def test__uncached_subscriptions_empty_ro(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry()
        alb = self._makeOne(registry)
        result = alb._uncached_subscriptions((IFoo,), IBar)
        self.assertEqual(result, [])
        self.assertEqual(len(alb._required), 1)
        self.assertIn(IFoo.weakref(), alb._required)

    def test__uncached_subscriptions_order_miss(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_subscriptions((IFoo,), IBar)
        self.assertEqual(result, [])

    def test__uncached_subscriptions_extendors_miss(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry()
        subr = self._makeSubregistry()
        subr._subscribers = [{}, {}] #utilities, single adapters
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_subscriptions((IFoo,), IBar)
        self.assertEqual(result, [])

    def test__uncached_subscriptions_components_miss_wrong_iface(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        IQux = InterfaceClass('IQux')
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        irrelevant = object()
        subr._subscribers = [ #utilities, single adapters
            {},
            {IFoo: {IQux: {'': irrelevant}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_subscriptions((IFoo,), IBar)
        self.assertEqual(result, [])

    def test__uncached_subscriptions_components_miss_wrong_name(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        wrongname = object()
        subr._subscribers = [ #utilities, single adapters
            {},
            {IFoo: {IBar: {'wrongname': wrongname}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_subscriptions((IFoo,), IBar)
        self.assertEqual(result, [])

    def test__uncached_subscriptions_simple_hit(self):
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        class Foo:
            def __lt__(self, other):
                return True
        _exp1, _exp2 = Foo(), Foo()
        subr._subscribers = [ #utilities, single adapters
            {},
            {IFoo: {IBar: {'': (_exp1, _exp2)}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        subr._v_lookup = alb
        result = alb._uncached_subscriptions((IFoo,), IBar)
        self.assertEqual(sorted(result), sorted([_exp1, _exp2]))

    def test_subscribers_wo_provided(self):
        from zope.interface.declarations import implementer
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        @implementer(IFoo)
        class Foo:
            pass
        foo = Foo()
        registry = self._makeRegistry(IFoo, IBar)
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        _called = {}
        def _factory1(context):
            _called.setdefault('_factory1', []).append(context)
        def _factory2(context):
            _called.setdefault('_factory2', []).append(context)
        subr._subscribers = [ #utilities, single adapters
            {},
            {IFoo: {None: {'': (_factory1, _factory2)}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        alb.subscriptions = alb._uncached_subscriptions
        subr._v_lookup = alb
        result = alb.subscribers((foo,), None)
        self.assertEqual(result, ())
        self.assertEqual(_called, {'_factory1': [foo], '_factory2': [foo]})

    def test_subscribers_w_provided(self):
        from zope.interface.declarations import implementer
        from zope.interface.interface import InterfaceClass
        IFoo = InterfaceClass('IFoo')
        IBar = InterfaceClass('IBar', (IFoo,))
        @implementer(IFoo)
        class Foo:
            pass
        foo = Foo()
        registry = self._makeRegistry(IFoo, IBar)
        registry = self._makeRegistry(IFoo, IBar)
        subr = self._makeSubregistry()
        _called = {}
        _exp1, _exp2 = object(), object()
        def _factory1(context):
            _called.setdefault('_factory1', []).append(context)
            return _exp1
        def _factory2(context):
            _called.setdefault('_factory2', []).append(context)
            return _exp2
        def _side_effect_only(context):
            _called.setdefault('_side_effect_only', []).append(context)

        subr._subscribers = [ #utilities, single adapters
            {},
            {IFoo: {IBar: {'': (_factory1, _factory2, _side_effect_only)}}},
        ]
        registry.ro.append(subr)
        alb = self._makeOne(registry)
        alb.subscriptions = alb._uncached_subscriptions
        subr._v_lookup = alb
        result = alb.subscribers((foo,), IBar)
        self.assertEqual(result, [_exp1, _exp2])
        self.assertEqual(_called,
                         {'_factory1': [foo],
                          '_factory2': [foo],
                          '_side_effect_only': [foo],
                         })


class VerifyingAdapterRegistryTests(unittest.TestCase):
    # This is also the base for AdapterRegistryTests. That makes the
    # inheritance seems backwards, but even though they implement the
    # same interfaces, VAR and AR each only extend BAR; and neither
    # one will pass the test cases for BAR (it uses a special
    # LookupClass just for the tests).

    def _getTargetClass(self):
        from zope.interface.adapter import VerifyingAdapterRegistry
        return VerifyingAdapterRegistry

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_verify_object_provides_IAdapterRegistry(self):
        from zope.interface.verify import verifyObject
        from zope.interface.interfaces import IAdapterRegistry
        registry = self._makeOne()
        verifyObject(IAdapterRegistry, registry)


class AdapterRegistryTests(VerifyingAdapterRegistryTests):

    def _getTargetClass(self):
        from zope.interface.adapter import AdapterRegistry
        return AdapterRegistry

    def test_ctor_no_bases(self):
        ar = self._makeOne()
        self.assertEqual(len(ar._v_subregistries), 0)

    def test_ctor_w_bases(self):
        base = self._makeOne()
        sub = self._makeOne([base])
        self.assertEqual(len(sub._v_subregistries), 0)
        self.assertEqual(len(base._v_subregistries), 1)
        self.assertIn(sub, base._v_subregistries)

    # test _addSubregistry / _removeSubregistry via only caller, _setBases

    def test__setBases_removing_existing_subregistry(self):
        before = self._makeOne()
        after = self._makeOne()
        sub = self._makeOne([before])
        sub.__bases__ = [after]
        self.assertEqual(len(before._v_subregistries), 0)
        self.assertEqual(len(after._v_subregistries), 1)
        self.assertIn(sub, after._v_subregistries)

    def test__setBases_wo_stray_entry(self):
        before = self._makeOne()
        stray = self._makeOne()
        after = self._makeOne()
        sub = self._makeOne([before])
        sub.__dict__['__bases__'].append(stray)
        sub.__bases__ = [after]
        self.assertEqual(len(before._v_subregistries), 0)
        self.assertEqual(len(after._v_subregistries), 1)
        self.assertIn(sub, after._v_subregistries)

    def test__setBases_w_existing_entry_continuing(self):
        before = self._makeOne()
        after = self._makeOne()
        sub = self._makeOne([before])
        sub.__bases__ = [before, after]
        self.assertEqual(len(before._v_subregistries), 1)
        self.assertEqual(len(after._v_subregistries), 1)
        self.assertIn(sub, before._v_subregistries)
        self.assertIn(sub, after._v_subregistries)

    def test_changed_w_subregistries(self):
        base = self._makeOne()
        class Derived:
            _changed = None
            def changed(self, originally_changed):
                self._changed = originally_changed
        derived1, derived2 = Derived(), Derived()
        base._addSubregistry(derived1)
        base._addSubregistry(derived2)
        orig = object()
        base.changed(orig)
        self.assertIs(derived1._changed, orig)
        self.assertIs(derived2._changed, orig)


class Test_utils(unittest.TestCase):

    def test__convert_None_to_Interface_w_None(self):
        from zope.interface.adapter import _convert_None_to_Interface
        from zope.interface.interface import Interface
        self.assertIs(_convert_None_to_Interface(None), Interface)

    def test__convert_None_to_Interface_w_other(self):
        from zope.interface.adapter import _convert_None_to_Interface
        other = object()
        self.assertIs(_convert_None_to_Interface(other), other)

    def test__normalize_name_str(self):
        from zope.interface.adapter import _normalize_name
        STR = b'str'
        UNICODE = 'str'
        norm = _normalize_name(STR)
        self.assertEqual(norm, UNICODE)
        self.assertIsInstance(norm, type(UNICODE))

    def test__normalize_name_unicode(self):
        from zope.interface.adapter import _normalize_name

        USTR = 'ustr'
        self.assertEqual(_normalize_name(USTR), USTR)

    def test__normalize_name_other(self):
        from zope.interface.adapter import _normalize_name
        for other in 1, 1.0, (), [], {}, object():
            self.assertRaises(TypeError, _normalize_name, other)

    # _lookup, _lookupAll, and _subscriptions tested via their callers
    # (AdapterLookupBase.{lookup,lookupAll,subscriptions}).
¿Qué es la limpieza dental de perros? - Clínica veterinaria


Es la eliminación del sarro y la placa adherida a la superficie de los dientes mediante un equipo de ultrasonidos que garantiza la integridad de las piezas dentales a la vez que elimina en profundidad cualquier resto de suciedad.

A continuación se procede al pulido de los dientes mediante una fresa especial que elimina la placa bacteriana y devuelve a los dientes el aspecto sano que deben tener.

Una vez terminado todo el proceso, se mantiene al perro en observación hasta que se despierta de la anestesia, bajo la atenta supervisión de un veterinario.

¿Cada cuánto tiempo tengo que hacerle una limpieza dental a mi perro?

A partir de cierta edad, los perros pueden necesitar una limpieza dental anual o bianual. Depende de cada caso. En líneas generales, puede decirse que los perros de razas pequeñas suelen acumular más sarro y suelen necesitar una atención mayor en cuanto a higiene dental.


Riesgos de una mala higiene


Los riesgos más evidentes de una mala higiene dental en los perros son los siguientes:

  • Cuando la acumulación de sarro no se trata, se puede producir una inflamación y retracción de las encías que puede descalzar el diente y provocar caídas.
  • Mal aliento (halitosis).
  • Sarro perros
  • Puede ir a más
  • Las bacterias de la placa pueden trasladarse a través del torrente circulatorio a órganos vitales como el corazón ocasionando problemas de endocarditis en las válvulas. Las bacterias pueden incluso acantonarse en huesos (La osteomielitis es la infección ósea, tanto cortical como medular) provocando mucho dolor y una artritis séptica).

¿Cómo se forma el sarro?

El sarro es la calcificación de la placa dental. Los restos de alimentos, junto con las bacterias presentes en la boca, van a formar la placa bacteriana o placa dental. Si la placa no se retira, al mezclarse con la saliva y los minerales presentes en ella, reaccionará formando una costra. La placa se calcifica y se forma el sarro.

El sarro, cuando se forma, es de color blanquecino pero a medida que pasa el tiempo se va poniendo amarillo y luego marrón.

Síntomas de una pobre higiene dental
La señal más obvia de una mala salud dental canina es el mal aliento.

Sin embargo, a veces no es tan fácil de detectar
Y hay perros que no se dejan abrir la boca por su dueño. Por ejemplo…

Recientemente nos trajeron a la clínica a un perro que parpadeaba de un ojo y decía su dueño que le picaba un lado de la cara. Tenía molestias y dificultad para comer, lo que había llevado a sus dueños a comprarle comida blanda (que suele ser un poco más cara y llevar más contenido en grasa) durante medio año. Después de una exploración oftalmológica, nos dimos cuenta de que el ojo tenía una úlcera en la córnea probablemente de rascarse . Además, el canto lateral del ojo estaba inflamado. Tenía lo que en humanos llamamos flemón pero como era un perro de pelo largo, no se le notaba a simple vista. Al abrirle la boca nos llamó la atención el ver una muela llena de sarro. Le realizamos una radiografía y encontramos una fístula que llegaba hasta la parte inferior del ojo.

Le tuvimos que extraer la muela. Tras esto, el ojo se curó completamente con unos colirios y una lentilla protectora de úlcera. Afortunadamente, la úlcera no profundizó y no perforó el ojo. Ahora el perro come perfectamente a pesar de haber perdido una muela.

¿Cómo mantener la higiene dental de tu perro?
Hay varias maneras de prevenir problemas derivados de la salud dental de tu perro.

Limpiezas de dientes en casa
Es recomendable limpiar los dientes de tu perro semanal o diariamente si se puede. Existe una gran variedad de productos que se pueden utilizar:

Pastas de dientes.
Cepillos de dientes o dedales para el dedo índice, que hacen más fácil la limpieza.
Colutorios para echar en agua de bebida o directamente sobre el diente en líquido o en spray.

En la Clínica Tus Veterinarios enseñamos a nuestros clientes a tomar el hábito de limpiar los dientes de sus perros desde que son cachorros. Esto responde a nuestro compromiso con la prevención de enfermedades caninas.

Hoy en día tenemos muchos clientes que limpian los dientes todos los días a su mascota, y como resultado, se ahorran el dinero de hacer limpiezas dentales profesionales y consiguen una mejor salud de su perro.


Limpiezas dentales profesionales de perros y gatos

Recomendamos hacer una limpieza dental especializada anualmente. La realizamos con un aparato de ultrasonidos que utiliza agua para quitar el sarro. Después, procedemos a pulir los dientes con un cepillo de alta velocidad y una pasta especial. Hacemos esto para proteger el esmalte.

La frecuencia de limpiezas dentales necesaria varía mucho entre razas. En general, las razas grandes tienen buena calidad de esmalte, por lo que no necesitan hacerlo tan a menudo e incluso pueden pasarse la vida sin requerir una limpieza. Sin embargo, razas pequeñas como el Yorkshire o el Maltés, deben hacérselas todos los años desde cachorros si se quiere conservar sus piezas dentales.

Otro factor fundamental es la calidad del pienso. Algunas marcas han diseñado croquetas que limpian la superficie del diente y de la muela al masticarse.

Ultrasonido para perros

¿Se necesita anestesia para las limpiezas dentales de perros y gatos?

La limpieza dental en perros no es una técnica que pueda practicarse sin anestesia general , aunque hay veces que los propietarios no quieren anestesiar y si tiene poco sarro y el perro es muy bueno se puede intentar…… , pero no se va a poder pulir ni acceder a todas la zona de la boca …. Además los limpiadores dentales van a irrigar agua y hay riesgo de aspiración a vías respiratorias si no se realiza una anestesia correcta con intubación traqueal . En resumen , sin anestesia no se va hacer una correcta limpieza dental.

Tampoco sirve la sedación ya que necesitamos que el animal esté totalmente quieto, y el veterinario tenga un acceso completo a todas sus piezas dentales y encías.

Alimentos para la limpieza dental

Hay que tener cierto cuidado a la hora de comprar determinados alimentos porque no todos son saludables. Algunos tienen demasiado contenido graso, que en exceso puede causar problemas cardiovasculares y obesidad.

Los mejores alimentos para los dientes son aquellos que están elaborados por empresas farmacéuticas y llevan componentes químicos con tratamientos específicos para el diente del perro. Esto implica no solo limpieza a través de la acción mecánica de morder sino también un tratamiento antibacteriano para prevenir el sarro.

Conclusión

Si eres como la mayoría de dueños, por falta de tiempo , es probable que no estés prestando la suficiente atención a la limpieza dental de tu perro. Por eso te animamos a que comiences a limpiar los dientes de tu perro y consideres atender a su higiene bucal con frecuencia.

Estas simples medidas pueden conllevar a que tu perro tenga una vida más larga y mucho más saludable.

Si te resulta imposible introducir un cepillo de dientes a tu perro en la boca, pásate con él por clínica Tus Veterinarios y te explicamos cómo hacerlo.

Necesitas hacer una limpieza dental profesional a tu mascota?
Llámanos al 622575274 o contacta con nosotros

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

¡Hola!