]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-138205: Remove the `resize` method on `mmap` object on platforms don't support...
authorAN Long <aisk@users.noreply.github.com>
Sat, 6 Sep 2025 08:40:04 +0000 (17:40 +0900)
committerGitHub <noreply@github.com>
Sat, 6 Sep 2025 08:40:04 +0000 (14:10 +0530)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
Doc/library/mmap.rst
Doc/whatsnew/3.15.rst
Lib/test/test_mmap.py
Misc/NEWS.d/next/Library/2025-08-31-12-34-02.gh-issue-138205.iHXb1z.rst [new file with mode: 0644]
Modules/mmapmodule.c

index 9c0b828fbd4d28e4ef07511841bf143c0e3a1e0d..d82dda9e54b15076e6fdd0a8d45e577d0142835e 100644 (file)
@@ -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
index 351ba0cd2afdfe12fb52c68333b92808b89d13da..3236213de5aaa21b454552e2f08098392d6d7500 100644 (file)
@@ -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
 -----------------
index da69770915092ac8decc2e1c8211b8385074f3dd..75ea1a671b67de124fd9a87667afe4090b3ad419 100644 (file)
@@ -57,6 +57,7 @@ class MmapTests(unittest.TestCase):
             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 @@ class MmapTests(unittest.TestCase):
         # 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 @@ class MmapTests(unittest.TestCase):
             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 @@ class MmapTests(unittest.TestCase):
             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 @@ class MmapTests(unittest.TestCase):
                 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 @@ class MmapTests(unittest.TestCase):
         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 @@ class MmapTests(unittest.TestCase):
             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 @@ class MmapTests(unittest.TestCase):
         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 @@ class MmapTests(unittest.TestCase):
         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 @@ class MmapTests(unittest.TestCase):
                 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 @@ class MmapTests(unittest.TestCase):
 
         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 @@ class MmapTests(unittest.TestCase):
 
         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 (file)
index 0000000..0dd9432
--- /dev/null
@@ -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`.
index dcaadb818e0bf70d9019cd03732aa979e78a7838..8c9d463ad903ec5855de75d56e1c586753de1ab7 100644 (file)
@@ -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},