Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Doc/library/mmap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ To map anonymous memory, -1 should be passed as the fileno along with the length
pagefile) will silently create a new map with the original data copied over
up to the length of the new size.

.. availability:: Linux, Windows
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is also available on other platforms (e.g. FreeBSD).

See how this is documented for madvise().


.. versionchanged:: 3.11
Correctly fails if attempting to resize when another map is held
Allows resize against an anonymous map on Windows
Expand Down
79 changes: 42 additions & 37 deletions Lib/test/test_mmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def test_basic(self):
f.write(b'\0'* (PAGESIZE-3) )
f.flush()
m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
self.addCleanup(m.close)
finally:
f.close()

Expand Down Expand Up @@ -114,31 +115,34 @@ def test_basic(self):
# Try to seek to negative position...
self.assertRaises(ValueError, m.seek, -len(m)-1, 2)

# Try resizing map
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
def test_resize(self):
# Create a file to be mmap'ed.
f = open(TESTFN, 'bw+')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can simply use with open(...) as f.

try:
m.resize(512)
except SystemError:
# resize() not supported
# No messages are printed, since the output of this test suite
# would then be different across platforms.
pass
else:
# resize() is supported
self.assertEqual(len(m), 512)
# Check that we can no longer seek beyond the new size.
self.assertRaises(ValueError, m.seek, 513, 0)

# Check that the underlying file is truncated too
# (bug #728515)
f = open(TESTFN, 'rb')
try:
f.seek(0, 2)
self.assertEqual(f.tell(), 512)
finally:
f.close()
self.assertEqual(m.size(), 512)
# Write 2 pages worth of data to the file
f.write(b'\0'* 2 * PAGESIZE)
f.flush()
m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
self.addCleanup(m.close)
finally:
f.close()

m.close()
# Try resizing map
m.resize(512)
self.assertEqual(len(m), 512)
# Check that we can no longer seek beyond the new size.
self.assertRaises(ValueError, m.seek, 513, 0)

# Check that the underlying file is truncated too
# (bug #728515)
f = open(TESTFN, 'rb')
try:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with

f.seek(0, 2)
self.assertEqual(f.tell(), 512)
finally:
f.close()
self.assertEqual(m.size(), 512)

def test_access_parameter(self):
# Test for "access" keyword parameter
Expand Down Expand Up @@ -186,7 +190,7 @@ def test_access_parameter(self):
# Ensuring that readonly mmap can't be resized
try:
m.resize(2*mapsize)
except SystemError: # resize is not universally supported
except AttributeError: # resize is not universally supported
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-            # Ensuring that readonly mmap can't be resized
-            try:
-                m.resize(2*mapsize)
-            except SystemError:   # resize is not universally supported
-                pass
-            except TypeError:
-                pass
-            else:
-                self.fail("Able to resize readonly memory map")
+            if hasattr(m, 'resize'):
+                # Ensuring that readonly mmap can't be resized
+                with self.assertRaises(TypeError):
+                    m.resize(2*mapsize)

pass
except TypeError:
pass
Expand Down Expand Up @@ -242,8 +246,9 @@ def test_access_parameter(self):
with open(TESTFN, "rb") as fp:
self.assertEqual(fp.read(), b'c'*mapsize,
"Copy-on-write test data file should not be modified.")
# Ensuring copy-on-write maps cannot be resized
self.assertRaises(TypeError, m.resize, 2*mapsize)
if hasattr(m, 'resize'):
# Ensuring copy-on-write maps cannot be resized
self.assertRaises(TypeError, m.resize, 2*mapsize)
m.close()

# Ensuring invalid access parameter raises exception
Expand Down Expand Up @@ -285,10 +290,11 @@ def test_trackfd_parameter(self):
with self.assertRaises(OSError) as err_cm:
m.size()
self.assertEqual(err_cm.exception.errno, errno.EBADF)
with self.assertRaises(ValueError):
m.resize(size * 2)
with self.assertRaises(ValueError):
m.resize(size // 2)
if hasattr(m, 'resize'):
with self.assertRaises(ValueError):
m.resize(size * 2)
with self.assertRaises(ValueError):
m.resize(size // 2)
self.assertEqual(m.closed, False)

# Smoke-test other API
Expand All @@ -311,8 +317,9 @@ def test_trackfd_neg1(self):
with mmap.mmap(-1, size, trackfd=False) as m:
with self.assertRaises(OSError):
m.size()
with self.assertRaises(ValueError):
m.resize(size // 2)
if hasattr(m, 'resize'):
with self.assertRaises(ValueError):
m.resize(size // 2)
self.assertEqual(len(m), size)
m[0] = ord('a')
assert m[0] == ord('a')
Expand Down Expand Up @@ -617,7 +624,7 @@ def test_offset (self):
# Try resizing map
try:
m.resize(512)
except SystemError:
except AttributeError:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simply use if hasattr(m, 'resize').

-            # Try resizing map
-            try:
+            if hasattr(m, 'resize'):
                 m.resize(512)
-            except SystemError:
-                pass
-            else:
-                # resize() is supported

pass
else:
# resize() is supported
Expand Down Expand Up @@ -812,14 +819,12 @@ def test_write_returning_the_number_of_bytes_written(self):
self.assertEqual(mm.write(b"yz"), 2)
self.assertEqual(mm.write(b"python"), 6)

@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
def test_resize_past_pos(self):
m = mmap.mmap(-1, 8192)
self.addCleanup(m.close)
m.read(5000)
try:
m.resize(4096)
except SystemError:
self.skipTest("resizing not supported")
m.resize(4096)
self.assertEqual(m.read(14), b'')
self.assertRaises(ValueError, m.read_byte)
self.assertRaises(ValueError, m.write_byte, 42)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Removed the :meth:`~mmap.mmap.resize` method on platforms that don't support the
underlying syscall, instead of raising a :exc:`SystemError`.
12 changes: 6 additions & 6 deletions Modules/mmapmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ is_writable(mmap_object *self)
return 0;
}

#if defined(MS_WINDOWS) || defined(HAVE_MREMAP)
static int
is_resizeable(mmap_object *self)
{
Expand All @@ -656,6 +657,7 @@ is_resizeable(mmap_object *self)
return 0;

}
#endif /* defined(MS_WINDOWS) || defined(HAVE_MREMAP) */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually write this simpler:

Suggested change
#endif /* defined(MS_WINDOWS) || defined(HAVE_MREMAP) */
#endif /* MS_WINDOWS || HAVE_MREMAP */



static PyObject *
Expand Down Expand Up @@ -768,6 +770,7 @@ mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored))
/ new size?
*/

#if defined(MS_WINDOWS) || defined(HAVE_MREMAP)
static PyObject *
mmap_resize_method(PyObject *op, PyObject *args)
{
Expand Down Expand Up @@ -882,11 +885,6 @@ mmap_resize_method(PyObject *op, PyObject *args)
#endif /* MS_WINDOWS */

#ifdef UNIX
#ifndef HAVE_MREMAP
PyErr_SetString(PyExc_SystemError,
"mmap: resizing not available--no mremap()");
return NULL;
#else
void *newmap;

if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) {
Expand All @@ -911,10 +909,10 @@ mmap_resize_method(PyObject *op, PyObject *args)
self->data = newmap;
self->size = new_size;
Py_RETURN_NONE;
#endif /* HAVE_MREMAP */
#endif /* UNIX */
}
}
#endif /* defined(MS_WINDOWS) || defined(HAVE_MREMAP) */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above.


static PyObject *
mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored))
Expand Down Expand Up @@ -1202,7 +1200,9 @@ static struct PyMethodDef mmap_object_methods[] = {
{"read", mmap_read_method, METH_VARARGS},
{"read_byte", mmap_read_byte_method, METH_NOARGS},
{"readline", mmap_read_line_method, METH_NOARGS},
#if defined(MS_WINDOWS) || defined(HAVE_MREMAP)
{"resize", mmap_resize_method, METH_VARARGS},
#endif
{"seek", mmap_seek_method, METH_VARARGS},
{"seekable", mmap_seekable_method, METH_NOARGS},
{"size", mmap_size_method, METH_NOARGS},
Expand Down
Loading