summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2015-03-05 10:28:23 -0600
committerMichael Merickel <michael@merickel.org>2015-03-05 10:28:23 -0600
commit6c1a1c60123d150a41fef3062df9a64b995305c5 (patch)
tree3be6e2cae14d7fd3b9951111a9c98c6bb7bfb4de
parent17997b33190056919395bda336f531a3b8bce057 (diff)
parentd476e6932f67adee5116620b546590ba0c2083de (diff)
downloadpyramid-6c1a1c60123d150a41fef3062df9a64b995305c5.tar.gz
pyramid-6c1a1c60123d150a41fef3062df9a64b995305c5.tar.bz2
pyramid-6c1a1c60123d150a41fef3062df9a64b995305c5.zip
Merge pull request #1471 from Pylons/feature.py3-coverage
update tox/travis to check code coverage on py3
-rw-r--r--.gitignore3
-rw-r--r--.travis.yml4
-rw-r--r--CHANGES.txt3
-rw-r--r--pyramid/compat.py55
-rw-r--r--pyramid/i18n.py8
-rw-r--r--pyramid/scaffolds/tests.py4
-rw-r--r--pyramid/tests/test_config/pkgs/asset/models.py8
-rw-r--r--pyramid/tests/test_config/pkgs/asset/views.py22
-rw-r--r--pyramid/tests/test_config/test_adapters.py2
-rw-r--r--pyramid/tests/test_path.py2
-rw-r--r--pyramid/tests/test_request.py2
-rw-r--r--pyramid/tests/test_scripts/pystartup.py1
-rw-r--r--pyramid/tests/test_scripts/pystartup.txt3
-rw-r--r--pyramid/tests/test_scripts/test_pserve.py2
-rw-r--r--pyramid/tests/test_scripts/test_pshell.py2
-rw-r--r--pyramid/tests/test_traversal.py2
-rw-r--r--pyramid/tests/test_urldispatch.py2
-rw-r--r--pyramid/tests/test_util.py4
-rw-r--r--pyramid/traversal.py2
-rw-r--r--pyramid/urldispatch.py2
-rw-r--r--pyramid/util.py2
-rw-r--r--setup.cfg3
-rw-r--r--tox.ini74
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, ')')
diff --git a/setup.cfg b/setup.cfg
index 9633b6980..875480594 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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]
diff --git a/tox.ini b/tox.ini
index 202e29e30..e0f99e7f6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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