mirror of
https://github.com/python/cpython.git
synced 2025-10-19 16:03:42 +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
|
||||
up to the length of the new size.
|
||||
|
||||
Availability: Windows and systems with the ``mremap()`` system call.
|
||||
|
||||
.. versionchanged:: 3.11
|
||||
Correctly fails if attempting to resize when another map is held
|
||||
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.
|
||||
(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
|
||||
-----------------
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
@ -114,31 +115,28 @@ def test_basic(self):
|
|||
# Try to seek to negative position...
|
||||
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:
|
||||
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)
|
||||
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:
|
||||
f.seek(0, 2)
|
||||
self.assertEqual(f.tell(), 512)
|
||||
finally:
|
||||
f.close()
|
||||
self.assertEqual(m.size(), 512)
|
||||
|
||||
m.close()
|
||||
# Check that the underlying file is truncated too
|
||||
# (bug #728515)
|
||||
with open(TESTFN, 'rb') as f:
|
||||
f.seek(0, 2)
|
||||
self.assertEqual(f.tell(), 512)
|
||||
self.assertEqual(m.size(), 512)
|
||||
|
||||
def test_access_parameter(self):
|
||||
# Test for "access" keyword parameter
|
||||
|
@ -183,15 +181,10 @@ def test_access_parameter(self):
|
|||
else:
|
||||
self.fail("Able to write to readonly memory map")
|
||||
|
||||
# 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)
|
||||
with open(TESTFN, "rb") as fp:
|
||||
self.assertEqual(fp.read(), b'a'*mapsize,
|
||||
"Readonly memory map data file was modified")
|
||||
|
@ -242,8 +235,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
|
||||
|
@ -282,10 +276,11 @@ def test_trackfd_parameter(self, close_original_fd):
|
|||
self.assertEqual(len(m), size)
|
||||
with self.assertRaises(ValueError):
|
||||
m.size()
|
||||
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.assertIs(m.closed, False)
|
||||
|
||||
# Smoke-test other API
|
||||
|
@ -313,8 +308,9 @@ def test_trackfd_neg1(self):
|
|||
with mmap.mmap(-1, size, trackfd=False) as m:
|
||||
with self.assertRaises(ValueError):
|
||||
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')
|
||||
|
@ -608,13 +604,9 @@ def test_offset (self):
|
|||
self.assertEqual(m[0:3], b'foo')
|
||||
f.close()
|
||||
|
||||
# Try resizing map
|
||||
try:
|
||||
if hasattr(m, 'resize'):
|
||||
# Try resizing map
|
||||
m.resize(512)
|
||||
except SystemError:
|
||||
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)
|
||||
|
@ -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"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)
|
||||
|
@ -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, size), None)
|
||||
|
||||
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
|
||||
def test_resize_up_anonymous_mapping(self):
|
||||
"""If the mmap is backed by the pagefile ensure a resize up can happen
|
||||
and that the original data is still in place
|
||||
|
@ -911,16 +902,13 @@ def test_resize_up_anonymous_mapping(self):
|
|||
with self.assertRaises(ValueError):
|
||||
m.resize(new_size)
|
||||
else:
|
||||
try:
|
||||
m.resize(new_size)
|
||||
except SystemError:
|
||||
pass
|
||||
else:
|
||||
self.assertEqual(len(m), new_size)
|
||||
self.assertEqual(m[:start_size], data)
|
||||
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
|
||||
m.resize(new_size)
|
||||
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(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
|
||||
def test_resize_up_private_anonymous_mapping(self):
|
||||
start_size = PAGESIZE
|
||||
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:
|
||||
m[:] = data
|
||||
try:
|
||||
m.resize(new_size)
|
||||
except SystemError:
|
||||
pass
|
||||
else:
|
||||
self.assertEqual(len(m), new_size)
|
||||
self.assertEqual(m[:start_size], data)
|
||||
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
|
||||
m.resize(new_size)
|
||||
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):
|
||||
"""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
|
||||
|
@ -947,17 +932,13 @@ def test_resize_down_anonymous_mapping(self):
|
|||
|
||||
with mmap.mmap(-1, start_size) as m:
|
||||
m[:] = data
|
||||
try:
|
||||
m.resize(new_size)
|
||||
except SystemError:
|
||||
pass
|
||||
else:
|
||||
self.assertEqual(len(m), new_size)
|
||||
self.assertEqual(m[:], data[:new_size])
|
||||
if sys.platform.startswith(('linux', 'android')):
|
||||
# Can't expand to its original size.
|
||||
with self.assertRaises(ValueError):
|
||||
m.resize(start_size)
|
||||
m.resize(new_size)
|
||||
self.assertEqual(len(m), new_size)
|
||||
self.assertEqual(m[:], data[:new_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')
|
||||
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;
|
||||
}
|
||||
|
||||
#if defined(MS_WINDOWS) || defined(HAVE_MREMAP)
|
||||
static int
|
||||
is_resizeable(mmap_object *self)
|
||||
{
|
||||
|
@ -648,6 +649,7 @@ is_resizeable(mmap_object *self)
|
|||
return 0;
|
||||
|
||||
}
|
||||
#endif /* MS_WINDOWS || HAVE_MREMAP */
|
||||
|
||||
|
||||
static PyObject *
|
||||
|
@ -766,6 +768,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)
|
||||
{
|
||||
|
@ -880,11 +883,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;
|
||||
|
||||
#ifdef __linux__
|
||||
|
@ -916,10 +914,10 @@ mmap_resize_method(PyObject *op, PyObject *args)
|
|||
self->data = newmap;
|
||||
self->size = new_size;
|
||||
Py_RETURN_NONE;
|
||||
#endif /* HAVE_MREMAP */
|
||||
#endif /* UNIX */
|
||||
}
|
||||
}
|
||||
#endif /* MS_WINDOWS || HAVE_MREMAP */
|
||||
|
||||
static PyObject *
|
||||
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_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},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue