############################################################################## # Copyright (c) 2020 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. ############################################################################## import array import sys import unittest from collections import OrderedDict from collections import abc from collections import deque from types import MappingProxyType from zope.interface import Invalid from zope.interface._compat import PYPY # Note that importing z.i.c.collections does work on import. from zope.interface.common import collections from . import VerifyClassMixin from . import VerifyObjectMixin from . import add_abc_interface_tests class TestVerifyClass(VerifyClassMixin, unittest.TestCase): # Here we test some known builtin classes that are defined to implement # various collection interfaces as a quick sanity test. def test_frozenset(self): self.assertIsInstance(frozenset(), abc.Set) self.assertTrue(self.verify(collections.ISet, frozenset)) def test_list(self): self.assertIsInstance(list(), abc.MutableSequence) self.assertTrue(self.verify(collections.IMutableSequence, list)) # Here we test some derived classes. def test_UserList(self): self.assertTrue(self.verify(collections.IMutableSequence, collections.UserList)) def test_UserDict(self): self.assertTrue(self.verify(collections.IMutableMapping, collections.UserDict)) def test_UserString(self): self.assertTrue(self.verify(collections.ISequence, collections.UserString)) # Now we go through the registry, which should have several things, mostly # builtins, but if we've imported other libraries already, it could # contain things from outside of there too. We aren't concerned about # third-party code here, just standard library types. We start with a # blacklist of things to exclude, but if that gets out of hand we can # figure out a better whitelisting. UNVERIFIABLE = { # This is declared to be an ISequence, but is missing lots of methods, # including some that aren't part of a language protocol, such as # ``index`` and ``count``. memoryview, # 'pkg_resources._vendor.pyparsing.ParseResults' is registered as a # MutableMapping but is missing methods like ``popitem`` and # ``setdefault``. It's imported due to namespace packages. 'ParseResults', # sqlite3.Row claims ISequence but also misses ``index`` and # ``count``. It's imported because...? Coverage imports it, but why # do we have it without coverage? 'Row', # In Python 3.10 ``array.array`` appears as ``IMutableSequence`` but it # does not provide a ``clear()`` method and it cannot be instantiated # using ``array.array()``. array.array, } if PYPY: UNVERIFIABLE.update({ # collections.deque.pop() doesn't support the index= argument to # MutableSequence.pop(). We can't verify this on CPython because # we can't get the signature, but on PyPy we /can/ get the # signature, and of course it doesn't match. deque, # Likewise for index range, }) UNVERIFIABLE_RO = { # ``array.array`` fails the ``test_auto_ro_*`` tests with and # without strict RO but only on Windows (AppVeyor) on Python 3.10.0 # (in older versions ``array.array`` does not appear as # ``IMutableSequence``). array.array, } add_abc_interface_tests(TestVerifyClass, collections.ISet.__module__) def _get_FrameLocalsProxy(): return type(sys._getframe().f_locals) class TestVerifyObject(VerifyObjectMixin, TestVerifyClass): CONSTRUCTORS = { collections.IValuesView: {}.values, collections.IItemsView: {}.items, collections.IKeysView: {}.keys, memoryview: lambda: memoryview(b'abc'), range: lambda: range(10), MappingProxyType: lambda: MappingProxyType({}), collections.UserString: lambda: collections.UserString('abc'), type(iter(bytearray())): lambda: iter(bytearray()), type(iter(b'abc')): lambda: iter(b'abc'), 'coroutine': unittest.SkipTest, type(iter({}.keys())): lambda: iter({}.keys()), type(iter({}.items())): lambda: iter({}.items()), type(iter({}.values())): lambda: iter({}.values()), type(i for i in range(1)): lambda: (i for i in range(3)), type(iter([])): lambda: iter([]), type(reversed([])): lambda: reversed([]), 'longrange_iterator': unittest.SkipTest, 'range_iterator': lambda: iter(range(3)), 'rangeiterator': lambda: iter(range(3)), type(iter(set())): lambda: iter(set()), type(iter('')): lambda: iter(''), 'async_generator': unittest.SkipTest, type(iter(tuple())): lambda: iter(tuple()), } if sys.version_info >= (3, 13): def FrameLocalsProxy_constructor(): return _get_FrameLocalsProxy()(sys._getframe()) FrameLocalsProxy = _get_FrameLocalsProxy() CONSTRUCTORS[FrameLocalsProxy] = FrameLocalsProxy_constructor UNVERIFIABLE_RO = { # ``array.array`` fails the ``test_auto_ro_*`` tests with and # without strict RO but only on Windows (AppVeyor) on Python 3.10.0 # (in older versions ``array.array`` does not appear as # ``IMutableSequence``). array.array, }