parent
a404e5fed4
commit
c500360aab
|
@ -45,6 +45,12 @@
|
|||
|
||||
See `issue 3 <https://github.com/zopefoundation/zope.interface/issues/3>`_.
|
||||
|
||||
- Make the internal singleton object returned by APIs like
|
||||
``implementedBy`` and ``directlyProvidedBy`` for objects that
|
||||
implement or provide no interfaces more immutable. Previously an
|
||||
internal cache could be mutated. See `issue 204
|
||||
<https://github.com/zopefoundation/zope.interface/issues/204>`_.
|
||||
|
||||
5.0.2 (2020-03-30)
|
||||
==================
|
||||
|
||||
|
|
|
@ -195,6 +195,18 @@ class _ImmutableDeclaration(Declaration):
|
|||
# object, and that includes a method.)
|
||||
return _ImmutableDeclaration
|
||||
|
||||
@property
|
||||
def _v_attrs(self):
|
||||
# _v_attrs is not a public, documented property, but some client
|
||||
# code uses it anyway as a convenient place to cache things. To keep
|
||||
# the empty declaration truly immutable, we must ignore that. That includes
|
||||
# ignoring assignments as well.
|
||||
return {}
|
||||
|
||||
@_v_attrs.setter
|
||||
def _v_attrs(self, new_attrs):
|
||||
pass
|
||||
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
|
|
|
@ -122,6 +122,20 @@ class EmptyDeclarationTests(unittest.TestCase):
|
|||
decl = self._getEmpty()
|
||||
self.assertEqual(decl.__iro__, (Interface,))
|
||||
|
||||
def test_get(self):
|
||||
decl = self._getEmpty()
|
||||
self.assertIsNone(decl.get('attr'))
|
||||
self.assertEqual(decl.get('abc', 'def'), 'def')
|
||||
# It's a positive cache only (when it even exists)
|
||||
# so this added nothing.
|
||||
self.assertFalse(decl._v_attrs)
|
||||
|
||||
def test_changed_w_existing__v_attrs(self):
|
||||
decl = self._getEmpty()
|
||||
decl._v_attrs = object()
|
||||
decl.changed(decl)
|
||||
self.assertFalse(decl._v_attrs)
|
||||
|
||||
|
||||
class DeclarationTests(EmptyDeclarationTests):
|
||||
|
||||
|
@ -153,12 +167,6 @@ class DeclarationTests(EmptyDeclarationTests):
|
|||
decl.changed(decl) # doesn't raise
|
||||
self.assertIsNone(decl._v_attrs)
|
||||
|
||||
def test_changed_w_existing__v_attrs(self):
|
||||
decl = self._makeOne()
|
||||
decl._v_attrs = object()
|
||||
decl.changed(decl)
|
||||
self.assertIsNone(decl._v_attrs)
|
||||
|
||||
def test___contains__w_self(self):
|
||||
decl = self._makeOne()
|
||||
self.assertNotIn(decl, decl)
|
||||
|
@ -335,6 +343,19 @@ class TestImmutableDeclaration(EmptyDeclarationTests):
|
|||
self.assertIsNone(self._getEmpty().get('name'))
|
||||
self.assertEqual(self._getEmpty().get('name', 42), 42)
|
||||
|
||||
def test_v_attrs(self):
|
||||
decl = self._getEmpty()
|
||||
self.assertEqual(decl._v_attrs, {})
|
||||
|
||||
decl._v_attrs['attr'] = 42
|
||||
self.assertEqual(decl._v_attrs, {})
|
||||
self.assertIsNone(decl.get('attr'))
|
||||
|
||||
attrs = decl._v_attrs = {}
|
||||
attrs['attr'] = 42
|
||||
self.assertEqual(decl._v_attrs, {})
|
||||
self.assertIsNone(decl.get('attr'))
|
||||
|
||||
|
||||
class TestImplements(NameAndModuleComparisonTestsMixin,
|
||||
unittest.TestCase):
|
||||
|
|
Loading…
Reference in New Issue