Issue #12021: Make mmap's read() method argument optional. Patch by Petri
Lehtinen.
diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst
index 7a901c9..7708028 100644
--- a/Doc/library/mmap.rst
+++ b/Doc/library/mmap.rst
@@ -190,12 +190,16 @@
move will raise a :exc:`TypeError` exception.
- .. method:: read(num)
+ .. method:: read([n])
- Return a :class:`bytes` containing up to *num* bytes starting from the
- current file position; the file position is updated to point after the
- bytes that were returned.
+ Return a :class:`bytes` containing up to *n* bytes starting from the
+ current file position. If the argument is omitted, *None* or negative,
+ return all bytes from the current file position to the end of the
+ mapping. The file position is updated to point after the bytes that were
+ returned.
+ .. versionchanged:: 3.3
+ Argument can be omitted or *None*.
.. method:: read_byte()
diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py
index 712378b..4bbb19b 100644
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -417,6 +417,35 @@
m[x] = b
self.assertEqual(m[x], b)
+ def test_read_all(self):
+ m = mmap.mmap(-1, 16)
+ self.addCleanup(m.close)
+
+ # With no parameters, or None or a negative argument, reads all
+ m.write(bytes(range(16)))
+ m.seek(0)
+ self.assertEqual(m.read(), bytes(range(16)))
+ m.seek(8)
+ self.assertEqual(m.read(), bytes(range(8, 16)))
+ m.seek(16)
+ self.assertEqual(m.read(), b'')
+ m.seek(3)
+ self.assertEqual(m.read(None), bytes(range(3, 16)))
+ m.seek(4)
+ self.assertEqual(m.read(-1), bytes(range(4, 16)))
+ m.seek(5)
+ self.assertEqual(m.read(-2), bytes(range(5, 16)))
+ m.seek(9)
+ self.assertEqual(m.read(-42), bytes(range(9, 16)))
+
+ def test_read_invalid_arg(self):
+ m = mmap.mmap(-1, 16)
+ self.addCleanup(m.close)
+
+ self.assertRaises(TypeError, m.read, 'foo')
+ self.assertRaises(TypeError, m.read, 5.5)
+ self.assertRaises(TypeError, m.read, [1, 2, 3])
+
def test_extended_getslice(self):
# Test extended slicing by comparing with list slicing.
s = bytes(reversed(range(256)))
diff --git a/Misc/ACKS b/Misc/ACKS
index 08fc572..900fae7 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -548,6 +548,7 @@
Kip Lehman
Joerg Lehmann
Robert Lehmann
+Petri Lehtinen
Luke Kenneth Casson Leighton
Marc-Andre Lemburg
John Lenton
diff --git a/Misc/NEWS b/Misc/NEWS
index 8a70ce9..1995b14 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -187,6 +187,9 @@
Library
-------
+- Issue #12021: Make mmap's read() method argument optional. Patch by Petri
+ Lehtinen.
+
- Issue #9205: concurrent.futures.ProcessPoolExecutor now detects killed
children and raises BrokenProcessPool in such a situation. Previously it
would reliably freeze/deadlock.
diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c
index 38f6157..ab12e2c 100644
--- a/Modules/mmapmodule.c
+++ b/Modules/mmapmodule.c
@@ -240,15 +240,37 @@
return result;
}
+/* Basically the "n" format code with the ability to turn None into -1. */
+static int
+mmap_convert_ssize_t(PyObject *obj, void *result) {
+ Py_ssize_t limit;
+ if (obj == Py_None) {
+ limit = -1;
+ }
+ else if (PyNumber_Check(obj)) {
+ limit = PyNumber_AsSsize_t(obj, PyExc_OverflowError);
+ if (limit == -1 && PyErr_Occurred())
+ return 0;
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "integer argument expected, got '%.200s'",
+ Py_TYPE(obj)->tp_name);
+ return 0;
+ }
+ *((Py_ssize_t *)result) = limit;
+ return 1;
+}
+
static PyObject *
mmap_read_method(mmap_object *self,
PyObject *args)
{
- Py_ssize_t num_bytes, n;
+ Py_ssize_t num_bytes = -1, n;
PyObject *result;
CHECK_VALID(NULL);
- if (!PyArg_ParseTuple(args, "n:read", &num_bytes))
+ if (!PyArg_ParseTuple(args, "|O&:read", mmap_convert_ssize_t, &num_bytes))
return(NULL);
/* silently 'adjust' out-of-range requests */