diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..777784e --- /dev/null +++ b/.coveragerc @@ -0,0 +1,9 @@ +[run] +source = zope.sequencesort + +[report] +exclude_lines = + pragma: no cover + if __name__ == '__main__': + raise NotImplementedError + raise AssertionError diff --git a/.gitignore b/.gitignore index c55f638..a321d85 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ docs/_build bin develop-eggs eggs +htmlcov/ diff --git a/.travis.yml b/.travis.yml index ac38df9..70e041e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,24 @@ language: python +sudo: false python: - - 2.6 - 2.7 - - 3.2 - - 3.3 + - 3.4 + - 3.5 + - 3.6 - pypy +matrix: + include: + - python: "3.7" + dist: xenial + sudo: true install: - - pip install . + - pip install -U pip setuptools wheel + - pip install -U coverage coveralls + - pip install -U -e .[test] script: - - python setup.py test -q + - coverage run -m zope.testrunner --test-path=src +after_success: + - coveralls notifications: email: false +cache: pip diff --git a/CHANGES.txt b/CHANGES.rst similarity index 58% rename from CHANGES.txt rename to CHANGES.rst index 5e12f51..fb4a393 100644 --- a/CHANGES.txt +++ b/CHANGES.rst @@ -1,18 +1,27 @@ -``zope.sequencesort`` Changelog -=============================== +=========== + Changelog +=========== -4.0.2 (unreleased) ------------------- +4.1.0 (unreleased) +================== - Updated ``boostrap.py`` to version 2.2. +- Drop support for Python 2.6, 3.2 and 3.3. + +- Add support for Python 3.4, 3.5, 3.6 and 3.7. + +- The locale comparison functions, ``strcoll`` and ``strcoll_nocase`` + are always available, not only if the ``locale`` module had been + imported before this module. + 4.0.1 (2013-03-04) ------------------- +================== - Fix omitted tests under Py3k. 4.0.0 (2013-02-28) ------------------- +================== - Added ``setup.py docs`` alias (installs ``Sphinx`` and dependencies). @@ -28,6 +37,6 @@ - Dropped support for Python 2.4 / 2.5. 3.4.0 (2007-10-03) ------------------- +================== - Initial release independent of the main Zope3 tree. diff --git a/MANIFEST.in b/MANIFEST.in index e652f91..8276fc1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,9 +1,15 @@ include *.py include *.txt +include *.rst include buildout.cfg include tox.ini +include .travis.yml +include .coveragerc + recursive-include docs *.bat recursive-include docs *.py recursive-include docs *.rst recursive-include docs *.txt recursive-include docs Makefile + +prune docs/_build diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..0aa5797 --- /dev/null +++ b/README.rst @@ -0,0 +1,22 @@ +=================== + zope.sequencesort +=================== + +.. image:: https://img.shields.io/pypi/v/zope.sequencesort.svg + :target: https://pypi.org/project/zope.sequencesort/ + :alt: Latest Version + +.. image:: https://travis-ci.org/zopefoundation/zope.sequencesort.svg?branch=master + :target: https://travis-ci.org/zopefoundation/zope.sequencesort + +.. image:: https://readthedocs.org/projects/zopesequencesort/badge/?version=latest + :target: https://zopesequencesort.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://coveralls.io/repos/github/zopefoundation/zope.sequencesort/badge.svg + :target: https://coveralls.io/github/zopefoundation/zope.sequencesort + + + +This package provides support for sorting sequences based on multiple +keys, including locale-based comparisons and per-key directions. diff --git a/README.txt b/README.txt deleted file mode 100644 index e7f2330..0000000 --- a/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -``zope.sequencesort`` README -============================ - -This package provides support for sorting sequences based on multiple -keys, including locale-based comparisons and per-key directions. diff --git a/docs/index.rst b/docs/index.rst index 01c4d0c..a7054a3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,4 +1,5 @@ .. zope.sequencesort documentation master file, created by + :mod:`zope.sequencesort` ======================== @@ -16,4 +17,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/setup.cfg b/setup.cfg index aa0bb83..3350b49 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,3 +8,6 @@ where=src [aliases] dev = develop easy_install zope.sequencesort[testing] docs = easy_install zope.sequencesort[docs] + +[bdist_wheel] +universal = 1 diff --git a/setup.py b/setup.py index 3f9f063..374e202 100644 --- a/setup.py +++ b/setup.py @@ -22,47 +22,58 @@ 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() -setup(name="zope.sequencesort", - version = '4.0.2dev', - author='Zope Foundation and Contributors', - author_email='zope-dev@zope.org', - description='Sequence Sorting', - long_description=( - read('README.txt') - + '\n\n' + - read('CHANGES.txt') - ), - keywords = "zope3 sequence sort", - 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.2', - 'Programming Language :: Python :: 3.3', - "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://cheeseshop.python.org/pypi/zope.sequencesort', - license='ZPL 2.1', - packages=find_packages('src'), - package_dir = {'': 'src'}, - namespace_packages=['zope'], - test_suite='zope.sequencesort', - extras_require={'docs': ['Sphinx'], - 'testing': ['nose', 'coverage'], - }, - install_requires = ['setuptools'], - include_package_data=True, - zip_safe = False - ) +setup( + name="zope.sequencesort", + version='4.1.0.dev0', + author='Zope Foundation and Contributors', + author_email='zope-dev@zope.org', + description='Sequence Sorting', + long_description=( + read('README.rst') + + '\n\n' + + read('CHANGES.rst') + ), + keywords="zope3 sequence sort", + 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.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 :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'Topic :: Internet :: WWW/HTTP', + 'Framework :: Zope3', + ], + url='https://github.com/zopefoundation/zope.sequencesort', + license='ZPL 2.1', + packages=find_packages('src'), + package_dir={'': 'src'}, + namespace_packages=['zope'], + test_suite='zope.sequencesort', + extras_require={ + 'docs': [ + 'Sphinx', + ], + 'test': [ + 'zope.testrunner', + ], + }, + install_requires=[ + 'setuptools', + ], + include_package_data=True, + zip_safe=False +) diff --git a/src/zope/__init__.py b/src/zope/__init__.py index de40ea7..2cdb0e4 100644 --- a/src/zope/__init__.py +++ b/src/zope/__init__.py @@ -1 +1 @@ -__import__('pkg_resources').declare_namespace(__name__) +__import__('pkg_resources').declare_namespace(__name__) # pragma: no cover diff --git a/src/zope/sequencesort/ssort.py b/src/zope/sequencesort/ssort.py index 9f47dc7..81496c8 100644 --- a/src/zope/sequencesort/ssort.py +++ b/src/zope/sequencesort/ssort.py @@ -15,10 +15,13 @@ e.g .Sort(sequence, (("akey", "nocase"), ("anotherkey", "cmp", "desc"))) """ +from functools import cmp_to_key +from locale import strcoll + try: - cmp = cmp -except NameError: #pragma NO COVER Py3k - def cmp(lhs, rhs): + cmp = cmp # always put in our namespace; tests import it from here +except NameError: + def cmp(lhs, rhs): # pylint:disable=redefined-builtin return int(rhs < lhs) - int(lhs < rhs) class _Smallest(object): @@ -33,24 +36,18 @@ def __gt__(self, other): return False _Smallest = _Smallest() -try: - # Python 3.2+ - from functools import cmp_to_key -except ImportError: #pragma NO COVER Python 2.x - cmp_to_key = None def sort(sequence, sort=(), _=None, mapping=0): """Return a sorted copy of 'sequence'. - - sequence is a sequence of objects to be sorted - - - sort is a sequence of tuples (key,func,direction) + :param sequence: is a sequence of objects to be sorted + :param sort: is a sequence of tuples (key,func,direction) that define the sort order: - - key is the name of an attribute to sort the objects by + - *key* is the name of an attribute to sort the objects by - - func is the name of a comparison function. This parameter is + - *func* is the name of a comparison function. This parameter is optional allowed values: @@ -66,7 +63,7 @@ def sort(sequence, sort=(), _=None, mapping=0): - "xxx" -- a user-defined comparison function - - direction -- defines the sort direction for the key (optional). + - *direction* defines the sort direction for the key (optional). (allowed values: "asc" (default) , "desc") """ need_sortfunc = 0 @@ -124,10 +121,10 @@ def sort(sequence, sort=(), _=None, mapping=0): except (AttributeError, KeyError): akey = _Smallest else: - if type(akey) not in BASIC_TYPES: + if not isinstance(akey, BASIC_TYPES): try: akey = akey() - except: + except: # pylint:disable=bare-except pass k.append(akey) else: # One sort key. @@ -138,20 +135,17 @@ def sort(sequence, sort=(), _=None, mapping=0): k = getattr(v, sort) except (AttributeError, KeyError): k = _Smallest - if type(k) not in BASIC_TYPES: + if not isinstance(k, BASIC_TYPES): try: k = k() - except: + except: # pylint:disable=bare-except pass - s.append((k,client)) + s.append((k, client)) if need_sortfunc: by = SortBy(multsort, sf_list) - if cmp_to_key is None: - s.sort(by) - else: #pragma NO COVER Py3k - s.sort(key=cmp_to_key(by)) + s.sort(key=cmp_to_key(by)) else: s.sort() @@ -160,38 +154,37 @@ def sort(sequence, sort=(), _=None, mapping=0): SortEx = sort -BASIC_TYPES = { - type(''): 1, - type(b''): 1, - type(0): 1, - type(0.0): 1, - type(()): 1, - type([]): 1, - type(None) : 1, -} +BASIC_TYPES = ( + type(u''), + type(b''), + type(0), + type(0.0), + type(()), + type([]), + type(None) +) -try: - unicode -except NameError: #pragma NO COVER Py3k - pass -else: #pragma NO COVER Python 2 - BASIC_TYPES[unicode] = 1 def nocase(str1, str2): return cmp(str1.lower(), str2.lower()) -import sys -if "locale" in sys.modules: # only if locale is already imported - from locale import strcoll - def strcoll_nocase(str1, str2): #pragma NO COVER XXX global locale - return strcoll(str1.lower(), str2.lower()) +def strcoll_nocase(str1, str2): + return strcoll(str1.lower(), str2.lower()) +_SORT_FUNCTIONS = { + "cmp": cmp, # builtin + "nocase": nocase, + "locale": strcoll, + "strcoll": strcoll, + "locale_nocase": strcoll_nocase, + "strcoll_nocase": strcoll_nocase, +} def make_sortfunctions(sortfields, _): """Accepts a list of sort fields; splits every field, finds comparison function. Returns a list of 3-tuples (field, cmp_function, asc_multplier)""" - + # pylint:disable=too-many-branches sf_list = [] for field in sortfields: info = list(field) @@ -211,15 +204,9 @@ def make_sortfunctions(sortfields, _): f_name = info[1] # predefined function? - if f_name == "cmp": - func = cmp # builtin - elif f_name == "nocase": - func = nocase - elif f_name in ("locale", "strcoll"): #pragma NO COVER - func = strcoll - elif f_name in ("locale_nocase", "strcoll_nocase"): #pragma NO COVER - func = strcoll_nocase - else: # no - look it up in the namespace + func = _SORT_FUNCTIONS.get(f_name) + # no - look it up in the namespace + if func is None: if hasattr(_, 'getitem'): # support for zope.documenttemplate.dt_util.TemplateDict func = _.getitem(f_name, 0) diff --git a/src/zope/sequencesort/tests/test_ssort.py b/src/zope/sequencesort/tests/test_ssort.py index 855d252..11f4b49 100644 --- a/src/zope/sequencesort/tests/test_ssort.py +++ b/src/zope/sequencesort/tests/test_ssort.py @@ -11,22 +11,15 @@ # ############################################################################## import unittest +import sys - -def _skip_on_Py3k(func): - import functools - import sys - if sys.version_info[0] > 2: - def _dummy(self): - pass - return functools.update_wrapper(_dummy, func) - return func - +# we have a lot of "foo" and "bar" +# pylint:disable=blacklisted-name +# pylint:disable=protected-access class Test_sort(unittest.TestCase): - """Test zope.sequencesort.sort() - """ + def _callFUT(self, *args, **kw): from zope.sequencesort.ssort import sort return sort(*args, **kw) @@ -51,6 +44,14 @@ def __init__(self, bar): result = self._callFUT(TO_SORT, (('bar', 'nocase'),)) self.assertEqual([x.bar for x in result], ['A', 'b', 'C']) + def test_w_attributes_strcoll_nocase(self): + class Foo(object): + def __init__(self, bar): + self.bar = bar + TO_SORT = [Foo('b'), Foo('A'), Foo('C')] + result = self._callFUT(TO_SORT, (('bar', 'strcoll_nocase'),)) + self.assertEqual([x.bar for x in result], ['A', 'b', 'C']) + def test_w_attributes_missing(self): class Foo(object): def __init__(self, bar): @@ -58,7 +59,7 @@ def __init__(self, bar): TO_SORT = [Foo('b'), Foo('a'), Foo('c'), object()] result = self._callFUT(TO_SORT, (('bar',),)) self.assertEqual([getattr(x, 'bar', 'ZZZ') for x in result], - ['ZZZ', 'a', 'b', 'c']) + ['ZZZ', 'a', 'b', 'c']) def test_w_multi_attributes(self): class Foo(object): @@ -68,7 +69,7 @@ def __init__(self, bar, baz): TO_SORT = [Foo('b', 'q'), Foo('a', 'r'), Foo('c', 's'), Foo('a', 'p')] result = self._callFUT(TO_SORT, (('bar',), ('baz',))) self.assertEqual([(x.bar, x.baz) for x in result], - [('a', 'p'), ('a', 'r'), ('b', 'q'), ('c', 's')]) + [('a', 'p'), ('a', 'r'), ('b', 'q'), ('c', 's')]) def test_w_multi_attributes_nocase(self): class Foo(object): @@ -78,7 +79,7 @@ def __init__(self, bar, baz): TO_SORT = [Foo('b', 'q'), Foo('a', 'R'), Foo('c', 's'), Foo('a', 'p')] result = self._callFUT(TO_SORT, (('bar',), ('baz', 'nocase'),)) self.assertEqual([(x.bar, x.baz) for x in result], - [('a', 'p'), ('a', 'R'), ('b', 'q'), ('c', 's')]) + [('a', 'p'), ('a', 'R'), ('b', 'q'), ('c', 's')]) def test_w_multi_attributes_missing(self): class Foo(object): @@ -88,18 +89,16 @@ def __init__(self, bar, baz): TO_SORT = [Foo('b', 'q'), Foo('a', 'r'), object(), Foo('a', 'p')] result = self._callFUT(TO_SORT, (('bar',), ('baz',))) self.assertEqual([(getattr(x, 'bar', 'ZZZ'), getattr(x, 'baz', 'YYY')) - for x in result], - [('ZZZ', 'YYY'), ('a', 'p'), ('a', 'r'), ('b', 'q')]) + for x in result], + [('ZZZ', 'YYY'), ('a', 'p'), ('a', 'r'), ('b', 'q')]) def test_w_non_basictype_key(self): - from zope.sequencesort.ssort import cmp + from zope.sequencesort.ssort import cmp as compare class Qux(object): def __init__(self, spam): self._spam = spam - def __cmp__(self, other): - return cmp(self._spam, other._spam) def __lt__(self, other): - return self._spam < other._spam + return compare(self._spam, other._spam) < 0 class Foo(object): def __init__(self, bar): self.bar = Qux(bar) @@ -130,14 +129,12 @@ def bar(self): [('b', 'p'), ('b', 'Q'), ('c', 'r')]) def test_w_multi_and_non_basictype_key(self): - from zope.sequencesort.ssort import cmp + from zope.sequencesort.ssort import cmp as compare class Qux(object): def __init__(self, spam): self._spam = spam - def __cmp__(self, other): - return cmp(self._spam, other._spam) def __lt__(self, other): - return self._spam < other._spam + return compare(self._spam, other._spam) < 0 class Foo(object): def __init__(self, bar, baz): self.bar = bar @@ -147,9 +144,13 @@ def __init__(self, bar, baz): self.assertEqual([(x.bar, x.baz._spam) for x in result], [('b', 'p'), ('b', 'q'), ('c', 'r')]) - @_skip_on_Py3k + def test_wo_args(self): - self.assertEqual(self._callFUT(WORDLIST), RES_WO_ARGS) + if sys.version_info[0] < 3: # pragma: no cover + self.assertEqual(self._callFUT(WORDLIST), RES_WO_ARGS) + else: + with self.assertRaises(TypeError): + self._callFUT(WORDLIST) def test_w_only_key(self): self.assertEqual(self._callFUT(WORDLIST, (("key",),), mapping=1), @@ -168,24 +169,28 @@ def test_w_multi_key(self): mapping=1), RES_W_MULTI_KEY) def test_w_multi_key_nocase_desc(self): - self.assertEqual(self._callFUT(WORDLIST, (("weight",), - ("key", "nocase", "desc")), mapping=1), - RES_W_MULTI_KEY_NOCASE_DESC) + self.assertEqual( + self._callFUT(WORDLIST, (("weight",), + ("key", "nocase", "desc")), mapping=1), + RES_W_MULTI_KEY_NOCASE_DESC) def test_w_custom_comparator(self): - from zope.sequencesort.ssort import cmp + from zope.sequencesort.ssort import cmp as compare def myCmp(s1, s2): - return -cmp(s1, s2) + return -compare(s1, s2) md = {"myCmp" : myCmp} - self.assertEqual(self._callFUT(WORDLIST, - (("weight",), ("key", "myCmp", "desc")), - md, - mapping=1 - ), RES_W_CUSTOM_COMPARATOR) + self.assertEqual( + self._callFUT( + WORDLIST, + (("weight",), ("key", "myCmp", "desc")), + md, + mapping=1 + ), + RES_W_CUSTOM_COMPARATOR) def test_w_custom_comparator_dtml_namespace(self): - from zope.sequencesort.ssort import cmp + from zope.sequencesort.ssort import cmp as compare class Namespace(object): def __init__(self, **kw): self.__dict__.update(kw) @@ -193,20 +198,22 @@ def getitem(self, name, default): return getattr(self, name, default) def myCmp(s1, s2): - return -cmp(s1, s2) + return -compare(s1, s2) ns = Namespace(myCmp=myCmp) - self.assertEqual(self._callFUT(WORDLIST, - (("weight",), ("key", "myCmp", "desc")), - ns, - mapping=1 - ), RES_W_CUSTOM_COMPARATOR) + self.assertEqual( + self._callFUT( + WORDLIST, + (("weight",), ("key", "myCmp", "desc")), + ns, + mapping=1 + ), + RES_W_CUSTOM_COMPARATOR) class Test_make_sortfunctions(unittest.TestCase): - """Test zope.sequencesort.sort() - """ + def _callFUT(self, sortfields, _): from zope.sequencesort.ssort import make_sortfunctions return make_sortfunctions(sortfields, _) @@ -279,32 +286,32 @@ def test_multiple(self): WORDLIST = [ - {"key": "aaa", "word": "AAA", "weight": 1}, - {"key": "bbb", "word": "BBB", "weight": 0}, - {"key": "ccc", "word": "CCC", "weight": 0}, - {"key": "ddd", "word": "DDD", "weight": 0}, - {"key": "eee", "word": "EEE", "weight": 1}, - {"key": "fff", "word": "FFF", "weight": 0}, - {"key": "ggg", "word": "GGG", "weight": 0}, - {"key": "hhh", "word": "HHH", "weight": 0}, - {"key": "iii", "word": "III", "weight": 1}, - {"key": "jjj", "word": "JJJ", "weight": -1}, - {"key": "kkk", "word": "KKK", "weight": 0}, - {"key": "lll", "word": "LLL", "weight": 0}, - {"key": "mmm", "word": "MMM", "weight": 0}, - {"key": "nnn", "word": "NNN", "weight": 0}, - {"key": "ooo", "word": "OOO", "weight": 1}, - {"key": "ppp", "word": "PPP", "weight": 0}, - {"key": "qqq", "word": "QQQ", "weight": -1}, - {"key": "rrr", "word": "RRR", "weight": 0}, - {"key": "sss", "word": "SSS", "weight": 0}, - {"key": "ttt", "word": "TTT", "weight": 0}, - {"key": "uuu", "word": "UUU", "weight": 1}, - {"key": "vvv", "word": "VVV", "weight": 0}, - {"key": "www", "word": "WWW", "weight": 0}, - {"key": "xxx", "word": "XXX", "weight": 0}, - {"key": "yyy", "word": "YYY", "weight": -1}, - {"key": "zzz", "word": "ZZZ", "weight": 0} + {"key": "aaa", "word": "AAA", "weight": 1}, + {"key": "bbb", "word": "BBB", "weight": 0}, + {"key": "ccc", "word": "CCC", "weight": 0}, + {"key": "ddd", "word": "DDD", "weight": 0}, + {"key": "eee", "word": "EEE", "weight": 1}, + {"key": "fff", "word": "FFF", "weight": 0}, + {"key": "ggg", "word": "GGG", "weight": 0}, + {"key": "hhh", "word": "HHH", "weight": 0}, + {"key": "iii", "word": "III", "weight": 1}, + {"key": "jjj", "word": "JJJ", "weight": -1}, + {"key": "kkk", "word": "KKK", "weight": 0}, + {"key": "lll", "word": "LLL", "weight": 0}, + {"key": "mmm", "word": "MMM", "weight": 0}, + {"key": "nnn", "word": "NNN", "weight": 0}, + {"key": "ooo", "word": "OOO", "weight": 1}, + {"key": "ppp", "word": "PPP", "weight": 0}, + {"key": "qqq", "word": "QQQ", "weight": -1}, + {"key": "rrr", "word": "RRR", "weight": 0}, + {"key": "sss", "word": "SSS", "weight": 0}, + {"key": "ttt", "word": "TTT", "weight": 0}, + {"key": "uuu", "word": "UUU", "weight": 1}, + {"key": "vvv", "word": "VVV", "weight": 0}, + {"key": "www", "word": "WWW", "weight": 0}, + {"key": "xxx", "word": "XXX", "weight": 0}, + {"key": "yyy", "word": "YYY", "weight": -1}, + {"key": "zzz", "word": "ZZZ", "weight": 0} ] RES_WO_ARGS = [ @@ -512,8 +519,4 @@ def test_multiple(self): def test_suite(): - return unittest.TestSuite(( - unittest.makeSuite(Test_sort), - unittest.makeSuite(Test_make_sortfunctions), - unittest.makeSuite(SortByTests), - )) + return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/tox.ini b/tox.ini index d2dc2e2..0651d0d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,27 +1,30 @@ [tox] -envlist = -# py26,py27,pypy,coverage,docs - py26,py27,pypy,,py32,py33,coverage +envlist = + py27,pypy,py34,py35,py36,py37,coverage [testenv] -commands = - python setup.py test -q +commands = + zope-testrunner --test-path=src +deps = + .[test] [testenv:coverage] +usedevelop = true basepython = - python2.6 -commands = - nosetests --with-xunit --with-xcoverage + python3.6 +commands = + coverage run -m zope.testrunner --test-path=src + coverage report --fail-under=100 deps = - nose + {[testenv]deps} coverage - nosexcover + [testenv:docs] basepython = - python2.6 -commands = + python3.6 +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 = - Sphinx + .[docs]