summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jason+github@nextthought.com>2017-09-07 11:25:32 -0500
committerGitHub <noreply@github.com>2017-09-07 11:25:32 -0500
commitf2de4625c116085404958724468899dbe784bce6 (patch)
treee12221ad7347584b0504a8bc3cca84d8000053a2
parent125c9a2818ebcf35a76d65c2f1dbe5c08531f21e (diff)
parentc7722e5f3df4ad25558693073c30004e217a2b33 (diff)
downloadzope-security-f2de4625c116085404958724468899dbe784bce6.tar.gz
Merge pull request #30 from zopefoundation/coveralls_and_doctests
Switch to modern 'coverage' environment and run doctests on all versions; fix Travis builds
-rw-r--r--.coveragerc12
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml30
-rw-r--r--CHANGES.rst5
-rw-r--r--README.rst4
-rw-r--r--appveyor.yml2
-rw-r--r--docs/api/checker.rst269
-rw-r--r--docs/api/decorator.rst6
-rw-r--r--docs/api/permission.rst32
-rw-r--r--setup.py15
-rw-r--r--src/zope/security/proxy.py7
-rw-r--r--tox.ini29
12 files changed, 219 insertions, 193 deletions
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..bb46267
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,12 @@
+[run]
+source = zope.security
+
+[report]
+precision = 2
+exclude_lines =
+ pragma: no cover
+ pragma NO COVER
+ if __name__ == '__main__':
+ raise NotImplementedError
+ self.fail
+ raise AssertionError
diff --git a/.gitignore b/.gitignore
index 25c3255..095537c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@ parts
nosetests.xml
coverage.xml
.eggs
+htmlcov/
diff --git a/.travis.yml b/.travis.yml
index c5b8cc2..c38e00c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,25 +2,25 @@ language: python
sudo: false
matrix:
include:
+ - python: 2.7
+ - python: 3.4
- python: 3.5
- env: TOXENV=py35
- python: 3.6
- env: TOXENV=py36
-env:
- - TOXENV=py27
- - TOXENV=py27-pure
- - TOXENV=py33
- - TOXENV=py33-pure
- - TOXENV=py34
- - TOXENV=pypy
- - TOXENV=coverage
- - TOXENV=docs
+ - python: pypy
+ - python: pypy3
+ - python: 2.7
+ env: PURE_PYTHON=1
+ - python: 3.4
+ env: PURE_PYTHON=1
install:
- - pip install -U pip
- - pip install -U setuptools
- - pip install -U tox
+ - pip install -U pip setuptools
+ - pip install -U coveralls coverage
+ - pip install -U -e .[test,docs]
script:
- - tox
+ - coverage run -m zope.testrunner --test-path=src
+ - coverage run -a -m sphinx -b doctest -d docs/_build/doctrees docs docs/_build/doctest
+after_success:
+ - coveralls
notifications:
email: false
cache: pip
diff --git a/CHANGES.rst b/CHANGES.rst
index 1533b06..c264be8 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -13,6 +13,11 @@ Changes
- Fix ``__length_hint__`` of proxied iterator objects. Previously it
was ignored.
+- Drop support for Python 3.3.
+
+- Enable coveralls.io for coverage measurement and run doctests on all
+ supported Python versions.
+
4.1.1 (2017-05-17)
------------------
diff --git a/README.rst b/README.rst
index b03d0b7..8c04f93 100644
--- a/README.rst
+++ b/README.rst
@@ -12,5 +12,9 @@
:target: http://zopesecurity.readthedocs.org/en/latest/
:alt: Documentation Status
+.. image:: https://coveralls.io/repos/github/zopefoundation/zope.security/badge.svg?branch=master
+ :target: https://coveralls.io/github/zopefoundation/zope.security?branch=master
+
+
The Security framework provides a generic mechanism to implement security
policies on Python objects.
diff --git a/appveyor.yml b/appveyor.yml
index 567e1a5..bfa97eb 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -3,8 +3,6 @@ environment:
matrix:
- python : 27
- python : 27-x64
- - python : 33
- - python : 33-x64
- python : 34
- python : 34-x64
- python : 35
diff --git a/docs/api/checker.rst b/docs/api/checker.rst
index 58d3595..419fe5e 100644
--- a/docs/api/checker.rst
+++ b/docs/api/checker.rst
@@ -45,7 +45,7 @@ name and permission:
>>> cdict = checker.get_permissions
>>> pprint(cdict)
{'foo': 'zope.security.metaconfigure.test'}
-
+
If we define additional names, they will be added to the dict:
>>> protectModule(test_zcml_functest, 'bar', TEST_PERM)
@@ -54,7 +54,7 @@ name and permission:
{'bar': 'zope.security.metaconfigure.test',
'baz': 'zope.security.metaconfigure.test',
'foo': 'zope.security.metaconfigure.test'}
-
+
The allow directive creates actions for each named defined
directly, or via interface:
@@ -209,43 +209,43 @@ Rocks are immuatle, non-callable objects without interesting methods. They
.. doctest::
- >>> int(type(ProxyFactory( object() )) is object)
- 1
- >>> int(type(ProxyFactory( 1 )) is int)
- 1
- >>> int(type(ProxyFactory( 1.0 )) is float)
- 1
- >>> int(type(ProxyFactory( 1j )) is complex)
- 1
- >>> int(type(ProxyFactory( None )) is type(None))
- 1
- >>> int(type(ProxyFactory( 'xxx' )) is str)
- 1
- >>> int(type(ProxyFactory( True )) is type(True))
- 1
+ >>> type(ProxyFactory( object() )) is object
+ True
+ >>> type(ProxyFactory( 1 )) is int
+ True
+ >>> type(ProxyFactory( 1.0 )) is float
+ True
+ >>> type(ProxyFactory( 1j )) is complex
+ True
+ >>> type(ProxyFactory( None )) is type(None)
+ True
+ >>> type(ProxyFactory( 'xxx' )) is str
+ True
+ >>> type(ProxyFactory( True )) is type(True)
+ True
Datetime-reltatd instances are rocks, too:
.. doctest::
>>> from datetime import timedelta, datetime, date, time, tzinfo
- >>> int(type(ProxyFactory( timedelta(1) )) is timedelta)
- 1
- >>> int(type(ProxyFactory( datetime(2000, 1, 1) )) is datetime)
- 1
- >>> int(type(ProxyFactory( date(2000, 1, 1) )) is date)
- 1
- >>> int(type(ProxyFactory( time() )) is time)
- 1
- >>> int(type(ProxyFactory( tzinfo() )) is tzinfo)
- 1
+ >>> type(ProxyFactory( timedelta(1) )) is timedelta
+ True
+ >>> type(ProxyFactory( datetime(2000, 1, 1) )) is datetime
+ True
+ >>> type(ProxyFactory( date(2000, 1, 1) )) is date
+ True
+ >>> type(ProxyFactory( time() )) is time
+ True
+ >>> type(ProxyFactory( tzinfo() )) is tzinfo
+ True
>>> try:
... from pytz import UTC
... except ImportError: # pytz checker only if pytz is present.
- ... 1
+ ... True
... else:
- ... int(type(ProxyFactory( UTC )) is type(UTC))
- 1
+ ... type(ProxyFactory( UTC )) is type(UTC)
+ True
dicts
@@ -268,15 +268,15 @@ We can do everything we expect to be able to do with proxied dicts.
['a', 'b']
>>> d.get('a')
1
- >>> int('a' in d)
- 1
+ >>> 'a' in d
+ True
>>> c = d.copy()
>>> check_forbidden_get(c, 'clear')
'ForbiddenAttribute: clear'
- >>> int(str(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}"))
- 1
- >>> int(repr(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}"))
- 1
+ >>> str(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")
+ True
+ >>> repr(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")
+ True
>>> def sorted(x):
... x = list(x)
... x.sort()
@@ -293,12 +293,12 @@ not checking that under python > 2):
.. doctest::
- >>> int(d != d)
- 0
- >>> int(bool(d))
- 1
- >>> int(d.__class__ == dict)
- 1
+ >>> d != d
+ False
+ >>> bool(d)
+ True
+ >>> d.__class__ == dict
+ True
lists
#####
@@ -322,8 +322,8 @@ We can do everything we expect to be able to do with proxied lists.
2
>>> tuple(l)
(1, 2)
- >>> int(1 in l)
- 1
+ >>> 1 in l
+ True
>>> l.index(2)
1
>>> l.count(2)
@@ -339,22 +339,22 @@ Always available:
.. doctest::
- >>> int(l < l)
- 0
- >>> int(l > l)
- 0
- >>> int(l <= l)
- 1
- >>> int(l >= l)
- 1
- >>> int(l == l)
- 1
- >>> int(l != l)
- 0
- >>> int(bool(l))
- 1
- >>> int(l.__class__ == list)
- 1
+ >>> l < l
+ False
+ >>> l > l
+ False
+ >>> l <= l
+ True
+ >>> l >= l
+ True
+ >>> l == l
+ True
+ >>> l != l
+ False
+ >>> bool(l)
+ True
+ >>> l.__class__ == list
+ True
tuples
######
@@ -373,8 +373,8 @@ We can do everything we expect to be able to do with proxied tuples.
2
>>> list(l)
[1, 2]
- >>> int(1 in l)
- 1
+ >>> 1 in l
+ True
>>> str(l)
'(1, 2)'
>>> repr(l)
@@ -386,22 +386,22 @@ Always available:
.. doctest::
- >>> int(l < l)
- 0
- >>> int(l > l)
- 0
- >>> int(l <= l)
- 1
- >>> int(l >= l)
- 1
- >>> int(l == l)
- 1
- >>> int(l != l)
- 0
- >>> int(bool(l))
- 1
- >>> int(l.__class__ == tuple)
- 1
+ >>> l < l
+ False
+ >>> l > l
+ False
+ >>> l <= l
+ True
+ >>> l >= l
+ True
+ >>> l == l
+ True
+ >>> l != l
+ False
+ >>> bool(l)
+ True
+ >>> l.__class__ == tuple
+ True
sets
####
@@ -543,7 +543,7 @@ we can do everything we expect to be able to do with proxied sets.
>>> str(s) == str(us)
True
-
+
>>> repr(s) == repr(us)
True
@@ -565,7 +565,7 @@ we can do everything we expect to be able to do with proxied sets.
Note that you can't compare proxied sets with other proxied sets
due a limitaion in the set comparison functions which won't work
with any kind of proxy.
-
+
.. doctest::
>>> bool(s)
@@ -723,7 +723,7 @@ we can do everything we expect to be able to do with proxied frozensets.
>>> str(s) == str(us)
True
-
+
>>> repr(s) == repr(us)
True
@@ -745,7 +745,7 @@ we can do everything we expect to be able to do with proxied frozensets.
Note that you can't compare proxied sets with other proxied sets
due a limitaion in the frozenset comparison functions which won't work
with any kind of proxy.
-
+
.. doctest::
>>> bool(s)
@@ -838,8 +838,8 @@ New-style classes
'ForbiddenAttribute: __dict__'
>>> s = str(C)
>>> s = repr(C)
- >>> int(C.__module__ == __name__)
- 1
+ >>> C.__module__ == __name__
+ True
>>> len(C.__bases__)
1
>>> len(C.__mro__)
@@ -849,14 +849,14 @@ Always available:
.. doctest::
- >>> int(C == C)
- 1
- >>> int(C != C)
- 0
- >>> int(bool(C))
- 1
- >>> int(C.__class__ == type)
- 1
+ >>> C == C
+ True
+ >>> C != C
+ False
+ >>> bool(C)
+ True
+ >>> C.__class__ == type
+ True
New-style Instances
###################
@@ -873,21 +873,21 @@ New-style Instances
'ForbiddenAttribute: z'
>>> c.x
1
- >>> int(c.__class__ == C)
- 1
+ >>> c.__class__ == C
+ True
Always available:
.. doctest::
- >>> int(c == c)
- 1
- >>> int(c != c)
- 0
- >>> int(bool(c))
- 1
- >>> int(c.__class__ == C)
- 1
+ >>> c == c
+ True
+ >>> c != c
+ False
+ >>> bool(c)
+ True
+ >>> c.__class__ == C
+ True
Classic Classes
@@ -904,21 +904,25 @@ Classic Classes
'ForbiddenAttribute: __dict__'
>>> s = str(C)
>>> s = repr(C)
- >>> int(C.__module__ == __name__)
- 1
- >>> len(C.__bases__)
- 1
+ >>> C.__module__ == __name__
+ True
+
+Note that these are really only classic on Python 2:
+
+ >>> import sys
+ >>> len(C.__bases__) == (0 if sys.version_info[0] == 2 else 1)
+ True
Always available:
.. doctest::
- >>> int(C == C)
- 1
- >>> int(C != C)
- 0
- >>> int(bool(C))
- 1
+ >>> C == C
+ True
+ >>> C != C
+ False
+ >>> bool(C)
+ True
Classic Instances
#################
@@ -934,21 +938,21 @@ Classic Instances
'ForbiddenAttribute: z'
>>> c.x
1
- >>> int(c.__class__ == C)
- 1
+ >>> c.__class__ == C
+ True
Always available:
.. doctest::
- >>> int(c == c)
- 1
- >>> int(c != c)
- 0
- >>> int(bool(c))
- 1
- >>> int(c.__class__ == C)
- 1
+ >>> c == c
+ True
+ >>> c != c
+ False
+ >>> bool(c)
+ True
+ >>> c.__class__ == C
+ True
Interfaces and declarations
###########################
@@ -992,8 +996,7 @@ We work with the ABCMeta meta class:
.. doctest::
>>> import abc
- >>> class MyABC:
- ... __metaclass__ = abc.ABCMeta
+ >>> MyABC = abc.ABCMeta('MyABC', (object,), {})
>>> class Foo(MyABC): pass
>>> class Bar(Foo): pass
>>> PBar = ProxyFactory(Bar)
@@ -1005,8 +1008,8 @@ We work with the ABCMeta meta class:
'ForbiddenAttribute: __dict__'
>>> s = str(PBar)
>>> s = repr(PBar)
- >>> int(PBar.__module__ == __name__)
- 1
+ >>> PBar.__module__ == __name__
+ True
>>> len(PBar.__bases__)
1
@@ -1014,11 +1017,11 @@ Always available:
.. doctest::
- >>> int(PBar == PBar)
- 1
- >>> int(PBar != PBar)
- 0
- >>> int(bool(PBar))
- 1
- >>> int(PBar.__class__ == type)
- 1
+ >>> PBar == PBar
+ True
+ >>> PBar != PBar
+ False
+ >>> bool(PBar)
+ True
+ >>> PBar.__class__ == type
+ False
diff --git a/docs/api/decorator.rst b/docs/api/decorator.rst
index c05c692..59a02bd 100644
--- a/docs/api/decorator.rst
+++ b/docs/api/decorator.rst
@@ -57,7 +57,7 @@ Using `selectChecker()`, we can confirm that a `Foo` object uses
... fooChecker.check(foo, 'b') # doctest: +ELLIPSIS
... except ForbiddenAttribute as e:
... e
- ForbiddenAttribute('b', <Foo object ...>)
+ ForbiddenAttribute('b', <...Foo object ...>)
and that a `Wrapper` object uses `wrappeChecker`:
@@ -71,7 +71,7 @@ and that a `Wrapper` object uses `wrappeChecker`:
... wrapperChecker.check(wrapper, 'a') # doctest: +ELLIPSIS
... except ForbiddenAttribute as e:
... e
- ForbiddenAttribute('a', <Foo object ...>)
+ ForbiddenAttribute('a', <...Foo object ...>)
(Note that the object description says `Foo` because the object is a
proxy and generally looks and acts like the object it's proxying.)
@@ -103,7 +103,7 @@ illustrate, we'll proxify `foo`:
... secure_foo.b # doctest: +ELLIPSIS
... except ForbiddenAttribute as e:
... e
- ForbiddenAttribute('b', <Foo object ...>)
+ ForbiddenAttribute('b', <...Foo object ...>)
when we wrap the secured `foo`:
diff --git a/docs/api/permission.rst b/docs/api/permission.rst
index 819cc90..268f8f6 100644
--- a/docs/api/permission.rst
+++ b/docs/api/permission.rst
@@ -27,11 +27,11 @@
Traceback (most recent call last):
...
ValueError: ('Undefined permission id', 'y')
-
+
The :data:`zope.security.checker.CheckerPublic` permission always exists:
.. doctest::
-
+
>>> from zope.security.checker import CheckerPublic
>>> checkPermission(None, CheckerPublic)
@@ -45,10 +45,11 @@ The :data:`zope.security.checker.CheckerPublic` permission always exists:
>>> y = Permission('y')
>>> provideUtility(y, IPermission, 'y')
- >>> ids = list(allPermissions(None))
- >>> ids.sort()
- >>> ids
- ['x', 'y']
+ >>> ids = sorted(allPermissions(None))
+ >>> for perm in sorted(allPermissions(None)):
+ ... print(perm)
+ x
+ y
.. autofunction:: zope.security.permission.PermissionsVocabulary
@@ -99,10 +100,10 @@ The non-public permissions 'x' and 'y' are string values:
.. doctest::
- >>> vocab.getTermByToken('x').value
- 'x'
- >>> vocab.getTermByToken('y').value
- 'y'
+ >>> print(vocab.getTermByToken('x').value)
+ x
+ >>> print(vocab.getTermByToken('y').value)
+ y
However, the public permission value is CheckerPublic:
@@ -115,16 +116,19 @@ and its title is shortened:
.. doctest::
- >>> vocab.getTermByToken('zope.Public').title
- 'Public'
+ >>> print(vocab.getTermByToken('zope.Public').title)
+ Public
The terms are sorted by title except for the public permission, which is
listed first:
.. doctest::
- >>> [term.title for term in vocab]
- ['Public', 'x', 'y']
+ >>> for term in vocab:
+ ... print(term.title)
+ Public
+ x
+ y
.. testcleanup::
diff --git a/setup.py b/setup.py
index 8bf984c..8368c92 100644
--- a/setup.py
+++ b/setup.py
@@ -102,7 +102,7 @@ else:
),
Extension("zope.security._zope_security_checker",
[os.path.join('src', 'zope', 'security',
- "_zope_security_checker.c")]
+ "_zope_security_checker.c")]
),
]
@@ -126,7 +126,6 @@ setup(name='zope.security',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
@@ -140,7 +139,7 @@ setup(name='zope.security',
url='http://pypi.python.org/pypi/zope.security',
license='ZPL 2.1',
packages=find_packages('src'),
- package_dir = {'': 'src'},
+ package_dir={'': 'src'},
namespace_packages=['zope'],
setup_requires=setup_requires,
ext_modules=ext_modules,
@@ -153,9 +152,9 @@ setup(name='zope.security',
'zope.proxy >= 4.1.0',
'zope.schema',
],
- test_suite = '__main__.alltests',
+ test_suite='__main__.alltests',
tests_require=TESTS_REQUIRE,
- extras_require = dict(
+ extras_require=dict(
pytz=["pytz"],
untrustedpython=['zope.untrustedpython'] if not py3 else [],
zcml=['zope.configuration'],
@@ -163,6 +162,6 @@ setup(name='zope.security',
testing=TESTS_REQUIRE + ['nose', 'coverage'],
docs=['Sphinx', 'repoze.sphinx.autointerface'],
),
- include_package_data = True,
- zip_safe = False,
- )
+ include_package_data=True,
+ zip_safe=False,
+)
diff --git a/src/zope/security/proxy.py b/src/zope/security/proxy.py
index 77a82d4..302257a 100644
--- a/src/zope/security/proxy.py
+++ b/src/zope/security/proxy.py
@@ -202,7 +202,12 @@ class ProxyPy(PyProxyBase):
def __length_hint__(self):
# no check
wrapped = super(PyProxyBase, self).__getattribute__('_wrapped')
- return wrapped.__length_hint__()
+ try:
+ hint = wrapped.__length_hint__
+ except AttributeError:
+ return NotImplemented
+ else:
+ return hint()
def __coerce__(self, other):
# For some reason _check_name does not work for coerce()
diff --git a/tox.ini b/tox.ini
index d9c069a..c0e57fb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,13 +3,14 @@ envlist =
# Jython support pending 2.7 support, due 2012-07-15 or so. See:
# http://fwierzbicki.blogspot.com/2012/03/adconion-to-fund-jython-27.html
# py27,pypy,jython,py33,coverage,docs
- py27,py27-pure,pypy,py33,py33-pure,py34,py35,py36,coverage,docs
+ py27,py27-pure,pypy,py34-pure,py34,py35,py36,coverage,docs
[testenv]
-deps =
- .[test]
commands =
- zope-testrunner --test-path=src --auto-progress --auto-color []
+ zope-testrunner --test-path=src []
+ sphinx-build -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest
+deps =
+ .[test,docs]
[testenv:py27-pure]
basepython =
@@ -18,28 +19,24 @@ setenv =
PURE_PYTHON = 1
PIP_CACHE_DIR = {envdir}/.cache
-[testenv:py33-pure]
+[testenv:py34-pure]
basepython =
- python3.3
+ python3.4
setenv =
PURE_PYTHON = 1
PIP_CACHE_DIR = {envdir}/.cache
[testenv:coverage]
+usedevelop = true
basepython =
python2.7
commands =
-# The installed version messes up nose's test discovery / coverage reporting
-# So, we uninstall that from the environment, and then install the editable
-# version, before running nosetests.
- pip uninstall -y zope.security
- python -c "import shutil; shutil.copyfile('src/zope/__init__.py', '{envdir}/lib/python2.7/site-packages/zope/__init__.py')"
- pip install -e .
- nosetests --with-xunit --with-xcoverage
+ coverage run -m zope.testrunner --test-path=src []
+ coverage run -a -m sphinx -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest
+ coverage report --fail-under=92
deps =
- .[testing]
+ {[testenv]deps}
coverage
- nosexcover
[testenv:docs]
basepython =
@@ -47,5 +44,3 @@ basepython =
commands =
sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
-deps =
- .[docs,test]