diff options
author | Jason Madden <jamadden@gmail.com> | 2020-03-23 08:15:47 -0500 |
---|---|---|
committer | Jason Madden <jamadden@gmail.com> | 2020-03-23 08:15:47 -0500 |
commit | 027ce52cc45d066ba25809473ba21beb2f8f1bf3 (patch) | |
tree | 47d82b9df69f716cfb911c7f119b69082f533d16 /src/zope | |
parent | 4382cde773a27d7699c74ad0cda3a9268da9e302 (diff) | |
download | zope-security-027ce52cc45d066ba25809473ba21beb2f8f1bf3.tar.gz |
Ensure all objects have consistent interface resolution orders.
Fixes #71
Diffstat (limited to 'src/zope')
-rw-r--r-- | src/zope/security/_compat.py | 23 | ||||
-rw-r--r-- | src/zope/security/checker.py | 3 | ||||
-rw-r--r-- | src/zope/security/zcml.py | 9 |
3 files changed, 29 insertions, 6 deletions
diff --git a/src/zope/security/_compat.py b/src/zope/security/_compat.py index 84ac98d..07b3a02 100644 --- a/src/zope/security/_compat.py +++ b/src/zope/security/_compat.py @@ -39,4 +39,25 @@ else: # pragma: no cover PYTHON2 = False -_BLANK = u'' +class implementer_if_needed(object): + # Helper to make sure we don't redundantly implement interfaces + # already inherited. Doing so tends to produce problems with the + # C3 order. Even though here we could easily statically determine + # if we need the interface or not, this is used for clarity, to + # reduce the testing load, and to insulate against changes in + # super classes. + def __init__(self, *ifaces): + self._ifaces = ifaces + + def __call__(self, cls): + from zope.interface import implementedBy + from zope.interface import implementer + + ifaces_needed = [] + implemented = implementedBy(cls) + ifaces_needed = [ + iface + for iface in self._ifaces + if not implemented.isOrExtends(iface) + ] + return implementer(*ifaces_needed)(cls) diff --git a/src/zope/security/checker.py b/src/zope/security/checker.py index 8fbf3ec..2dbc4e1 100644 --- a/src/zope/security/checker.py +++ b/src/zope/security/checker.py @@ -82,6 +82,7 @@ from zope.security._definitions import thread_local from zope.security._compat import CLASS_TYPES from zope.security._compat import PYTHON2 from zope.security._compat import PURE_PYTHON +from zope.security._compat import implementer_if_needed from zope.security.proxy import Proxy from zope.security.proxy import getChecker @@ -503,7 +504,7 @@ if _c_available: _getChecker = _checkers.get -@implementer(IChecker) +@implementer_if_needed(IChecker) class CombinedChecker(Checker): """A checker that combines two other checkers in a logical-or fashion. diff --git a/src/zope/security/zcml.py b/src/zope/security/zcml.py index 52bcf91..473c06e 100644 --- a/src/zope/security/zcml.py +++ b/src/zope/security/zcml.py @@ -18,21 +18,22 @@ __docformat__ = 'restructuredtext' from zope.configuration.fields import GlobalObject from zope.configuration.fields import MessageID from zope.interface import Interface -from zope.interface import implementer from zope.schema import Id from zope.schema.interfaces import IFromUnicode +from zope.security._compat import implementer_if_needed from zope.security.permission import checkPermission from zope.security.management import setSecurityPolicy from zope.security.interfaces import PUBLIC_PERMISSION_NAME as zope_Public -@implementer(IFromUnicode) + +@implementer_if_needed(IFromUnicode) class Permission(Id): r"""This field describes a permission. """ - def fromUnicode(self, u): - u = super(Permission, self).fromUnicode(u) + def fromUnicode(self, value): + u = super(Permission, self).fromUnicode(value) map = getattr(self.context, 'permission_mapping', {}) return map.get(u, u) |