[pypy-commit] pypy default: merge heads
arigo
noreply at buildbot.pypy.org
Fri Jul 22 13:24:07 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r45873:63a68e798693
Date: 2011-07-22 13:24 +0200
http://bitbucket.org/pypy/pypy/changeset/63a68e798693/
Log: merge heads
diff --git a/lib-python/modified-2.7/distutils/sysconfig_pypy.py b/lib-python/modified-2.7/distutils/sysconfig_pypy.py
--- a/lib-python/modified-2.7/distutils/sysconfig_pypy.py
+++ b/lib-python/modified-2.7/distutils/sysconfig_pypy.py
@@ -116,6 +116,12 @@
if compiler.compiler_type == "unix":
compiler.compiler_so.extend(['-fPIC', '-Wimplicit'])
compiler.shared_lib_extension = get_config_var('SO')
+ if "CFLAGS" in os.environ:
+ cflags = os.environ["CFLAGS"]
+ compiler.compiler.append(cflags)
+ compiler.compiler_so.append(cflags)
+ compiler.linker_so.append(cflags)
+
from sysconfig_cpython import (
parse_makefile, _variable_rx, expand_makefile_vars)
diff --git a/pypy/doc/coding-guide.rst b/pypy/doc/coding-guide.rst
--- a/pypy/doc/coding-guide.rst
+++ b/pypy/doc/coding-guide.rst
@@ -929,6 +929,19 @@
located in the ``py/bin/`` directory. For switches to
modify test execution pass the ``-h`` option.
+Coverage reports
+----------------
+
+In order to get coverage reports the `pytest-cov`_ plugin is included.
+it adds some extra requirements ( coverage_ and `cov-core`_ )
+and can once they are installed coverage testing can be invoked via::
+
+ python test_all.py --cov file_or_direcory_to_cover file_or_directory
+
+.. _`pytest-cov`: http://pypi.python.org/pypi/pytest-cov
+.. _`coverage`: http://pypi.python.org/pypi/coverage
+.. _`cov-core`: http://pypi.python.org/pypi/cov-core
+
Test conventions
----------------
diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst
--- a/pypy/doc/windows.rst
+++ b/pypy/doc/windows.rst
@@ -32,6 +32,15 @@
modules that relies on third-party libraries. See below how to get
and build them.
+Preping Windows for the Large Build
+-----------------------------------
+
+Follow http://usa.autodesk.com/adsk/servlet/ps/dl/item?siteID=123112&id=9583842&linkID=9240617 to allow Windows up to 3GB for 32bit applications if you are on a 32bit version of windows. If you are using Visual C++ 2008 (untested with 2005), then you will have a utility called editbin.exe within Visual Studio 9.0\VC\bin. You will need to execute::
+
+ editbin /largeaddressaware pypy.exe
+
+on the pypy.exe or python.exe you are using to buld the new pypy. Reboot now if you followed the 3G instructions for 32bit windows.
+
Installing external packages
----------------------------
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -2820,11 +2820,11 @@
def test_residual_call_invalidate_some_arrays(self):
ops = """
[p1, p2, i1]
- p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p3 = getarrayitem_gc(p2, 0, descr=arraydescr2)
p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
i3 = call(i1, descr=writearraydescr)
- p5 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p5 = getarrayitem_gc(p2, 0, descr=arraydescr2)
p6 = getarrayitem_gc(p2, 1, descr=arraydescr2)
i4 = getarrayitem_gc(p1, 1, descr=arraydescr)
escape(p3)
@@ -2837,7 +2837,7 @@
"""
expected = """
[p1, p2, i1]
- p3 = getarrayitem_gc(p1, 0, descr=arraydescr2)
+ p3 = getarrayitem_gc(p2, 0, descr=arraydescr2)
p4 = getarrayitem_gc(p2, 1, descr=arraydescr2)
i2 = getarrayitem_gc(p1, 1, descr=arraydescr)
i3 = call(i1, descr=writearraydescr)
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -2586,7 +2586,23 @@
return n
res = self.meta_interp(f, [10, 1])
self.check_loops(getfield_gc=2)
+ assert res == f(10, 1)
+ def test_jit_merge_point_with_raw_pointer(self):
+ driver = JitDriver(greens = [], reds = ['n', 'x'])
+
+ TP = lltype.Array(lltype.Signed, hints={'nolength': True})
+
+ def f(n):
+ x = lltype.malloc(TP, 10, flavor='raw')
+ x[0] = 1
+ while n > 0:
+ driver.jit_merge_point(n=n, x=x)
+ n -= x[0]
+ lltype.free(x, flavor='raw')
+ return n
+
+ self.meta_interp(f, [10], repeat=3)
class TestLLtype(BaseLLtypeTests, LLJitMixin):
pass
diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py
--- a/pypy/jit/metainterp/warmstate.py
+++ b/pypy/jit/metainterp/warmstate.py
@@ -138,6 +138,9 @@
refvalue = cpu.ts.cast_to_ref(value)
cpu.set_future_value_ref(j, refvalue)
elif typecode == 'int':
+ if isinstance(lltype.typeOf(value), lltype.Ptr):
+ intvalue = llmemory.AddressAsInt(llmemory.cast_ptr_to_adr(value))
+ else:
intvalue = lltype.cast_primitive(lltype.Signed, value)
cpu.set_future_value_int(j, intvalue)
elif typecode == 'float':
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -11,29 +11,15 @@
from pypy.tool.sourcetools import func_with_new_name
import math
-def dummy1(v):
- assert isinstance(v, float)
- return v
-
-def dummy2(v):
- assert isinstance(v, float)
- return v
-
TP = lltype.Array(lltype.Float, hints={'nolength': True})
numpy_driver = jit.JitDriver(greens = ['signature'],
reds = ['result_size', 'i', 'self', 'result'])
all_driver = jit.JitDriver(greens=['signature'], reds=['i', 'size', 'self'])
any_driver = jit.JitDriver(greens=['signature'], reds=['i', 'size', 'self'])
-slice_driver1 = jit.JitDriver(greens=['signature'], reds=['i', 'j', 'step', 'stop', 'storage', 'arr'])
-slice_driver2 = jit.JitDriver(greens=['signature'], reds=['i', 'j', 'step', 'stop', 'storage', 'arr'])
+slice_driver1 = jit.JitDriver(greens=['signature'], reds=['i', 'j', 'step', 'stop', 'source', 'dest'])
+slice_driver2 = jit.JitDriver(greens=['signature'], reds=['i', 'j', 'step', 'stop', 'source', 'dest'])
-def pos(v):
- return v
-def neg(v):
- return -v
-def absolute(v):
- return abs(v)
def add(v1, v2):
return v1 + v2
def mul(v1, v2):
@@ -59,21 +45,14 @@
arr.force_if_needed()
del self.invalidates[:]
- def _unop_impl(function):
- signature = Signature()
+ def _unaryop_impl(w_ufunc):
def impl(self, space):
- new_sig = self.signature.transition(signature)
- res = Call1(
- function,
- self,
- new_sig)
- self.invalidates.append(res)
- return space.wrap(res)
- return func_with_new_name(impl, "uniop_%s_impl" % function.__name__)
+ return w_ufunc(space, self)
+ return func_with_new_name(impl, "unaryop_%s_impl" % w_ufunc.__name__)
- descr_pos = _unop_impl(pos)
- descr_neg = _unop_impl(neg)
- descr_abs = _unop_impl(absolute)
+ descr_pos = _unaryop_impl(interp_ufuncs.positive)
+ descr_neg = _unaryop_impl(interp_ufuncs.negative)
+ descr_abs = _unaryop_impl(interp_ufuncs.absolute)
def _binop_impl(w_ufunc):
def impl(self, space, w_other):
@@ -268,23 +247,25 @@
def descr_mean(self, space):
return space.wrap(space.float_w(self.descr_sum(space))/self.find_size())
- def _sliceloop1(self, start, stop, step, arr, storage):
+ def _sliceloop1(self, start, stop, step, source, dest):
i = start
j = 0
while i < stop:
- slice_driver1.jit_merge_point(signature=arr.signature,
- step=step, stop=stop, i=i, j=j, arr=arr, storage=storage)
- storage[i] = arr.eval(j)
+ slice_driver1.jit_merge_point(signature=source.signature,
+ step=step, stop=stop, i=i, j=j, source=source,
+ dest=dest)
+ dest.storage[i] = source.eval(j)
j += 1
i += step
- def _sliceloop2(self, start, stop, step, arr, storage):
+ def _sliceloop2(self, start, stop, step, source, dest):
i = start
j = 0
while i > stop:
- slice_driver2.jit_merge_point(signature=arr.signature,
- step=step, stop=stop, i=i, j=j, arr=arr, storage=storage)
- storage[i] = arr.eval(j)
+ slice_driver2.jit_merge_point(signature=source.signature,
+ step=step, stop=stop, i=i, j=j, source=source,
+ dest=dest)
+ dest.storage[i] = source.eval(j)
j += 1
i += step
@@ -469,9 +450,9 @@
stop = self.calc_index(stop)
step = self.step * step
if step > 0:
- self._sliceloop1(start, stop, step, arr, self.parent.storage)
+ self._sliceloop1(start, stop, step, arr, self.parent)
else:
- self._sliceloop2(start, stop, step, arr, self.parent.storage)
+ self._sliceloop2(start, stop, step, arr, self.parent)
def calc_index(self, item):
return (self.start + item * self.step)
@@ -510,9 +491,9 @@
if not isinstance(arr, BaseArray):
arr = convert_to_array(space, arr)
if step > 0:
- self._sliceloop1(start, stop, step, arr, self.storage)
+ self._sliceloop1(start, stop, step, arr, self)
else:
- self._sliceloop2(start, stop, step, arr, self.storage)
+ self._sliceloop2(start, stop, step, arr, self)
def __del__(self):
lltype.free(self.storage, flavor='raw')
diff --git a/pypy/module/micronumpy/interp_ufuncs.py b/pypy/module/micronumpy/interp_ufuncs.py
--- a/pypy/module/micronumpy/interp_ufuncs.py
+++ b/pypy/module/micronumpy/interp_ufuncs.py
@@ -72,6 +72,11 @@
def multiply(lvalue, rvalue):
return lvalue * rvalue
+# Used by numarray for __pos__. Not visible from numpy application space.
+ at ufunc
+def positive(value):
+ return value
+
@ufunc
def negative(value):
return -value
@@ -114,4 +119,4 @@
@ufunc2
def mod(lvalue, rvalue):
- return math.fmod(lvalue, rvalue)
\ No newline at end of file
+ return math.fmod(lvalue, rvalue)
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -171,6 +171,12 @@
for i in range(5):
assert b[i] == i + 5
+ def test_radd(self):
+ from numpy import array
+ r = 3 + array(range(3))
+ for i in range(3):
+ assert r[i] == i + 3
+
def test_add_list(self):
from numpy import array
a = array(range(5))
diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py
--- a/pypy/module/micronumpy/test/test_zjit.py
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -1,7 +1,7 @@
from pypy.jit.metainterp.test.support import LLJitMixin
from pypy.rpython.test.test_llinterp import interpret
from pypy.module.micronumpy.interp_numarray import (SingleDimArray, Signature,
- FloatWrapper, Call2, SingleDimSlice, add, mul, neg, Call1)
+ FloatWrapper, Call2, SingleDimSlice, add, mul, Call1)
from pypy.module.micronumpy.interp_ufuncs import negative
from pypy.module.micronumpy.compile import numpy_compile
from pypy.rlib.objectmodel import specialize
@@ -48,19 +48,6 @@
"int_lt": 1, "guard_true": 1, "jump": 1})
assert result == f(5)
- def test_neg(self):
- def f(i):
- ar = SingleDimArray(i)
- v = Call1(neg, ar, Signature())
- return v.get_concrete().storage[3]
-
- result = self.meta_interp(f, [5], listops=True, backendopt=True)
- self.check_loops({"getarrayitem_raw": 1, "float_neg": 1,
- "setarrayitem_raw": 1, "int_add": 1,
- "int_lt": 1, "guard_true": 1, "jump": 1})
-
- assert result == f(5)
-
def test_sum(self):
space = self.space
diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py
--- a/pypy/module/pypyjit/test_pypy_c/test_string.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_string.py
@@ -92,10 +92,10 @@
p51 = new_with_vtable(21136408)
setfield_gc(p51, p28, descr=<GcPtrFieldDescr .*NumberStringParser.inst_literal .*>)
setfield_gc(p51, ConstPtr(ptr51), descr=<GcPtrFieldDescr pypy.objspace.std.strutil.NumberStringParser.inst_fname .*>)
- setfield_gc(p51, i29, descr=<SignedFieldDescr .*NumberStringParser.inst_n .*>)
setfield_gc(p51, 1, descr=<SignedFieldDescr .*NumberStringParser.inst_sign .*>)
setfield_gc(p51, 16, descr=<SignedFieldDescr .*NumberStringParser.inst_base .*>)
setfield_gc(p51, p28, descr=<GcPtrFieldDescr .*NumberStringParser.inst_s .*>)
+ setfield_gc(p51, i29, descr=<SignedFieldDescr .*NumberStringParser.inst_n .*>)
p55 = call(ConstClass(parse_digit_string), p51, descr=<GcPtrCallDescr>)
guard_no_exception(descr=...)
i57 = call(ConstClass(rbigint.toint), p55, descr=<SignedCallDescr>)
@@ -104,4 +104,4 @@
guard_no_overflow(descr=...)
--TICK--
jump(p0, p1, p2, p3, p4, p5, i58, i7, i8, p9, p10, descr=<Loop4>)
- """)
\ No newline at end of file
+ """)
diff --git a/pypy/module/select/test/test_epoll.py b/pypy/module/select/test/test_epoll.py
--- a/pypy/module/select/test/test_epoll.py
+++ b/pypy/module/select/test/test_epoll.py
@@ -138,7 +138,7 @@
expected.sort()
assert events == expected
- assert then - now < 0.01
+ assert then - now < 0.02
now = time.time()
events = ep.poll(timeout=2.1, maxevents=4)
@@ -151,7 +151,7 @@
now = time.time()
events = ep.poll(1, 4)
then = time.time()
- assert then - now < 0.01
+ assert then - now < 0.02
events.sort()
expected = [
@@ -168,7 +168,7 @@
now = time.time()
events = ep.poll(1, 4)
then = time.time()
- assert then - now < 0.01
+ assert then - now < 0.02
expected = [(server.fileno(), select.EPOLLOUT)]
assert events == expected
@@ -192,7 +192,7 @@
now = time.time()
ep.poll(1, 4)
then = time.time()
- assert then - now < 0.01
+ assert then - now < 0.02
server.close()
ep.unregister(fd)
diff --git a/pypy/objspace/std/dictproxyobject.py b/pypy/objspace/std/dictproxyobject.py
--- a/pypy/objspace/std/dictproxyobject.py
+++ b/pypy/objspace/std/dictproxyobject.py
@@ -86,7 +86,7 @@
def clear(self, w_dict):
self.unerase(w_dict.dstorage).dict_w.clear()
- self.unerase(w_dict.dstorage).mutated()
+ self.unerase(w_dict.dstorage).mutated(None)
class DictProxyIteratorImplementation(IteratorImplementation):
def __init__(self, space, strategy, dictimplementation):
diff --git a/pypy/objspace/std/test/test_identitydict.py b/pypy/objspace/std/test/test_identitydict.py
--- a/pypy/objspace/std/test/test_identitydict.py
+++ b/pypy/objspace/std/test/test_identitydict.py
@@ -1,3 +1,4 @@
+import py
from pypy.interpreter.gateway import interp2app
from pypy.conftest import gettestobjspace
from pypy.conftest import option
@@ -8,6 +9,8 @@
from pypy.objspace.std import identitydict
cls.space = gettestobjspace(
**{"objspace.std.withidentitydict": True})
+ if option.runappdirect:
+ py.test.skip("interp2app doesn't work on appdirect")
def compares_by_identity(space, w_cls):
return space.wrap(w_cls.compares_by_identity())
@@ -49,7 +52,7 @@
def setup_class(cls):
cls.space = gettestobjspace(**{"objspace.std.withidentitydict": True})
if option.runappdirect:
- py.test.skip("__repr__ doesn't work on appdirect")
+ py.test.skip("interp2app doesn't work on appdirect")
def w_uses_identity_strategy(self, obj):
import __pypy__
diff --git a/pypy/rlib/streamio.py b/pypy/rlib/streamio.py
--- a/pypy/rlib/streamio.py
+++ b/pypy/rlib/streamio.py
@@ -875,28 +875,32 @@
if bufsize == -1: # Get default from the class
bufsize = self.bufsize
self.bufsize = bufsize # buffer size (hint only)
- self.buf = ""
+ self.buf = []
+ self.buflen = 0
def flush_buffers(self):
if self.buf:
- self.do_write(self.buf)
- self.buf = ""
+ self.do_write(''.join(self.buf))
+ self.buf = []
+ self.buflen = 0
def tell(self):
- return self.do_tell() + len(self.buf)
+ return self.do_tell() + self.buflen
def write(self, data):
- buflen = len(self.buf)
+ buflen = self.buflen
datalen = len(data)
if datalen + buflen < self.bufsize:
- self.buf += data
+ self.buf.append(data)
+ self.buflen += datalen
elif buflen:
- slice = self.bufsize - buflen
- assert slice >= 0
- self.buf += data[:slice]
- self.do_write(self.buf)
- self.buf = ""
- self.write(data[slice:])
+ i = self.bufsize - buflen
+ assert i >= 0
+ self.buf.append(data[:i])
+ self.do_write(''.join(self.buf))
+ self.buf = []
+ self.buflen = 0
+ self.write(data[i:])
else:
self.do_write(data)
@@ -922,11 +926,27 @@
"""
def write(self, data):
- BufferingOutputStream.write(self, data)
- p = self.buf.rfind('\n') + 1
- if p >= 0:
- self.do_write(self.buf[:p])
- self.buf = self.buf[p:]
+ p = data.rfind('\n') + 1
+ assert p >= 0
+ if self.buflen + len(data) < self.bufsize:
+ if p == 0:
+ self.buf.append(data)
+ self.buflen += len(data)
+ else:
+ if self.buflen:
+ self.do_write(''.join(self.buf))
+ self.do_write(data[:p])
+ self.buf = [data[p:]]
+ self.buflen = len(self.buf[0])
+ else:
+ if self.buflen + p < self.bufsize:
+ p = self.bufsize - self.buflen
+ if self.buflen:
+ self.do_write(''.join(self.buf))
+ assert p >= 0
+ self.do_write(data[:p])
+ self.buf = [data[p:]]
+ self.buflen = len(self.buf[0])
# ____________________________________________________________
diff --git a/pypy/test_all.py b/pypy/test_all.py
--- a/pypy/test_all.py
+++ b/pypy/test_all.py
@@ -18,4 +18,5 @@
if __name__ == '__main__':
import tool.autopath
import pytest
- sys.exit(pytest.main())
+ import pytest_cov
+ sys.exit(pytest.main(plugins=[pytest_cov]))
diff --git a/pypy/tool/jitlogparser/parser.py b/pypy/tool/jitlogparser/parser.py
--- a/pypy/tool/jitlogparser/parser.py
+++ b/pypy/tool/jitlogparser/parser.py
@@ -30,6 +30,9 @@
def getres(self):
return self._getvar(self.res)
+ def getdescr(self):
+ return self.descr
+
def _getvar(self, v):
return v
@@ -37,9 +40,9 @@
return self._is_guard
def repr(self):
- args = self.args
+ args = self.getargs()
if self.descr is not None:
- args.append('descr=%s' % self.descr)
+ args.append('descr=%s' % self.getdescr())
arglist = ', '.join(args)
if self.res is not None:
return '%s = %s(%s)' % (self.getres(), self.name, arglist)
diff --git a/pypy/tool/jitlogparser/test/test_parser.py b/pypy/tool/jitlogparser/test/test_parser.py
--- a/pypy/tool/jitlogparser/test/test_parser.py
+++ b/pypy/tool/jitlogparser/test/test_parser.py
@@ -1,6 +1,6 @@
from pypy.tool.jitlogparser.parser import (SimpleParser, TraceForOpcode,
Function, adjust_bridges,
- import_log)
+ import_log, Op)
from pypy.tool.jitlogparser.storage import LoopStorage
import py, sys
@@ -225,3 +225,9 @@
assert 'cmp' in loops[1].operations[1].asm
# bridge
assert 'jo' in loops[3].operations[3].asm
+
+def test_Op_repr_is_pure():
+ op = Op('foobar', ['a', 'b'], 'c', 'mydescr')
+ myrepr = 'c = foobar(a, b, descr=mydescr)'
+ assert op.repr() == myrepr
+ assert op.repr() == myrepr # do it twice
diff --git a/pypy/tool/release/win32build.py b/pypy/tool/release/win32build.py
--- a/pypy/tool/release/win32build.py
+++ b/pypy/tool/release/win32build.py
@@ -24,6 +24,6 @@
shutil.copy(str(pypydir.join('..', '..', 'expat-2.0.1', 'win32', 'bin', 'release', 'libexpat.dll')), str(builddir))
make_pypy('', ['-Ojit'])
-make_pypy('-nojit', [])
+make_pypy('-nojit', ['-O2'])
#make_pypy('-stackless', [--stackless])
#make_pypy('-sandbox', [--sandbox])
diff --git a/pytest.py b/pytest.py
--- a/pytest.py
+++ b/pytest.py
@@ -9,6 +9,8 @@
from _pytest import __version__
if __name__ == '__main__': # if run as a script or by 'python -m pytest'
- raise SystemExit(main())
+ #XXX: sync to upstream later
+ import pytest_cov
+ raise SystemExit(main(plugins=[pytest_cov]))
else:
_preloadplugins() # to populate pytest.* namespace so help(pytest) works
diff --git a/pytest_cov.py b/pytest_cov.py
new file mode 100644
--- /dev/null
+++ b/pytest_cov.py
@@ -0,0 +1,353 @@
+"""produce code coverage reports using the 'coverage' package, including support for distributed testing.
+
+This plugin produces coverage reports. It supports centralised testing and distributed testing in
+both load and each modes. It also supports coverage of subprocesses.
+
+All features offered by the coverage package should be available, either through pytest-cov or
+through coverage's config file.
+
+
+Installation
+------------
+
+The `pytest-cov`_ package may be installed with pip or easy_install::
+
+ pip install pytest-cov
+ easy_install pytest-cov
+
+.. _`pytest-cov`: http://pypi.python.org/pypi/pytest-cov/
+
+
+Uninstallation
+--------------
+
+Uninstalling packages is supported by pip::
+
+ pip uninstall pytest-cov
+
+However easy_install does not provide an uninstall facility.
+
+.. IMPORTANT::
+
+ Ensure that you manually delete the init_cov_core.pth file in your site-packages directory.
+
+ This file starts coverage collection of subprocesses if appropriate during site initialisation
+ at python startup.
+
+
+Usage
+-----
+
+Centralised Testing
+~~~~~~~~~~~~~~~~~~~
+
+Centralised testing will report on the combined coverage of the main process and all of it's
+subprocesses.
+
+Running centralised testing::
+
+ py.test --cov myproj tests/
+
+Shows a terminal report::
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Distributed Testing: Load
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Distributed testing with dist mode set to load will report on the combined coverage of all slaves.
+The slaves may be spread out over any number of hosts and each slave may be located anywhere on the
+file system. Each slave will have it's subprocesses measured.
+
+Running distributed testing with dist mode set to load::
+
+ py.test --cov myproj -n 2 tests/
+
+Shows a terminal report::
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Again but spread over different hosts and different directories::
+
+ py.test --cov myproj --dist load
+ --tx ssh=memedough at host1//chdir=testenv1
+ --tx ssh=memedough at host2//chdir=/tmp/testenv2//python=/tmp/env1/bin/python
+ --rsyncdir myproj --rsyncdir tests --rsync examples
+ tests/
+
+Shows a terminal report::
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Distributed Testing: Each
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Distributed testing with dist mode set to each will report on the combined coverage of all slaves.
+Since each slave is running all tests this allows generating a combined coverage report for multiple
+environments.
+
+Running distributed testing with dist mode set to each::
+
+ py.test --cov myproj --dist each
+ --tx popen//chdir=/tmp/testenv3//python=/usr/local/python27/bin/python
+ --tx ssh=memedough at host2//chdir=/tmp/testenv4//python=/tmp/env2/bin/python
+ --rsyncdir myproj --rsyncdir tests --rsync examples
+ tests/
+
+Shows a terminal report::
+
+ ---------------------------------------- coverage ----------------------------------------
+ platform linux2, python 2.6.5-final-0
+ platform linux2, python 2.7.0-final-0
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+Reporting
+---------
+
+It is possible to generate any combination of the reports for a single test run.
+
+The available reports are terminal (with or without missing line numbers shown), HTML, XML and
+annotated source code.
+
+The terminal report without line numbers (default)::
+
+ py.test --cov-report term --cov myproj tests/
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover
+ ----------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94%
+ myproj/feature4286 94 7 92%
+ ----------------------------------------
+ TOTAL 353 20 94%
+
+
+The terminal report with line numbers::
+
+ py.test --cov-report term-missing --cov myproj tests/
+
+ -------------------- coverage: platform linux2, python 2.6.4-final-0 ---------------------
+ Name Stmts Miss Cover Missing
+ --------------------------------------------------
+ myproj/__init__ 2 0 100%
+ myproj/myproj 257 13 94% 24-26, 99, 149, 233-236, 297-298, 369-370
+ myproj/feature4286 94 7 92% 183-188, 197
+ --------------------------------------------------
+ TOTAL 353 20 94%
+
+
+The remaining three reports output to files without showing anything on the terminal (useful for
+when the output is going to a continuous integration server)::
+
+ py.test --cov-report html
+ --cov-report xml
+ --cov-report annotate
+ --cov myproj tests/
+
+
+Coverage Data File
+------------------
+
+The data file is erased at the beginning of testing to ensure clean data for each test run.
+
+The data file is left at the end of testing so that it is possible to use normal coverage tools to
+examine it.
+
+
+Coverage Config File
+--------------------
+
+This plugin provides a clean minimal set of command line options that are added to pytest. For
+further control of coverage use a coverage config file.
+
+For example if tests are contained within the directory tree being measured the tests may be
+excluded if desired by using a .coveragerc file with the omit option set::
+
+ py.test --cov-config .coveragerc
+ --cov myproj
+ myproj/tests/
+
+Where the .coveragerc file contains file globs::
+
+ [run]
+ omit = tests/*
+
+For full details refer to the `coverage config file`_ documentation.
+
+.. _`coverage config file`: http://nedbatchelder.com/code/coverage/config.html
+
+Note that this plugin controls some options and setting the option in the config file will have no
+effect. These include specifying source to be measured (source option) and all data file handling
+(data_file and parallel options).
+
+
+Limitations
+-----------
+
+For distributed testing the slaves must have the pytest-cov package installed. This is needed since
+the plugin must be registered through setuptools / distribute for pytest to start the plugin on the
+slave.
+
+For subprocess measurement environment variables must make it from the main process to the
+subprocess. The python used by the subprocess must have pytest-cov installed. The subprocess must
+do normal site initialisation so that the environment variables can be detected and coverage
+started.
+
+
+Acknowledgements
+----------------
+
+Whilst this plugin has been built fresh from the ground up it has been influenced by the work done
+on pytest-coverage (Ross Lawley, James Mills, Holger Krekel) and nose-cover (Jason Pellerin) which are
+other coverage plugins.
+
+Ned Batchelder for coverage and its ability to combine the coverage results of parallel runs.
+
+Holger Krekel for pytest with its distributed testing support.
+
+Jason Pellerin for nose.
+
+Michael Foord for unittest2.
+
+No doubt others have contributed to these tools as well.
+"""
+
+
+def pytest_addoption(parser):
+ """Add options to control coverage."""
+
+ group = parser.getgroup('coverage reporting with distributed testing support')
+ group.addoption('--cov', action='append', default=[], metavar='path',
+ dest='cov_source',
+ help='measure coverage for filesystem path (multi-allowed)')
+ group.addoption('--cov-report', action='append', default=[], metavar='type',
+ choices=['term', 'term-missing', 'annotate', 'html', 'xml'],
+ dest='cov_report',
+ help='type of report to generate: term, term-missing, annotate, html, xml (multi-allowed)')
+ group.addoption('--cov-config', action='store', default='.coveragerc', metavar='path',
+ dest='cov_config',
+ help='config file for coverage, default: .coveragerc')
+
+
+def pytest_configure(config):
+ """Activate coverage plugin if appropriate."""
+
+ if config.getvalue('cov_source'):
+ config.pluginmanager.register(CovPlugin(), '_cov')
+
+
+class CovPlugin(object):
+ """Use coverage package to produce code coverage reports.
+
+ Delegates all work to a particular implementation based on whether
+ this test process is centralised, a distributed master or a
+ distributed slave.
+ """
+
+ def __init__(self):
+ """Creates a coverage pytest plugin.
+
+ We read the rc file that coverage uses to get the data file
+ name. This is needed since we give coverage through it's API
+ the data file name.
+ """
+
+ # Our implementation is unknown at this time.
+ self.cov_controller = None
+
+ def pytest_sessionstart(self, session):
+ """At session start determine our implementation and delegate to it."""
+
+ import cov_core
+
+ cov_source = session.config.getvalue('cov_source')
+ cov_report = session.config.getvalue('cov_report') or ['term']
+ cov_config = session.config.getvalue('cov_config')
+
+ session_name = session.__class__.__name__
+ is_master = (session.config.pluginmanager.hasplugin('dsession') or
+ session_name == 'DSession')
+ is_slave = (hasattr(session.config, 'slaveinput') or
+ session_name == 'SlaveSession')
+ nodeid = None
+
+ if is_master:
+ controller_cls = cov_core.DistMaster
+ elif is_slave:
+ controller_cls = cov_core.DistSlave
+ nodeid = session.config.slaveinput.get('slaveid', getattr(session, 'nodeid'))
+ else:
+ controller_cls = cov_core.Central
+
+ self.cov_controller = controller_cls(cov_source,
+ cov_report,
+ cov_config,
+ session.config,
+ nodeid)
+
+ self.cov_controller.start()
+
+ def pytest_configure_node(self, node):
+ """Delegate to our implementation."""
+
+ self.cov_controller.configure_node(node)
+ pytest_configure_node.optionalhook = True
+
+ def pytest_testnodedown(self, node, error):
+ """Delegate to our implementation."""
+
+ self.cov_controller.testnodedown(node, error)
+ pytest_testnodedown.optionalhook = True
+
+ def pytest_sessionfinish(self, session, exitstatus):
+ """Delegate to our implementation."""
+
+ self.cov_controller.finish()
+
+ def pytest_terminal_summary(self, terminalreporter):
+ """Delegate to our implementation."""
+
+ self.cov_controller.summary(terminalreporter._tw)
+
+
+def pytest_funcarg__cov(request):
+ """A pytest funcarg that provides access to the underlying coverage object."""
+
+ # Check with hasplugin to avoid getplugin exception in older pytest.
+ if request.config.pluginmanager.hasplugin('_cov'):
+ plugin = request.config.pluginmanager.getplugin('_cov')
+ if plugin.cov_controller:
+ return plugin.cov_controller.cov
+ return None
More information about the pypy-commit
mailing list