diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | .travis.yml | 4 | ||||
| -rw-r--r-- | CHANGES.txt | 3 | ||||
| -rw-r--r-- | pyramid/compat.py | 55 | ||||
| -rw-r--r-- | pyramid/i18n.py | 8 | ||||
| -rw-r--r-- | pyramid/scaffolds/tests.py | 4 | ||||
| -rw-r--r-- | pyramid/tests/test_config/pkgs/asset/models.py | 8 | ||||
| -rw-r--r-- | pyramid/tests/test_config/pkgs/asset/views.py | 22 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_adapters.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_path.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_request.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/pystartup.py | 1 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/pystartup.txt | 3 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/test_pserve.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/test_pshell.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_traversal.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_urldispatch.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_util.py | 4 | ||||
| -rw-r--r-- | pyramid/traversal.py | 2 | ||||
| -rw-r--r-- | pyramid/urldispatch.py | 2 | ||||
| -rw-r--r-- | pyramid/util.py | 2 | ||||
| -rw-r--r-- | setup.cfg | 3 | ||||
| -rw-r--r-- | tox.ini | 74 |
23 files changed, 112 insertions, 100 deletions
diff --git a/.gitignore b/.gitignore index 1e3f68f26..fe132412a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,9 +8,12 @@ *~ .*.swp .coverage +.coverage.* .tox/ nosetests.xml coverage.xml +nosetests-*.xml +coverage-*.xml tutorial.db build/ dist/ diff --git a/.travis.yml b/.travis.yml index cb98fddbe..42b3073c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,9 @@ env: - TOXENV=py34 - TOXENV=pypy - TOXENV=pypy3 - - TOXENV=cover + - TOXENV=py2-docs + - TOXENV=py3-docs + - TOXENV=py2-cover,py3-cover,coverage install: - travis_retry pip install tox diff --git a/CHANGES.txt b/CHANGES.txt index b7a8f4764..19d77eb68 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -26,6 +26,9 @@ Features - Added support / testing for 'pypy3' under Tox and Travis. See https://github.com/Pylons/pyramid/pull/1469 +- Automate code coverage metrics across py2 and py3 instead of just py2. + See https://github.com/Pylons/pyramid/pull/1471 + - Cache busting for static resources has been added and is available via a new argument to ``pyramid.config.Configurator.add_static_view``: ``cachebust``. Core APIs are shipped for both cache busting via query strings and diff --git a/pyramid/compat.py b/pyramid/compat.py index a12790d82..e9edda359 100644 --- a/pyramid/compat.py +++ b/pyramid/compat.py @@ -23,7 +23,7 @@ except ImportError: # pragma: no cover # True if we are running on Python 3. PY3 = sys.version_info[0] == 3 -if PY3: # pragma: no cover +if PY3: string_types = str, integer_types = int, class_types = type, @@ -38,23 +38,21 @@ else: binary_type = str long = long - def text_(s, encoding='latin-1', errors='strict'): """ If ``s`` is an instance of ``binary_type``, return ``s.decode(encoding, errors)``, otherwise return ``s``""" if isinstance(s, binary_type): return s.decode(encoding, errors) - return s # pragma: no cover - + return s def bytes_(s, encoding='latin-1', errors='strict'): """ If ``s`` is an instance of ``text_type``, return ``s.encode(encoding, errors)``, otherwise return ``s``""" - if isinstance(s, text_type): # pragma: no cover + if isinstance(s, text_type): return s.encode(encoding, errors) return s -if PY3: # pragma: no cover +if PY3: def ascii_native_(s): if isinstance(s, text_type): s = s.encode('ascii') @@ -74,7 +72,7 @@ Python 2: If ``s`` is an instance of ``text_type``, return """ -if PY3: # pragma: no cover +if PY3: def native_(s, encoding='latin-1', errors='strict'): """ If ``s`` is an instance of ``text_type``, return ``s``, otherwise return ``str(s, encoding, errors)``""" @@ -97,7 +95,7 @@ Python 2: If ``s`` is an instance of ``text_type``, return ``s.encode(encoding, errors)``, otherwise return ``str(s)`` """ -if PY3: # pragma: no cover +if PY3: from urllib import parse urlparse = parse from urllib.parse import quote as url_quote @@ -174,13 +172,13 @@ else: # pragma: no cover return d.iterkeys() -if PY3: # pragma: no cover +if PY3: def map_(*arg): return list(map(*arg)) else: map_ = map -if PY3: # pragma: no cover +if PY3: def is_nonstr_iter(v): if isinstance(v, str): return False @@ -189,51 +187,48 @@ else: def is_nonstr_iter(v): return hasattr(v, '__iter__') -if PY3: # pragma: no cover +if PY3: im_func = '__func__' im_self = '__self__' else: im_func = 'im_func' im_self = 'im_self' -try: # pragma: no cover +try: import configparser -except ImportError: # pragma: no cover +except ImportError: import ConfigParser as configparser try: - from Cookie import SimpleCookie -except ImportError: # pragma: no cover from http.cookies import SimpleCookie +except ImportError: + from Cookie import SimpleCookie -if PY3: # pragma: no cover +if PY3: from html import escape else: from cgi import escape -try: # pragma: no cover - input_ = raw_input -except NameError: # pragma: no cover +if PY3: input_ = input +else: + input_ = raw_input - -# support annotations and keyword-only arguments in PY3 -if PY3: # pragma: no cover +if PY3: from inspect import getfullargspec as getargspec else: from inspect import getargspec -try: - from StringIO import StringIO as NativeIO -except ImportError: # pragma: no cover +if PY3: from io import StringIO as NativeIO +else: + from io import BytesIO as NativeIO # "json" is not an API; it's here to support older pyramid_debugtoolbar # versions which attempt to import it import json - -if PY3: # pragma: no cover +if PY3: # see PEP 3333 for why we encode WSGI PATH_INFO to latin-1 before # decoding it to utf-8 def decode_path_info(path): @@ -242,8 +237,8 @@ else: def decode_path_info(path): return path.decode('utf-8') -if PY3: # pragma: no cover - # see PEP 3333 for why we decode the path to latin-1 +if PY3: + # see PEP 3333 for why we decode the path to latin-1 from urllib.parse import unquote_to_bytes def unquote_bytes_to_wsgi(bytestring): @@ -277,7 +272,7 @@ def is_unbound_method(fn): is_bound = is_bound_method(fn) if not is_bound and inspect.isroutine(fn): - spec = inspect.getargspec(fn) + spec = getargspec(fn) has_self = len(spec.args) > 0 and spec.args[0] == 'self' if PY3 and inspect.isfunction(fn) and has_self: # pragma: no cover diff --git a/pyramid/i18n.py b/pyramid/i18n.py index 4c8f4b55d..c30351f7a 100644 --- a/pyramid/i18n.py +++ b/pyramid/i18n.py @@ -331,9 +331,9 @@ class Translations(gettext.GNUTranslations, object): """Like ``ugettext()``, but look the message up in the specified domain. """ - if PY3: # pragma: no cover + if PY3: return self._domains.get(domain, self).gettext(message) - else: # pragma: no cover + else: return self._domains.get(domain, self).ugettext(message) def dngettext(self, domain, singular, plural, num): @@ -352,10 +352,10 @@ class Translations(gettext.GNUTranslations, object): """Like ``ungettext()`` but look the message up in the specified domain. """ - if PY3: # pragma: no cover + if PY3: return self._domains.get(domain, self).ngettext( singular, plural, num) - else: # pragma: no cover + else: return self._domains.get(domain, self).ungettext( singular, plural, num) diff --git a/pyramid/scaffolds/tests.py b/pyramid/scaffolds/tests.py index dfbf9b6cf..db828759e 100644 --- a/pyramid/scaffolds/tests.py +++ b/pyramid/scaffolds/tests.py @@ -6,9 +6,9 @@ import tempfile import time try: + import http.client as httplib +except ImportError: import httplib -except ImportError: # pragma: no cover - import http.client as httplib #py3 class TemplateTest(object): def make_venv(self, directory): # pragma: no cover diff --git a/pyramid/tests/test_config/pkgs/asset/models.py b/pyramid/tests/test_config/pkgs/asset/models.py deleted file mode 100644 index d80d14bb3..000000000 --- a/pyramid/tests/test_config/pkgs/asset/models.py +++ /dev/null @@ -1,8 +0,0 @@ -from zope.interface import Interface - -class IFixture(Interface): - pass - -def fixture(): - """ """ - diff --git a/pyramid/tests/test_config/pkgs/asset/views.py b/pyramid/tests/test_config/pkgs/asset/views.py deleted file mode 100644 index cbfc5a574..000000000 --- a/pyramid/tests/test_config/pkgs/asset/views.py +++ /dev/null @@ -1,22 +0,0 @@ -from zope.interface import Interface -from webob import Response -from pyramid.httpexceptions import HTTPForbidden - -def fixture_view(context, request): - """ """ - return Response('fixture') - -def erroneous_view(context, request): - """ """ - raise RuntimeError() - -def exception_view(context, request): - """ """ - return Response('supressed') - -def protected_view(context, request): - """ """ - raise HTTPForbidden() - -class IDummy(Interface): - pass diff --git a/pyramid/tests/test_config/test_adapters.py b/pyramid/tests/test_config/test_adapters.py index 4cbb1bf80..b3b7576a3 100644 --- a/pyramid/tests/test_config/test_adapters.py +++ b/pyramid/tests/test_config/test_adapters.py @@ -219,7 +219,7 @@ class AdaptersConfiguratorMixinTests(unittest.TestCase): def test_add_response_adapter_dottednames(self): from pyramid.interfaces import IResponse config = self._makeOne(autocommit=True) - if PY3: # pragma: no cover + if PY3: str_name = 'builtins.str' else: str_name = '__builtin__.str' diff --git a/pyramid/tests/test_path.py b/pyramid/tests/test_path.py index fd927996a..f85373fd9 100644 --- a/pyramid/tests/test_path.py +++ b/pyramid/tests/test_path.py @@ -376,7 +376,7 @@ class TestDottedNameResolver(unittest.TestCase): def test_zope_dottedname_style_resolve_builtin(self): typ = self._makeOne() - if PY3: # pragma: no cover + if PY3: result = typ._zope_dottedname_style('builtins.str', None) else: result = typ._zope_dottedname_style('__builtin__.str', None) diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py index f142e4536..79cf1abb8 100644 --- a/pyramid/tests/test_request.py +++ b/pyramid/tests/test_request.py @@ -310,7 +310,7 @@ class TestRequest(unittest.TestCase): b'/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8' ) - if PY3: # pragma: no cover + if PY3: body = bytes(json.dumps({'a':inp}), 'utf-16') else: body = json.dumps({'a':inp}).decode('utf-8').encode('utf-16') diff --git a/pyramid/tests/test_scripts/pystartup.py b/pyramid/tests/test_scripts/pystartup.py deleted file mode 100644 index c4e5bcc80..000000000 --- a/pyramid/tests/test_scripts/pystartup.py +++ /dev/null @@ -1 +0,0 @@ -foo = 1 diff --git a/pyramid/tests/test_scripts/pystartup.txt b/pyramid/tests/test_scripts/pystartup.txt new file mode 100644 index 000000000..c62c4ca74 --- /dev/null +++ b/pyramid/tests/test_scripts/pystartup.txt @@ -0,0 +1,3 @@ +# this file has a .txt extension to avoid coverage reports +# since it is not imported but rather the contents are read and exec'd +foo = 1 diff --git a/pyramid/tests/test_scripts/test_pserve.py b/pyramid/tests/test_scripts/test_pserve.py index 107ff4c0a..75d4f5bef 100644 --- a/pyramid/tests/test_scripts/test_pserve.py +++ b/pyramid/tests/test_scripts/test_pserve.py @@ -4,7 +4,7 @@ import tempfile import unittest from pyramid.compat import PY3 -if PY3: # pragma: no cover +if PY3: import builtins as __builtin__ else: import __builtin__ diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py index a6ba2eaea..dab32fecd 100644 --- a/pyramid/tests/test_scripts/test_pshell.py +++ b/pyramid/tests/test_scripts/test_pshell.py @@ -379,7 +379,7 @@ class TestPShellCommand(unittest.TestCase): os.path.abspath( os.path.join( os.path.dirname(__file__), - 'pystartup.py'))) + 'pystartup.txt'))) shell = dummy.DummyShell() command.run(shell) self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') diff --git a/pyramid/tests/test_traversal.py b/pyramid/tests/test_traversal.py index 0dcc4a027..aa3f1ad16 100644 --- a/pyramid/tests/test_traversal.py +++ b/pyramid/tests/test_traversal.py @@ -335,7 +335,7 @@ class ResourceTreeTraverserTests(unittest.TestCase): foo = DummyContext(bar, path) root = DummyContext(foo, 'root') policy = self._makeOne(root) - if PY3: # pragma: no cover + if PY3: vhm_root = b'/Qu\xc3\xa9bec'.decode('latin-1') else: vhm_root = b'/Qu\xc3\xa9bec' diff --git a/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py index 1755d9f47..20a3a4fc8 100644 --- a/pyramid/tests/test_urldispatch.py +++ b/pyramid/tests/test_urldispatch.py @@ -120,7 +120,7 @@ class RoutesMapperTests(unittest.TestCase): def test___call__pathinfo_cant_be_decoded(self): from pyramid.exceptions import URLDecodeError mapper = self._makeOne() - if PY3: # pragma: no cover + if PY3: path_info = b'\xff\xfe\xe6\x00'.decode('latin-1') else: path_info = b'\xff\xfe\xe6\x00' diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py index 459c729a0..2bf6a710f 100644 --- a/pyramid/tests/test_util.py +++ b/pyramid/tests/test_util.py @@ -431,9 +431,9 @@ class Test_object_description(unittest.TestCase): self.assertEqual(self._callFUT(('a', 'b')), "('a', 'b')") def test_set(self): - if PY3: # pragma: no cover + if PY3: self.assertEqual(self._callFUT(set(['a'])), "{'a'}") - else: # pragma: no cover + else: self.assertEqual(self._callFUT(set(['a'])), "set(['a'])") def test_list(self): diff --git a/pyramid/traversal.py b/pyramid/traversal.py index 4c275c4c1..a38cf271e 100644 --- a/pyramid/traversal.py +++ b/pyramid/traversal.py @@ -575,7 +575,7 @@ the ``safe`` argument to this function. This corresponds to the """ -if PY3: # pragma: no cover +if PY3: # special-case on Python 2 for speed? unchecked def quote_path_segment(segment, safe=''): """ %s """ % quote_path_segment_doc diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py index 349742c4a..4a8828810 100644 --- a/pyramid/urldispatch.py +++ b/pyramid/urldispatch.py @@ -210,7 +210,7 @@ def _compile_route(route): def generator(dict): newdict = {} for k, v in dict.items(): - if PY3: # pragma: no cover + if PY3: if v.__class__ is binary_type: # url_quote below needs a native string, not bytes on Py3 v = v.decode('utf-8') diff --git a/pyramid/util.py b/pyramid/util.py index 5721a93fc..7a8af4899 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -309,7 +309,7 @@ def object_description(object): if isinstance(object, (bool, float, type(None))): return text_(str(object)) if isinstance(object, set): - if PY3: # pragma: no cover + if PY3: return shortrepr(object, '}') else: return shortrepr(object, ')') @@ -5,9 +5,6 @@ zip_ok = false match=^test where=pyramid nocapture=1 -cover-package=pyramid -cover-erase=1 -cover-min-percentage=100 [aliases] dev = develop easy_install pyramid[testing] @@ -1,23 +1,63 @@ [tox] -skipsdist = True -envlist = - py26,py27,py32,py33,py34,pypy,pypy3,cover +envlist = + py26,py27,py32,py33,py34,pypy,pypy3, + {py2,py3}-docs, + {py2,py3}-cover,coverage [testenv] -commands = - python setup.py -q dev - python setup.py -q test -q - -[testenv:cover] +# Most of these are defaults but if you specify any you can't fall back +# to defaults for others. basepython = - python2.6 -commands = - python setup.py -q dev - nosetests --with-xunit --with-xcoverage -deps = - nosexcover + py26: python2.6 + py27: python2.7 + py32: python3.2 + py33: python3.3 + py34: python3.4 + pypy: pypy + pypy3: pypy3 + py2: python2.7 + py3: python3.4 + +commands = + pip install pyramid[testing] + nosetests --with-xunit --xunit-file=nosetests-{envname}.xml {posargs:} -# we separate coverage into its own testenv because a) "last run wins" wrt -# cobertura jenkins reporting and b) pypy and jython can't handle any -# combination of versions of coverage and nosexcover that i can find. +[testenv:py2-cover] +commands = + pip install pyramid[testing] + coverage run --source=pyramid {envbindir}/nosetests + coverage xml -o coverage-py2.xml +setenv = + COVERAGE_FILE=.coverage.py2 +[testenv:py3-cover] +commands = + pip install pyramid[testing] + coverage run --source=pyramid {envbindir}/nosetests + coverage xml -o coverage-py3.xml +setenv = + COVERAGE_FILE=.coverage.py3 + +[testenv:py2-docs] +whitelist_externals = make +commands = + pip install pyramid[docs] + make -C docs html + +[testenv:py3-docs] +whitelist_externals = make +commands = + pip install pyramid[docs] + make -C docs html + +[testenv:coverage] +basepython = python3.4 +commands = + coverage erase + coverage combine + coverage xml + coverage report --show-missing --fail-under=100 +deps = + coverage +setenv = + COVERAGE_FILE=.coverage |
