summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2013-07-31 21:54:18 +0200
committerAntoine Pitrou <solipsis@pitrou.net>2013-07-31 21:54:18 +0200
commitc27cd71cd71e5b3f464f6994e2a73f201eb430ca (patch)
treeb973bb782096772fc8a38a0c3bd534acc867c10a
parent5c30a75722f1fb42f4e49dcc5dd30d90d7717086 (diff)
parentc91d5eea106d191e931f953bdbdb1db25e051767 (diff)
downloadcpython-git-c27cd71cd71e5b3f464f6994e2a73f201eb430ca.tar.gz
Merge
-rw-r--r--.hgignore5
-rw-r--r--Doc/library/aifc.rst3
-rw-r--r--Doc/library/difflib.rst2
-rw-r--r--Doc/library/email.iterators.rst2
-rw-r--r--Doc/library/email.policy.rst2
-rw-r--r--Doc/library/enum.rst10
-rw-r--r--Doc/library/unittest.rst6
-rw-r--r--Doc/library/wave.rst5
-rw-r--r--Doc/tutorial/inputoutput.rst13
-rw-r--r--Doc/whatsnew/3.4.rst5
-rw-r--r--Lib/test/test_wave.py29
-rw-r--r--Lib/wave.py13
-rw-r--r--Makefile.pre.in49
-rw-r--r--Misc/NEWS6
-rw-r--r--Modules/_io/iobase.c4
-rw-r--r--Modules/_sha3/sha3module.c4
-rw-r--r--Modules/_testcapimodule.c11
17 files changed, 137 insertions, 32 deletions
diff --git a/.hgignore b/.hgignore
index 2c0ed631ea..73e1e56536 100644
--- a/.hgignore
+++ b/.hgignore
@@ -36,6 +36,7 @@ Modules/Setup.local
Modules/config.c
Modules/ld_so_aix$
Parser/pgen$
+^lcov-report/
^core
^python-gdb.py
^python.exe-gdb.py
@@ -91,3 +92,7 @@ Modules/_testembed
.coverage
coverage/
htmlcov/
+*.gcda
+*.gcno
+*.gcov
+coverage.info
diff --git a/Doc/library/aifc.rst b/Doc/library/aifc.rst
index c1cd215c3f..44a0a24ff5 100644
--- a/Doc/library/aifc.rst
+++ b/Doc/library/aifc.rst
@@ -51,7 +51,8 @@ Module :mod:`aifc` defines the following function:
used for writing, the file object should be seekable, unless you know ahead of
time how many samples you are going to write in total and use
:meth:`writeframesraw` and :meth:`setnframes`.
- Objects returned by :func:`.open` also supports the :keyword:`with` statement.
+ The :func:`.open` function may be used in a :keyword:`with` statement. When
+ the :keyword:`with` block completes, the :meth:`~aifc.close` method is called.
.. versionchanged:: 3.4
Support for the :keyword:`with` statement was added.
diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst
index 836e240b83..ad1466efaa 100644
--- a/Doc/library/difflib.rst
+++ b/Doc/library/difflib.rst
@@ -752,7 +752,7 @@ It is also contained in the Python source distribution, as
# we're passing these as arguments to the diff function
fromdate = time.ctime(os.stat(fromfile).st_mtime)
todate = time.ctime(os.stat(tofile).st_mtime)
- with open(fromlines) as fromf, open(tofile) as tof:
+ with open(fromfile) as fromf, open(tofile) as tof:
fromlines, tolines = list(fromf), list(tof)
if options.u:
diff --git a/Doc/library/email.iterators.rst b/Doc/library/email.iterators.rst
index 6c7200f98a..7882718695 100644
--- a/Doc/library/email.iterators.rst
+++ b/Doc/library/email.iterators.rst
@@ -68,7 +68,7 @@ The following function has been added as a useful debugging tool. It should
text/plain
text/plain
- .. testcleanup::
+ .. testsetup::
>>> somefile.close()
diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst
index d85054ac83..54ebb1cf11 100644
--- a/Doc/library/email.policy.rst
+++ b/Doc/library/email.policy.rst
@@ -85,7 +85,7 @@ file on disk and pass it to the system ``sendmail`` program on a Unix system:
>>> p.stdin.close()
>>> rc = p.wait()
-.. testcleanup::
+.. testsetup::
>>> mymsg.close()
>>> mocker.stop()
diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
index 1e464d7361..686470534d 100644
--- a/Doc/library/enum.rst
+++ b/Doc/library/enum.rst
@@ -483,7 +483,7 @@ Avoids having to specify the value for each enumeration member::
... def __new__(cls):
... value = len(cls.__members__) + 1
... obj = object.__new__(cls)
- ... obj._value = value
+ ... obj._value_ = value
... return obj
...
>>> class Color(AutoNumber):
@@ -505,19 +505,19 @@ enumerations)::
>>> class OrderedEnum(Enum):
... def __ge__(self, other):
... if self.__class__ is other.__class__:
- ... return self._value >= other._value
+ ... return self.value >= other.value
... return NotImplemented
... def __gt__(self, other):
... if self.__class__ is other.__class__:
- ... return self._value > other._value
+ ... return self.value > other.value
... return NotImplemented
... def __le__(self, other):
... if self.__class__ is other.__class__:
- ... return self._value <= other._value
+ ... return self.value <= other.value
... return NotImplemented
... def __lt__(self, other):
... if self.__class__ is other.__class__:
- ... return self._value < other._value
+ ... return self.value < other.value
... return NotImplemented
...
>>> class Grade(OrderedEnum):
diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
index a5c4783682..6c9f2f1868 100644
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -974,12 +974,12 @@ Test cases
Test that a warning is triggered when *callable* is called with any
positional or keyword arguments that are also passed to
:meth:`assertWarns`. The test passes if *warning* is triggered and
- fails if it isn't. Also, any unexpected exception is an error.
+ fails if it isn't. Any exception is an error.
To catch any of a group of warnings, a tuple containing the warning
classes may be passed as *warnings*.
If only the *warning* and possibly the *msg* arguments are given,
- returns a context manager so that the code under test can be written
+ return a context manager so that the code under test can be written
inline rather than as a function::
with self.assertWarns(SomeWarning):
@@ -992,7 +992,7 @@ Test cases
:attr:`warning` attribute, and the source line which triggered the
warnings in the :attr:`filename` and :attr:`lineno` attributes.
This can be useful if the intention is to perform additional checks
- on the exception raised::
+ on the warning caught::
with self.assertWarns(SomeWarning) as cm:
do_something()
diff --git a/Doc/library/wave.rst b/Doc/library/wave.rst
index 2e64d00305..c52af89774 100644
--- a/Doc/library/wave.rst
+++ b/Doc/library/wave.rst
@@ -39,6 +39,11 @@ The :mod:`wave` module defines the following function and exception:
:meth:`close` method is called; it is the caller's responsibility to close
the file object.
+ The :func:`.open` function may be used in a :keyword:`with` statement. When
+ the :keyword:`with` block completes, the :meth:`Wave_read.close()
+ <wave.Wave_read.close>` or :meth:`Wave_write.close()
+ <wave.Wave_write.close()>` method is called.
+
.. function:: openfp(file, mode)
diff --git a/Doc/tutorial/inputoutput.rst b/Doc/tutorial/inputoutput.rst
index 744abab988..7daf89b79b 100644
--- a/Doc/tutorial/inputoutput.rst
+++ b/Doc/tutorial/inputoutput.rst
@@ -322,9 +322,11 @@ first::
>>> f.write(s)
18
-``f.tell()`` returns an integer giving the file object's current position in the
-file, measured in bytes from the beginning of the file. To change the file
-object's position, use ``f.seek(offset, from_what)``. The position is computed
+``f.tell()`` returns an integer giving the file object's current position in the file
+represented as number of bytes from the beginning of the file when in `binary mode` and
+an opaque number when in `text mode`.
+
+To change the file object's position, use ``f.seek(offset, from_what)``. The position is computed
from adding *offset* to a reference point; the reference point is selected by
the *from_what* argument. A *from_what* value of 0 measures from the beginning
of the file, 1 uses the current file position, and 2 uses the end of the file as
@@ -345,7 +347,10 @@ beginning of the file as the reference point. ::
In text files (those opened without a ``b`` in the mode string), only seeks
relative to the beginning of the file are allowed (the exception being seeking
-to the very file end with ``seek(0, 2)``).
+to the very file end with ``seek(0, 2)``) and the only valid *offset* values are
+those returned from the ``f.tell()``, or zero. Any other *offset* value produces
+undefined behaviour.
+
When you're done with a file, call ``f.close()`` to close it and free up any
system resources taken up by the open file. After calling ``f.close()``,
diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst
index 17cec3f2ad..2575170f44 100644
--- a/Doc/whatsnew/3.4.rst
+++ b/Doc/whatsnew/3.4.rst
@@ -239,8 +239,11 @@ wave
The :meth:`~wave.getparams` method now returns a namedtuple rather than a
plain tuple. (Contributed by Claudiu Popa in :issue:`17487`.)
+:meth:`wave.open` now supports the context manager protocol. (Contributed
+by Claudiu Popa in :issue:`17616`.)
+
stat
----
+----
The stat module is now backed by a C implementation in :mod:`_stat`. A C
implementation is required as most of the values aren't standardized and
diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py
index 581d9739d5..e9ee15ca9a 100644
--- a/Lib/test/test_wave.py
+++ b/Lib/test/test_wave.py
@@ -1,7 +1,5 @@
-from test.support import TESTFN, run_unittest
-import os
+from test.support import TESTFN, unlink
import wave
-import struct
import unittest
nchannels = 2
@@ -17,10 +15,7 @@ class TestWave(unittest.TestCase):
def tearDown(self):
if self.f is not None:
self.f.close()
- try:
- os.remove(TESTFN)
- except OSError:
- pass
+ unlink(TESTFN)
def test_it(self, test_rounding=False):
self.f = wave.open(TESTFN, 'wb')
@@ -74,9 +69,23 @@ class TestWave(unittest.TestCase):
self.assertEqual(params.comptype, self.f.getcomptype())
self.assertEqual(params.compname, self.f.getcompname())
+ def test_context_manager(self):
+ self.f = wave.open(TESTFN, 'wb')
+ self.f.setnchannels(nchannels)
+ self.f.setsampwidth(sampwidth)
+ self.f.setframerate(framerate)
+ self.f.close()
+
+ with wave.open(TESTFN) as f:
+ self.assertFalse(f.getfp().closed)
+ self.assertIs(f.getfp(), None)
+
+ with open(TESTFN, 'wb') as testfile:
+ with self.assertRaises(wave.Error):
+ with wave.open(testfile, 'wb'):
+ pass
+ self.assertEqual(testfile.closed, False)
-def test_main():
- run_unittest(TestWave)
if __name__ == '__main__':
- test_main()
+ unittest.main()
diff --git a/Lib/wave.py b/Lib/wave.py
index ea410c12d7..695a4be838 100644
--- a/Lib/wave.py
+++ b/Lib/wave.py
@@ -167,6 +167,13 @@ class Wave_read:
def __del__(self):
self.close()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
#
# User visible methods.
#
@@ -323,6 +330,12 @@ class Wave_write:
def __del__(self):
self.close()
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
#
# User visible methods.
#
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 6e0492235c..ad4eb30824 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -211,6 +211,12 @@ HOST_GNU_TYPE= @host@
PROFILE_TASK= $(srcdir)/Tools/pybench/pybench.py -n 2 --with-gc --with-syscheck
#PROFILE_TASK= $(srcdir)/Lib/test/regrtest.py
+# report files for gcov / lcov coverage report
+COVERAGE_INFO= $(abs_builddir)/coverage.info
+COVERAGE_REPORT=$(abs_builddir)/lcov-report
+COVERAGE_REPORT_OPTIONS=--no-branch-coverage --title "CPython lcov report"
+
+
# === Definitions added by makesetup ===
@@ -463,11 +469,48 @@ run_profile_task:
build_all_use_profile:
$(MAKE) all CFLAGS="$(CFLAGS) -fprofile-use -fprofile-correction"
+# Compile and run with gcov
+.PHONY=coverage coverage-lcov coverage-report
coverage:
@echo "Building with support for coverage checking:"
- $(MAKE) clean
+ $(MAKE) clean profile-removal
$(MAKE) all CFLAGS="$(CFLAGS) -O0 -pg -fprofile-arcs -ftest-coverage" LIBS="$(LIBS) -lgcov"
+coverage-lcov:
+ @echo "Creating Coverage HTML report with LCOV:"
+ @rm -f $(COVERAGE_INFO)
+ @rm -rf $(COVERAGE_REPORT)
+ @lcov --capture --directory $(abs_builddir) \
+ --base-directory $(realpath $(abs_builddir)) \
+ --path $(realpath $(abs_srcdir)) \
+ --output-file $(COVERAGE_INFO)
+ : # remove 3rd party modules and system headers
+ @lcov --remove $(COVERAGE_INFO) \
+ '*/Modules/_ctypes/libffi*/*' \
+ '*/Modules/_sha3/keccak/*' \
+ '*/Modules/_decimal/libmpdec/*' \
+ '*/Modules/expat/*' \
+ '*/Modules/zlib/*' \
+ '*/Include/*' \
+ '/usr/include/*' \
+ '/usr/local/include/*' \
+ --output-file $(COVERAGE_INFO)
+ @genhtml $(COVERAGE_INFO) --output-directory $(COVERAGE_REPORT) \
+ $(COVERAGE_REPORT_OPTIONS)
+ @echo
+ @echo "lcov report at $(COVERAGE_REPORT)/index.html"
+ @echo
+
+coverage-report:
+ : # force rebuilding of parser and importlib
+ @touch $(GRAMMAR_INPUT)
+ @touch $(srcdir)/Lib/importlib/_bootstrap.py
+ : # build with coverage info
+ $(MAKE) coverage
+ : # run tests, ignore failures
+ $(TESTRUNNER) $(TESTOPTS) || true
+ : # build lcov report
+ $(MAKE) coverage-lcov
# Build the interpreter
$(BUILDPYTHON): Modules/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY)
@@ -1012,7 +1055,7 @@ LIBSUBDIRS= tkinter tkinter/test tkinter/test/test_tkinter \
tkinter/test/test_ttk site-packages test \
test/capath test/data \
test/cjkencodings test/decimaltestdata test/xmltestdata \
- test/subprocessdata test/sndhdrdata \
+ test/subprocessdata test/sndhdrdata test/support \
test/tracedmodules test/encoded_modules \
test/namespace_pkgs \
test/namespace_pkgs/both_portions \
@@ -1396,6 +1439,8 @@ clean: pycremoval
profile-removal:
find . -name '*.gc??' -exec rm -f {} ';'
+ rm -f $(COVERAGE_INFO)
+ rm -rf $(COVERAGE_REPORT)
clobber: clean profile-removal
-rm -f $(BUILDPYTHON) $(PGEN) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY) \
diff --git a/Misc/NEWS b/Misc/NEWS
index 1ef477f09f..e5fd939b16 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -177,6 +177,8 @@ Library
initialization, so as to reclaim allocated resources (Python callbacks)
at shutdown. Original patch by Robin Schreiber.
+- Issue #17616: wave.open now supports the context manager protocol.
+
- Issue #18599: Fix name attribute of _sha1.sha1() object. It now returns
'SHA1' instead of 'SHA'.
@@ -729,6 +731,10 @@ IDLE
Build
-----
+- Issue #18481: Add C coverage reporting with gcov and lcov. A new make target
+ "coverage-report" creates an instrumented Python build, runs unit tests
+ and creates a HTML. The report can be updated with "make coverage-lcov".
+
- Issue #17845: Clarified the message printed when some module are not built.
- Issue #18256: Compilation fix for recent AIX releases. Patch by
diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c
index a4f2c0e917..ae188dd7fe 100644
--- a/Modules/_io/iobase.c
+++ b/Modules/_io/iobase.c
@@ -210,8 +210,10 @@ iobase_finalize(PyObject *self)
/* If `closed` doesn't exist or can't be evaluated as bool, then the
object is probably in an unusable state, so ignore. */
res = PyObject_GetAttr(self, _PyIO_str_closed);
- if (res == NULL)
+ if (res == NULL) {
PyErr_Clear();
+ closed = -1;
+ }
else {
closed = PyObject_IsTrue(res);
Py_DECREF(res);
diff --git a/Modules/_sha3/sha3module.c b/Modules/_sha3/sha3module.c
index 32cd85a1ef..4e6352b7ba 100644
--- a/Modules/_sha3/sha3module.c
+++ b/Modules/_sha3/sha3module.c
@@ -322,7 +322,7 @@ SHA3_update(SHA3object *self, PyObject *args)
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
/* add new data, the function takes the length in bits not bytes */
-#ifdef WITH_THREADS
+#ifdef WITH_THREAD
if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
self->lock = PyThread_allocate_lock();
}
@@ -464,7 +464,7 @@ SHA3_factory(PyObject *args, PyObject *kwdict, const char *fmt,
}
if (data_obj) {
-#ifdef WITH_THREADS
+#ifdef WITH_THREAD
if (buf.len >= HASHLIB_GIL_MINSIZE) {
/* invariant: New objects can't be accessed by other code yet,
* thus it's safe to release the GIL without locking the object.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 6527a53362..1ed56873ce 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2614,6 +2614,16 @@ test_decref_doesnt_leak(PyObject *ob)
}
static PyObject *
+test_incref_decref_API(PyObject *ob)
+{
+ PyObject *obj = PyLong_FromLong(0);
+ Py_IncRef(ob);
+ Py_DecRef(obj);
+ Py_DecRef(obj);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
test_pymem_alloc0(PyObject *self)
{
void *ptr;
@@ -2781,6 +2791,7 @@ static PyMethodDef TestMethods[] = {
{"test_incref_doesnt_leak", (PyCFunction)test_incref_doesnt_leak, METH_NOARGS},
{"test_xdecref_doesnt_leak",(PyCFunction)test_xdecref_doesnt_leak, METH_NOARGS},
{"test_decref_doesnt_leak", (PyCFunction)test_decref_doesnt_leak, METH_NOARGS},
+ {"test_incref_decref_API", (PyCFunction)test_incref_decref_API, METH_NOARGS},
{"test_long_and_overflow", (PyCFunction)test_long_and_overflow,
METH_NOARGS},
{"test_long_as_double", (PyCFunction)test_long_as_double,METH_NOARGS},