From de4babac869d5d9f4df3db4a7c6f6466bb9951a4 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 24 Aug 2025 00:38:50 +0900 Subject: [PATCH 1/8] Convert mmap.flush to use argument clinic --- Modules/mmapmodule.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 0cb4b62d734550..4d32ec32156650 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -28,6 +28,12 @@ #include "pycore_fileutils.h" // _Py_stat_struct #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() +/*[clinic input] +module mmap +class mmap.mmap "mmap_object *" "mmap_object_type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=4ebde54549b9daa7]*/ + #include // offsetof() #ifndef MS_WINDOWS # include // close() @@ -127,6 +133,9 @@ typedef struct { } mmap_object; #define mmap_object_CAST(op) ((mmap_object *)(op)) +#define MMAP_GET_SIZE(self) (mmap_object_CAST(self)->size) + +#include "clinic/mmapmodule.c.h" static int mmap_object_traverse(PyObject *op, visitproc visit, void *arg) @@ -924,15 +933,26 @@ mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored)) return PyLong_FromSize_t(self->pos); } +/*[clinic input] +mmap.mmap.flush + + offset: Py_ssize_t = 0 + size: Py_ssize_t(c_default="MMAP_GET_SIZE(self)") = None + / + +Flushes changes made to the in-memory copy of a file back to disk. + +If offset and size are specified, only the specified range will +be flushed. If not specified, the entire mapped region will be +flushed. +[clinic start generated code]*/ + static PyObject * -mmap_flush_method(PyObject *op, PyObject *args) +mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size) +/*[clinic end generated code: output=956ced67466149cf input=23ac67c08804a13a]*/ { - Py_ssize_t offset = 0; - mmap_object *self = mmap_object_CAST(op); - Py_ssize_t size = self->size; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size)) - return NULL; + if (size < 0 || offset < 0 || self->size - offset < size) { PyErr_SetString(PyExc_ValueError, "flush values out of range"); return NULL; @@ -1194,7 +1214,7 @@ static struct PyMethodDef mmap_object_methods[] = { {"close", mmap_close_method, METH_NOARGS}, {"find", mmap_find_method, METH_VARARGS}, {"rfind", mmap_rfind_method, METH_VARARGS}, - {"flush", mmap_flush_method, METH_VARARGS}, + MMAP_MMAP_FLUSH_METHODDEF #ifdef HAVE_MADVISE {"madvise", mmap_madvise_method, METH_VARARGS}, #endif From 3e284dc2c7be27a7b036ca3924d999fa37dfaa50 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 24 Aug 2025 01:35:32 +0900 Subject: [PATCH 2/8] Add test --- Lib/test/test_mmap.py | 12 ++++++++++++ Modules/mmapmodule.c | 10 +++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index b2a299ed172967..83def9c31799af 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -1136,6 +1136,18 @@ def test_access_violations(self): self.assertEqual(stdout.strip(), b'') self.assertEqual(stderr.strip(), b'') + def test_flush_parameters(self): + with open(TESTFN, 'wb+') as f: + f.write(b'x' * PAGESIZE * 3) + f.flush() + + m = mmap.mmap(f.fileno(), PAGESIZE * 3) + self.addCleanup(m.close) + + m.flush() + m.flush(PAGESIZE) + m.flush(PAGESIZE, PAGESIZE) + class LargeMmapTests(unittest.TestCase): diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 4d32ec32156650..ba658868972aa1 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -133,7 +133,6 @@ typedef struct { } mmap_object; #define mmap_object_CAST(op) ((mmap_object *)(op)) -#define MMAP_GET_SIZE(self) (mmap_object_CAST(self)->size) #include "clinic/mmapmodule.c.h" @@ -937,7 +936,7 @@ mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored)) mmap.mmap.flush offset: Py_ssize_t = 0 - size: Py_ssize_t(c_default="MMAP_GET_SIZE(self)") = None + size: Py_ssize_t(c_default="-1") = None / Flushes changes made to the in-memory copy of a file back to disk. @@ -949,10 +948,15 @@ flushed. static PyObject * mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size) -/*[clinic end generated code: output=956ced67466149cf input=23ac67c08804a13a]*/ +/*[clinic end generated code: output=956ced67466149cf input=07c2c6d4e69263a4]*/ { CHECK_VALID(NULL); + /* If size is -1 (default), calculate size from offset to end */ + if (size == -1) { + size = self->size - offset; + } + if (size < 0 || offset < 0 || self->size - offset < size) { PyErr_SetString(PyExc_ValueError, "flush values out of range"); return NULL; From ff3a039933b3590f776180a4bf982ea774717053 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 24 Aug 2025 01:52:35 +0900 Subject: [PATCH 3/8] Add clinic generated file --- Modules/clinic/mmapmodule.c.h | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Modules/clinic/mmapmodule.c.h diff --git a/Modules/clinic/mmapmodule.c.h b/Modules/clinic/mmapmodule.c.h new file mode 100644 index 00000000000000..ec4282e8082ab7 --- /dev/null +++ b/Modules/clinic/mmapmodule.c.h @@ -0,0 +1,70 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +PyDoc_STRVAR(mmap_mmap_flush__doc__, +"flush($self, offset=0, size=None, /)\n" +"--\n" +"\n" +"Flushes changes made to the in-memory copy of a file back to disk.\n" +"\n" +"If offset and size are specified, only the specified range will\n" +"be flushed. If not specified, the entire mapped region will be\n" +"flushed."); + +#define MMAP_MMAP_FLUSH_METHODDEF \ + {"flush", _PyCFunction_CAST(mmap_mmap_flush), METH_FASTCALL, mmap_mmap_flush__doc__}, + +static PyObject * +mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size); + +static PyObject * +mmap_mmap_flush(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t offset = 0; + Py_ssize_t size = -1; + + if (!_PyArg_CheckPositional("flush", nargs, 0, 2)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + offset = ival; + } + if (nargs < 2) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + size = ival; + } +skip_optional: + return_value = mmap_mmap_flush_impl((mmap_object *)self, offset, size); + +exit: + return return_value; +} +/*[clinic end generated code: output=c95c7ffe05f26c3a input=a9049054013a1b77]*/ From f09fa8a52b8daf26f4404feda5fad82d297671f7 Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 24 Aug 2025 02:05:04 +0900 Subject: [PATCH 4/8] Add news entry --- .../next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst diff --git a/Misc/NEWS.d/next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst b/Misc/NEWS.d/next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst new file mode 100644 index 00000000000000..31421ef5e49489 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst @@ -0,0 +1,2 @@ +Fixed a bug in :meth:mmap.mmap.flush where calling with only an offset +parameter would fail. From 1493fbf7475b42c63edfb8d60c4edd1b68442dfd Mon Sep 17 00:00:00 2001 From: AN Long Date: Sun, 24 Aug 2025 02:12:01 +0900 Subject: [PATCH 5/8] Fix docs --- .../next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst | 2 +- Modules/mmapmodule.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst b/Misc/NEWS.d/next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst index 31421ef5e49489..21c28c49628cba 100644 --- a/Misc/NEWS.d/next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst +++ b/Misc/NEWS.d/next/Library/2025-08-24-02-04-32.gh-issue-138092.V4-wTO.rst @@ -1,2 +1,2 @@ -Fixed a bug in :meth:mmap.mmap.flush where calling with only an offset +Fixed a bug in :meth:`mmap.mmap.flush` where calling with only an offset parameter would fail. diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index ba658868972aa1..f4d739320952f1 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -952,7 +952,7 @@ mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size) { CHECK_VALID(NULL); - /* If size is -1 (default), calculate size from offset to end */ + // If size is -1 (default), calculate size from offset to end. if (size == -1) { size = self->size - offset; } From 5274cc3f3c6c0dc9d92ab1cd781c82fdb9861a36 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 9 Sep 2025 00:19:01 +0900 Subject: [PATCH 6/8] Revert the change to use Argument Clinic --- Modules/clinic/mmapmodule.c.h | 70 ----------------------------------- Modules/mmapmodule.c | 43 ++++----------------- 2 files changed, 8 insertions(+), 105 deletions(-) delete mode 100644 Modules/clinic/mmapmodule.c.h diff --git a/Modules/clinic/mmapmodule.c.h b/Modules/clinic/mmapmodule.c.h deleted file mode 100644 index ec4282e8082ab7..00000000000000 --- a/Modules/clinic/mmapmodule.c.h +++ /dev/null @@ -1,70 +0,0 @@ -/*[clinic input] -preserve -[clinic start generated code]*/ - -#include "pycore_abstract.h" // _PyNumber_Index() -#include "pycore_modsupport.h" // _PyArg_CheckPositional() - -PyDoc_STRVAR(mmap_mmap_flush__doc__, -"flush($self, offset=0, size=None, /)\n" -"--\n" -"\n" -"Flushes changes made to the in-memory copy of a file back to disk.\n" -"\n" -"If offset and size are specified, only the specified range will\n" -"be flushed. If not specified, the entire mapped region will be\n" -"flushed."); - -#define MMAP_MMAP_FLUSH_METHODDEF \ - {"flush", _PyCFunction_CAST(mmap_mmap_flush), METH_FASTCALL, mmap_mmap_flush__doc__}, - -static PyObject * -mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size); - -static PyObject * -mmap_mmap_flush(PyObject *self, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value = NULL; - Py_ssize_t offset = 0; - Py_ssize_t size = -1; - - if (!_PyArg_CheckPositional("flush", nargs, 0, 2)) { - goto exit; - } - if (nargs < 1) { - goto skip_optional; - } - { - Py_ssize_t ival = -1; - PyObject *iobj = _PyNumber_Index(args[0]); - if (iobj != NULL) { - ival = PyLong_AsSsize_t(iobj); - Py_DECREF(iobj); - } - if (ival == -1 && PyErr_Occurred()) { - goto exit; - } - offset = ival; - } - if (nargs < 2) { - goto skip_optional; - } - { - Py_ssize_t ival = -1; - PyObject *iobj = _PyNumber_Index(args[1]); - if (iobj != NULL) { - ival = PyLong_AsSsize_t(iobj); - Py_DECREF(iobj); - } - if (ival == -1 && PyErr_Occurred()) { - goto exit; - } - size = ival; - } -skip_optional: - return_value = mmap_mmap_flush_impl((mmap_object *)self, offset, size); - -exit: - return return_value; -} -/*[clinic end generated code: output=c95c7ffe05f26c3a input=a9049054013a1b77]*/ diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 72832c3449302d..4ce8e318e9bfe4 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -28,12 +28,6 @@ #include "pycore_fileutils.h" // _Py_stat_struct #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() -/*[clinic input] -module mmap -class mmap.mmap "mmap_object *" "mmap_object_type" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=4ebde54549b9daa7]*/ - #include // offsetof() #ifndef MS_WINDOWS # include // close() @@ -135,15 +129,6 @@ typedef struct { #define mmap_object_CAST(op) ((mmap_object *)(op)) -#include "clinic/mmapmodule.c.h" - -static int -mmap_object_traverse(PyObject *op, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(op)); - return 0; -} - static void mmap_object_dealloc(PyObject *op) { @@ -942,31 +927,19 @@ mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored)) return PyLong_FromSize_t(self->pos); } -/*[clinic input] -mmap.mmap.flush - - offset: Py_ssize_t = 0 - size: Py_ssize_t(c_default="-1") = None - / - -Flushes changes made to the in-memory copy of a file back to disk. - -If offset and size are specified, only the specified range will -be flushed. If not specified, the entire mapped region will be -flushed. -[clinic start generated code]*/ - static PyObject * -mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size) -/*[clinic end generated code: output=956ced67466149cf input=07c2c6d4e69263a4]*/ +mmap_flush_method(PyObject *op, PyObject *args) { + Py_ssize_t offset = 0; + Py_ssize_t size = -1; + mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); - - // If size is -1 (default), calculate size from offset to end. + if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size)) { + return NULL; + } if (size == -1) { size = self->size - offset; } - if (size < 0 || offset < 0 || self->size - offset < size) { PyErr_SetString(PyExc_ValueError, "flush values out of range"); return NULL; @@ -1228,7 +1201,7 @@ static struct PyMethodDef mmap_object_methods[] = { {"close", mmap_close_method, METH_NOARGS}, {"find", mmap_find_method, METH_VARARGS}, {"rfind", mmap_rfind_method, METH_VARARGS}, - MMAP_MMAP_FLUSH_METHODDEF + {"flush", mmap_flush_method, METH_VARARGS}, #ifdef HAVE_MADVISE {"madvise", mmap_madvise_method, METH_VARARGS}, #endif From e7655cc9365cf4919fca7804f3927da07c9cbca3 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 23 Sep 2025 01:54:17 +0900 Subject: [PATCH 7/8] Update document --- Doc/library/mmap.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst index bd3f7229bdaf70..adedbd37ed2ed9 100644 --- a/Doc/library/mmap.rst +++ b/Doc/library/mmap.rst @@ -212,8 +212,7 @@ To map anonymous memory, -1 should be passed as the fileno along with the length Writable :term:`bytes-like object` is now accepted. - .. method:: flush() - flush(offset, size, /) + .. method:: flush([offset[, size]]) Flushes changes made to the in-memory copy of a file back to disk. Without use of this call there is no guarantee that changes are written back before @@ -230,6 +229,12 @@ To map anonymous memory, -1 should be passed as the fileno along with the length on error under Windows. A zero value was returned on success; an exception was raised on error under Unix. + .. versionchanged:: next + Allow specifying *offset* without *size*. Previously, both *offset* + and *size* parameters were required together. Now *offset* can be + specified alone, and the flush operation will extend from *offset* + to the end of the buffer. + .. method:: madvise(option[, start[, length]]) From 40147bbb96d25d3f07ae9dc2e157cad919ceef58 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 23 Sep 2025 02:05:45 +0900 Subject: [PATCH 8/8] Update document --- Doc/library/mmap.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst index adedbd37ed2ed9..b6ffb5cebc020e 100644 --- a/Doc/library/mmap.rst +++ b/Doc/library/mmap.rst @@ -233,7 +233,7 @@ To map anonymous memory, -1 should be passed as the fileno along with the length Allow specifying *offset* without *size*. Previously, both *offset* and *size* parameters were required together. Now *offset* can be specified alone, and the flush operation will extend from *offset* - to the end of the buffer. + to the end of the mmap. .. method:: madvise(option[, start[, length]])