Skip to content

Commit 5da862a

Browse files
aiskserhiy-storchakavstinner
authored andcommitted
pythongh-138205: Remove the resize method on mmap object on platforms don't support it (python#138276)
Co-authored-by: Serhiy Storchaka <[email protected]> Co-authored-by: Victor Stinner <[email protected]>
1 parent 3a2d60c commit 5da862a

File tree

5 files changed

+71
-83
lines changed

5 files changed

+71
-83
lines changed

Doc/library/mmap.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ To map anonymous memory, -1 should be passed as the fileno along with the length
289289
pagefile) will silently create a new map with the original data copied over
290290
up to the length of the new size.
291291

292+
Availability: Windows and systems with the ``mremap()`` system call.
293+
292294
.. versionchanged:: 3.11
293295
Correctly fails if attempting to resize when another map is held
294296
Allows resize against an anonymous map on Windows

Doc/whatsnew/3.15.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,9 @@ Porting to Python 3.15
701701
:func:`resource.setrlimit` and :func:`resource.prlimit` is now deprecated.
702702
(Contributed by Serhiy Storchaka in :gh:`137044`.)
703703

704+
* :meth:`~mmap.mmap.resize` has been removed on platforms that don't support the
705+
underlying syscall, instead of raising a :exc:`SystemError`.
706+
704707

705708
Deprecated C APIs
706709
-----------------

Lib/test/test_mmap.py

Lines changed: 58 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def test_basic(self):
5757
f.write(b'\0'* (PAGESIZE-3) )
5858
f.flush()
5959
m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
60+
self.addCleanup(m.close)
6061
finally:
6162
f.close()
6263

@@ -114,31 +115,28 @@ def test_basic(self):
114115
# Try to seek to negative position...
115116
self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
116117

118+
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
119+
def test_resize(self):
120+
# Create a file to be mmap'ed.
121+
with open(TESTFN, 'bw+') as f:
122+
# Write 2 pages worth of data to the file
123+
f.write(b'\0'* 2 * PAGESIZE)
124+
f.flush()
125+
m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
126+
self.addCleanup(m.close)
127+
117128
# Try resizing map
118-
try:
119-
m.resize(512)
120-
except SystemError:
121-
# resize() not supported
122-
# No messages are printed, since the output of this test suite
123-
# would then be different across platforms.
124-
pass
125-
else:
126-
# resize() is supported
127-
self.assertEqual(len(m), 512)
128-
# Check that we can no longer seek beyond the new size.
129-
self.assertRaises(ValueError, m.seek, 513, 0)
130-
131-
# Check that the underlying file is truncated too
132-
# (bug #728515)
133-
f = open(TESTFN, 'rb')
134-
try:
135-
f.seek(0, 2)
136-
self.assertEqual(f.tell(), 512)
137-
finally:
138-
f.close()
139-
self.assertEqual(m.size(), 512)
129+
m.resize(512)
130+
self.assertEqual(len(m), 512)
131+
# Check that we can no longer seek beyond the new size.
132+
self.assertRaises(ValueError, m.seek, 513, 0)
140133

141-
m.close()
134+
# Check that the underlying file is truncated too
135+
# (bug #728515)
136+
with open(TESTFN, 'rb') as f:
137+
f.seek(0, 2)
138+
self.assertEqual(f.tell(), 512)
139+
self.assertEqual(m.size(), 512)
142140

143141
def test_access_parameter(self):
144142
# Test for "access" keyword parameter
@@ -183,15 +181,10 @@ def test_access_parameter(self):
183181
else:
184182
self.fail("Able to write to readonly memory map")
185183

186-
# Ensuring that readonly mmap can't be resized
187-
try:
188-
m.resize(2*mapsize)
189-
except SystemError: # resize is not universally supported
190-
pass
191-
except TypeError:
192-
pass
193-
else:
194-
self.fail("Able to resize readonly memory map")
184+
if hasattr(m, 'resize'):
185+
# Ensuring that readonly mmap can't be resized
186+
with self.assertRaises(TypeError):
187+
m.resize(2 * mapsize)
195188
with open(TESTFN, "rb") as fp:
196189
self.assertEqual(fp.read(), b'a'*mapsize,
197190
"Readonly memory map data file was modified")
@@ -242,8 +235,9 @@ def test_access_parameter(self):
242235
with open(TESTFN, "rb") as fp:
243236
self.assertEqual(fp.read(), b'c'*mapsize,
244237
"Copy-on-write test data file should not be modified.")
245-
# Ensuring copy-on-write maps cannot be resized
246-
self.assertRaises(TypeError, m.resize, 2*mapsize)
238+
if hasattr(m, 'resize'):
239+
# Ensuring copy-on-write maps cannot be resized
240+
self.assertRaises(TypeError, m.resize, 2 * mapsize)
247241
m.close()
248242

249243
# Ensuring invalid access parameter raises exception
@@ -282,10 +276,11 @@ def test_trackfd_parameter(self, close_original_fd):
282276
self.assertEqual(len(m), size)
283277
with self.assertRaises(ValueError):
284278
m.size()
285-
with self.assertRaises(ValueError):
286-
m.resize(size * 2)
287-
with self.assertRaises(ValueError):
288-
m.resize(size // 2)
279+
if hasattr(m, 'resize'):
280+
with self.assertRaises(ValueError):
281+
m.resize(size * 2)
282+
with self.assertRaises(ValueError):
283+
m.resize(size // 2)
289284
self.assertIs(m.closed, False)
290285

291286
# Smoke-test other API
@@ -313,8 +308,9 @@ def test_trackfd_neg1(self):
313308
with mmap.mmap(-1, size, trackfd=False) as m:
314309
with self.assertRaises(ValueError):
315310
m.size()
316-
with self.assertRaises(ValueError):
317-
m.resize(size // 2)
311+
if hasattr(m, 'resize'):
312+
with self.assertRaises(ValueError):
313+
m.resize(size // 2)
318314
self.assertEqual(len(m), size)
319315
m[0] = ord('a')
320316
assert m[0] == ord('a')
@@ -608,13 +604,9 @@ def test_offset (self):
608604
self.assertEqual(m[0:3], b'foo')
609605
f.close()
610606

611-
# Try resizing map
612-
try:
607+
if hasattr(m, 'resize'):
608+
# Try resizing map
613609
m.resize(512)
614-
except SystemError:
615-
pass
616-
else:
617-
# resize() is supported
618610
self.assertEqual(len(m), 512)
619611
# Check that we can no longer seek beyond the new size.
620612
self.assertRaises(ValueError, m.seek, 513, 0)
@@ -806,14 +798,12 @@ def test_write_returning_the_number_of_bytes_written(self):
806798
self.assertEqual(mm.write(b"yz"), 2)
807799
self.assertEqual(mm.write(b"python"), 6)
808800

801+
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
809802
def test_resize_past_pos(self):
810803
m = mmap.mmap(-1, 8192)
811804
self.addCleanup(m.close)
812805
m.read(5000)
813-
try:
814-
m.resize(4096)
815-
except SystemError:
816-
self.skipTest("resizing not supported")
806+
m.resize(4096)
817807
self.assertEqual(m.read(14), b'')
818808
self.assertRaises(ValueError, m.read_byte)
819809
self.assertRaises(ValueError, m.write_byte, 42)
@@ -895,6 +885,7 @@ def test_madvise(self):
895885
self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, 2), None)
896886
self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, size), None)
897887

888+
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
898889
def test_resize_up_anonymous_mapping(self):
899890
"""If the mmap is backed by the pagefile ensure a resize up can happen
900891
and that the original data is still in place
@@ -911,32 +902,26 @@ def test_resize_up_anonymous_mapping(self):
911902
with self.assertRaises(ValueError):
912903
m.resize(new_size)
913904
else:
914-
try:
915-
m.resize(new_size)
916-
except SystemError:
917-
pass
918-
else:
919-
self.assertEqual(len(m), new_size)
920-
self.assertEqual(m[:start_size], data)
921-
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
905+
m.resize(new_size)
906+
self.assertEqual(len(m), new_size)
907+
self.assertEqual(m[:start_size], data)
908+
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
922909

923910
@unittest.skipUnless(os.name == 'posix', 'requires Posix')
911+
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
924912
def test_resize_up_private_anonymous_mapping(self):
925913
start_size = PAGESIZE
926914
new_size = 2 * start_size
927915
data = random.randbytes(start_size)
928916

929917
with mmap.mmap(-1, start_size, flags=mmap.MAP_PRIVATE) as m:
930918
m[:] = data
931-
try:
932-
m.resize(new_size)
933-
except SystemError:
934-
pass
935-
else:
936-
self.assertEqual(len(m), new_size)
937-
self.assertEqual(m[:start_size], data)
938-
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
919+
m.resize(new_size)
920+
self.assertEqual(len(m), new_size)
921+
self.assertEqual(m[:start_size], data)
922+
self.assertEqual(m[start_size:], b'\0' * (new_size - start_size))
939923

924+
@unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize')
940925
def test_resize_down_anonymous_mapping(self):
941926
"""If the mmap is backed by the pagefile ensure a resize down up can happen
942927
and that a truncated form of the original data is still in place
@@ -947,17 +932,13 @@ def test_resize_down_anonymous_mapping(self):
947932

948933
with mmap.mmap(-1, start_size) as m:
949934
m[:] = data
950-
try:
951-
m.resize(new_size)
952-
except SystemError:
953-
pass
954-
else:
955-
self.assertEqual(len(m), new_size)
956-
self.assertEqual(m[:], data[:new_size])
957-
if sys.platform.startswith(('linux', 'android')):
958-
# Can't expand to its original size.
959-
with self.assertRaises(ValueError):
960-
m.resize(start_size)
935+
m.resize(new_size)
936+
self.assertEqual(len(m), new_size)
937+
self.assertEqual(m[:], data[:new_size])
938+
if sys.platform.startswith(('linux', 'android')):
939+
# Can't expand to its original size.
940+
with self.assertRaises(ValueError):
941+
m.resize(start_size)
961942

962943
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
963944
def test_resize_fails_if_mapping_held_elsewhere(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Removed the :meth:`~mmap.mmap.resize` method on platforms that don't support the
2+
underlying syscall, instead of raising a :exc:`SystemError`.

Modules/mmapmodule.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ is_writable(mmap_object *self)
628628
return 0;
629629
}
630630

631+
#if defined(MS_WINDOWS) || defined(HAVE_MREMAP)
631632
static int
632633
is_resizeable(mmap_object *self)
633634
{
@@ -648,6 +649,7 @@ is_resizeable(mmap_object *self)
648649
return 0;
649650

650651
}
652+
#endif /* MS_WINDOWS || HAVE_MREMAP */
651653

652654

653655
static PyObject *
@@ -766,6 +768,7 @@ mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored))
766768
/ new size?
767769
*/
768770

771+
#if defined(MS_WINDOWS) || defined(HAVE_MREMAP)
769772
static PyObject *
770773
mmap_resize_method(PyObject *op, PyObject *args)
771774
{
@@ -880,11 +883,6 @@ mmap_resize_method(PyObject *op, PyObject *args)
880883
#endif /* MS_WINDOWS */
881884

882885
#ifdef UNIX
883-
#ifndef HAVE_MREMAP
884-
PyErr_SetString(PyExc_SystemError,
885-
"mmap: resizing not available--no mremap()");
886-
return NULL;
887-
#else
888886
void *newmap;
889887

890888
#ifdef __linux__
@@ -916,10 +914,10 @@ mmap_resize_method(PyObject *op, PyObject *args)
916914
self->data = newmap;
917915
self->size = new_size;
918916
Py_RETURN_NONE;
919-
#endif /* HAVE_MREMAP */
920917
#endif /* UNIX */
921918
}
922919
}
920+
#endif /* MS_WINDOWS || HAVE_MREMAP */
923921

924922
static PyObject *
925923
mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored))
@@ -1207,7 +1205,9 @@ static struct PyMethodDef mmap_object_methods[] = {
12071205
{"read", mmap_read_method, METH_VARARGS},
12081206
{"read_byte", mmap_read_byte_method, METH_NOARGS},
12091207
{"readline", mmap_read_line_method, METH_NOARGS},
1208+
#if defined(MS_WINDOWS) || defined(HAVE_MREMAP)
12101209
{"resize", mmap_resize_method, METH_VARARGS},
1210+
#endif
12111211
{"seek", mmap_seek_method, METH_VARARGS},
12121212
{"seekable", mmap_seekable_method, METH_NOARGS},
12131213
{"size", mmap_size_method, METH_NOARGS},

0 commit comments

Comments
 (0)