From a993f554fc68312a5b32ac509af144ffac9fc6c3 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Thu, 20 Apr 2017 14:17:26 -0500 Subject: [PATCH 1/5] Python 3 port --- .coveragerc | 6 +++ .travis.yml | 15 ++++++- CHANGES.txt => CHANGES.rst | 6 ++- MANIFEST.in | 2 +- README.txt => README.rst | 0 setup.py | 57 ++++++++++++++++----------- src/zope/app/dependable/__init__.py | 12 +++--- src/zope/app/dependable/dependency.py | 3 +- src/zope/app/dependable/interfaces.py | 3 +- src/zope/app/dependable/tests.py | 19 ++++----- tox.ini | 28 ++----------- 11 files changed, 77 insertions(+), 74 deletions(-) create mode 100644 .coveragerc rename CHANGES.txt => CHANGES.rst (81%) rename README.txt => README.rst (100%) diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..af40312 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,6 @@ +[run] +source = src + +[report] +exclude_lines = + pragma: no cover diff --git a/.travis.yml b/.travis.yml index 02dca11..dc7e4b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,20 @@ language: python +sudo: false python: - 2.7 + - pypy-5.4.1 + - 3.5 + - 3.6 install: - - pip install . + - pip install -U pip setuptools + - pip install -U coverage coveralls + - pip install -U -e .[test] script: - - python setup.py test -q + - coverage run -m zope.testrunner --test-path=src --auto-color --auto-progress notifications: email: false +after_success: + - coveralls +cache: pip +before_cache: + - rm -f $HOME/.cache/pip/log/debug.log diff --git a/CHANGES.txt b/CHANGES.rst similarity index 81% rename from CHANGES.txt rename to CHANGES.rst index ad26321..3a52fd6 100644 --- a/CHANGES.txt +++ b/CHANGES.rst @@ -2,9 +2,13 @@ CHANGES ======= -3.5.2 (unreleased) +4.0.0 (unreleased) ------------------ +- Add support for Python 3.5 and 3.6 and PyPy. + +- Removed support for Python 2.6. + - Added `tox.ini` and manifest. - Removed zope.app.testing dependency. diff --git a/MANIFEST.in b/MANIFEST.in index 61dad0f..de58807 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,7 @@ include *.rst include *.txt include tox.ini include bootstrap.py -include buildout.cfg +include *.cfg recursive-include src * diff --git a/README.txt b/README.rst similarity index 100% rename from README.txt rename to README.rst diff --git a/setup.py b/setup.py index ed8a981..74b6a9e 100644 --- a/setup.py +++ b/setup.py @@ -17,55 +17,64 @@ # Zope Toolkit policies as described by this documentation. ############################################################################## """Setup for zope.app.dependable package - -$Id$ """ import os from setuptools import setup, find_packages def read(*rnames): - return open(os.path.join(os.path.dirname(__file__), *rnames)).read() + with open(os.path.join(os.path.dirname(__file__), *rnames)) as f: + return f.read() + +version = '4.0.0.dev0' setup(name='zope.app.dependable', - version = '3.5.2dev', + version=version, author='Zope Corporation and Contributors', author_email='zope-dev@zope.org', description='Simple Dependency API', long_description=( - read('README.txt') + read('README.rst') + '\n\n' + - read('CHANGES.txt') + read('CHANGES.rst') ), - keywords = "zope3 dependency", - classifiers = [ + keywords="zope3 dependency", + classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: Zope Public License', 'Programming Language :: Python', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', - 'Framework :: Zope3'], - url='http://pypi.python.org/pypi/zope.app.dependable', + 'Framework :: Zope3' + ], + url='http://github.com/zopefoundation/zope.app.dependable', license='ZPL 2.1', packages=find_packages('src'), - package_dir = {'': 'src'}, + package_dir={'': 'src'}, namespace_packages=['zope', 'zope.app'], - extras_require=dict(test=[]), - install_requires=['setuptools', - 'zope.annotation', - 'zope.exceptions', - 'zope.i18nmessageid', - 'zope.interface', - 'zope.lifecycleevent', - 'zope.location', - 'zope.traversing', - ], - include_package_data = True, - zip_safe = False, + extras_require=dict(test=[ + 'zope.testrunner', + ]), + install_requires=[ + 'setuptools', + 'zope.annotation', + 'zope.exceptions', + 'zope.i18nmessageid', + 'zope.interface', + 'zope.lifecycleevent', + 'zope.location', + 'zope.traversing', + ], + include_package_data=True, + zip_safe=False, + test_suite='zope.app.dependable.tests', ) diff --git a/src/zope/app/dependable/__init__.py b/src/zope/app/dependable/__init__.py index 7b69dba..03ebe6c 100644 --- a/src/zope/app/dependable/__init__.py +++ b/src/zope/app/dependable/__init__.py @@ -13,11 +13,11 @@ ############################################################################## """Dependable Framework. -$Id$ """ +from __future__ import print_function, absolute_import, division __docformat__ = 'restructuredtext' -from zope.interface import implements +from zope.interface import implementer from zope.traversing.api import getParent, canonicalPath, getPath from zope.annotation.interfaces import IAnnotations @@ -53,7 +53,7 @@ def addPath(self, path): path = self._make_relative(path) annotations = IAnnotations(self.context) old = annotations.get(self.key, ()) - fixed = map(self._make_relative, old) + fixed = [self._make_relative(o) for o in old] if path not in fixed: fixed.append(path) new = tuple(fixed) @@ -65,7 +65,7 @@ def removePath(self, path): annotations = IAnnotations(self.context) old = annotations.get(self.key, ()) if old: - fixed = map(self._make_relative, old) + fixed = [self._make_relative(o) for o in old] fixed = [loc for loc in fixed if loc != path] new = tuple(fixed) if new != old: @@ -93,12 +93,10 @@ def _make_absolute(self, path): path = self.pp + path return path - +@implementer(IDependable) class Dependable(PathSetAnnotation): """See `IDependable`.""" - implements(IDependable) - key = "zope.app.dependable.Dependents" addDependent = PathSetAnnotation.addPath diff --git a/src/zope/app/dependable/dependency.py b/src/zope/app/dependable/dependency.py index 60ed384..512a8ac 100644 --- a/src/zope/app/dependable/dependency.py +++ b/src/zope/app/dependable/dependency.py @@ -16,6 +16,8 @@ on an object having dependencies. It raises an exception if it's the case. """ +from __future__ import print_function, absolute_import, division + __docformat__ = 'restructuredtext' from zope.i18nmessageid import Message @@ -44,4 +46,3 @@ def CheckDependency(event): "dependents": ", ".join(dependents) } raise DependencyError(Message(exception_msg, mapping=mapping)) - diff --git a/src/zope/app/dependable/interfaces.py b/src/zope/app/dependable/interfaces.py index 0baab0c..e1edd4c 100644 --- a/src/zope/app/dependable/interfaces.py +++ b/src/zope/app/dependable/interfaces.py @@ -12,9 +12,8 @@ # ############################################################################## """Dependable framework interfaces - -$Id$ """ +from __future__ import print_function, absolute_import, division __docformat__ = 'restructuredtext' from zope.interface import Interface diff --git a/src/zope/app/dependable/tests.py b/src/zope/app/dependable/tests.py index 6c1d396..617afbf 100644 --- a/src/zope/app/dependable/tests.py +++ b/src/zope/app/dependable/tests.py @@ -12,14 +12,13 @@ # ############################################################################## """Unit tests for Dependable class. - -$Id$ """ -from unittest import TestCase, TestSuite, main, makeSuite +from __future__ import print_function, absolute_import, division +import unittest from zope.annotation.attribute import AttributeAnnotations from zope.location.interfaces import ILocationInfo -from zope.interface import implements, verify +from zope.interface import implementer, verify from zope.lifecycleevent import ObjectRemovedEvent from zope.app.dependable.dependency import CheckDependency @@ -30,10 +29,9 @@ class C(object): pass +@implementer(IDependable, ILocationInfo) class DummyObject(object): - implements(IDependable, ILocationInfo) - def dependents(self): return ['dependency1', 'dependency2'] @@ -41,7 +39,7 @@ def getPath(self): return '/dummy-object' -class Test(TestCase): +class Test(unittest.TestCase): def factory(self): from zope.app.dependable import Dependable @@ -92,9 +90,8 @@ def testCheckDependency(self): def test_suite(): - return TestSuite(( - makeSuite(Test), - )) + return unittest.defaultTestLoader.loadTestsFromName(__name__) + if __name__=='__main__': - main(defaultTest='test_suite') + unittest.main(defaultTest='test_suite') diff --git a/tox.ini b/tox.ini index 6fef181..c758890 100644 --- a/tox.ini +++ b/tox.ini @@ -1,31 +1,9 @@ [tox] envlist = - py26,py27 + py27, pypy, py35, py36 [testenv] commands = - python setup.py test -q -# without explicit deps, setup.py test will download a bunch of eggs into $PWD + zope-testrunner --test-path=src deps = - zope.annotation - zope.exceptions - zope.i18nmessageid - zope.interface - zope.lifecycleevent - zope.location - zope.traversing - -[testenv:coverage] -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.app.dependable - pip install -e . - nosetests --with-xunit --with-xcoverage -deps = - nose - coverage - nosexcover + .[test] From 3cce6b68b1098816598cf34218fc8fc2c2197868 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Sat, 22 Apr 2017 07:32:54 -0500 Subject: [PATCH 2/5] Add setup.cfg to mark wheels universal; remove unnecessary __future__ imports --- setup.cfg | 2 ++ setup.py | 2 +- src/zope/app/dependable/__init__.py | 2 +- src/zope/app/dependable/dependency.py | 2 +- src/zope/app/dependable/interfaces.py | 2 +- src/zope/app/dependable/tests.py | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..2a9acf1 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/setup.py b/setup.py index 74b6a9e..a3acc93 100644 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ def read(*rnames): 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', - 'Framework :: Zope3' + 'Framework :: Zope3', ], url='http://github.com/zopefoundation/zope.app.dependable', license='ZPL 2.1', diff --git a/src/zope/app/dependable/__init__.py b/src/zope/app/dependable/__init__.py index 03ebe6c..5800b7f 100644 --- a/src/zope/app/dependable/__init__.py +++ b/src/zope/app/dependable/__init__.py @@ -14,7 +14,7 @@ """Dependable Framework. """ -from __future__ import print_function, absolute_import, division + __docformat__ = 'restructuredtext' from zope.interface import implementer diff --git a/src/zope/app/dependable/dependency.py b/src/zope/app/dependable/dependency.py index 512a8ac..747a878 100644 --- a/src/zope/app/dependable/dependency.py +++ b/src/zope/app/dependable/dependency.py @@ -16,7 +16,7 @@ on an object having dependencies. It raises an exception if it's the case. """ -from __future__ import print_function, absolute_import, division + __docformat__ = 'restructuredtext' diff --git a/src/zope/app/dependable/interfaces.py b/src/zope/app/dependable/interfaces.py index e1edd4c..881be9a 100644 --- a/src/zope/app/dependable/interfaces.py +++ b/src/zope/app/dependable/interfaces.py @@ -13,7 +13,7 @@ ############################################################################## """Dependable framework interfaces """ -from __future__ import print_function, absolute_import, division + __docformat__ = 'restructuredtext' from zope.interface import Interface diff --git a/src/zope/app/dependable/tests.py b/src/zope/app/dependable/tests.py index 617afbf..f2abdd6 100644 --- a/src/zope/app/dependable/tests.py +++ b/src/zope/app/dependable/tests.py @@ -13,7 +13,7 @@ ############################################################################## """Unit tests for Dependable class. """ -from __future__ import print_function, absolute_import, division + import unittest from zope.annotation.attribute import AttributeAnnotations From 962fd49976cb73ec381f117a58350f720302f216 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Sat, 22 Apr 2017 07:36:10 -0500 Subject: [PATCH 3/5] 100% coverage via judicious #pragma: no cover --- src/zope/__init__.py | 8 +------- src/zope/app/__init__.py | 8 +------- src/zope/app/dependable/tests.py | 2 +- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/zope/__init__.py b/src/zope/__init__.py index bf99a9d..155a31d 100644 --- a/src/zope/__init__.py +++ b/src/zope/__init__.py @@ -1,8 +1,2 @@ # this is a namespace package -try: - import pkg_resources - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - __path__ = pkgutil.extend_path(__path__, __name__) - +__import__('pkg_resources').declare_namespace(__name__) # pragma: no cover diff --git a/src/zope/app/__init__.py b/src/zope/app/__init__.py index bf99a9d..155a31d 100644 --- a/src/zope/app/__init__.py +++ b/src/zope/app/__init__.py @@ -1,8 +1,2 @@ # this is a namespace package -try: - import pkg_resources - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - __path__ = pkgutil.extend_path(__path__, __name__) - +__import__('pkg_resources').declare_namespace(__name__) # pragma: no cover diff --git a/src/zope/app/dependable/tests.py b/src/zope/app/dependable/tests.py index f2abdd6..cb74075 100644 --- a/src/zope/app/dependable/tests.py +++ b/src/zope/app/dependable/tests.py @@ -93,5 +93,5 @@ def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) -if __name__=='__main__': +if __name__=='__main__': # pragma: no cover unittest.main(defaultTest='test_suite') From e6fa4fa906b186606f814caf580f14aeafe6f6be Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Sat, 22 Apr 2017 08:09:34 -0500 Subject: [PATCH 4/5] Really hit 100% coverage through additional tests, including of configure.zcml --- .gitignore | 2 + setup.py | 12 ++++-- src/zope/app/dependable/__init__.py | 7 +++- src/zope/app/dependable/configure.zcml | 2 + src/zope/app/dependable/tests.py | 54 ++++++++++++++++++++++++-- 5 files changed, 69 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 5149542..6bacfe5 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ build/ dist/ *.egg-info/ .tox/ +.coverage +htmlcov/ diff --git a/setup.py b/setup.py index a3acc93..3bff94b 100644 --- a/setup.py +++ b/setup.py @@ -26,6 +26,11 @@ def read(*rnames): return f.read() version = '4.0.0.dev0' +tests_require = [ + 'zope.container', + 'zope.testing', + 'zope.testrunner', +] setup(name='zope.app.dependable', version=version, @@ -61,9 +66,10 @@ def read(*rnames): packages=find_packages('src'), package_dir={'': 'src'}, namespace_packages=['zope', 'zope.app'], - extras_require=dict(test=[ - 'zope.testrunner', - ]), + extras_require={ + 'test': tests_require, + }, + tests_require=tests_require, install_requires=[ 'setuptools', 'zope.annotation', diff --git a/src/zope/app/dependable/__init__.py b/src/zope/app/dependable/__init__.py index 5800b7f..d6712e9 100644 --- a/src/zope/app/dependable/__init__.py +++ b/src/zope/app/dependable/__init__.py @@ -37,6 +37,7 @@ def __init__(self, context): parent = getParent(self.context) except TypeError: parent = None + if parent is not None: try: pp = getPath(parent) @@ -84,7 +85,11 @@ def _make_relative(self, path): path = canonicalPath(path) if path.startswith(self.pp): path = path[self.pplen:] - while path.startswith("/"): + while path.startswith("/"): # pragma: no cover + # We should not be able to get here. canonicalPath + # doesn't allow trailing / in a path segment, and we + # already cut off the whole length of the parent, which + # we guaranteed to begin and end with a / path = path[1:] return path diff --git a/src/zope/app/dependable/configure.zcml b/src/zope/app/dependable/configure.zcml index e236c6c..fc19784 100644 --- a/src/zope/app/dependable/configure.zcml +++ b/src/zope/app/dependable/configure.zcml @@ -3,6 +3,8 @@ xmlns:zcml="http://namespaces.zope.org/zcml" > + + Date: Sat, 22 Apr 2017 09:03:42 -0500 Subject: [PATCH 5/5] Take @mgedmin 's suggestion and simplify loop to path.lstrip('/') --- src/zope/app/dependable/__init__.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/zope/app/dependable/__init__.py b/src/zope/app/dependable/__init__.py index d6712e9..b715241 100644 --- a/src/zope/app/dependable/__init__.py +++ b/src/zope/app/dependable/__init__.py @@ -85,12 +85,15 @@ def _make_relative(self, path): path = canonicalPath(path) if path.startswith(self.pp): path = path[self.pplen:] - while path.startswith("/"): # pragma: no cover - # We should not be able to get here. canonicalPath - # doesn't allow trailing / in a path segment, and we - # already cut off the whole length of the parent, which - # we guaranteed to begin and end with a / - path = path[1:] + # Now, the path should not actually begin with a /. + # canonicalPath doesn't allow trailing / in a path + # segment, and we already cut off the whole length of + # the parent, which we guaranteed to begin and end + # with a /. But it's possible that older dependencies + # than we test with could produce this scenario, so we + # leave it for BWC. + path = path.lstrip("/") + return path def _make_absolute(self, path):