Skip to content

Commit

Permalink
Breaking cyclic requirements on zope.security.
Browse files Browse the repository at this point in the history
- Use unittest runner directly (remove unneeded test_suite() methods). The same number of tests get run, but coverage will go down since the security-relevant tests just get skipped.
- Catch import errors due to inconsistent IROs and trigger test skips.
  • Loading branch information
jamadden committed Mar 23, 2020
1 parent de12a4c commit f517cee
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 95 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Expand Up @@ -26,7 +26,10 @@ install:
- if [[ -z "$MINIMAL" ]]; then pip install -U -e ".[test,docs]"; fi

script:
- coverage run -m zope.testrunner --test-path=src
# Temporary workaround. Pending
# https://github.com/zopefoundation/zope.security/issues/71
# avoid zope.testrunner.
- coverage run -m unittest -s src
- if [[ -z "$MINIMAL" ]]; then sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html; fi
- if [[ -z "$MINIMAL" ]]; then coverage run -a -m sphinx -b doctest -d docs/_build/doctrees docs docs/_build/doctest; fi

Expand Down
24 changes: 22 additions & 2 deletions docs/conf.py
Expand Up @@ -12,11 +12,26 @@
# serve to show the default.

import sys, os
# We get better doc strings and signatures from the Python components.
# XXX: Except CPython 2.7 produces some warnings from CFFI with persistent 4.6.3:
# From callback for ffi.gc <cdata 'struct CPersistentRingCFFI_struct *' owning 24 bytes>:
# Traceback (most recent call last):
# File "//.tox/docs/lib/python2.7/site-packages/persistent/picklecache.py", line 105, in cleanup_hook
# oid = self._addr_to_oid.pop(cdata.pobj_id, None)
# AttributeError: '_WeakValueDictionary' object has no attribute '_addr_to_oid'
#os.environ['PURE_PYTHON'] = "1"

# Prior to https://github.com/zopefoundation/zope.security/issues/71,
# zope.security cannot be imported in zope.interface's strict IRO mode.
os.environ['ZOPE_INTERFACE_STRICT_IRO'] = "0"

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.append(os.path.abspath('../src'))
import pkg_resources
rqmt = pkg_resources.require('zope.component')[0]

# -- General configuration -----------------------------------------------------

Expand Down Expand Up @@ -54,9 +69,9 @@
# built documents.
#
# The short X.Y version.
version = '4.3'
version = '%s.%s' % tuple(map(int, rqmt.version.split('.')[:2]))
# The full version, including alpha/beta/rc tags.
release = '4.3'
release = rqmt.version

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down Expand Up @@ -246,3 +261,8 @@

# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
intersphinx_mapping = {
'https://docs.python.org/': None,
'https://zopeinterface.readthedocs.io/': None,
'https://zopesecurity.readthedocs.io/': None,
}
16 changes: 16 additions & 0 deletions src/zope/component/_compat.py
Expand Up @@ -32,3 +32,19 @@

PYTHON3 = True
PYTHON2 = False


# Prior to https://github.com/zopefoundation/zope.security/issues/71
# zope.security cannot be imported if zope.interface is enforcing
# strict resolution orders. But because zope.security has a dependency
# on this library, and older versions of this library also have problems
# with strict resolution orders, we have a chicken-and-egg scenario. In the
# interim, our only choice is to skip it. (But we don't want a hard dependency
# on zope.interface 5.0, so we do a conditional import.)
ZOPE_SECURITY_NOT_AVAILABLE_EX = (ImportError,)
try:
from zope.interface.ro import InconsistentResolutionOrderError
except ImportError:
pass
else:
ZOPE_SECURITY_NOT_AVAILABLE_EX += (InconsistentResolutionOrderError,)
4 changes: 3 additions & 1 deletion src/zope/component/hooks.py
Expand Up @@ -18,9 +18,11 @@
import contextlib
import threading

from zope.component._compat import ZOPE_SECURITY_NOT_AVAILABLE_EX

try:
from zope.security.proxy import removeSecurityProxy
except ImportError: #pragma NO COVER
except ZOPE_SECURITY_NOT_AVAILABLE_EX: # pragma: no cover
def removeSecurityProxy(x):
return x

Expand Down
34 changes: 25 additions & 9 deletions src/zope/component/security.py
Expand Up @@ -16,13 +16,29 @@
from zope.interface import providedBy
from zope.proxy import ProxyBase
from zope.proxy import getProxiedObject
from zope.security.adapter import LocatingTrustedAdapterFactory
from zope.security.adapter import LocatingUntrustedAdapterFactory
from zope.security.adapter import TrustedAdapterFactory
from zope.security.checker import Checker
from zope.security.checker import CheckerPublic
from zope.security.checker import InterfaceChecker
from zope.security.proxy import Proxy

from zope.component._compat import ZOPE_SECURITY_NOT_AVAILABLE_EX

try:
from zope.security.adapter import LocatingTrustedAdapterFactory
from zope.security.adapter import LocatingUntrustedAdapterFactory
from zope.security.adapter import TrustedAdapterFactory
from zope.security.checker import Checker
from zope.security.checker import CheckerPublic
from zope.security.checker import InterfaceChecker
from zope.security.proxy import Proxy
except ZOPE_SECURITY_NOT_AVAILABLE_EX: # pragma: no cover
def _no_security(*args, **kw):
raise TypeError(
"security proxied components are not "
"supported because zope.security is not available")

LocatingTrustedAdapterFactory = _no_security
LocatingUntrustedAdapterFactory = _no_security
TrustedAdapterFactory = _no_security
Checker = _no_security
CheckerPublic = _no_security
InterfaceChecker = _no_security


PublicPermission = 'zope.Public'
Expand All @@ -42,7 +58,7 @@ def _checker(_context, permission, allowed_interface, allowed_attributes):
if permission == PublicPermission:
permission = CheckerPublic

require={}
require = {}
if allowed_attributes:
for name in allowed_attributes:
require[name] = permission
Expand All @@ -63,7 +79,7 @@ def proxify(ob, checker=None, provides=None, permission=None):
if checker is None:
if provides is None or permission is None:
raise ValueError('Required arguments: '
'checker or both provides and permissions')
'checker or both provides and permissions')
if permission == PublicPermission:
permission = CheckerPublic
checker = InterfaceChecker(provides, permission)
Expand Down
4 changes: 3 additions & 1 deletion src/zope/component/tests/__init__.py
@@ -1,9 +1,11 @@
import unittest

def skipIfNoSecurity(testfunc):
from zope.component._compat import ZOPE_SECURITY_NOT_AVAILABLE_EX

try:
import zope.security
except ImportError: # pragma: no cover
except ZOPE_SECURITY_NOT_AVAILABLE_EX: # pragma: no cover
return unittest.skip("zope.security not installed")(testfunc)
return testfunc

Expand Down
8 changes: 0 additions & 8 deletions src/zope/component/tests/test___init__.py
Expand Up @@ -82,11 +82,3 @@ def __init__(self, context):
adapted = IFoo(ctx)
self.assertTrue(adapted.__class__ is Baz)
self.assertTrue(adapted.context is ctx)



def test_suite():
return unittest.TestSuite((
unittest.makeSuite(Test_package),
unittest.makeSuite(Test_Interface_call),
))
8 changes: 0 additions & 8 deletions src/zope/component/tests/test_event.py
Expand Up @@ -55,11 +55,3 @@ def __init__(self, object):
event = _ObjectEvent(context)
objectEventNotify(event)
self.assertEqual(_adapted, [(context, event)])



def test_suite():
return unittest.TestSuite((
unittest.makeSuite(Test_dispatch),
unittest.makeSuite(Test_objectEventNotify),
))
14 changes: 0 additions & 14 deletions src/zope/component/tests/test_hooks.py
Expand Up @@ -336,17 +336,3 @@ def __enter__(self):
def __exit__(self, exc_type, exc_val, exc_tb):
for key, value in self.to_restore.items():
setattr(self.module, key, value)


def test_suite():
return unittest.TestSuite((
unittest.makeSuite(Test_read_property),
unittest.makeSuite(SiteInfoTests),
unittest.makeSuite(Test_setSite),
unittest.makeSuite(Test_getSite),
unittest.makeSuite(Test_site),
unittest.makeSuite(Test_getSiteManager),
unittest.makeSuite(Test_adapter_hook),
unittest.makeSuite(Test_setHooks),
unittest.makeSuite(Test_resetHooks),
))
14 changes: 0 additions & 14 deletions src/zope/component/tests/test_interface.py
Expand Up @@ -380,17 +380,3 @@ class IFoo(Interface):
gsm.registerUtility(IFoo, IInterface, 'foo')
self.assertEqual(self._callFUT(object(), IFoo),
'zope.component.tests.test_interface.IFoo')


def test_suite():
return unittest.TestSuite((
unittest.makeSuite(Test_provideInterface),
unittest.makeSuite(Test_getInterface),
unittest.makeSuite(Test_queryInterface),
unittest.makeSuite(Test_searchInterface),
unittest.makeSuite(Test_searchInterfaceIds),
unittest.makeSuite(Test_searchInterfaceUtilities),
unittest.makeSuite(Test_getInterfaceAllDocs),
unittest.makeSuite(Test_nameToInterface),
unittest.makeSuite(Test_interfaceToName),
))
14 changes: 8 additions & 6 deletions src/zope/component/zcml.py
Expand Up @@ -26,16 +26,18 @@
from zope.schema import TextLine

from zope.component._api import getSiteManager
from zope.component._compat import ZOPE_SECURITY_NOT_AVAILABLE_EX
from zope.component._declaration import adaptedBy, getName
from zope.component.interface import provideInterface

try:
from zope.security.zcml import Permission
except ImportError: #pragma NO COVER
except ZOPE_SECURITY_NOT_AVAILABLE_EX: # pragma: no cover
def _no_security(*args, **kw):
raise ConfigurationError("security proxied components are not "
raise ConfigurationError(
"security proxied components are not "
"supported because zope.security is not available")
_checker = proxify = protectedFactory = security =_no_security
_checker = proxify = protectedFactory = security = _no_security
Permission = TextLine
else:
from zope.component.security import _checker
Expand Down Expand Up @@ -104,9 +106,9 @@ class IAdapterDirective(Interface):
description=_("This should be a list of interfaces or classes"),
required=False,
value_type=GlobalObject(
missing_value=object(),
),
)
missing_value=object(),
),
)

permission = Permission(
title=_("Permission"),
Expand Down
44 changes: 13 additions & 31 deletions tox.ini
Expand Up @@ -13,39 +13,19 @@ deps =
.[test]

[testenv]
usedevelop = true
deps =
{[fulldeps]deps}
!minimal: {[fulldeps]deps}
minimal: {[mindeps]deps}
.[docs]
commands =
zope-testrunner --test-path=src
sphinx-build -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest
# Temporary workaround. Avoid zope.testrunner pending
# IRO fixes in zope.security. https://github.com/zopefoundation/zope.security/issues/71
python -m unittest discover -s src
!minimal: sphinx-build -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest
setenv =
ZOPE_INTERFACE_STRICT_IRO = 1

[testenv:py27-minimal]
basepython =
python2.7
deps =
{[mindeps]deps}
commands =
zope-testrunner --test-path=src


[testenv:py35-pure]
basepython =
python3.5
setenv =
{[testenv]setenv}
PURE_PYTHON = 1
PIP_CACHE_DIR = {envdir}/.cache

[testenv:py27-pure]
basepython =
python3.4
setenv =
{[testenv]setenv}
PURE_PYTHON = 1
PIP_CACHE_DIR = {envdir}/.cache
pure: PURE_PYTHON = 1

[testenv:docs]
basepython =
Expand All @@ -56,14 +36,16 @@ commands =
deps =
{[fulldeps]deps}
.[docs]
setenv =
ZOPE_INTERFACE_STRICT_IRO = 0

[testenv:coverage]
basepython =
python3.6
python3
usedevelop = true
commands =
coverage run -m zope.testrunner --test-path=src
coverage run -a -m sphinx.cmd.build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
coverage run -m unittest discover -s src
coverage run -a -m sphinx -b doctest -d docs/_build/doctrees docs docs/_build/doctest
coverage report --fail-under=100
deps =
{[testenv]deps}
Expand Down

0 comments on commit f517cee

Please sign in to comment.