mirror of
https://github.com/python/cpython.git
synced 2025-11-07 17:12:03 +00:00
gh-138205: Remove the resize method on mmap object on platforms don't support it (#138276)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
8554c0917e
commit
c919d02ede
5 changed files with 71 additions and 83 deletions
|
|
@ -289,6 +289,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
|
pagefile) will silently create a new map with the original data copied over
|
||||||
up to the length of the new size.
|
up to the length of the new size.
|
||||||
|
|
||||||
|
Availability: Windows and systems with the ``mremap()`` system call.
|
||||||
|
|
||||||
.. versionchanged:: 3.11
|
.. versionchanged:: 3.11
|
||||||
Correctly fails if attempting to resize when another map is held
|
Correctly fails if attempting to resize when another map is held
|
||||||
Allows resize against an anonymous map on Windows
|
Allows resize against an anonymous map on Windows
|
||||||
|
|
|
||||||
|
|
@ -701,6 +701,9 @@ Porting to Python 3.15
|
||||||
:func:`resource.setrlimit` and :func:`resource.prlimit` is now deprecated.
|
:func:`resource.setrlimit` and :func:`resource.prlimit` is now deprecated.
|
||||||
(Contributed by Serhiy Storchaka in :gh:`137044`.)
|
(Contributed by Serhiy Storchaka in :gh:`137044`.)
|
||||||
|
|
||||||
|
* :meth:`~mmap.mmap.resize` has been removed on platforms that don't support the
|
||||||
|
underlying syscall, instead of raising a :exc:`SystemError`.
|
||||||
|
|
||||||
|
|
||||||
Deprecated C APIs
|
Deprecated C APIs
|
||||||
-----------------
|
-----------------
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ def test_basic(self):
|
||||||
f.write(b'\0'* (PAGESIZE-3) )
|
f.write(b'\0'* (PAGESIZE-3) )
|
||||||
f.flush()
|
f.flush()
|
||||||
m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
|
m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
|
||||||
|
self.addCleanup(m.close)
|
||||||
finally:
|
finally:
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
@ -114,31 +115,28 @@ def test_basic(self):
|
||||||
# Try to seek to negative position...
|
# Try to seek to negative position...
|
||||||
self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
|
self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
|
||||||
|
def test_resize(self):
|
||||||
|
# Create a file to be mmap'ed.
|
||||||
|
with open(TESTFN, 'bw+') as f:
|
||||||
|
# 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)
|
||||||
|
|
||||||
# Try resizing map
|
# Try resizing map
|
||||||
try:
|
m.resize(512)
|
||||||
m.resize(512)
|
self.assertEqual(len(m), 512)
|
||||||
except SystemError:
|
# Check that we can no longer seek beyond the new size.
|
||||||
# resize() not supported
|
self.assertRaises(ValueError, m.seek, 513, 0)
|
||||||
# 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
|
# Check that the underlying file is truncated too
|
||||||
# (bug #728515)
|
# (bug #728515)
|
||||||
f = open(TESTFN, 'rb')
|
with open(TESTFN, 'rb') as f:
|
||||||
try:
|
f.seek(0, 2)
|
||||||
f.seek(0, 2)
|
self.assertEqual(f.tell(), 512)
|
||||||
self.assertEqual(f.tell(), 512)
|
self.assertEqual(m.size(), 512)
|
||||||
finally:
|
|
||||||
f.close()
|
|
||||||
self.assertEqual(m.size(), 512)
|
|
||||||
|
|
||||||
m.close()
|
|
||||||
|
|
||||||
def test_access_parameter(self):
|
def test_access_parameter(self):
|
||||||
# Test for "access" keyword parameter
|
# Test for "access" keyword parameter
|
||||||
|
|
@ -183,15 +181,10 @@ def test_access_parameter(self):
|
||||||
else:
|
else:
|
||||||
self.fail("Able to write to readonly memory map")
|
self.fail("Able to write to readonly memory map")
|
||||||
|
|
||||||
# Ensuring that readonly mmap can't be resized
|
if hasattr(m, 'resize'):
|
||||||
try:
|
# Ensuring that readonly mmap can't be resized
|
||||||
m.resize(2*mapsize)
|
with self.assertRaises(TypeError):
|
||||||
except SystemError: # resize is not universally supported
|
m.resize(2 * mapsize)
|
||||||
pass
|
|
||||||
except TypeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.fail("Able to resize readonly memory map")
|
|
||||||
with open(TESTFN, "rb") as fp:
|
with open(TESTFN, "rb") as fp:
|
||||||
self.assertEqual(fp.read(), b'a'*mapsize,
|
self.assertEqual(fp.read(), b'a'*mapsize,
|
||||||
"Readonly memory map data file was modified")
|
"Readonly memory map data file was modified")
|
||||||
|
|
@ -242,8 +235,9 @@ def test_access_parameter(self):
|
||||||
with open(TESTFN, "rb") as fp:
|
with open(TESTFN, "rb") as fp:
|
||||||
self.assertEqual(fp.read(), b'c'*mapsize,
|
self.assertEqual(fp.read(), b'c'*mapsize,
|
||||||
"Copy-on-write test data file should not be modified.")
|
"Copy-on-write test data file should not be modified.")
|
||||||
# Ensuring copy-on-write maps cannot be resized
|
if hasattr(m, 'resize'):
|
||||||
self.assertRaises(TypeError, m.resize, 2*mapsize)
|
# Ensuring copy-on-write maps cannot be resized
|
||||||
|
self.assertRaises(TypeError, m.resize, 2 * mapsize)
|
||||||
m.close()
|
m.close()
|
||||||
|
|
||||||
# Ensuring invalid access parameter raises exception
|
# Ensuring invalid access parameter raises exception
|
||||||
|
|
@ -282,10 +276,11 @@ def test_trackfd_parameter(self, close_original_fd):
|
||||||
self.assertEqual(len(m), size)
|
self.assertEqual(len(m), size)
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
m.size()
|
m.size()
|
||||||
with self.assertRaises(ValueError):
|
if hasattr(m, 'resize'):
|
||||||
m.resize(size * 2)
|
with self.assertRaises(ValueError):
|
||||||
with self.assertRaises(ValueError):
|
m.resize(size * 2)
|
||||||
m.resize(size // 2)
|
with self.assertRaises(ValueError):
|
||||||
|
m.resize(size // 2)
|
||||||
self.assertIs(m.closed, False)
|
self.assertIs(m.closed, False)
|
||||||
|
|
||||||
# Smoke-test other API
|
# Smoke-test other API
|
||||||
|
|
@ -313,8 +308,9 @@ def test_trackfd_neg1(self):
|
||||||
with mmap.mmap(-1, size, trackfd=False) as m:
|
with mmap.mmap(-1, size, trackfd=False) as m:
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
m.size()
|
m.size()
|
||||||
with self.assertRaises(ValueError):
|
if hasattr(m, 'resize'):
|
||||||
m.resize(size // 2)
|
with self.assertRaises(ValueError):
|
||||||
|
m.resize(size // 2)
|
||||||
self.assertEqual(len(m), size)
|
self.assertEqual(len(m), size)
|
||||||
m[0] = ord('a')
|
m[0] = ord('a')
|
||||||
assert m[0] == ord('a')
|
assert m[0] == ord('a')
|
||||||
|
|
@ -608,13 +604,9 @@ def test_offset (self):
|
||||||
self.assertEqual(m[0:3], b'foo')
|
self.assertEqual(m[0:3], b'foo')
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# Try resizing map
|
if hasattr(m, 'resize'):
|
||||||
try:
|
# Try resizing map
|
||||||
m.resize(512)
|
m.resize(512)
|
||||||
except SystemError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# resize() is supported
|
|
||||||
self.assertEqual(len(m), 512)
|
self.assertEqual(len(m), 512)
|
||||||
# Check that we can no longer seek beyond the new size.
|
# Check that we can no longer seek beyond the new size.
|
||||||
self.assertRaises(ValueError, m.seek, 513, 0)
|
self.assertRaises(ValueError, m.seek, 513, 0)
|
||||||
|
|
@ -806,14 +798,12 @@ def test_write_returning_the_number_of_bytes_written(self):
|
||||||
self.assertEqual(mm.write(b"yz"), 2)
|
self.assertEqual(mm.write(b"yz"), 2)
|
||||||
self.assertEqual(mm.write(b"python"), 6)
|
self.assertEqual(mm.write(b"python"), 6)
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
|
||||||
def test_resize_past_pos(self):
|
def test_resize_past_pos(self):
|
||||||
m = mmap.mmap(-1, 8192)
|
m = mmap.mmap(-1, 8192)
|
||||||
self.addCleanup(m.close)
|
self.addCleanup(m.close)
|
||||||
m.read(5000)
|
m.read(5000)
|
||||||
try:
|
m.resize(4096)
|
||||||
m.resize(4096)
|
|
||||||
except SystemError:
|
|
||||||
self.skipTest("resizing not supported")
|
|
||||||
self.assertEqual(m.read(14), b'')
|
self.assertEqual(m.read(14), b'')
|
||||||
self.assertRaises(ValueError, m.read_byte)
|
self.assertRaises(ValueError, m.read_byte)
|
||||||
self.assertRaises(ValueError, m.write_byte, 42)
|
self.assertRaises(ValueError, m.write_byte, 42)
|
||||||
|
|
@ -895,6 +885,7 @@ def test_madvise(self):
|
||||||
self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, 2), None)
|
self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, 2), None)
|
||||||
self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, size), None)
|
self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, size), None)
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
|
||||||
def test_resize_up_anonymous_mapping(self):
|
def test_resize_up_anonymous_mapping(self):
|
||||||
"""If the mmap is backed by the pagefile ensure a resize up can happen
|
"""If the mmap is backed by the pagefile ensure a resize up can happen
|
||||||
and that the original data is still in place
|
and that the original data is still in place
|
||||||
|
|
@ -911,16 +902,13 @@ def test_resize_up_anonymous_mapping(self):
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
m.resize(new_size)
|
m.resize(new_size)
|
||||||
else:
|
else:
|
||||||
try:
|
m.resize(new_size)
|
||||||
m.resize(new_size)
|
self.assertEqual(len(m), new_size)
|
||||||
except SystemError:
|
self.assertEqual(m[:start_size], data)
|
||||||
pass
|
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
|
||||||
else:
|
|
||||||
self.assertEqual(len(m), new_size)
|
|
||||||
self.assertEqual(m[:start_size], data)
|
|
||||||
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
|
|
||||||
|
|
||||||
@unittest.skipUnless(os.name == 'posix', 'requires Posix')
|
@unittest.skipUnless(os.name == 'posix', 'requires Posix')
|
||||||
|
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
|
||||||
def test_resize_up_private_anonymous_mapping(self):
|
def test_resize_up_private_anonymous_mapping(self):
|
||||||
start_size = PAGESIZE
|
start_size = PAGESIZE
|
||||||
new_size = 2 * start_size
|
new_size = 2 * start_size
|
||||||
|
|
@ -928,15 +916,12 @@ def test_resize_up_private_anonymous_mapping(self):
|
||||||
|
|
||||||
with mmap.mmap(-1, start_size, flags=mmap.MAP_PRIVATE) as m:
|
with mmap.mmap(-1, start_size, flags=mmap.MAP_PRIVATE) as m:
|
||||||
m[:] = data
|
m[:] = data
|
||||||
try:
|
m.resize(new_size)
|
||||||
m.resize(new_size)
|
self.assertEqual(len(m), new_size)
|
||||||
except SystemError:
|
self.assertEqual(m[:start_size], data)
|
||||||
pass
|
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
|
||||||
else:
|
|
||||||
self.assertEqual(len(m), new_size)
|
|
||||||
self.assertEqual(m[:start_size], data)
|
|
||||||
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
|
||||||
def test_resize_down_anonymous_mapping(self):
|
def test_resize_down_anonymous_mapping(self):
|
||||||
"""If the mmap is backed by the pagefile ensure a resize down up can happen
|
"""If the mmap is backed by the pagefile ensure a resize down up can happen
|
||||||
and that a truncated form of the original data is still in place
|
and that a truncated form of the original data is still in place
|
||||||
|
|
@ -947,17 +932,13 @@ def test_resize_down_anonymous_mapping(self):
|
||||||
|
|
||||||
with mmap.mmap(-1, start_size) as m:
|
with mmap.mmap(-1, start_size) as m:
|
||||||
m[:] = data
|
m[:] = data
|
||||||
try:
|
m.resize(new_size)
|
||||||
m.resize(new_size)
|
self.assertEqual(len(m), new_size)
|
||||||
except SystemError:
|
self.assertEqual(m[:], data[:new_size])
|
||||||
pass
|
if sys.platform.startswith(('linux', 'android')):
|
||||||
else:
|
# Can't expand to its original size.
|
||||||
self.assertEqual(len(m), new_size)
|
with self.assertRaises(ValueError):
|
||||||
self.assertEqual(m[:], data[:new_size])
|
m.resize(start_size)
|
||||||
if sys.platform.startswith(('linux', 'android')):
|
|
||||||
# Can't expand to its original size.
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
m.resize(start_size)
|
|
||||||
|
|
||||||
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
|
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
|
||||||
def test_resize_fails_if_mapping_held_elsewhere(self):
|
def test_resize_fails_if_mapping_held_elsewhere(self):
|
||||||
|
|
|
||||||
|
|
@ -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`.
|
||||||
|
|
@ -628,6 +628,7 @@ is_writable(mmap_object *self)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(MS_WINDOWS) || defined(HAVE_MREMAP)
|
||||||
static int
|
static int
|
||||||
is_resizeable(mmap_object *self)
|
is_resizeable(mmap_object *self)
|
||||||
{
|
{
|
||||||
|
|
@ -648,6 +649,7 @@ is_resizeable(mmap_object *self)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endif /* MS_WINDOWS || HAVE_MREMAP */
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
@ -766,6 +768,7 @@ mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored))
|
||||||
/ new size?
|
/ new size?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if defined(MS_WINDOWS) || defined(HAVE_MREMAP)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
mmap_resize_method(PyObject *op, PyObject *args)
|
mmap_resize_method(PyObject *op, PyObject *args)
|
||||||
{
|
{
|
||||||
|
|
@ -880,11 +883,6 @@ mmap_resize_method(PyObject *op, PyObject *args)
|
||||||
#endif /* MS_WINDOWS */
|
#endif /* MS_WINDOWS */
|
||||||
|
|
||||||
#ifdef UNIX
|
#ifdef UNIX
|
||||||
#ifndef HAVE_MREMAP
|
|
||||||
PyErr_SetString(PyExc_SystemError,
|
|
||||||
"mmap: resizing not available--no mremap()");
|
|
||||||
return NULL;
|
|
||||||
#else
|
|
||||||
void *newmap;
|
void *newmap;
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|
@ -916,10 +914,10 @@ mmap_resize_method(PyObject *op, PyObject *args)
|
||||||
self->data = newmap;
|
self->data = newmap;
|
||||||
self->size = new_size;
|
self->size = new_size;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
#endif /* HAVE_MREMAP */
|
|
||||||
#endif /* UNIX */
|
#endif /* UNIX */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif /* MS_WINDOWS || HAVE_MREMAP */
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored))
|
mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored))
|
||||||
|
|
@ -1207,7 +1205,9 @@ static struct PyMethodDef mmap_object_methods[] = {
|
||||||
{"read", mmap_read_method, METH_VARARGS},
|
{"read", mmap_read_method, METH_VARARGS},
|
||||||
{"read_byte", mmap_read_byte_method, METH_NOARGS},
|
{"read_byte", mmap_read_byte_method, METH_NOARGS},
|
||||||
{"readline", mmap_read_line_method, METH_NOARGS},
|
{"readline", mmap_read_line_method, METH_NOARGS},
|
||||||
|
#if defined(MS_WINDOWS) || defined(HAVE_MREMAP)
|
||||||
{"resize", mmap_resize_method, METH_VARARGS},
|
{"resize", mmap_resize_method, METH_VARARGS},
|
||||||
|
#endif
|
||||||
{"seek", mmap_seek_method, METH_VARARGS},
|
{"seek", mmap_seek_method, METH_VARARGS},
|
||||||
{"seekable", mmap_seekable_method, METH_NOARGS},
|
{"seekable", mmap_seekable_method, METH_NOARGS},
|
||||||
{"size", mmap_size_method, METH_NOARGS},
|
{"size", mmap_size_method, METH_NOARGS},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue