Repository: zopefoundation/zope.interface (language: python)

Issue:
Tests in Sphinx documentation fail with `ZOPE_INTERFACE_STRICT_IRO=1`
### What I did:

In tox.ini I changed the `setenv` parameter of `[testenv:docs]` from `ZOPE_INTERFACE_STRICT_IRO=0` to `ZOPE_INTERFACE_STRICT_IRO=1`

### What I expect to happen:

`tox -edocs` runs fine without any failures.

### What actually happened:

```python
$ tox -edocs
docs develop-inst-noop: .../zope.interface
docs installed: alabaster==0.7.12,Babel==2.9.0,certifi==2020.12.5,chardet==4.0.0,coverage==5.5,docutils==0.16,idna==2.10,imagesize==1.2.0,Jinja2==2.11.3,MarkupSafe==1.1.1,packaging==20.9,Pygments==2.8.1,pyparsing==2.4.7,pytz==2021.1,repoze.sphinx.autointerface==0.8,requests==2.25.1,snowballstemmer==2.1.0,Sphinx==3.5.4,sphinxcontrib-applehelp==1.0.2,sphinxcontrib-devhelp==1.0.2,sphinxcontrib-htmlhelp==1.0.3,sphinxcontrib-jsmath==1.0.1,sphinxcontrib-qthelp==1.0.3,sphinxcontrib-serializinghtml==1.1.4,urllib3==1.26.4,zope.event==4.5.0,-e git+https://github.com/zopefoundation/zope.interface@7928bb337425c4a905eabf8697001abc634ff788#egg=zope.interface,zope.testing==4.9
docs run-test-pre: PYTHONHASHSEED='3058271445'
docs run-test: commands[0] | sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
Sphinx v3.5.4 in Verwendung
loading pickled environment... erledigt
building [mo]: targets for 0 po files that are out of date
building [html]: targets for 0 source files that are out of date
updating environment: 0 added, 0 changed, 0 removed
looking for now-outdated files... none found
no targets are out of date.
build abgeschlossen.

The HTML pages are in docs/_build/html.
docs run-test: commands[1] | coverage run -p -m sphinx -b doctest -d docs/_build/doctrees docs docs/_build/doctest
Sphinx v3.5.4 in Verwendung
loading pickled environment... erledigt
building [mo]: targets for 0 po files that are out of date
building [doctest]: targets for 18 source files that are out of date
updating environment: 0 added, 0 changed, 0 removed
looking for now-outdated files... none found
running tests...

Document: verify
----------------
**********************************************************************
File "verify.rst", line 89, in default
Failed example:
    classImplements(Foo, IFoo)
Exception raised:
    Traceback (most recent call last):
      File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/doctest.py", line 1337, in __run
        compileflags, 1), test.globs)
      File "<doctest default[1]>", line 1, in <module>
        classImplements(Foo, IFoo)
      File ".../zope.interface/src/zope/interface/declarations.py", line 569, in classImplements
        _classImplements_ordered(spec, tuple(before), tuple(after))
      File ".../zope.interface/src/zope/interface/declarations.py", line 624, in _classImplements_ordered
        spec.__bases__ = tuple(bases)
      File ".../zope.interface/src/zope/interface/interface.py", line 409, in __setBases
        self.changed(self)
      File ".../zope.interface/src/zope/interface/declarations.py", line 338, in changed
        return super(Implements, self).changed(originally_changed)
      File ".../zope.interface/src/zope/interface/interface.py", line 505, in changed
        dependent.changed(originally_changed)
      File ".../zope.interface/src/zope/interface/interface.py", line 492, in changed
        ancestors = self._calculate_sro()
      File ".../zope.interface/src/zope/interface/interface.py", line 460, in _calculate_sro
        for b in self.__bases__
      File ".../zope.interface/src/zope/interface/ro.py", line 624, in ro
        mro = resolver.mro()
      File ".../zope.interface/src/zope/interface/ro.py", line 440, in mro
        self.__mro = tuple(self._merge())
      File ".../zope.interface/src/zope/interface/ro.py", line 431, in _merge
        base = self._choose_next_base(base_tree_remaining)
      File ".../zope.interface/src/zope/interface/ro.py", line 353, in _choose_next_base
        return self._guess_next_base(base_tree_remaining)
      File ".../zope.interface/src/zope/interface/ro.py", line 447, in _guess_next_base
        raise InconsistentResolutionOrderError(self, base_tree_remaining)
    zope.interface.ro.InconsistentResolutionOrderError: InconsistentResolutionOrderError: For object directlyProvides(Foo, IFoo, classImplements(Foo, IFoo)).
    Base ROs:
    {<InterfaceClass module.IFoo>: [<InterfaceClass module.IFoo>,
                                    <InterfaceClass base.IBase>,
                                    <InterfaceClass zope.interface.Interface>],
     classImplements(Foo, IFoo): [classImplements(Foo, IFoo),
                                  <InterfaceClass module.IFoo>,
                                  <InterfaceClass base.IBase>,
                                  classImplements(object),
                                  <InterfaceClass zope.interface.Interface>]}
    Conflict Location:
    [[<InterfaceClass module.IFoo>,
      <InterfaceClass base.IBase>,
      <InterfaceClass zope.interface.Interface>],
     [classImplements(Foo, IFoo),
      <InterfaceClass module.IFoo>,
      <InterfaceClass base.IBase>,
      classImplements(object),
      <InterfaceClass zope.interface.Interface>],
     [<InterfaceClass module.IFoo>, classImplements(Foo, IFoo)]]
**********************************************************************
1 items had failures:
   1 of  74 in default
74 tests in 1 items.
73 passed and 1 failed.
***Test Failed*** 1 failures.

Document: README
----------------
1 items passed all tests:
 218 tests in default
218 tests in 1 items.
218 passed and 0 failed.
Test passed.

Document: foodforthought
------------------------
1 items passed all tests:
  25 tests in default
25 tests in 1 items.
25 passed and 0 failed.
Test passed.

Document: human
---------------
1 items passed all tests:
  18 tests in default
18 tests in 1 items.
18 passed and 0 failed.
Test passed.

Document: api/declarations
--------------------------
**********************************************************************
File "api/declarations.rst", line 745, in default
Failed example:
    spec -= Declaration(I1, I2)
Exception raised:
    Traceback (most recent call last):
      File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/doctest.py", line 1337, in __run
        compileflags, 1), test.globs)
      File "<doctest default[9]>", line 1, in <module>
        spec -= Declaration(I1, I2)
      File ".../zope.interface/src/zope/interface/declarations.py", line 87, in __init__
        Specification.__init__(self, _normalizeargs(bases))
      File ".../zope.interface/src/zope/interface/interface.py", line 376, in __init__
        self.__bases__ = tuple(bases)
      File ".../zope.interface/src/zope/interface/interface.py", line 409, in __setBases
        self.changed(self)
      File ".../zope.interface/src/zope/interface/interface.py", line 492, in changed
        ancestors = self._calculate_sro()
      File ".../zope.interface/src/zope/interface/interface.py", line 460, in _calculate_sro
        for b in self.__bases__
      File ".../zope.interface/src/zope/interface/ro.py", line 624, in ro
        mro = resolver.mro()
      File ".../zope.interface/src/zope/interface/ro.py", line 440, in mro
        self.__mro = tuple(self._merge())
      File ".../zope.interface/src/zope/interface/ro.py", line 431, in _merge
        base = self._choose_next_base(base_tree_remaining)
      File ".../zope.interface/src/zope/interface/ro.py", line 353, in _choose_next_base
        return self._guess_next_base(base_tree_remaining)
      File ".../zope.interface/src/zope/interface/ro.py", line 447, in _guess_next_base
        raise InconsistentResolutionOrderError(self, base_tree_remaining)
    zope.interface.ro.InconsistentResolutionOrderError: InconsistentResolutionOrderError: For object <zope.interface.declarations.Declaration object at 0x112b41980>.
    Base ROs:
    {<InterfaceClass builtins.I1>: [<InterfaceClass builtins.I1>,
                                    <InterfaceClass zope.interface.Interface>],
     <InterfaceClass builtins.I2>: [<InterfaceClass builtins.I2>,
                                    <InterfaceClass builtins.I1>,
                                    <InterfaceClass zope.interface.Interface>]}
    Conflict Location:
    [[<InterfaceClass builtins.I1>, <InterfaceClass zope.interface.Interface>],
     [<InterfaceClass builtins.I2>,
      <InterfaceClass builtins.I1>,
      <InterfaceClass zope.interface.Interface>],
     [<InterfaceClass builtins.I1>, <InterfaceClass builtins.I2>]]
**********************************************************************
File "api/declarations.rst", line 755, in default
Failed example:
    [iface.getName() for iface
     in spec - Declaration(I3, I4)]
Exception raised:
    Traceback (most recent call last):
      File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/doctest.py", line 1337, in __run
        compileflags, 1), test.globs)
      File "<doctest default[15]>", line 2, in <module>
        in spec - Declaration(I3, I4)]
      File ".../zope.interface/src/zope/interface/declarations.py", line 87, in __init__
        Specification.__init__(self, _normalizeargs(bases))
      File ".../zope.interface/src/zope/interface/interface.py", line 376, in __init__
        self.__bases__ = tuple(bases)
      File ".../zope.interface/src/zope/interface/interface.py", line 409, in __setBases
        self.changed(self)
      File ".../zope.interface/src/zope/interface/interface.py", line 492, in changed
        ancestors = self._calculate_sro()
      File ".../zope.interface/src/zope/interface/interface.py", line 460, in _calculate_sro
        for b in self.__bases__
      File ".../zope.interface/src/zope/interface/ro.py", line 624, in ro
        mro = resolver.mro()
      File ".../zope.interface/src/zope/interface/ro.py", line 440, in mro
        self.__mro = tuple(self._merge())
      File ".../zope.interface/src/zope/interface/ro.py", line 431, in _merge
        base = self._choose_next_base(base_tree_remaining)
      File ".../zope.interface/src/zope/interface/ro.py", line 353, in _choose_next_base
        return self._guess_next_base(base_tree_remaining)
      File ".../zope.interface/src/zope/interface/ro.py", line 447, in _guess_next_base
        raise InconsistentResolutionOrderError(self, base_tree_remaining)
    zope.interface.ro.InconsistentResolutionOrderError: InconsistentResolutionOrderError: For object <zope.interface.declarations.Declaration object at 0x1129b99f0>.
    Base ROs:
    {<InterfaceClass builtins.I3>: [<InterfaceClass builtins.I3>,
                                    <InterfaceClass zope.interface.Interface>],
     <InterfaceClass builtins.I4>: [<InterfaceClass builtins.I4>,
                                    <InterfaceClass builtins.I3>,
                                    <InterfaceClass zope.interface.Interface>]}
    Conflict Location:
    [[<InterfaceClass builtins.I3>, <InterfaceClass zope.interface.Interface>],
     [<InterfaceClass builtins.I4>,
      <InterfaceClass builtins.I3>,
      <InterfaceClass zope.interface.Interface>],
     [<InterfaceClass builtins.I3>, <InterfaceClass builtins.I4>]]
**********************************************************************
File "api/declarations.rst", line 785, in default
Failed example:
    spec2 += Declaration(IRoot2, IDerived2)
Exception raised:
    Traceback (most recent call last):
      File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/doctest.py", line 1337, in __run
        compileflags, 1), test.globs)
      File "<doctest default[13]>", line 1, in <module>
        spec2 += Declaration(IRoot2, IDerived2)
      File ".../zope.interface/src/zope/interface/declarations.py", line 87, in __init__
        Specification.__init__(self, _normalizeargs(bases))
      File ".../zope.interface/src/zope/interface/interface.py", line 376, in __init__
        self.__bases__ = tuple(bases)
      File ".../zope.interface/src/zope/interface/interface.py", line 409, in __setBases
        self.changed(self)
      File ".../zope.interface/src/zope/interface/interface.py", line 492, in changed
        ancestors = self._calculate_sro()
      File ".../zope.interface/src/zope/interface/interface.py", line 460, in _calculate_sro
        for b in self.__bases__
      File ".../zope.interface/src/zope/interface/ro.py", line 624, in ro
        mro = resolver.mro()
      File ".../zope.interface/src/zope/interface/ro.py", line 440, in mro
        self.__mro = tuple(self._merge())
      File ".../zope.interface/src/zope/interface/ro.py", line 431, in _merge
        base = self._choose_next_base(base_tree_remaining)
      File ".../zope.interface/src/zope/interface/ro.py", line 353, in _choose_next_base
        return self._guess_next_base(base_tree_remaining)
      File ".../zope.interface/src/zope/interface/ro.py", line 447, in _guess_next_base
        raise InconsistentResolutionOrderError(self, base_tree_remaining)
    zope.interface.ro.InconsistentResolutionOrderError: InconsistentResolutionOrderError: For object <zope.interface.declarations.Declaration object at 0x1129b9b40>.
    Base ROs:
    {<InterfaceClass builtins.IDerived2>: [<InterfaceClass builtins.IDerived2>,
                                           <InterfaceClass builtins.IRoot2>,
                                           <InterfaceClass zope.interface.Interface>],
     <InterfaceClass builtins.IRoot2>: [<InterfaceClass builtins.IRoot2>,
                                        <InterfaceClass zope.interface.Interface>]}
    Conflict Location:
    [[<InterfaceClass builtins.IRoot2>, <InterfaceClass zope.interface.Interface>],
     [<InterfaceClass builtins.IDerived2>,
      <InterfaceClass builtins.IRoot2>,
      <InterfaceClass zope.interface.Interface>],
     [<InterfaceClass builtins.IRoot2>, <InterfaceClass builtins.IDerived2>]]
**********************************************************************
File "api/declarations.rst", line 786, in default
Failed example:
    [iface.getName() for iface in spec2]
Expected:
    ['IDerived2', 'IRoot2']
Got:
    []
**********************************************************************
File "api/declarations.rst", line 788, in default
Failed example:
    [iface.getName() for iface in spec+spec2]
Expected:
    ['IRoot1', 'IDerived2', 'IRoot2']
Got:
    ['IRoot1']
**********************************************************************
File "api/declarations.rst", line 790, in default
Failed example:
    [iface.getName() for iface in spec2+spec]
Expected:
    ['IDerived2', 'IRoot2', 'IRoot1']
Got:
    ['IRoot1']
**********************************************************************
File "api/declarations.rst", line 792, in default
Failed example:
    [iface.getName() for iface in (spec+spec2).__bases__]
Expected:
    ['IRoot1', 'IDerived2', 'IRoot2']
Got:
    ['IRoot1']
**********************************************************************
File "api/declarations.rst", line 794, in default
Failed example:
    [iface.getName() for iface in (spec2+spec).__bases__]
Expected:
    ['IDerived2', 'IRoot2', 'IRoot1']
Got:
    ['IRoot1']
**********************************************************************
1 items had failures:
   8 of 302 in default
302 tests in 1 items.
294 passed and 8 failed.
***Test Failed*** 8 failures.

Document: adapter
-----------------
1 items passed all tests:
 164 tests in default
164 tests in 1 items.
164 passed and 0 failed.
Test passed.

Document: api/specifications
----------------------------
1 items passed all tests:
 109 tests in default
109 tests in 1 items.
109 passed and 0 failed.
Test passed.

Doctest summary
===============
  910 tests
    9 failures in tests
    0 failures in setup code
    0 failures in cleanup code
build mit Problemen beendet.
ERROR: InvocationError for command .../zope.interface/.tox/docs/bin/coverage run -p -m sphinx -b doctest -d docs/_build/doctrees docs docs/_build/doctest (exited with code 1)
_________________________________________________________________________ summary __________________________________________________________________________
ERROR:   docs: commands failed
```
### What version of Python and Zope/Addons I am using:

* Python 3.7.10
* latest version of `master` of this repository



Discussion / hints:

Fix patch:
diff --git a/docs/api/declarations.rst b/docs/api/declarations.rst
index 4327c721..57c11f0c 100644
--- a/docs/api/declarations.rst
+++ b/docs/api/declarations.rst
@@ -744,7 +744,7 @@ Exmples for :meth:`Declaration.__sub__`:
    >>> spec -= I1
    >>> [iface.getName() for iface in spec]
    []
-   >>> spec -= Declaration(I1, I2)
+   >>> spec -= Declaration(I2)
    >>> [iface.getName() for iface in spec]
    []
    >>> spec = Declaration(I2, I4)
@@ -755,7 +755,7 @@ Exmples for :meth:`Declaration.__sub__`:
    >>> [iface.getName() for iface in spec - I1]
    ['I4']
    >>> [iface.getName() for iface
-   ...  in spec - Declaration(I3, I4)]
+   ...  in spec - Declaration(I4)]
    ['I2']
 
 Exmples for :meth:`Declaration.__add__`:
@@ -784,7 +784,7 @@ Exmples for :meth:`Declaration.__add__`:
    ['IRoot1']
    >>> [iface.getName() for iface in spec2]
    []
-   >>> spec2 += Declaration(IRoot2, IDerived2)
+   >>> spec2 += Declaration(IDerived2, IRoot2)
    >>> [iface.getName() for iface in spec2]
    ['IDerived2', 'IRoot2']
    >>> [iface.getName() for iface in spec+spec2]
diff --git a/docs/verify.rst b/docs/verify.rst
index 6a313cd4..bc1be1c6 100644
--- a/docs/verify.rst
+++ b/docs/verify.rst
@@ -83,11 +83,17 @@ desired interface.
    True
 
 If all instances will provide the interface, we can
-mark the class as implementing it.
+mark a class as implementing it. But we have to remove the interface from the
+instance first so a consistent interface resolution order can be achieved.
+(Calling ``gc.collect()`` is also necessary because we use weakrefs.)
 
 .. doctest::
 
    >>> from zope.interface import classImplements
+   >>> from zope.interface import noLongerProvides
+   >>> import gc
+   >>> noLongerProvides(foo, IFoo)
+   >>> _ = gc.collect()
    >>> classImplements(Foo, IFoo)
    >>> verify_foo()
    True
diff --git a/tox.ini b/tox.ini
index dbdebffe..3394e792 100644
--- a/tox.ini
+++ b/tox.ini
@@ -65,5 +65,3 @@ commands =
 deps =
     Sphinx
     repoze.sphinx.autointerface
-setenv =
-    ZOPE_INTERFACE_STRICT_IRO=0


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index f11865b7..f0f41776 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -283,7 +283,7 @@ jobs:
           pip install -U  "`ls dist/zope.interface-*.whl`[docs]"
       - name: Build docs
         env:
-          ZOPE_INTERFACE_STRICT_IRO: 0
+          ZOPE_INTERFACE_STRICT_IRO: 1
         run: |
           sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
           sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest