summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2020-03-22 07:37:37 -0500
committerJason Madden <jamadden@gmail.com>2020-03-22 07:37:37 -0500
commitedece6949e2cc0e39f5b6052e3b11ebb3484797f (patch)
tree36b6552a03e6ab071884626050b2d488b74aea91
parent0194dd6143049d319774dd56b53ef21384c6ef27 (diff)
downloadzope-configuration-edece6949e2cc0e39f5b6052e3b11ebb3484797f.tar.gz
Ensure consistent IRO for all objects.issue49
This requires switching some tests to the plain unittest runner right now because of cyclic dependencies. Fixes #49
-rw-r--r--.travis.yml29
-rw-r--r--CHANGES.rst7
-rw-r--r--setup.py2
-rw-r--r--src/zope/configuration/_compat.py24
-rw-r--r--src/zope/configuration/config.py3
-rw-r--r--src/zope/configuration/fields.py15
-rw-r--r--tox.ini14
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 <https://github.com/zopefoundation/zope.configuration/issues/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