From edece6949e2cc0e39f5b6052e3b11ebb3484797f Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Sun, 22 Mar 2020 07:37:37 -0500 Subject: Ensure consistent IRO for all objects. This requires switching some tests to the plain unittest runner right now because of cyclic dependencies. Fixes #49 --- .travis.yml | 29 +++++++++++++++++++++++------ CHANGES.rst | 7 ++++++- setup.py | 2 +- src/zope/configuration/_compat.py | 24 ++++++++++++++++++++++++ src/zope/configuration/config.py | 3 ++- src/zope/configuration/fields.py | 15 ++++++++------- tox.ini | 14 ++++++++++++-- 7 files changed, 76 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05ad82f..3c93078 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,36 @@ language: python sudo: false + +env: + global: + ZOPE_INTERFACE_STRICT_IRO: 1 + python: - 2.7 - - 3.4 - 3.5 - 3.6 + - 3.7 + - 3.8 - pypy - pypy3 -matrix: +jobs: include: - - python: "3.7" - dist: xenial - sudo: true + - name: Documentation + python: 3.8 + install: + - pip install -U -e .[docs] + env: ZOPE_INTERFACE_STRICT_IRO=0 + script: + - sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html + - sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest + after_success: + + script: - - coverage run -m zope.testrunner --test-path=src --auto-color --auto-progress +# Temporary work around. Avoid zope.testrunner +# pending https://github.com/zopefoundation/zope.security/issues/71 +# due to cyclic dependency. + - coverage run -m unittest discover -s src after_success: - coveralls diff --git a/CHANGES.rst b/CHANGES.rst index ad1b358..b083fb9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,12 @@ Changes 4.4.0 (unreleased) ------------------ -- Nothing changed yet. +- Ensure a consistent interface resolution order for all objects. See + `issue 49 `_. + +- Drop support for Python 3.4. + +- Add support for Python 3.8. 4.3.1 (2019-02-12) diff --git a/setup.py b/setup.py index 90a1c03..003eeaf 100644 --- a/setup.py +++ b/setup.py @@ -54,10 +54,10 @@ setup(name='zope.configuration', "Programming Language :: Python :: 2", 'Programming Language :: Python :: 2.7', "Programming Language :: Python :: 3", - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Natural Language :: English', diff --git a/src/zope/configuration/_compat.py b/src/zope/configuration/_compat.py index a7d8286..e8b1b51 100644 --- a/src/zope/configuration/_compat.py +++ b/src/zope/configuration/_compat.py @@ -42,3 +42,27 @@ else: # pragma: no cover def reraise(tp, value, tb=None): raise tp, value, tb """) + + +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. In this package, we could easily + # statically determine to elide the relevant interfaces, but + # this is a defense against changes in parent classes and lessens + # the testing burden. + 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/configuration/config.py b/src/zope/configuration/config.py index 0123021..30a7566 100644 --- a/src/zope/configuration/config.py +++ b/src/zope/configuration/config.py @@ -37,6 +37,7 @@ from zope.configuration._compat import builtins from zope.configuration._compat import reraise from zope.configuration._compat import string_types from zope.configuration._compat import text_type +from zope.configuration._compat import implementer_if_needed __all__ = [ 'ConfigurationContext', @@ -897,7 +898,7 @@ class RootStackItem(object): def finish(self): pass -@implementer(IStackItem) +@implementer_if_needed(IStackItem) class GroupingStackItem(RootStackItem): """ Stack item for a grouping directive diff --git a/src/zope/configuration/fields.py b/src/zope/configuration/fields.py index c4af0ad..4a83103 100644 --- a/src/zope/configuration/fields.py +++ b/src/zope/configuration/fields.py @@ -31,6 +31,7 @@ from zope.schema.interfaces import InvalidValue from zope.configuration.exceptions import ConfigurationError from zope.configuration.interfaces import InvalidToken +from zope.configuration._compat import implementer_if_needed __all__ = [ 'Bool', @@ -91,7 +92,7 @@ class PythonIdentifier(schema_PythonIdentifier): raise ValidationError(value).with_field_and_value(self, value) -@implementer(IFromUnicode) +@implementer_if_needed(IFromUnicode) class GlobalObject(Field): """ An object that can be accessed as a module global. @@ -176,7 +177,7 @@ class GlobalObject(Field): return value -@implementer(IFromUnicode) +@implementer_if_needed(IFromUnicode) class GlobalInterface(GlobalObject): """ An interface that can be accessed from a module. @@ -297,7 +298,7 @@ class PathProcessor(object): return filename, True -@implementer(IFromUnicode) +@implementer_if_needed(IFromUnicode) class Path(Text): """ A file path name, which may be input as a relative path @@ -376,7 +377,7 @@ class Path(Text): return filename -@implementer(IFromUnicode) +@implementer_if_needed(IFromUnicode) class Bool(schema_Bool): """ A boolean value. @@ -428,7 +429,7 @@ class Bool(schema_Bool): -@implementer(IFromUnicode) +@implementer_if_needed(IFromUnicode) class MessageID(Text): """ Text string that should be translated. @@ -439,7 +440,7 @@ class MessageID(Text): __factories = {} - def fromUnicode(self, u): + def fromUnicode(self, value): """ Translate a string to a MessageID. @@ -539,7 +540,7 @@ class MessageID(Text): enc = sys.getfilesystemencoding() or sys.getdefaultencoding() domain = domain.decode(enc) - v = super(MessageID, self).fromUnicode(u) + v = super(MessageID, self).fromUnicode(value) # Check whether there is an explicit message is specified default = None diff --git a/tox.ini b/tox.ini index 7d1984e..6f373e6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,15 @@ [tox] envlist = - py27,py34,py35,py36,py37,pypy,pypy3,coverage,docs + py27,py35,py36,py37,pypy,pypy3,coverage,docs [testenv] +usedevelop = true deps = .[test] commands = - zope-testrunner --test-path=src --all [] + python -m unittest discover -s src +setenv = + ZOPE_INTERFACE_STRICT_IRO = 1 [testenv:coverage] usedevelop = true @@ -18,6 +21,10 @@ commands = deps = {[testenv]deps} coverage +# Disabling STRICT IRO is temporary. +setenv = + ZOPE_INTERFACE_STRICT_IRO = 0 + [testenv:docs] basepython = @@ -29,3 +36,6 @@ deps = {[testenv]deps} Sphinx repoze.sphinx.autointerface +# Disabling STRICT IRO is temporary. +setenv = + ZOPE_INTERFACE_STRICT_IRO = 0 -- cgit v1.2.1