]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-135748: use argument clinic for more socket methods (#135749)
authorKumar Aditya <kumaraditya@python.org>
Fri, 20 Jun 2025 12:02:37 +0000 (17:32 +0530)
committerGitHub <noreply@github.com>
Fri, 20 Jun 2025 12:02:37 +0000 (12:02 +0000)
Modules/clinic/socketmodule.c.h
Modules/socketmodule.c

index 573903be87e6ea85e1a728560049beed402e114c..0cedab597db714c355ed63b8700fb6adaa728c23 100644 (file)
@@ -7,7 +7,7 @@ preserve
 #  include "pycore_runtime.h"     // _Py_ID()
 #endif
 #include "pycore_long.h"          // _PyLong_UInt16_Converter()
-#include "pycore_modsupport.h"    // _PyArg_UnpackKeywords()
+#include "pycore_modsupport.h"    // _PyArg_CheckPositional()
 
 PyDoc_STRVAR(_socket_socket_close__doc__,
 "close($self, /)\n"
@@ -29,6 +29,170 @@ _socket_socket_close(PyObject *s, PyObject *Py_UNUSED(ignored))
     return _socket_socket_close_impl((PySocketSockObject *)s);
 }
 
+PyDoc_STRVAR(_socket_socket_send__doc__,
+"send($self, data, flags=0, /)\n"
+"--\n"
+"\n"
+"Send a data string to the socket.\n"
+"\n"
+"For the optional flags argument, see the Unix manual.\n"
+"Return the number of bytes sent; this may be less than len(data) if the network is busy.");
+
+#define _SOCKET_SOCKET_SEND_METHODDEF    \
+    {"send", _PyCFunction_CAST(_socket_socket_send), METH_FASTCALL, _socket_socket_send__doc__},
+
+static PyObject *
+_socket_socket_send_impl(PySocketSockObject *s, Py_buffer *pbuf, int flags);
+
+static PyObject *
+_socket_socket_send(PyObject *s, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    Py_buffer pbuf = {NULL, NULL};
+    int flags = 0;
+
+    if (!_PyArg_CheckPositional("send", nargs, 1, 2)) {
+        goto exit;
+    }
+    if (PyObject_GetBuffer(args[0], &pbuf, PyBUF_SIMPLE) != 0) {
+        goto exit;
+    }
+    if (nargs < 2) {
+        goto skip_optional;
+    }
+    flags = PyLong_AsInt(args[1]);
+    if (flags == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+skip_optional:
+    return_value = _socket_socket_send_impl((PySocketSockObject *)s, &pbuf, flags);
+
+exit:
+    /* Cleanup for pbuf */
+    if (pbuf.obj) {
+       PyBuffer_Release(&pbuf);
+    }
+
+    return return_value;
+}
+
+PyDoc_STRVAR(_socket_socket_sendall__doc__,
+"sendall($self, data, flags=0, /)\n"
+"--\n"
+"\n"
+"Send a data string to the socket.\n"
+"\n"
+"For the optional flags argument, see the Unix manual.\n"
+"This calls send() repeatedly until all data is sent.\n"
+"If an error occurs, it\'s impossible to tell how much data has been sent.");
+
+#define _SOCKET_SOCKET_SENDALL_METHODDEF    \
+    {"sendall", _PyCFunction_CAST(_socket_socket_sendall), METH_FASTCALL, _socket_socket_sendall__doc__},
+
+static PyObject *
+_socket_socket_sendall_impl(PySocketSockObject *s, Py_buffer *pbuf,
+                            int flags);
+
+static PyObject *
+_socket_socket_sendall(PyObject *s, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    Py_buffer pbuf = {NULL, NULL};
+    int flags = 0;
+
+    if (!_PyArg_CheckPositional("sendall", nargs, 1, 2)) {
+        goto exit;
+    }
+    if (PyObject_GetBuffer(args[0], &pbuf, PyBUF_SIMPLE) != 0) {
+        goto exit;
+    }
+    if (nargs < 2) {
+        goto skip_optional;
+    }
+    flags = PyLong_AsInt(args[1]);
+    if (flags == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+skip_optional:
+    return_value = _socket_socket_sendall_impl((PySocketSockObject *)s, &pbuf, flags);
+
+exit:
+    /* Cleanup for pbuf */
+    if (pbuf.obj) {
+       PyBuffer_Release(&pbuf);
+    }
+
+    return return_value;
+}
+
+#if defined(CMSG_LEN)
+
+PyDoc_STRVAR(_socket_socket_sendmsg__doc__,
+"sendmsg($self, buffers, ancdata=<unrepresentable>, flags=0,\n"
+"        address=<unrepresentable>, /)\n"
+"--\n"
+"\n"
+"Send normal and ancillary data to the socket.\n"
+"\n"
+"It gathering the non-ancillary data from a series of buffers\n"
+"and concatenating it into a single message.\n"
+"The buffers argument specifies the non-ancillary\n"
+"data as an iterable of bytes-like objects (e.g. bytes objects).\n"
+"The ancdata argument specifies the ancillary data (control messages)\n"
+"as an iterable of zero or more tuples (cmsg_level, cmsg_type,\n"
+"cmsg_data), where cmsg_level and cmsg_type are integers specifying the\n"
+"protocol level and protocol-specific type respectively, and cmsg_data\n"
+"is a bytes-like object holding the associated data.  The flags\n"
+"argument defaults to 0 and has the same meaning as for send().  If\n"
+"address is supplied and not None, it sets a destination address for\n"
+"the message.  The return value is the number of bytes of non-ancillary\n"
+"data sent.");
+
+#define _SOCKET_SOCKET_SENDMSG_METHODDEF    \
+    {"sendmsg", _PyCFunction_CAST(_socket_socket_sendmsg), METH_FASTCALL, _socket_socket_sendmsg__doc__},
+
+static PyObject *
+_socket_socket_sendmsg_impl(PySocketSockObject *s, PyObject *data_arg,
+                            PyObject *cmsg_arg, int flags,
+                            PyObject *addr_arg);
+
+static PyObject *
+_socket_socket_sendmsg(PyObject *s, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    PyObject *data_arg;
+    PyObject *cmsg_arg = NULL;
+    int flags = 0;
+    PyObject *addr_arg = NULL;
+
+    if (!_PyArg_CheckPositional("sendmsg", nargs, 1, 4)) {
+        goto exit;
+    }
+    data_arg = args[0];
+    if (nargs < 2) {
+        goto skip_optional;
+    }
+    cmsg_arg = args[1];
+    if (nargs < 3) {
+        goto skip_optional;
+    }
+    flags = PyLong_AsInt(args[2]);
+    if (flags == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    if (nargs < 4) {
+        goto skip_optional;
+    }
+    addr_arg = args[3];
+skip_optional:
+    return_value = _socket_socket_sendmsg_impl((PySocketSockObject *)s, data_arg, cmsg_arg, flags, addr_arg);
+
+exit:
+    return return_value;
+}
+
+#endif /* defined(CMSG_LEN) */
+
 static int
 sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
                   PyObject *fdobj);
@@ -359,6 +523,10 @@ exit:
 
 #endif /* (defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)) */
 
+#ifndef _SOCKET_SOCKET_SENDMSG_METHODDEF
+    #define _SOCKET_SOCKET_SENDMSG_METHODDEF
+#endif /* !defined(_SOCKET_SOCKET_SENDMSG_METHODDEF) */
+
 #ifndef _SOCKET_INET_NTOA_METHODDEF
     #define _SOCKET_INET_NTOA_METHODDEF
 #endif /* !defined(_SOCKET_INET_NTOA_METHODDEF) */
@@ -370,4 +538,4 @@ exit:
 #ifndef _SOCKET_IF_INDEXTONAME_METHODDEF
     #define _SOCKET_IF_INDEXTONAME_METHODDEF
 #endif /* !defined(_SOCKET_IF_INDEXTONAME_METHODDEF) */
-/*[clinic end generated code: output=07776dd21d1e3b56 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=0376c46b76ae2bce input=a9049054013a1b77]*/
index 85c72779bac607a726e52336f9dee90419a85451..f3ad01854de93b27bd7d614d4b8ed2d2b947ff75 100644 (file)
@@ -4592,55 +4592,62 @@ sock_send_impl(PySocketSockObject *s, void *data)
     return (ctx->result >= 0);
 }
 
-/* s.send(data [,flags]) method */
+/*[clinic input]
+_socket.socket.send
+    self as s: self(type="PySocketSockObject *")
+    data as pbuf: Py_buffer
+    flags: int = 0
+    /
+
+Send a data string to the socket.
+
+For the optional flags argument, see the Unix manual.
+Return the number of bytes sent; this may be less than len(data) if the network is busy.
+[clinic start generated code]*/
 
 static PyObject *
-sock_send(PyObject *self, PyObject *args)
-{
-    PySocketSockObject *s = _PySocketSockObject_CAST(self);
+_socket_socket_send_impl(PySocketSockObject *s, Py_buffer *pbuf, int flags)
+/*[clinic end generated code: output=3ddf83f17d0c875b input=befe7d7790ccb035]*/
 
-    int flags = 0;
-    Py_buffer pbuf;
+{
     struct sock_send ctx;
 
-    if (!PyArg_ParseTuple(args, "y*|i:send", &pbuf, &flags))
-        return NULL;
-
     if (!IS_SELECTABLE(s)) {
-        PyBuffer_Release(&pbuf);
         return select_error();
     }
-    ctx.buf = pbuf.buf;
-    ctx.len = pbuf.len;
+    ctx.buf = pbuf->buf;
+    ctx.len = pbuf->len;
     ctx.flags = flags;
     if (sock_call(s, 1, sock_send_impl, &ctx) < 0) {
-        PyBuffer_Release(&pbuf);
         return NULL;
     }
-    PyBuffer_Release(&pbuf);
 
     return PyLong_FromSsize_t(ctx.result);
 }
 
-PyDoc_STRVAR(send_doc,
-"send(data[, flags]) -> count\n\
-\n\
-Send a data string to the socket.  For the optional flags\n\
-argument, see the Unix manual.  Return the number of bytes\n\
-sent; this may be less than len(data) if the network is busy.");
 
+/*[clinic input]
+_socket.socket.sendall
+    self as s: self(type="PySocketSockObject *")
+    data as pbuf: Py_buffer
+    flags: int = 0
+    /
 
-/* s.sendall(data [,flags]) method */
+Send a data string to the socket.
+
+For the optional flags argument, see the Unix manual.
+This calls send() repeatedly until all data is sent.
+If an error occurs, it's impossible to tell how much data has been sent.
+[clinic start generated code]*/
 
 static PyObject *
-sock_sendall(PyObject *self, PyObject *args)
-{
-    PySocketSockObject *s = _PySocketSockObject_CAST(self);
+_socket_socket_sendall_impl(PySocketSockObject *s, Py_buffer *pbuf,
+                            int flags)
+/*[clinic end generated code: output=ec92861424d3faa8 input=732b15b9ca64dce6]*/
 
+{
     char *buf;
     Py_ssize_t len, n;
-    int flags = 0;
-    Py_buffer pbuf;
     struct sock_send ctx;
     int has_timeout = (s->sock_timeout > 0);
     PyTime_t timeout = s->sock_timeout;
@@ -4648,13 +4655,10 @@ sock_sendall(PyObject *self, PyObject *args)
     int deadline_initialized = 0;
     PyObject *res = NULL;
 
-    if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags))
-        return NULL;
-    buf = pbuf.buf;
-    len = pbuf.len;
+    buf = pbuf->buf;
+    len = pbuf->len;
 
     if (!IS_SELECTABLE(s)) {
-        PyBuffer_Release(&pbuf);
         return select_error();
     }
 
@@ -4692,23 +4696,13 @@ sock_sendall(PyObject *self, PyObject *args)
         if (PyErr_CheckSignals())
             goto done;
     } while (len > 0);
-    PyBuffer_Release(&pbuf);
 
     res = Py_NewRef(Py_None);
 
 done:
-    PyBuffer_Release(&pbuf);
     return res;
 }
 
-PyDoc_STRVAR(sendall_doc,
-"sendall(data[, flags])\n\
-\n\
-Send a data string to the socket.  For the optional flags\n\
-argument, see the Unix manual.  This calls send() repeatedly\n\
-until all data is sent.  If an error occurs, it's impossible\n\
-to tell how much data has been sent.");
-
 
 #ifdef HAVE_SENDTO
 struct sock_sendto {
@@ -4858,10 +4852,8 @@ sock_sendmsg_iovec(PySocketSockObject *s, PyObject *data_arg,
         }
     }
     for (; ndatabufs < ndataparts; ndatabufs++) {
-        if (!PyArg_Parse(PySequence_Fast_GET_ITEM(data_fast, ndatabufs),
-                         "y*;sendmsg() argument 1 must be an iterable of "
-                         "bytes-like objects",
-                         &databufs[ndatabufs]))
+        if (PyObject_GetBuffer(PySequence_Fast_GET_ITEM(data_fast, ndatabufs),
+            &databufs[ndatabufs], PyBUF_SIMPLE) < 0)
             goto finally;
         iovs[ndatabufs].iov_base = databufs[ndatabufs].buf;
         iovs[ndatabufs].iov_len = databufs[ndatabufs].len;
@@ -4883,13 +4875,39 @@ sock_sendmsg_impl(PySocketSockObject *s, void *data)
     return (ctx->result >= 0);
 }
 
-/* s.sendmsg(buffers[, ancdata[, flags[, address]]]) method */
+/*[clinic input]
+_socket.socket.sendmsg
+    self as s: self(type="PySocketSockObject *")
+    buffers as data_arg: object
+    ancdata as cmsg_arg: object = NULL
+    flags: int = 0
+    address as addr_arg: object = NULL
+    /
+
+Send normal and ancillary data to the socket.
+
+It gathering the non-ancillary data from a series of buffers
+and concatenating it into a single message.
+The buffers argument specifies the non-ancillary
+data as an iterable of bytes-like objects (e.g. bytes objects).
+The ancdata argument specifies the ancillary data (control messages)
+as an iterable of zero or more tuples (cmsg_level, cmsg_type,
+cmsg_data), where cmsg_level and cmsg_type are integers specifying the
+protocol level and protocol-specific type respectively, and cmsg_data
+is a bytes-like object holding the associated data.  The flags
+argument defaults to 0 and has the same meaning as for send().  If
+address is supplied and not None, it sets a destination address for
+the message.  The return value is the number of bytes of non-ancillary
+data sent.
+[clinic start generated code]*/
 
 static PyObject *
-sock_sendmsg(PyObject *self, PyObject *args)
-{
-    PySocketSockObject *s = _PySocketSockObject_CAST(self);
+_socket_socket_sendmsg_impl(PySocketSockObject *s, PyObject *data_arg,
+                            PyObject *cmsg_arg, int flags,
+                            PyObject *addr_arg)
+/*[clinic end generated code: output=3b4cb1110644ce39 input=479c13d90bd2f88b]*/
 
+{
     Py_ssize_t i, ndatabufs = 0, ncmsgs, ncmsgbufs = 0;
     Py_buffer *databufs = NULL;
     sock_addr_t addrbuf;
@@ -4901,16 +4919,10 @@ sock_sendmsg(PyObject *self, PyObject *args)
     } *cmsgs = NULL;
     void *controlbuf = NULL;
     size_t controllen, controllen_last;
-    int addrlen, flags = 0;
-    PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL,
-        *cmsg_fast = NULL, *retval = NULL;
+    int addrlen;
+    PyObject *cmsg_fast = NULL, *retval = NULL;
     struct sock_sendmsg ctx;
 
-    if (!PyArg_ParseTuple(args, "O|OiO:sendmsg",
-                          &data_arg, &cmsg_arg, &flags, &addr_arg)) {
-        return NULL;
-    }
-
     memset(&msg, 0, sizeof(msg));
 
     /* Parse destination address. */
@@ -5072,22 +5084,6 @@ finally:
     return retval;
 }
 
-PyDoc_STRVAR(sendmsg_doc,
-"sendmsg(buffers[, ancdata[, flags[, address]]]) -> count\n\
-\n\
-Send normal and ancillary data to the socket, gathering the\n\
-non-ancillary data from a series of buffers and concatenating it into\n\
-a single message.  The buffers argument specifies the non-ancillary\n\
-data as an iterable of bytes-like objects (e.g. bytes objects).\n\
-The ancdata argument specifies the ancillary data (control messages)\n\
-as an iterable of zero or more tuples (cmsg_level, cmsg_type,\n\
-cmsg_data), where cmsg_level and cmsg_type are integers specifying the\n\
-protocol level and protocol-specific type respectively, and cmsg_data\n\
-is a bytes-like object holding the associated data.  The flags\n\
-argument defaults to 0 and has the same meaning as for send().  If\n\
-address is supplied and not None, it sets a destination address for\n\
-the message.  The return value is the number of bytes of non-ancillary\n\
-data sent.");
 #endif    /* CMSG_LEN */
 
 #ifdef HAVE_SOCKADDR_ALG
@@ -5424,8 +5420,8 @@ static PyMethodDef sock_methods[] = {
         recvfrom_into_doc
     },
 #endif
-    {"send", sock_send, METH_VARARGS, send_doc},
-    {"sendall", sock_sendall, METH_VARARGS, sendall_doc},
+    _SOCKET_SOCKET_SEND_METHODDEF
+    _SOCKET_SOCKET_SENDALL_METHODDEF
 #ifdef HAVE_SENDTO
     {"sendto", sock_sendto, METH_VARARGS, sendto_doc},
 #endif
@@ -5445,7 +5441,7 @@ static PyMethodDef sock_methods[] = {
 #ifdef CMSG_LEN
     {"recvmsg", sock_recvmsg, METH_VARARGS, recvmsg_doc},
     {"recvmsg_into", sock_recvmsg_into, METH_VARARGS, recvmsg_into_doc},
-    {"sendmsg", sock_sendmsg, METH_VARARGS, sendmsg_doc},
+    _SOCKET_SOCKET_SENDMSG_METHODDEF
 #endif
 #ifdef HAVE_SOCKADDR_ALG
     {