https://github.com/python/cpython/commit/c919d02edecfe9d75fe374756fb8aa1db8d95f55
commit: c919d02edecfe9d75fe374756fb8aa1db8d95f55
branch: main
author: AN Long <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2025-09-06T14:10:04+05:30
summary:

gh-138205: Remove the `resize` method on `mmap` object on platforms don't 
support it (#138276)

Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-08-31-12-34-02.gh-issue-138205.iHXb1z.rst
M Doc/library/mmap.rst
M Doc/whatsnew/3.15.rst
M Lib/test/test_mmap.py
M Modules/mmapmodule.c

diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst
index 9c0b828fbd4d28..d82dda9e54b150 100644
--- a/Doc/library/mmap.rst
+++ b/Doc/library/mmap.rst
@@ -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
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index 351ba0cd2afdfe..3236213de5aaa2 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -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
 -----------------
diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py
index da69770915092a..75ea1a671b67de 100644
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -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)
-
-            # 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.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)
 
-        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):
diff --git 
a/Misc/NEWS.d/next/Library/2025-08-31-12-34-02.gh-issue-138205.iHXb1z.rst 
b/Misc/NEWS.d/next/Library/2025-08-31-12-34-02.gh-issue-138205.iHXb1z.rst
new file mode 100644
index 00000000000000..0dd94324ffd2ea
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-08-31-12-34-02.gh-issue-138205.iHXb1z.rst
@@ -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`.
diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c
index dcaadb818e0bf7..8c9d463ad903ec 100644
--- a/Modules/mmapmodule.c
+++ b/Modules/mmapmodule.c
@@ -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},

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to