From c30d5677e9a485afdfa04a80c2d4267a33fec361 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 13 Jul 2026 11:43:50 +0300 Subject: [PATCH] gh-71086: Document negative offset support in struct.pack_into() and unpack_from() Co-Authored-By: Claude Opus 4.8 (1M context) --- Doc/library/struct.rst | 2 ++ Modules/_struct.c | 24 +++++++++++++----------- Modules/clinic/_struct.c.h | 18 ++++++++++-------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index ed5a719028d60ef..07e4c82375fc1ee 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -68,6 +68,7 @@ The module defines the following exception and functions: Pack the values *v1*, *v2*, ... according to the format string *format* and write the packed bytes into the writable buffer *buffer* starting at position *offset*. Note that *offset* is a required argument. + A negative *offset* counts from the end of *buffer*. .. function:: unpack(format, buffer) @@ -84,6 +85,7 @@ The module defines the following exception and functions: string *format*. The result is a tuple even if it contains exactly one item. The buffer's size in bytes, starting at position *offset*, must be at least the size required by the format, as reflected by :func:`calcsize`. + A negative *offset* counts from the end of *buffer*. .. function:: iter_unpack(format, buffer) diff --git a/Modules/_struct.c b/Modules/_struct.c index 6bf3993439cf1d7..260dc85faf9fb95 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -2156,14 +2156,14 @@ Return a tuple containing unpacked values. Values are unpacked according to the struct format string. The buffer's size in bytes, starting at position offset, must be at -least the struct size. See help(struct) for more on format -strings. +least the struct size. A negative offset counts from the end of +the buffer. See help(struct) for more on format strings. [clinic start generated code]*/ static PyObject * Struct_unpack_from_impl(PyStructObject *self, Py_buffer *buffer, Py_ssize_t offset) -/*[clinic end generated code: output=57fac875e0977316 input=57cfcf84c088faa4]*/ +/*[clinic end generated code: output=57fac875e0977316 input=49b7c90dd8faeb97]*/ { _structmodulestate *state = get_struct_state_structinst(self); ENSURE_STRUCT_IS_READY(self); @@ -2497,15 +2497,16 @@ Pack values and write the packed bytes into the buffer. Pack the provided values according to the struct format string and write the packed bytes into the writable buffer starting at -offset. Note that the offset is a required argument. See -help(struct) for more on format strings. +offset. Note that the offset is a required argument. A negative +offset counts from the end of the buffer. See help(struct) for +more on format strings. [clinic start generated code]*/ static PyObject * Struct_pack_into_impl(PyStructObject *self, Py_buffer *buffer, Py_ssize_t offset, PyObject * const *values, Py_ssize_t values_length) -/*[clinic end generated code: output=aa9d9a93f5f8f77b input=9d842a368ee14245]*/ +/*[clinic end generated code: output=aa9d9a93f5f8f77b input=a2b8749e3843f01b]*/ { _structmodulestate *state = get_struct_state_structinst(self); @@ -2735,15 +2736,15 @@ Pack values and write the packed bytes into the buffer. Pack the provided values according to the format string and write the packed bytes into the writable buffer starting at offset. Note that the -offset is a required argument. See help(struct) for more on format -strings. +offset is a required argument. A negative offset counts from the end of +the buffer. See help(struct) for more on format strings. [clinic start generated code]*/ static PyObject * pack_into_impl(PyObject *module, PyStructObject *s_object, Py_buffer *buffer, Py_ssize_t offset, PyObject * const *values, Py_ssize_t values_length) -/*[clinic end generated code: output=e8bf7d422b2088ef input=086867c0f5d8a8e4]*/ +/*[clinic end generated code: output=e8bf7d422b2088ef input=548c35c57db7436a]*/ { return Struct_pack_into_impl(s_object, buffer, offset, values, values_length); @@ -2781,14 +2782,15 @@ unpack_from Return a tuple containing values unpacked according to the format string. -The buffer's size, minus offset, must be at least calcsize(format). See +The buffer must contain at least calcsize(format) bytes starting at +offset. A negative offset counts from the end of the buffer. See help(struct) for more on format strings. [clinic start generated code]*/ static PyObject * unpack_from_impl(PyObject *module, PyStructObject *s_object, Py_buffer *buffer, Py_ssize_t offset) -/*[clinic end generated code: output=1042631674c6e0d3 input=3e46619756fb0293]*/ +/*[clinic end generated code: output=1042631674c6e0d3 input=fb755400a7a47a51]*/ { return Struct_unpack_from_impl(s_object, buffer, offset); } diff --git a/Modules/clinic/_struct.c.h b/Modules/clinic/_struct.c.h index e75698e3ed00cc5..22fec68a4e9ee63 100644 --- a/Modules/clinic/_struct.c.h +++ b/Modules/clinic/_struct.c.h @@ -173,8 +173,8 @@ PyDoc_STRVAR(Struct_unpack_from__doc__, "\n" "Values are unpacked according to the struct format string. The\n" "buffer\'s size in bytes, starting at position offset, must be at\n" -"least the struct size. See help(struct) for more on format\n" -"strings."); +"least the struct size. A negative offset counts from the end of\n" +"the buffer. See help(struct) for more on format strings."); #define STRUCT_UNPACK_FROM_METHODDEF \ {"unpack_from", _PyCFunction_CAST(Struct_unpack_from), METH_FASTCALL|METH_KEYWORDS, Struct_unpack_from__doc__}, @@ -319,8 +319,9 @@ PyDoc_STRVAR(Struct_pack_into__doc__, "\n" "Pack the provided values according to the struct format string\n" "and write the packed bytes into the writable buffer starting at\n" -"offset. Note that the offset is a required argument. See\n" -"help(struct) for more on format strings."); +"offset. Note that the offset is a required argument. A negative\n" +"offset counts from the end of the buffer. See help(struct) for\n" +"more on format strings."); #define STRUCT_PACK_INTO_METHODDEF \ {"pack_into", _PyCFunction_CAST(Struct_pack_into), METH_FASTCALL, Struct_pack_into__doc__}, @@ -490,8 +491,8 @@ PyDoc_STRVAR(pack_into__doc__, "\n" "Pack the provided values according to the format string and write the\n" "packed bytes into the writable buffer starting at offset. Note that the\n" -"offset is a required argument. See help(struct) for more on format\n" -"strings."); +"offset is a required argument. A negative offset counts from the end of\n" +"the buffer. See help(struct) for more on format strings."); #define PACK_INTO_METHODDEF \ {"pack_into", _PyCFunction_CAST(pack_into), METH_FASTCALL, pack_into__doc__}, @@ -598,7 +599,8 @@ PyDoc_STRVAR(unpack_from__doc__, "\n" "Return a tuple containing values unpacked according to the format string.\n" "\n" -"The buffer\'s size, minus offset, must be at least calcsize(format). See\n" +"The buffer must contain at least calcsize(format) bytes starting at\n" +"offset. A negative offset counts from the end of the buffer. See\n" "help(struct) for more on format strings."); #define UNPACK_FROM_METHODDEF \ @@ -724,4 +726,4 @@ iter_unpack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -/*[clinic end generated code: output=0f417d43a2a387c8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=021b89d2d3c3c225 input=a9049054013a1b77]*/