summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2020-04-06 09:06:48 -0500
committerGitHub <noreply@github.com>2020-04-06 09:06:48 -0500
commit6c2f71487eed46896647edc9dc770f1fd5da823b (patch)
treeaa4356dedbcb8f1702df92dbd4397fdb8a240332
parentabf12ada0a5806f233a426ae6fc93120da2ba598 (diff)
parent719851072c41ff0914def1368716a049859ed32a (diff)
downloadzope-interface-6c2f71487eed46896647edc9dc770f1fd5da823b.tar.gz
Merge pull request #201 from zopefoundation/issue200
Remove the bare except: statements.
-rw-r--r--CHANGES.rst28
-rw-r--r--benchmarks/micro.py118
-rw-r--r--src/zope/interface/_zope_interface_coptimizations.c134
-rw-r--r--src/zope/interface/declarations.py10
-rw-r--r--src/zope/interface/interface.py12
-rw-r--r--src/zope/interface/tests/__init__.py60
-rw-r--r--src/zope/interface/tests/test_adapter.py135
-rw-r--r--src/zope/interface/tests/test_declarations.py218
-rw-r--r--src/zope/interface/tests/test_interface.py10
9 files changed, 525 insertions, 200 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 7075008..bf8ce26 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -2,10 +2,30 @@
Changes
=========
-5.0.3 (unreleased)
-==================
-
-- Nothing changed yet.
+5.1.0 (unreleased)
+==================
+
+- Remove all bare ``except:`` statements. Previously, when accessing
+ special attributes such as ``__provides__``, ``__providedBy__``,
+ ``__class__`` and ``__conform__``, this package wrapped such access
+ in a bare ``except:`` statement, meaning that many errors could pass
+ silently; typically this would result in a fallback path being taken
+ and sometimes (like with ``providedBy()``) the result would be
+ non-sensical. This is especially true when those attributes are
+ implemented with descriptors. Now, only ``AttributeError`` is
+ caught. This makes errors more obvious.
+
+ Obviously, this means that some exceptions will be propagated
+ differently than before. In particular, ``RuntimeError`` raised by
+ Acquisition in the case of circular containment will now be
+ propagated. Previously, when adapting such a broken object, a
+ ``TypeError`` would be the common result, but now it will be a more
+ informative ``RuntimeError``.
+
+ In addition, ZODB errors like ``POSKeyError`` could now be
+ propagated where previously they would ignored by this package.
+
+ See `issue 200 <https://github.com/zopefoundation/zope.interface/issues/200>`_.
5.0.2 (2020-03-30)
diff --git a/benchmarks/micro.py b/benchmarks/micro.py
index a9527db..dce40ce 100644
--- a/benchmarks/micro.py
+++ b/benchmarks/micro.py
@@ -37,6 +37,21 @@ class DeepestInheritance(object):
pass
classImplements(DeepestInheritance, deep_ifaces[-1])
+
+class ImplementsNothing(object):
+ pass
+
+
+class HasConformReturnNone(object):
+ def __conform__(self, iface):
+ return None
+
+
+class HasConformReturnObject(object):
+ def __conform__(self, iface):
+ return self
+
+
def make_implementer(iface):
c = type('Implementer' + iface.__name__, (object,), {})
classImplements(c, iface)
@@ -77,16 +92,17 @@ def bench_sort(loops, objs):
return pyperf.perf_counter() - t0
def bench_query_adapter(loops, components, objs=providers):
+ components_queryAdapter = components.queryAdapter
# One time through to prime the caches
for iface in ifaces:
for provider in providers:
- components.queryAdapter(provider, iface)
+ components_queryAdapter(provider, iface)
t0 = pyperf.perf_counter()
for _ in range(loops):
for iface in ifaces:
for provider in objs:
- components.queryAdapter(provider, iface)
+ components_queryAdapter(provider, iface)
return pyperf.perf_counter() - t0
@@ -106,9 +122,107 @@ def bench_getattr(loops, name, get=getattr):
get(Interface, name) # 10
return pyperf.perf_counter() - t0
+
+def bench_iface_call_no_conform_no_alternate_not_provided(loops):
+ inst = ImplementsNothing()
+ t0 = pyperf.perf_counter()
+ for _ in range(loops):
+ for _ in range(INNER):
+ for iface in ifaces:
+ try:
+ iface(inst)
+ except TypeError:
+ pass
+ else:
+ raise TypeError("Should have failed")
+ return pyperf.perf_counter() - t0
+
+
+def bench_iface_call_no_conform_w_alternate_not_provided(loops):
+ inst = ImplementsNothing()
+ t0 = pyperf.perf_counter()
+ for _ in range(loops):
+ for _ in range(INNER):
+ for iface in ifaces:
+ iface(inst, 42)
+ return pyperf.perf_counter() - t0
+
+
+def bench_iface_call_w_conform_return_none_not_provided(loops):
+ inst = HasConformReturnNone()
+ t0 = pyperf.perf_counter()
+ for _ in range(loops):
+ for _ in range(INNER):
+ for iface in ifaces:
+ iface(inst, 42)
+ return pyperf.perf_counter() - t0
+
+
+def bench_iface_call_w_conform_return_non_none_not_provided(loops):
+ inst = HasConformReturnObject()
+ t0 = pyperf.perf_counter()
+ for _ in range(loops):
+ for _ in range(INNER):
+ for iface in ifaces:
+ iface(inst)
+ return pyperf.perf_counter() - t0
+
+def _bench_iface_call_simple(loops, inst):
+ t0 = pyperf.perf_counter()
+ for _ in range(loops):
+ for _ in range(INNER):
+ for iface in ifaces:
+ iface(inst)
+ return pyperf.perf_counter() - t0
+
+
+def bench_iface_call_no_conform_provided_wide(loops):
+ return _bench_iface_call_simple(loops, WideInheritance())
+
+
+def bench_iface_call_no_conform_provided_deep(loops):
+ return _bench_iface_call_simple(loops, DeepestInheritance())
+
+
runner = pyperf.Runner()
runner.bench_time_func(
+ 'call interface (provides; deep)',
+ bench_iface_call_no_conform_provided_deep,
+ inner_loops=INNER * len(ifaces)
+)
+
+runner.bench_time_func(
+ 'call interface (provides; wide)',
+ bench_iface_call_no_conform_provided_wide,
+ inner_loops=INNER * len(ifaces)
+)
+
+runner.bench_time_func(
+ 'call interface (no alternate, no conform, not provided)',
+ bench_iface_call_no_conform_no_alternate_not_provided,
+ inner_loops=INNER * len(ifaces)
+)
+
+runner.bench_time_func(
+ 'call interface (alternate, no conform, not provided)',
+ bench_iface_call_no_conform_w_alternate_not_provided,
+ inner_loops=INNER * len(ifaces)
+)
+
+runner.bench_time_func(
+ 'call interface (no alternate, valid conform, not provided)',
+ bench_iface_call_w_conform_return_non_none_not_provided,
+ inner_loops=INNER * len(ifaces)
+)
+
+runner.bench_time_func(
+ 'call interface (alternate, invalid conform, not provided)',
+ bench_iface_call_w_conform_return_none_not_provided,
+ inner_loops=INNER * len(ifaces)
+)
+
+runner.bench_time_func(
'read __module__', # stored in C, accessed through __getattribute__
bench_getattr,
'__module__',
diff --git a/src/zope/interface/_zope_interface_coptimizations.c b/src/zope/interface/_zope_interface_coptimizations.c
index 4e09614..69f5bbc 100644
--- a/src/zope/interface/_zope_interface_coptimizations.c
+++ b/src/zope/interface/_zope_interface_coptimizations.c
@@ -178,23 +178,46 @@ getObjectSpecification(PyObject *ignored, PyObject *ob)
PyObject *cls, *result;
result = PyObject_GetAttr(ob, str__provides__);
- if (result != NULL && PyObject_TypeCheck(result, &SpecificationBaseType))
- return result;
-
-
- PyErr_Clear();
+ if (!result)
+ {
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+ {
+ /* Propagate non AttributeError exceptions. */
+ return NULL;
+ }
+ PyErr_Clear();
+ }
+ else
+ {
+ int is_instance = -1;
+ is_instance = PyObject_IsInstance(result, (PyObject*)&SpecificationBaseType);
+ if (is_instance < 0)
+ {
+ /* Propagate all errors */
+ return NULL;
+ }
+ if (is_instance)
+ {
+ return result;
+ }
+ }
/* We do a getattr here so as not to be defeated by proxies */
cls = PyObject_GetAttr(ob, str__class__);
if (cls == NULL)
- {
+ {
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+ {
+ /* Propagate non-AttributeErrors */
+ return NULL;
+ }
PyErr_Clear();
if (imported_declarations == 0 && import_declarations() < 0)
return NULL;
Py_INCREF(empty);
return empty;
- }
+ }
result = implementedBy(NULL, cls);
Py_DECREF(cls);
@@ -205,10 +228,20 @@ static PyObject *
providedBy(PyObject *ignored, PyObject *ob)
{
PyObject *result, *cls, *cp;
-
+ int is_instance = -1;
result = NULL;
- if (PyObject_TypeCheck(ob, &PySuper_Type))
+ is_instance = PyObject_IsInstance(ob, (PyObject*)&PySuper_Type);
+ if (is_instance < 0)
+ {
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+ {
+ /* Propagate non-AttributeErrors */
+ return NULL;
+ }
+ PyErr_Clear();
+ }
+ if (is_instance)
{
return implementedBy(NULL, ob);
}
@@ -216,10 +249,15 @@ providedBy(PyObject *ignored, PyObject *ob)
result = PyObject_GetAttr(ob, str__providedBy__);
if (result == NULL)
- {
+ {
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+ {
+ return NULL;
+ }
+
PyErr_Clear();
return getObjectSpecification(NULL, ob);
- }
+ }
/* We want to make sure we have a spec. We can't do a type check
@@ -737,63 +775,85 @@ static struct PyMethodDef ib_methods[] = {
};
/*
- def __call__(self, obj, alternate=_marker):
- conform = getattr(obj, '__conform__', None)
- if conform is not None:
- adapter = self._call_conform(conform)
- if adapter is not None:
- return adapter
-
- adapter = self.__adapt__(obj)
-
+ def __call__(self, obj, alternate=_marker):
+ try:
+ conform = obj.__conform__
+ except AttributeError: # pylint:disable=bare-except
+ conform = None
+
+ if conform is not None:
+ adapter = self._call_conform(conform)
if adapter is not None:
return adapter
- elif alternate is not _marker:
- return alternate
- else:
- raise TypeError("Could not adapt", obj, self)
+
+ adapter = self.__adapt__(obj)
+
+ if adapter is not None:
+ return adapter
+ if alternate is not _marker:
+ return alternate
+ raise TypeError("Could not adapt", obj, self)
+
*/
static PyObject *
ib_call(PyObject *self, PyObject *args, PyObject *kwargs)
{
- PyObject *conform, *obj, *alternate=NULL, *adapter;
-
+ PyObject *conform, *obj, *alternate, *adapter;
static char *kwlist[] = {"obj", "alternate", NULL};
+ conform = obj = alternate = adapter = NULL;
+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist,
&obj, &alternate))
return NULL;
conform = PyObject_GetAttr(obj, str__conform__);
- if (conform != NULL)
- {
+ if (conform == NULL)
+ {
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+ {
+ /* Propagate non-AttributeErrors */
+ return NULL;
+ }
+ PyErr_Clear();
+
+ Py_INCREF(Py_None);
+ conform = Py_None;
+ }
+
+ if (conform != Py_None)
+ {
adapter = PyObject_CallMethodObjArgs(self, str_call_conform,
conform, NULL);
Py_DECREF(conform);
if (adapter == NULL || adapter != Py_None)
- return adapter;
+ return adapter;
Py_DECREF(adapter);
- }
+ }
else
- PyErr_Clear();
+ {
+ Py_DECREF(conform);
+ }
- adapter = __adapt__(self, obj);
+ adapter = __adapt__(self, obj); // XXX: should be self.__adapt__.
if (adapter == NULL || adapter != Py_None)
- return adapter;
+ {
+ return adapter;
+ }
Py_DECREF(adapter);
if (alternate != NULL)
- {
+ {
Py_INCREF(alternate);
return alternate;
- }
+ }
adapter = Py_BuildValue("sOO", "Could not adapt", obj, self);
if (adapter != NULL)
- {
+ {
PyErr_SetObject(PyExc_TypeError, adapter);
Py_DECREF(adapter);
- }
+ }
return NULL;
}
diff --git a/src/zope/interface/declarations.py b/src/zope/interface/declarations.py
index e84a728..b42c906 100644
--- a/src/zope/interface/declarations.py
+++ b/src/zope/interface/declarations.py
@@ -984,6 +984,7 @@ def moduleProvides(*interfaces):
# XXX: is this a fossil? Nobody calls it, no unit tests exercise it, no
# doctests import it, and the package __init__ doesn't import it.
+# (Answer: Versions of zope.container prior to 4.4.0 called this.)
def ObjectSpecification(direct, cls):
"""Provide object specifications
@@ -994,16 +995,17 @@ def ObjectSpecification(direct, cls):
@_use_c_impl
def getObjectSpecification(ob):
try:
- provides = getattr(ob, '__provides__', None)
- except:
+ provides = ob.__provides__
+ except AttributeError:
provides = None
+
if provides is not None:
if isinstance(provides, SpecificationBase):
return provides
try:
cls = ob.__class__
- except:
+ except AttributeError:
# We can't get the class, so just consider provides
return _empty
return implementedBy(cls)
@@ -1028,7 +1030,7 @@ def providedBy(ob):
return implementedBy(ob)
r = ob.__providedBy__
- except:
+ except AttributeError:
# Not set yet. Fall back to lower-level thing that computes it
return getObjectSpecification(ob)
diff --git a/src/zope/interface/interface.py b/src/zope/interface/interface.py
index ac55633..d035ade 100644
--- a/src/zope/interface/interface.py
+++ b/src/zope/interface/interface.py
@@ -275,16 +275,10 @@ class InterfaceBase(NameAndModuleComparisonMixin, SpecificationBasePy):
"""Adapt an object to the interface
"""
try:
- conform = getattr(obj, '__conform__', None)
- # XXX: Ideally, we don't want to catch BaseException. Things
- # like MemoryError, KeyboardInterrupt, and other BaseExceptions should
- # get through. Here, and everywhere we have bare ``except:`` clauses.
- # The bare ``except:`` clauses exist for compatibility with the C code in
- # ``_zope_interface_coptimizations.c`` (``ib_call()`` in this case). Both
- # implementations would need to change. But beware of things like proxies and
- # Acquisition wrappers. See https://github.com/zopefoundation/zope.interface/issues/163
- except: # pylint:disable=bare-except
+ conform = obj.__conform__
+ except AttributeError:
conform = None
+
if conform is not None:
adapter = self._call_conform(conform)
if adapter is not None:
diff --git a/src/zope/interface/tests/__init__.py b/src/zope/interface/tests/__init__.py
index 8b9ef25..6a11218 100644
--- a/src/zope/interface/tests/__init__.py
+++ b/src/zope/interface/tests/__init__.py
@@ -32,6 +32,66 @@ class OptimizationTestMixin(object):
else:
self.assertIs(used, fallback)
+
+class MissingSomeAttrs(object):
+ """
+ Helper for tests that raises a specific exception
+ for attributes that are missing. This is usually not
+ an AttributeError, and this object is used to test that
+ those errors are not improperly caught and treated like
+ an AttributeError.
+ """
+
+ def __init__(self, exc_kind, **other_attrs):
+ self.__exc_kind = exc_kind
+ d = object.__getattribute__(self, '__dict__')
+ d.update(other_attrs)
+
+ def __getattribute__(self, name):
+ # Note that we ignore objects found in the class dictionary.
+ d = object.__getattribute__(self, '__dict__')
+ try:
+ return d[name]
+ except KeyError:
+ raise d['_MissingSomeAttrs__exc_kind'](name)
+
+ EXCEPTION_CLASSES = (
+ TypeError,
+ RuntimeError,
+ BaseException,
+ ValueError,
+ )
+
+ @classmethod
+ def test_raises(cls, unittest, test_func, expected_missing, **other_attrs):
+ """
+ Loop through various exceptions, calling *test_func* inside a ``assertRaises`` block.
+
+ :param test_func: A callable of one argument, the instance of this
+ class.
+ :param str expected_missing: The attribute that should fail with the exception.
+ This is used to ensure that we're testing the path we think we are.
+ :param other_attrs: Attributes that should be provided on the test object.
+ Must not contain *expected_missing*.
+ """
+ assert isinstance(expected_missing, str)
+ assert expected_missing not in other_attrs
+ for exc in cls.EXCEPTION_CLASSES:
+ ob = cls(exc, **other_attrs)
+ with unittest.assertRaises(exc) as ex:
+ test_func(ob)
+
+ unittest.assertEqual(ex.exception.args[0], expected_missing)
+
+ # Now test that the AttributeError for that expected_missing is *not* raised.
+ ob = cls(AttributeError, **other_attrs)
+ try:
+ test_func(ob)
+ except AttributeError as e:
+ unittest.assertNotIn(expected_missing, str(e))
+ except Exception: # pylint:disable=broad-except
+ pass
+
# Be sure cleanup functionality is available; classes that use the adapter hook
# need to be sure to subclass ``CleanUp``.
#
diff --git a/src/zope/interface/tests/test_adapter.py b/src/zope/interface/tests/test_adapter.py
index 0456b1c..869df66 100644
--- a/src/zope/interface/tests/test_adapter.py
+++ b/src/zope/interface/tests/test_adapter.py
@@ -17,21 +17,32 @@ 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
+ 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
@@ -51,7 +62,7 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self._extendors += (provided,)
def remove_extendor(self, provided):
self._extendors = tuple([x for x in self._extendors
- if x != provided])
+ if x != provided])
for name in BaseAdapterRegistry._delegated:
setattr(_CUT.LookupClass, name, object())
return _CUT
@@ -80,13 +91,14 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.assertEqual(registry._v_lookup._changed, (registry, orig,))
def test__generation_after_changing___bases__(self):
- class _Base(object): pass
+ class _Base(object):
+ pass
registry = self._makeOne()
registry.__bases__ = (_Base,)
self.assertEqual(registry._generation, 2)
def test_register(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ 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')
@@ -94,20 +106,20 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.assertEqual(registry._generation, 2)
def test_register_with_invalid_name(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ 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()
+ 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)
def test_register_with_same_value(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
registry = self._makeOne()
_value = object()
registry.register([None], IR0, '', _value)
@@ -120,7 +132,7 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.assertEqual(registry.registered([None], None, ''), None)
def test_registered_non_empty_miss(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ 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)
@@ -136,21 +148,21 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.assertEqual(registry.registered([None], None, ''), None)
def test_unregister_non_empty_miss_on_required(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ 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')
def test_unregister_non_empty_miss_on_name(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ 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')
def test_unregister_with_value_not_None_miss(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
registry = self._makeOne()
orig = object()
nomatch = object()
@@ -159,7 +171,7 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.assertTrue(registry.registered([IB1], None, '') is orig)
def test_unregister_hit_clears_empty_subcomponents(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
registry = self._makeOne()
one = object()
another = object()
@@ -177,7 +189,7 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.assertEqual(registry.registered([None], None, ''), None)
def test_unsubscribe_hit(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
registry = self._makeOne()
orig = object()
registry.subscribe([IB1], None, orig)
@@ -185,7 +197,7 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.assertEqual(len(registry._subscribers), 0)
def test_unsubscribe_after_multiple(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
registry = self._makeOne()
first = object()
second = object()
@@ -202,18 +214,18 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.assertEqual(len(registry._subscribers), 0)
def test_unsubscribe_w_None_after_multiple(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
registry = self._makeOne()
first = object()
second = object()
- third = object()
+
registry.subscribe([IB1], None, first)
registry.subscribe([IB1], None, second)
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()
+ IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
registry = self._makeOne()
registry.subscribe([IB1], None, 'A1')
self.assertEqual(len(registry._subscribers), 2)
@@ -221,7 +233,7 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.assertEqual(len(registry._subscribers), 2)
def test_unsubscribe_non_empty_miss_on_value(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
registry = self._makeOne()
registry.subscribe([IB1], None, 'A1')
self.assertEqual(len(registry._subscribers), 2)
@@ -229,7 +241,7 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.assertEqual(len(registry._subscribers), 2)
def test_unsubscribe_with_value_not_None_miss(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable
registry = self._makeOne()
orig = object()
nomatch = object()
@@ -241,7 +253,7 @@ class BaseAdapterRegistryTests(unittest.TestCase):
self.fail("Example method, not intended to be called.")
def test_unsubscribe_instance_method(self):
- IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces()
+ 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)
@@ -252,13 +264,14 @@ class BaseAdapterRegistryTests(unittest.TestCase):
class LookupBaseFallbackTests(unittest.TestCase):
def _getFallbackClass(self):
- from zope.interface.adapter import LookupBaseFallback
+ 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
@@ -285,7 +298,7 @@ class LookupBaseFallbackTests(unittest.TestCase):
_called_with = []
def _lookup(self, required, provided, name):
_called_with.append((required, provided, name))
- return None
+
lb = self._makeOne(uc_lookup=_lookup)
found = lb.lookup(('A',), 'B', 'C')
self.assertTrue(found is None)
@@ -296,7 +309,7 @@ class LookupBaseFallbackTests(unittest.TestCase):
_default = object()
def _lookup(self, required, provided, name):
_called_with.append((required, provided, name))
- return None
+
lb = self._makeOne(uc_lookup=_lookup)
found = lb.lookup(('A',), 'B', 'C', _default)
self.assertTrue(found is _default)
@@ -384,7 +397,7 @@ class LookupBaseFallbackTests(unittest.TestCase):
_called_with = []
def _lookup(self, required, provided, name):
_called_with.append((required, provided, name))
- return None
+
lb = self._makeOne(uc_lookup=_lookup)
found = lb.lookup1('A', 'B', 'C')
self.assertTrue(found is None)
@@ -395,7 +408,7 @@ class LookupBaseFallbackTests(unittest.TestCase):
_default = object()
def _lookup(self, required, provided, name):
_called_with.append((required, provided, name))
- return None
+
lb = self._makeOne(uc_lookup=_lookup)
found = lb.lookup1('A', 'B', 'C', _default)
self.assertTrue(found is _default)
@@ -406,7 +419,7 @@ class LookupBaseFallbackTests(unittest.TestCase):
_default = object()
def _lookup(self, required, provided, name):
_called_with.append((required, provided, name))
- return None
+
lb = self._makeOne(uc_lookup=_lookup)
found = lb.lookup1('A', 'B', 'C', _default)
self.assertTrue(found is _default)
@@ -479,7 +492,7 @@ class LookupBaseFallbackTests(unittest.TestCase):
_f_called_with = []
def _factory(context):
_f_called_with.append(context)
- return None
+
def _lookup(self, required, provided, name):
return _factory
req, prv, _default = object(), object(), object()
@@ -588,13 +601,14 @@ class LookupBaseTests(LookupBaseFallbackTests,
class VerifyingBaseFallbackTests(unittest.TestCase):
def _getFallbackClass(self):
- from zope.interface.adapter import VerifyingBaseFallback
+ 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()
@@ -641,7 +655,7 @@ class VerifyingBaseFallbackTests(unittest.TestCase):
found = lb.lookup(('A',), 'B', 'C')
self.assertTrue(found is b)
self.assertEqual(_called_with,
- [(('A',), 'B', 'C'), (('A',), 'B', 'C')])
+ [(('A',), 'B', 'C'), (('A',), 'B', 'C')])
self.assertEqual(_results, [c])
def test_lookup1(self):
@@ -662,7 +676,7 @@ class VerifyingBaseFallbackTests(unittest.TestCase):
found = lb.lookup1('A', 'B', 'C')
self.assertTrue(found is b)
self.assertEqual(_called_with,
- [(('A',), 'B', 'C'), (('A',), 'B', 'C')])
+ [(('A',), 'B', 'C'), (('A',), 'B', 'C')])
self.assertEqual(_results, [c])
def test_adapter_hook(self):
@@ -815,8 +829,7 @@ class AdapterLookupBaseTests(unittest.TestCase):
def __init__(self, here):
self._here = here
def __call__(self):
- if self._here:
- return self
+ return self if self._here else None
def unsubscribe(self, target):
self._unsub = target
gone = FauxWeakref(False)
@@ -939,7 +952,7 @@ class AdapterLookupBaseTests(unittest.TestCase):
IBar = InterfaceClass('IBar', IFoo)
registry = self._makeRegistry(IFoo, IBar)
subr = self._makeSubregistry()
- irrelevant = object()
+
wrongname = object()
subr._adapters = [ #utilities, single adapters
{},
@@ -1012,22 +1025,27 @@ class AdapterLookupBaseTests(unittest.TestCase):
self.assertTrue(result is _default)
def test_queryMultiAdapter_errors_on_attribute_access(self):
- # Which leads to using the _empty singleton as "requires"
- # argument. See https://github.com/zopefoundation/zope.interface/issues/162
+ # 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
- class UnexpectedErrorsLikeAcquisition(object):
- def __getattribute__(self, name):
- raise RuntimeError("Acquisition does this. Ha-ha!")
+ def test(ob):
+ return alb.queryMultiAdapter(
+ (ob,),
+ IFoo,
+ )
- result = alb.queryMultiAdapter(
- (UnexpectedErrorsLikeAcquisition(),),
- IFoo,
- )
+ PY3 = str is not bytes
+ MissingSomeAttrs.test_raises(self, test,
+ expected_missing='__class__' if PY3 else '__providedBy__')
def test_queryMultiAdaptor_factory_miss(self):
from zope.interface.declarations import implementer
@@ -1044,7 +1062,7 @@ class AdapterLookupBaseTests(unittest.TestCase):
_called_with = []
def _factory(context):
_called_with.append(context)
- return None
+
subr._adapters = [ #utilities, single adapters
{},
{IFoo: {IBar: {'': _factory}}},
@@ -1343,7 +1361,7 @@ class AdapterLookupBaseTests(unittest.TestCase):
return _exp2
def _side_effect_only(context):
_called.setdefault('_side_effect_only', []).append(context)
- return None
+
subr._subscribers = [ #utilities, single adapters
{},
{IFoo: {IBar: {'': (_factory1, _factory2, _side_effect_only)}}},
@@ -1441,13 +1459,12 @@ class Test_utils(unittest.TestCase):
self.assertTrue(_convert_None_to_Interface(other) is other)
def test__normalize_name_str(self):
- import sys
from zope.interface.adapter import _normalize_name
STR = b'str'
- if sys.version_info[0] < 3:
- self.assertEqual(_normalize_name(STR), unicode(STR))
- else:
- self.assertEqual(_normalize_name(STR), str(STR, 'ascii'))
+ UNICODE = u'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
diff --git a/src/zope/interface/tests/test_declarations.py b/src/zope/interface/tests/test_declarations.py
index 9344968..5d7272a 100644
--- a/src/zope/interface/tests/test_declarations.py
+++ b/src/zope/interface/tests/test_declarations.py
@@ -16,31 +16,36 @@
import unittest
from zope.interface._compat import _skip_under_py3k
+from zope.interface._compat import PYTHON3
from zope.interface.tests import OptimizationTestMixin
+from zope.interface.tests import MissingSomeAttrs
from zope.interface.tests.test_interface import NameAndModuleComparisonTestsMixin
+# pylint:disable=inherit-non-class,too-many-lines,protected-access
+# pylint:disable=blacklisted-name,attribute-defined-outside-init
class _Py3ClassAdvice(object):
def _run_generated_code(self, code, globs, locs,
fails_under_py3k=True,
):
+ # pylint:disable=exec-used,no-member
import warnings
- from zope.interface._compat import PYTHON3
with warnings.catch_warnings(record=True) as log:
warnings.resetwarnings()
if not PYTHON3:
exec(code, globs, locs)
self.assertEqual(len(log), 0) # no longer warn
return True
+
+ try:
+ exec(code, globs, locs)
+ except TypeError:
+ return False
else:
- try:
- exec(code, globs, locs)
- except TypeError:
- return False
- else:
- if fails_under_py3k:
- self.fail("Didn't raise TypeError")
+ if fails_under_py3k:
+ self.fail("Didn't raise TypeError")
+ return None
class NamedTests(unittest.TestCase):
@@ -52,7 +57,7 @@ class NamedTests(unittest.TestCase):
class Foo(object):
pass
- self.assertEqual(Foo.__component_name__, u'foo')
+ self.assertEqual(Foo.__component_name__, u'foo') # pylint:disable=no-member
def test_function(self):
from zope.interface.declarations import named
@@ -71,7 +76,7 @@ class NamedTests(unittest.TestCase):
foo = Foo()
named(u'foo')(foo)
- self.assertEqual(foo.__component_name__, u'foo')
+ self.assertEqual(foo.__component_name__, u'foo') # pylint:disable=no-member
class EmptyDeclarationTests(unittest.TestCase):
@@ -155,8 +160,6 @@ class DeclarationTests(EmptyDeclarationTests):
self.assertIsNone(decl._v_attrs)
def test___contains__w_self(self):
- from zope.interface.interface import InterfaceClass
- IFoo = InterfaceClass('IFoo')
decl = self._makeOne()
self.assertNotIn(decl, decl)
@@ -241,7 +244,7 @@ class DeclarationTests(EmptyDeclarationTests):
IBar = InterfaceClass('IBar')
before = self._makeOne(IFoo)
after = before - IBar
- self.assertIsInstance(after, self._getTargetClass())
+ self.assertIsInstance(after, self._getTargetClass())
self.assertEqual(list(after), [IFoo])
def test___sub___related_interface(self):
@@ -265,7 +268,7 @@ class DeclarationTests(EmptyDeclarationTests):
IBar = InterfaceClass('IBar')
before = self._makeOne(IFoo)
after = before + IBar
- self.assertIsInstance(after, self._getTargetClass())
+ self.assertIsInstance(after, self._getTargetClass())
self.assertEqual(list(after), [IFoo, IBar])
def test___add___related_interface(self):
@@ -379,7 +382,7 @@ class TestImplements(NameAndModuleComparisonTestsMixin,
self.assertEqual(implementedBy(A), implementedBy(A))
self.assertEqual(hash(implementedBy(A)), hash(implementedBy(A)))
self.assertTrue(implementedBy(A) < None)
- self.assertTrue(None > implementedBy(A))
+ self.assertTrue(None > implementedBy(A)) # pylint:disable=misplaced-comparison-constant
self.assertTrue(implementedBy(A) < implementedBy(B))
self.assertTrue(implementedBy(A) > IFoo)
self.assertTrue(implementedBy(A) <= implementedBy(B))
@@ -414,7 +417,7 @@ class TestImplements(NameAndModuleComparisonTestsMixin,
# The order of arguments to the operators matters,
# test both
- self.assertTrue(implementedByA == implementedByA)
+ self.assertTrue(implementedByA == implementedByA) # pylint:disable=comparison-with-itself
self.assertTrue(implementedByA != implementedByB)
self.assertTrue(implementedByB != implementedByA)
@@ -451,6 +454,7 @@ class TestImplements(NameAndModuleComparisonTestsMixin,
class Test_implementedByFallback(unittest.TestCase):
def _getTargetClass(self):
+ # pylint:disable=no-name-in-module
from zope.interface.declarations import implementedByFallback
return implementedByFallback
@@ -527,10 +531,10 @@ class Test_implementedByFallback(unittest.TestCase):
self.assertEqual(list(self._callFUT(dict)), [])
for typ in (tuple, list, dict):
spec = specs[typ]
- self.assertIsInstance(spec, Implements)
+ self.assertIsInstance(spec, Implements)
self.assertEqual(repr(spec),
- '<implementedBy %s.%s>'
- % (_BUILTINS, typ.__name__))
+ '<implementedBy %s.%s>'
+ % (_BUILTINS, typ.__name__))
def test_builtins_w_existing_cache(self):
from zope.interface import declarations
@@ -574,9 +578,9 @@ class Test_implementedByFallback(unittest.TestCase):
spec = self._callFUT(foo)
self.assertEqual(spec.__name__,
'zope.interface.tests.test_declarations.foo')
- self.assertTrue(spec.inherit is foo)
- self.assertTrue(foo.__implemented__ is spec)
- self.assertTrue(foo.__providedBy__ is objectSpecificationDescriptor)
+ self.assertIs(spec.inherit, foo)
+ self.assertIs(foo.__implemented__, spec)
+ self.assertIs(foo.__providedBy__, objectSpecificationDescriptor) # pylint:disable=no-member
self.assertNotIn('__provides__', foo.__dict__)
def test_w_None_no_bases_w_class(self):
@@ -586,11 +590,11 @@ class Test_implementedByFallback(unittest.TestCase):
spec = self._callFUT(Foo)
self.assertEqual(spec.__name__,
'zope.interface.tests.test_declarations.Foo')
- self.assertTrue(spec.inherit is Foo)
- self.assertTrue(Foo.__implemented__ is spec)
- self.assertIsInstance(Foo.__providedBy__, ClassProvides)
- self.assertIsInstance(Foo.__provides__, ClassProvides)
- self.assertEqual(Foo.__provides__, Foo.__providedBy__)
+ self.assertIs(spec.inherit, Foo)
+ self.assertIs(Foo.__implemented__, spec)
+ self.assertIsInstance(Foo.__providedBy__, ClassProvides) # pylint:disable=no-member
+ self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member
+ self.assertEqual(Foo.__provides__, Foo.__providedBy__) # pylint:disable=no-member
def test_w_existing_Implements(self):
from zope.interface.declarations import Implements
@@ -793,14 +797,14 @@ class Test_classImplementsOnly(unittest.TestCase):
pass
ifoo = InterfaceClass('IFoo')
self._callFUT(Foo, ifoo)
- spec = Foo.__implemented__
+ spec = Foo.__implemented__ # pylint:disable=no-member
self.assertEqual(spec.__name__,
'zope.interface.tests.test_declarations.Foo')
- self.assertTrue(spec.inherit is None)
- self.assertTrue(Foo.__implemented__ is spec)
- self.assertIsInstance(Foo.__providedBy__, ClassProvides)
- self.assertIsInstance(Foo.__provides__, ClassProvides)
- self.assertEqual(Foo.__provides__, Foo.__providedBy__)
+ self.assertIsNone(spec.inherit)
+ self.assertIs(Foo.__implemented__, spec) # pylint:disable=no-member
+ self.assertIsInstance(Foo.__providedBy__, ClassProvides) # pylint:disable=no-member
+ self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member
+ self.assertEqual(Foo.__provides__, Foo.__providedBy__) # pylint:disable=no-member
def test_w_existing_Implements(self):
from zope.interface.declarations import Implements
@@ -832,14 +836,14 @@ class Test_classImplements(unittest.TestCase):
pass
IFoo = InterfaceClass('IFoo')
self._callFUT(Foo, IFoo)
- spec = Foo.__implemented__
+ spec = Foo.__implemented__ # pylint:disable=no-member
self.assertEqual(spec.__name__,
'zope.interface.tests.test_declarations.Foo')
- self.assertTrue(spec.inherit is Foo)
- self.assertTrue(Foo.__implemented__ is spec)
- self.assertIsInstance(Foo.__providedBy__, ClassProvides)
- self.assertIsInstance(Foo.__provides__, ClassProvides)
- self.assertEqual(Foo.__provides__, Foo.__providedBy__)
+ self.assertIs(spec.inherit, Foo)
+ self.assertIs(Foo.__implemented__, spec) # pylint:disable=no-member
+ self.assertIsInstance(Foo.__providedBy__, ClassProvides) # pylint:disable=no-member
+ self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member
+ self.assertEqual(Foo.__provides__, Foo.__providedBy__) # pylint:disable=no-member
def test_w_existing_Implements(self):
from zope.interface.declarations import Implements
@@ -896,8 +900,8 @@ class Test__implements_advice(unittest.TestCase):
__implements_advice_data__ = ((IFoo,), classImplements)
self._callFUT(Foo)
self.assertNotIn('__implements_advice_data__', Foo.__dict__)
- self.assertIsInstance(Foo.__implemented__, Implements)
- self.assertEqual(list(Foo.__implemented__), [IFoo])
+ self.assertIsInstance(Foo.__implemented__, Implements) # pylint:disable=no-member
+ self.assertEqual(list(Foo.__implemented__), [IFoo]) # pylint:disable=no-member
class Test_implementer(unittest.TestCase):
@@ -919,14 +923,14 @@ class Test_implementer(unittest.TestCase):
decorator = self._makeOne(IFoo)
returned = decorator(Foo)
self.assertTrue(returned is Foo)
- spec = Foo.__implemented__
+ spec = Foo.__implemented__ # pylint:disable=no-member
self.assertEqual(spec.__name__,
'zope.interface.tests.test_declarations.Foo')
- self.assertTrue(spec.inherit is Foo)
- self.assertTrue(Foo.__implemented__ is spec)
- self.assertIsInstance(Foo.__providedBy__, ClassProvides)
- self.assertIsInstance(Foo.__provides__, ClassProvides)
- self.assertEqual(Foo.__provides__, Foo.__providedBy__)
+ self.assertIs(spec.inherit, Foo)
+ self.assertIs(Foo.__implemented__, spec) # pylint:disable=no-member
+ self.assertIsInstance(Foo.__providedBy__, ClassProvides) # pylint:disable=no-member
+ self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member
+ self.assertEqual(Foo.__provides__, Foo.__providedBy__) # pylint:disable=no-member
def test_newstyle_class(self):
from zope.interface.declarations import ClassProvides
@@ -937,14 +941,14 @@ class Test_implementer(unittest.TestCase):
decorator = self._makeOne(IFoo)
returned = decorator(Foo)
self.assertTrue(returned is Foo)
- spec = Foo.__implemented__
+ spec = Foo.__implemented__ # pylint:disable=no-member
self.assertEqual(spec.__name__,
'zope.interface.tests.test_declarations.Foo')
- self.assertTrue(spec.inherit is Foo)
- self.assertTrue(Foo.__implemented__ is spec)
- self.assertIsInstance(Foo.__providedBy__, ClassProvides)
- self.assertIsInstance(Foo.__provides__, ClassProvides)
- self.assertEqual(Foo.__provides__, Foo.__providedBy__)
+ self.assertIs(spec.inherit, Foo)
+ self.assertIs(Foo.__implemented__, spec) # pylint:disable=no-member
+ self.assertIsInstance(Foo.__providedBy__, ClassProvides) # pylint:disable=no-member
+ self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member
+ self.assertEqual(Foo.__provides__, Foo.__providedBy__) # pylint:disable=no-member
def test_nonclass_cannot_assign_attr(self):
from zope.interface.interface import InterfaceClass
@@ -961,10 +965,10 @@ class Test_implementer(unittest.TestCase):
decorator = self._makeOne(IFoo)
returned = decorator(foo)
self.assertTrue(returned is foo)
- spec = foo.__implemented__
+ spec = foo.__implemented__ # pylint:disable=no-member
self.assertEqual(spec.__name__, 'zope.interface.tests.test_declarations.?')
- self.assertTrue(spec.inherit is None)
- self.assertTrue(foo.__implemented__ is spec)
+ self.assertIsNone(spec.inherit,)
+ self.assertIs(foo.__implemented__, spec) # pylint:disable=no-member
class Test_implementer_only(unittest.TestCase):
@@ -989,7 +993,7 @@ class Test_implementer_only(unittest.TestCase):
IFoo = InterfaceClass('IFoo')
decorator = self._makeOne(IFoo)
class Bar:
- def _method():
+ def _method(self):
raise NotImplementedError()
self.assertRaises(ValueError, decorator, Bar._method)
@@ -1034,7 +1038,6 @@ class Test_implementsOnly(unittest.TestCase, _Py3ClassAdvice):
def test_simple(self):
import warnings
from zope.interface.declarations import implementsOnly
- from zope.interface._compat import PYTHON3
from zope.interface.interface import InterfaceClass
IFoo = InterfaceClass("IFoo")
globs = {'implementsOnly': implementsOnly,
@@ -1048,7 +1051,7 @@ class Test_implementsOnly(unittest.TestCase, _Py3ClassAdvice):
with warnings.catch_warnings(record=True) as log:
warnings.resetwarnings()
try:
- exec(CODE, globs, locs)
+ exec(CODE, globs, locs) # pylint:disable=exec-used
except TypeError:
self.assertTrue(PYTHON3, "Must be Python 3")
else:
@@ -1107,7 +1110,6 @@ class Test_implements(unittest.TestCase, _Py3ClassAdvice):
import warnings
from zope.interface.declarations import implements
from zope.interface.interface import InterfaceClass
- from zope.interface._compat import PYTHON3
IFoo = InterfaceClass("IFoo")
IBar = InterfaceClass("IBar")
globs = {'implements': implements, 'IFoo': IFoo, 'IBar': IBar}
@@ -1120,7 +1122,7 @@ class Test_implements(unittest.TestCase, _Py3ClassAdvice):
with warnings.catch_warnings(record=True) as log:
warnings.resetwarnings()
try:
- exec(CODE, globs, locs)
+ exec(CODE, globs, locs) # pylint:disable=exec-used
except TypeError:
if not PYTHON3:
self.assertEqual(len(log), 0) # no longer warn
@@ -1245,8 +1247,8 @@ class Test_directlyProvides(unittest.TestCase):
pass
obj = Foo()
self._callFUT(obj, IFoo)
- self.assertIsInstance(obj.__provides__, ProvidesClass)
- self.assertEqual(list(obj.__provides__), [IFoo])
+ self.assertIsInstance(obj.__provides__, ProvidesClass) # pylint:disable=no-member
+ self.assertEqual(list(obj.__provides__), [IFoo]) # pylint:disable=no-member
def test_w_class(self):
from zope.interface.declarations import ClassProvides
@@ -1255,8 +1257,8 @@ class Test_directlyProvides(unittest.TestCase):
class Foo(object):
pass
self._callFUT(Foo, IFoo)
- self.assertIsInstance(Foo.__provides__, ClassProvides)
- self.assertEqual(list(Foo.__provides__), [IFoo])
+ self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member
+ self.assertEqual(list(Foo.__provides__), [IFoo]) # pylint:disable=no-member
@_skip_under_py3k
def test_w_non_descriptor_aware_metaclass(self):
@@ -1292,7 +1294,7 @@ class Test_directlyProvides(unittest.TestCase):
the_dict[name] = value
obj = Foo()
self._callFUT(obj, IFoo)
- self.assertIsInstance(the_dict['__provides__'], ProvidesClass)
+ self.assertIsInstance(the_dict['__provides__'], ProvidesClass)
self.assertEqual(list(the_dict['__provides__']), [IFoo])
@@ -1310,8 +1312,8 @@ class Test_alsoProvides(unittest.TestCase):
pass
obj = Foo()
self._callFUT(obj, IFoo)
- self.assertIsInstance(obj.__provides__, ProvidesClass)
- self.assertEqual(list(obj.__provides__), [IFoo])
+ self.assertIsInstance(obj.__provides__, ProvidesClass) # pylint:disable=no-member
+ self.assertEqual(list(obj.__provides__), [IFoo]) # pylint:disable=no-member
def test_w_existing_provides(self):
from zope.interface.declarations import directlyProvides
@@ -1324,8 +1326,8 @@ class Test_alsoProvides(unittest.TestCase):
obj = Foo()
directlyProvides(obj, IFoo)
self._callFUT(obj, IBar)
- self.assertIsInstance(obj.__provides__, ProvidesClass)
- self.assertEqual(list(obj.__provides__), [IFoo, IBar])
+ self.assertIsInstance(obj.__provides__, ProvidesClass) # pylint:disable=no-member
+ self.assertEqual(list(obj.__provides__), [IFoo, IBar]) # pylint:disable=no-member
class Test_noLongerProvides(unittest.TestCase):
@@ -1341,7 +1343,7 @@ class Test_noLongerProvides(unittest.TestCase):
pass
obj = Foo()
self._callFUT(obj, IFoo)
- self.assertEqual(list(obj.__provides__), [])
+ self.assertEqual(list(obj.__provides__), []) # pylint:disable=no-member
def test_w_existing_provides_hit(self):
from zope.interface.declarations import directlyProvides
@@ -1352,7 +1354,7 @@ class Test_noLongerProvides(unittest.TestCase):
obj = Foo()
directlyProvides(obj, IFoo)
self._callFUT(obj, IFoo)
- self.assertEqual(list(obj.__provides__), [])
+ self.assertEqual(list(obj.__provides__), []) # pylint:disable=no-member
def test_w_existing_provides_miss(self):
from zope.interface.declarations import directlyProvides
@@ -1364,7 +1366,7 @@ class Test_noLongerProvides(unittest.TestCase):
obj = Foo()
directlyProvides(obj, IFoo)
self._callFUT(obj, IBar)
- self.assertEqual(list(obj.__provides__), [IFoo])
+ self.assertEqual(list(obj.__provides__), [IFoo]) # pylint:disable=no-member
def test_w_iface_implemented_by_class(self):
from zope.interface.declarations import implementer
@@ -1380,6 +1382,7 @@ class Test_noLongerProvides(unittest.TestCase):
class ClassProvidesBaseFallbackTests(unittest.TestCase):
def _getTargetClass(self):
+ # pylint:disable=no-name-in-module
from zope.interface.declarations import ClassProvidesBaseFallback
return ClassProvidesBaseFallback
@@ -1406,8 +1409,8 @@ class ClassProvidesBaseFallbackTests(unittest.TestCase):
class Foo(object):
pass
foo = Foo()
- cpbp = Foo.__provides__ = self._makeOne(Foo, IFoo)
- self.assertTrue(foo.__provides__ is IFoo)
+ Foo.__provides__ = self._makeOne(Foo, IFoo)
+ self.assertIs(foo.__provides__, IFoo)
def test_w_different_class(self):
from zope.interface.interface import InterfaceClass
@@ -1417,7 +1420,7 @@ class ClassProvidesBaseFallbackTests(unittest.TestCase):
class Bar(Foo):
pass
bar = Bar()
- cpbp = Foo.__provides__ = self._makeOne(Foo, IFoo)
+ Foo.__provides__ = self._makeOne(Foo, IFoo)
self.assertRaises(AttributeError, getattr, Bar, '__provides__')
self.assertRaises(AttributeError, getattr, bar, '__provides__')
@@ -1431,6 +1434,7 @@ class ClassProvidesBaseTests(OptimizationTestMixin,
return ClassProvidesBase
def _getFallbackClass(self):
+ # pylint:disable=no-name-in-module
from zope.interface.declarations import ClassProvidesBaseFallback
return ClassProvidesBaseFallback
@@ -1522,12 +1526,12 @@ class Test_directlyProvidedBy(unittest.TestCase):
class Test_classProvides(unittest.TestCase, _Py3ClassAdvice):
+ # pylint:disable=exec-used
def test_called_from_function(self):
import warnings
from zope.interface.declarations import classProvides
from zope.interface.interface import InterfaceClass
- from zope.interface._compat import PYTHON3
IFoo = InterfaceClass("IFoo")
globs = {'classProvides': classProvides, 'IFoo': IFoo}
locs = {}
@@ -1547,7 +1551,6 @@ class Test_classProvides(unittest.TestCase, _Py3ClassAdvice):
import warnings
from zope.interface.declarations import classProvides
from zope.interface.interface import InterfaceClass
- from zope.interface._compat import PYTHON3
IFoo = InterfaceClass("IFoo")
IBar = InterfaceClass("IBar")
globs = {'classProvides': classProvides, 'IFoo': IFoo, 'IBar': IBar}
@@ -1601,11 +1604,12 @@ class Test_provider(unittest.TestCase):
@self._makeOne(IFoo)
class Foo(object):
pass
- self.assertIsInstance(Foo.__provides__, ClassProvides)
- self.assertEqual(list(Foo.__provides__), [IFoo])
+ self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member
+ self.assertEqual(list(Foo.__provides__), [IFoo]) # pylint:disable=no-member
class Test_moduleProvides(unittest.TestCase):
+ # pylint:disable=exec-used
def test_called_from_function(self):
from zope.interface.declarations import moduleProvides
@@ -1655,7 +1659,7 @@ class Test_moduleProvides(unittest.TestCase):
IFoo = InterfaceClass("IFoo")
globs = {'__name__': 'zope.interface.tests.foo',
'moduleProvides': moduleProvides, 'IFoo': IFoo}
- locs = {}
+
CODE = "\n".join([
'moduleProvides(IFoo)',
'moduleProvides(IFoo)',
@@ -1667,6 +1671,7 @@ class Test_moduleProvides(unittest.TestCase):
class Test_getObjectSpecificationFallback(unittest.TestCase):
def _getFallbackClass(self):
+ # pylint:disable=no-name-in-module
from zope.interface.declarations import getObjectSpecificationFallback
return getObjectSpecificationFallback
@@ -1700,7 +1705,7 @@ class Test_getObjectSpecificationFallback(unittest.TestCase):
raise NotImplementedError()
directlyProvides(foo, IFoo)
spec = self._callFUT(foo)
- self.assertTrue(spec is foo.__provides__)
+ self.assertIs(spec, foo.__provides__) # pylint:disable=no-member
def test_existing_provides_is_not_spec(self):
def foo():
@@ -1738,6 +1743,38 @@ class Test_getObjectSpecificationFallback(unittest.TestCase):
spec = self._callFUT(foo)
self.assertEqual(list(spec), [])
+ def test_catches_only_AttributeError_on_provides(self):
+ MissingSomeAttrs.test_raises(self, self._callFUT, expected_missing='__provides__')
+
+ def test_catches_only_AttributeError_on_class(self):
+ MissingSomeAttrs.test_raises(self, self._callFUT, expected_missing='__class__',
+ __provides__=None)
+
+ def test_raises_AttributeError_when_provides_fails_type_check_AttributeError(self):
+ # isinstance(ob.__provides__, SpecificationBase) is not
+ # protected inside any kind of block.
+
+ class Foo(object):
+ __provides__ = MissingSomeAttrs(AttributeError)
+
+ # isinstance() ignores AttributeError on __class__
+ self._callFUT(Foo())
+
+ def test_raises_AttributeError_when_provides_fails_type_check_RuntimeError(self):
+ # isinstance(ob.__provides__, SpecificationBase) is not
+ # protected inside any kind of block.
+ class Foo(object):
+ __provides__ = MissingSomeAttrs(RuntimeError)
+
+ if PYTHON3:
+ with self.assertRaises(RuntimeError) as exc:
+ self._callFUT(Foo())
+
+ self.assertEqual('__class__', exc.exception.args[0])
+ else:
+ # Python 2 catches everything.
+ self._callFUT(Foo())
+
class Test_getObjectSpecification(Test_getObjectSpecificationFallback,
OptimizationTestMixin):
@@ -1751,6 +1788,7 @@ class Test_getObjectSpecification(Test_getObjectSpecificationFallback,
class Test_providedByFallback(unittest.TestCase):
def _getFallbackClass(self):
+ # pylint:disable=no-name-in-module
from zope.interface.declarations import providedByFallback
return providedByFallback
@@ -1978,6 +2016,19 @@ class Test_providedByFallback(unittest.TestCase):
self.assertEqual(list(self._callFUT(sm2)),
[IBase])
+ def test_catches_only_AttributeError_on_providedBy(self):
+ MissingSomeAttrs.test_raises(self, self._callFUT,
+ expected_missing='__providedBy__',
+ __class__=object)
+
+ def test_catches_only_AttributeError_on_class(self):
+ # isinstance() tries to get the __class__, which is non-obvious,
+ # so it must be protected too.
+ PY3 = str is not bytes
+ MissingSomeAttrs.test_raises(self, self._callFUT,
+ expected_missing='__class__' if PY3 else '__providedBy__')
+
+
class Test_providedBy(Test_providedByFallback,
OptimizationTestMixin):
@@ -1991,6 +2042,7 @@ class Test_providedBy(Test_providedByFallback,
class ObjectSpecificationDescriptorFallbackTests(unittest.TestCase):
def _getFallbackClass(self):
+ # pylint:disable=no-name-in-module
from zope.interface.declarations \
import ObjectSpecificationDescriptorFallback
return ObjectSpecificationDescriptorFallback
@@ -2059,7 +2111,7 @@ class _Monkey(object):
# context-manager for replacing module names in the scope of a test.
def __init__(self, module, **kw):
self.module = module
- self.to_restore = dict([(key, getattr(module, key)) for key in kw])
+ self.to_restore = {key: getattr(module, key) for key in kw}
for key, value in kw.items():
setattr(module, key, value)
diff --git a/src/zope/interface/tests/test_interface.py b/src/zope/interface/tests/test_interface.py
index 7b7b7e8..2100340 100644
--- a/src/zope/interface/tests/test_interface.py
+++ b/src/zope/interface/tests/test_interface.py
@@ -24,6 +24,7 @@
import unittest
from zope.interface._compat import _skip_under_py3k
+from zope.interface.tests import MissingSomeAttrs
from zope.interface.tests import OptimizationTestMixin
from zope.interface.tests import CleanUp
@@ -396,8 +397,13 @@ class InterfaceBaseTestsMixin(NameAndModuleComparisonTestsMixin):
def test___call___w___conform___ob_no_provides_wo_alternate(self):
ib = self._makeOne(False)
- adapted = object()
- self.assertRaises(TypeError, ib, adapted)
+ with self.assertRaises(TypeError) as exc:
+ ib(object())
+
+ self.assertIn('Could not adapt', str(exc.exception))
+
+ def test___call___w_no_conform_catches_only_AttributeError(self):
+ MissingSomeAttrs.test_raises(self, self._makeOne(), expected_missing='__conform__')
class InterfaceBaseTests(InterfaceBaseTestsMixin,