[Python-checkins] gh-103092: Isolate `socket` module (#103094)

kumaraditya303 webhook-mailer at python.org
Sat Apr 8 21:04:00 EDT 2023


https://github.com/python/cpython/commit/f329a8bc1e57e454852f8887df6267b42047cd1b
commit: f329a8bc1e57e454852f8887df6267b42047cd1b
branch: main
author: Erlend E. Aasland <erlend.aasland at protonmail.com>
committer: kumaraditya303 <59607654+kumaraditya303 at users.noreply.github.com>
date: 2023-04-09T06:33:52+05:30
summary:

gh-103092: Isolate `socket` module (#103094)

files:
A Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst
M Lib/test/test_socket.py
M Modules/socketmodule.c
M Modules/socketmodule.h
M Tools/c-analyzer/cpython/globals-to-fix.tsv

diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 32252f7b741f..bb7bf436d2d7 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -8,6 +8,7 @@
 import array
 import contextlib
 import errno
+import gc
 import io
 import itertools
 import math
@@ -836,6 +837,12 @@ def requireSocket(*args):
 
 class GeneralModuleTests(unittest.TestCase):
 
+    @unittest.skipUnless(_socket is not None, 'need _socket module')
+    def test_socket_type(self):
+        self.assertTrue(gc.is_tracked(_socket.socket))
+        with self.assertRaisesRegex(TypeError, "immutable"):
+            _socket.socket.foo = 1
+
     def test_SocketType_is_socketobject(self):
         import _socket
         self.assertTrue(socket.SocketType is _socket.socket)
diff --git a/Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst b/Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst
new file mode 100644
index 000000000000..e7586a223c14
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst
@@ -0,0 +1 @@
+Isolate :mod:`!_socket` (apply :pep:`687`). Patch by Erlend E. Aasland.
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 49342b3d48de..656cd546d46d 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -108,6 +108,7 @@ Local naming conventions:
 #define PY_SSIZE_T_CLEAN
 #include "Python.h"
 #include "pycore_fileutils.h"     // _Py_set_inheritable()
+#include "pycore_moduleobject.h"  // _PyModule_GetState
 #include "structmember.h"         // PyMemberDef
 
 #ifdef _Py_MEMORY_SANITIZER
@@ -337,9 +338,9 @@ static FlagRuntimeInfo win_runtime_flags[] = {
 
 /*[clinic input]
 module _socket
-class _socket.socket "PySocketSockObject *" "&sock_type"
+class _socket.socket "PySocketSockObject *" "clinic_state()->sock_type"
 [clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7a8313d9b7f51988]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=2db2489bd2219fd8]*/
 
 static int
 remove_unusable_flags(PyObject *m)
@@ -541,22 +542,59 @@ remove_unusable_flags(PyObject *m)
 #define INADDR_NONE (-1)
 #endif
 
+typedef struct _socket_state {
+    /* The sock_type variable contains pointers to various functions,
+       some of which call new_sockobject(), which uses sock_type, so
+       there has to be a circular reference. */
+    PyTypeObject *sock_type;
+
+    /* Global variable holding the exception type for errors detected
+       by this module (but not argument type or memory errors, etc.). */
+    PyObject *socket_herror;
+    PyObject *socket_gaierror;
+
+    /* Default timeout for new sockets */
+    _PyTime_t defaulttimeout;
+
+#if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4)
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+    /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
+    int accept4_works;
+#endif
+#endif
+
+#ifdef SOCK_CLOEXEC
+    /* socket() and socketpair() fail with EINVAL on Linux kernel older
+     * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */
+    int sock_cloexec_works;
+#endif
+} socket_state;
+
+static inline socket_state *
+get_module_state(PyObject *mod)
+{
+    void *state = _PyModule_GetState(mod);
+    assert(state != NULL);
+    return (socket_state *)state;
+}
+
+static struct PyModuleDef socketmodule;
+
+static inline socket_state *
+find_module_state_by_def(PyTypeObject *type)
+{
+    PyObject *mod = PyType_GetModuleByDef(type, &socketmodule);
+    assert(mod != NULL);
+    return get_module_state(mod);
+}
+
+#define clinic_state() (find_module_state_by_def(type))
 #include "clinic/socketmodule.c.h"
+#undef clinic_state
 
 /* XXX There's a problem here: *static* functions are not supposed to have
    a Py prefix (or use CapitalizedWords).  Later... */
 
-/* Global variable holding the exception type for errors detected
-   by this module (but not argument type or memory errors, etc.). */
-static PyObject *socket_herror;
-static PyObject *socket_gaierror;
-
-/* A forward reference to the socket type object.
-   The sock_type variable contains pointers to various functions,
-   some of which call new_sockobject(), which uses sock_type, so
-   there has to be a circular reference. */
-static PyTypeObject sock_type;
-
 #if defined(HAVE_POLL_H)
 #include <poll.h>
 #elif defined(HAVE_SYS_POLL_H)
@@ -641,7 +679,7 @@ set_error(void)
 
 #if defined(HAVE_GETHOSTBYNAME_R) || defined (HAVE_GETHOSTBYNAME) || defined (HAVE_GETHOSTBYADDR)
 static PyObject *
-set_herror(int h_error)
+set_herror(socket_state *state, int h_error)
 {
     PyObject *v;
 
@@ -651,7 +689,7 @@ set_herror(int h_error)
     v = Py_BuildValue("(is)", h_error, "host not found");
 #endif
     if (v != NULL) {
-        PyErr_SetObject(socket_herror, v);
+        PyErr_SetObject(state->socket_herror, v);
         Py_DECREF(v);
     }
 
@@ -662,7 +700,7 @@ set_herror(int h_error)
 
 #ifdef HAVE_GETADDRINFO
 static PyObject *
-set_gaierror(int error)
+set_gaierror(socket_state *state, int error)
 {
     PyObject *v;
 
@@ -678,7 +716,7 @@ set_gaierror(int error)
     v = Py_BuildValue("(is)", error, "getaddrinfo failed");
 #endif
     if (v != NULL) {
-        PyErr_SetObject(socket_gaierror, v);
+        PyErr_SetObject(state->socket_gaierror, v);
         Py_DECREF(v);
     }
 
@@ -991,11 +1029,8 @@ sock_call(PySocketSockObject *s,
 
 /* Initialize a new socket object. */
 
-/* Default timeout for new sockets */
-static _PyTime_t defaulttimeout = _PYTIME_FROMSECONDS(-1);
-
 static int
-init_sockobject(PySocketSockObject *s,
+init_sockobject(socket_state *state, PySocketSockObject *s,
                 SOCKET_T fd, int family, int type, int proto)
 {
     s->sock_fd = fd;
@@ -1025,13 +1060,14 @@ init_sockobject(PySocketSockObject *s,
     else
 #endif
     {
-        s->sock_timeout = defaulttimeout;
-        if (defaulttimeout >= 0) {
+        s->sock_timeout = state->defaulttimeout;
+        if (state->defaulttimeout >= 0) {
             if (internal_setblocking(s, 0) == -1) {
                 return -1;
             }
         }
     }
+    s->state = state;
     return 0;
 }
 
@@ -1043,14 +1079,15 @@ init_sockobject(PySocketSockObject *s,
    in NEWOBJ()). */
 
 static PySocketSockObject *
-new_sockobject(SOCKET_T fd, int family, int type, int proto)
+new_sockobject(socket_state *state, SOCKET_T fd, int family, int type,
+               int proto)
 {
-    PySocketSockObject *s;
-    s = (PySocketSockObject *)
-        PyType_GenericNew(&sock_type, NULL, NULL);
-    if (s == NULL)
+    PyTypeObject *tp = state->sock_type;
+    PySocketSockObject *s = (PySocketSockObject *)tp->tp_alloc(tp, 0);
+    if (s == NULL) {
         return NULL;
-    if (init_sockobject(s, fd, family, type, proto) == -1) {
+    }
+    if (init_sockobject(state, s, fd, family, type, proto) == -1) {
         Py_DECREF(s);
         return NULL;
     }
@@ -1074,7 +1111,8 @@ static PyThread_type_lock netdb_lock;
    an error occurred; then an exception is raised. */
 
 static int
-setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int af)
+setipaddr(socket_state *state, const char *name, struct sockaddr *addr_ret,
+          size_t addr_ret_size, int af)
 {
     struct addrinfo hints, *res;
     int error;
@@ -1095,7 +1133,7 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int
            outcome of the first call. */
         if (error) {
             res = NULL;  // no-op, remind us that it is invalid; gh-100795
-            set_gaierror(error);
+            set_gaierror(state, error);
             return -1;
         }
         switch (res->ai_family) {
@@ -1206,7 +1244,7 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int
     Py_END_ALLOW_THREADS
     if (error) {
         res = NULL;  // no-op, remind us that it is invalid; gh-100795
-        set_gaierror(error);
+        set_gaierror(state, error);
         return -1;
     }
     if (res->ai_addrlen < addr_ret_size)
@@ -1889,7 +1927,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
             return 0;
         }
         struct sockaddr_in* addr = &addrbuf->in;
-        result = setipaddr(host.buf, (struct sockaddr *)addr,
+        result = setipaddr(s->state, host.buf, (struct sockaddr *)addr,
                            sizeof(*addr),  AF_INET);
         idna_cleanup(&host);
         if (result < 0)
@@ -1934,7 +1972,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
             return 0;
         }
         struct sockaddr_in6* addr = &addrbuf->in6;
-        result = setipaddr(host.buf, (struct sockaddr *)addr,
+        result = setipaddr(s->state, host.buf, (struct sockaddr *)addr,
                            sizeof(*addr), AF_INET6);
         idna_cleanup(&host);
         if (result < 0)
@@ -2813,10 +2851,6 @@ struct sock_accept {
 };
 
 #if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4)
-#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
-/* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
-static int accept4_works = -1;
-#endif
 
 static int
 sock_accept_impl(PySocketSockObject *s, void *data)
@@ -2835,15 +2869,16 @@ sock_accept_impl(PySocketSockObject *s, void *data)
 #endif
 
 #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
-    if (accept4_works != 0) {
+    socket_state *state = s->state;
+    if (state->accept4_works != 0) {
         ctx->result = accept4(s->sock_fd, addr, paddrlen,
                               SOCK_CLOEXEC);
-        if (ctx->result == INVALID_SOCKET && accept4_works == -1) {
+        if (ctx->result == INVALID_SOCKET && state->accept4_works == -1) {
             /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
-            accept4_works = (errno != ENOSYS);
+            state->accept4_works = (errno != ENOSYS);
         }
     }
-    if (accept4_works == 0)
+    if (state->accept4_works == 0)
         ctx->result = accept(s->sock_fd, addr, paddrlen);
 #else
     ctx->result = accept(s->sock_fd, addr, paddrlen);
@@ -2896,7 +2931,8 @@ sock_accept(PySocketSockObject *s, PyObject *Py_UNUSED(ignored))
 #else
 
 #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
-    if (!accept4_works)
+    socket_state *state = s->state;
+    if (!state->accept4_works)
 #endif
     {
         if (_Py_set_inheritable(newfd, 0, NULL) < 0) {
@@ -5219,13 +5255,23 @@ sock_finalize(PySocketSockObject *s)
     PyErr_SetRaisedException(exc);
 }
 
+static int
+sock_traverse(PySocketSockObject *s, visitproc visit, void *arg)
+{
+    Py_VISIT(Py_TYPE(s));
+    return 0;
+}
+
 static void
 sock_dealloc(PySocketSockObject *s)
 {
-    if (PyObject_CallFinalizerFromDealloc((PyObject *)s) < 0)
+    if (PyObject_CallFinalizerFromDealloc((PyObject *)s) < 0) {
         return;
-
-    Py_TYPE(s)->tp_free((PyObject *)s);
+    }
+    PyTypeObject *tp = Py_TYPE(s);
+    PyObject_GC_UnTrack(s);
+    tp->tp_free((PyObject *)s);
+    Py_DECREF(tp);
 }
 
 
@@ -5277,12 +5323,6 @@ sock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
 /* Initialize a new socket object. */
 
-#ifdef SOCK_CLOEXEC
-/* socket() and socketpair() fail with EINVAL on Linux kernel older
- * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */
-static int sock_cloexec_works = -1;
-#endif
-
 /*ARGSUSED*/
 
 #ifndef HAVE_SOCKET
@@ -5310,10 +5350,11 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
 {
 
     SOCKET_T fd = INVALID_SOCKET;
+    socket_state *state = find_module_state_by_def(Py_TYPE(self));
 
 #ifndef MS_WINDOWS
 #ifdef SOCK_CLOEXEC
-    int *atomic_flag_works = &sock_cloexec_works;
+    int *atomic_flag_works = &state->sock_cloexec_works;
 #else
     int *atomic_flag_works = NULL;
 #endif
@@ -5468,15 +5509,15 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
         /* UNIX */
         Py_BEGIN_ALLOW_THREADS
 #ifdef SOCK_CLOEXEC
-        if (sock_cloexec_works != 0) {
+        if (state->sock_cloexec_works != 0) {
             fd = socket(family, type | SOCK_CLOEXEC, proto);
-            if (sock_cloexec_works == -1) {
+            if (state->sock_cloexec_works == -1) {
                 if (fd >= 0) {
-                    sock_cloexec_works = 1;
+                    state->sock_cloexec_works = 1;
                 }
                 else if (errno == EINVAL) {
                     /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */
-                    sock_cloexec_works = 0;
+                    state->sock_cloexec_works = 0;
                     fd = socket(family, type, proto);
                 }
             }
@@ -5499,7 +5540,7 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
         }
 #endif
     }
-    if (init_sockobject(self, fd, family, type, proto) == -1) {
+    if (init_sockobject(state, self, fd, family, type, proto) == -1) {
         SOCKETCLOSE(fd);
         return -1;
     }
@@ -5511,55 +5552,26 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
 
 /* Type object for socket objects. */
 
-static PyTypeObject sock_type = {
-    PyVarObject_HEAD_INIT(0, 0)         /* Must fill in type value later */
-    "_socket.socket",                           /* tp_name */
-    sizeof(PySocketSockObject),                 /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    (destructor)sock_dealloc,                   /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    (reprfunc)sock_repr,                        /* tp_repr */
-    0,                                          /* tp_as_number */
-    0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
-    0,                                          /* tp_str */
-    PyObject_GenericGetAttr,                    /* tp_getattro */
-    0,                                          /* tp_setattro */
-    0,                                          /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
-    sock_doc,                                   /* tp_doc */
-    0,                                          /* tp_traverse */
-    0,                                          /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
-    0,                                          /* tp_iternext */
-    sock_methods,                               /* tp_methods */
-    sock_memberlist,                            /* tp_members */
-    sock_getsetlist,                            /* tp_getset */
-    0,                                          /* tp_base */
-    0,                                          /* tp_dict */
-    0,                                          /* tp_descr_get */
-    0,                                          /* tp_descr_set */
-    0,                                          /* tp_dictoffset */
-    sock_initobj,                               /* tp_init */
-    PyType_GenericAlloc,                        /* tp_alloc */
-    sock_new,                                   /* tp_new */
-    PyObject_Del,                               /* tp_free */
-    0,                                          /* tp_is_gc */
-    0,                                          /* tp_bases */
-    0,                                          /* tp_mro */
-    0,                                          /* tp_cache */
-    0,                                          /* tp_subclasses */
-    0,                                          /* tp_weaklist */
-    0,                                          /* tp_del */
-    0,                                          /* tp_version_tag */
-    (destructor)sock_finalize,                  /* tp_finalize */
+static PyType_Slot sock_slots[] = {
+    {Py_tp_dealloc, sock_dealloc},
+    {Py_tp_traverse, sock_traverse},
+    {Py_tp_repr, sock_repr},
+    {Py_tp_doc, (void *)sock_doc},
+    {Py_tp_methods, sock_methods},
+    {Py_tp_members, sock_memberlist},
+    {Py_tp_getset, sock_getsetlist},
+    {Py_tp_init, sock_initobj},
+    {Py_tp_new, sock_new},
+    {Py_tp_finalize, sock_finalize},
+    {0, NULL},
+};
+
+static PyType_Spec sock_spec = {
+    .name = "_socket.socket",
+    .basicsize = sizeof(PySocketSockObject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = sock_slots,
 };
 
 
@@ -5687,8 +5699,12 @@ socket_gethostbyname(PyObject *self, PyObject *args)
     if (PySys_Audit("socket.gethostbyname", "O", args) < 0) {
         goto finally;
     }
-    if (setipaddr(name, (struct sockaddr *)&addrbuf,  sizeof(addrbuf), AF_INET) < 0)
+    socket_state *state = get_module_state(self);
+    int rc = setipaddr(state, name, (struct sockaddr *)&addrbuf,
+                       sizeof(addrbuf), AF_INET);
+    if (rc < 0) {
         goto finally;
+    }
     ret = make_ipv4_addr(&addrbuf);
 finally:
     PyMem_Free(name);
@@ -5719,7 +5735,8 @@ sock_decode_hostname(const char *name)
 /* Convenience function common to gethostbyname_ex and gethostbyaddr */
 
 static PyObject *
-gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af)
+gethost_common(socket_state *state, struct hostent *h, struct sockaddr *addr,
+               size_t alen, int af)
 {
     char **pch;
     PyObject *rtn_tuple = (PyObject *)NULL;
@@ -5730,7 +5747,7 @@ gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af)
 
     if (h == NULL) {
         /* Let's get real error message to return */
-        set_herror(h_errno);
+        set_herror(state, h_errno);
         return NULL;
     }
 
@@ -5877,8 +5894,10 @@ socket_gethostbyname_ex(PyObject *self, PyObject *args)
     if (PySys_Audit("socket.gethostbyname", "O", args) < 0) {
         goto finally;
     }
-    if (setipaddr(name, SAS2SA(&addr), sizeof(addr), AF_INET) < 0)
+    socket_state *state = get_module_state(self);
+    if (setipaddr(state, name, SAS2SA(&addr), sizeof(addr), AF_INET) < 0) {
         goto finally;
+    }
     Py_BEGIN_ALLOW_THREADS
 #ifdef HAVE_GETHOSTBYNAME_R
 #if   defined(HAVE_GETHOSTBYNAME_R_6_ARG)
@@ -5904,7 +5923,7 @@ socket_gethostbyname_ex(PyObject *self, PyObject *args)
        Therefore, we cast the sockaddr_storage into sockaddr to
        access sa_family. */
     sa = SAS2SA(&addr);
-    ret = gethost_common(h, SAS2SA(&addr), sizeof(addr),
+    ret = gethost_common(state, h, SAS2SA(&addr), sizeof(addr),
                          sa->sa_family);
 #ifdef USE_GETHOSTBYNAME_LOCK
     PyThread_release_lock(netdb_lock);
@@ -5960,8 +5979,10 @@ socket_gethostbyaddr(PyObject *self, PyObject *args)
         goto finally;
     }
     af = AF_UNSPEC;
-    if (setipaddr(ip_num, sa, sizeof(addr), af) < 0)
+    socket_state *state = get_module_state(self);
+    if (setipaddr(state, ip_num, sa, sizeof(addr), af) < 0) {
         goto finally;
+    }
     af = sa->sa_family;
     ap = NULL;
     /* al = 0; */
@@ -6002,7 +6023,7 @@ socket_gethostbyaddr(PyObject *self, PyObject *args)
     h = gethostbyaddr(ap, al, af);
 #endif /* HAVE_GETHOSTBYNAME_R */
     Py_END_ALLOW_THREADS
-    ret = gethost_common(h, SAS2SA(&addr), sizeof(addr), af);
+    ret = gethost_common(state, h, SAS2SA(&addr), sizeof(addr), af);
 #ifdef USE_GETHOSTBYNAME_LOCK
     PyThread_release_lock(netdb_lock);
 #endif
@@ -6221,8 +6242,9 @@ socket_socketpair(PyObject *self, PyObject *args)
     SOCKET_T sv[2];
     int family, type = SOCK_STREAM, proto = 0;
     PyObject *res = NULL;
+    socket_state *state = get_module_state(self);
 #ifdef SOCK_CLOEXEC
-    int *atomic_flag_works = &sock_cloexec_works;
+    int *atomic_flag_works = &state->sock_cloexec_works;
 #else
     int *atomic_flag_works = NULL;
 #endif
@@ -6240,15 +6262,15 @@ socket_socketpair(PyObject *self, PyObject *args)
     /* Create a pair of socket fds */
     Py_BEGIN_ALLOW_THREADS
 #ifdef SOCK_CLOEXEC
-    if (sock_cloexec_works != 0) {
+    if (state->sock_cloexec_works != 0) {
         ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv);
-        if (sock_cloexec_works == -1) {
+        if (state->sock_cloexec_works == -1) {
             if (ret >= 0) {
-                sock_cloexec_works = 1;
+                state->sock_cloexec_works = 1;
             }
             else if (errno == EINVAL) {
                 /* Linux older than 2.6.27 does not support SOCK_CLOEXEC */
-                sock_cloexec_works = 0;
+                state->sock_cloexec_works = 0;
                 ret = socketpair(family, type, proto, sv);
             }
         }
@@ -6268,10 +6290,10 @@ socket_socketpair(PyObject *self, PyObject *args)
     if (_Py_set_inheritable(sv[1], 0, atomic_flag_works) < 0)
         goto finally;
 
-    s0 = new_sockobject(sv[0], family, type, proto);
+    s0 = new_sockobject(state, sv[0], family, type, proto);
     if (s0 == NULL)
         goto finally;
-    s1 = new_sockobject(sv[1], family, type, proto);
+    s1 = new_sockobject(state, sv[1], family, type, proto);
     if (s1 == NULL)
         goto finally;
     res = PyTuple_Pack(2, s0, s1);
@@ -6726,7 +6748,8 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
     Py_END_ALLOW_THREADS
     if (error) {
         res0 = NULL;  // gh-100795
-        set_gaierror(error);
+        socket_state *state = get_module_state(self);
+        set_gaierror(state, error);
         goto err;
     }
 
@@ -6825,7 +6848,8 @@ socket_getnameinfo(PyObject *self, PyObject *args)
     Py_END_ALLOW_THREADS
     if (error) {
         res = NULL;  // gh-100795
-        set_gaierror(error);
+        socket_state *state = get_module_state(self);
+        set_gaierror(state, error);
         goto fail;
     }
     if (res->ai_next) {
@@ -6857,7 +6881,8 @@ socket_getnameinfo(PyObject *self, PyObject *args)
     error = getnameinfo(res->ai_addr, (socklen_t) res->ai_addrlen,
                     hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), flags);
     if (error) {
-        set_gaierror(error);
+        socket_state *state = get_module_state(self);
+        set_gaierror(state, error);
         goto fail;
     }
 
@@ -6883,11 +6908,12 @@ Get host and port for a sockaddr.");
 static PyObject *
 socket_getdefaulttimeout(PyObject *self, PyObject *Py_UNUSED(ignored))
 {
-    if (defaulttimeout < 0) {
+    socket_state *state = get_module_state(self);
+    if (state->defaulttimeout < 0) {
         Py_RETURN_NONE;
     }
     else {
-        double seconds = _PyTime_AsSecondsDouble(defaulttimeout);
+        double seconds = _PyTime_AsSecondsDouble(state->defaulttimeout);
         return PyFloat_FromDouble(seconds);
     }
 }
@@ -6907,7 +6933,8 @@ socket_setdefaulttimeout(PyObject *self, PyObject *arg)
     if (socket_parse_timeout(&timeout, arg) < 0)
         return NULL;
 
-    defaulttimeout = timeout;
+    socket_state *state = get_module_state(self);
+    state->defaulttimeout = timeout;
 
     Py_RETURN_NONE;
 }
@@ -7293,7 +7320,7 @@ sock_destroy_api(PyObject *capsule)
 }
 
 static PySocketModule_APIObject *
-sock_get_api(void)
+sock_get_api(socket_state *state)
 {
     PySocketModule_APIObject *capi = PyMem_Malloc(sizeof(PySocketModule_APIObject));
     if (capi == NULL) {
@@ -7301,7 +7328,7 @@ sock_get_api(void)
         return NULL;
     }
 
-    capi->Sock_Type = (PyTypeObject *)Py_NewRef(&sock_type);
+    capi->Sock_Type = (PyTypeObject *)Py_NewRef(state->sock_type);
     capi->error = Py_NewRef(PyExc_OSError);
     capi->timeout_error = Py_NewRef(PyExc_TimeoutError);
     return capi;
@@ -7323,47 +7350,38 @@ PyDoc_STRVAR(socket_doc,
 \n\
 See the socket module for documentation.");
 
-static struct PyModuleDef socketmodule = {
-    PyModuleDef_HEAD_INIT,
-    PySocket_MODULE_NAME,
-    socket_doc,
-    -1,
-    socket_methods,
-    NULL,
-    NULL,
-    NULL,
-    NULL
-};
-
-PyMODINIT_FUNC
-PyInit__socket(void)
+static int
+socket_exec(PyObject *m)
 {
-    PyObject *m = NULL;
-
     if (!os_init()) {
         goto error;
     }
 
-    Py_SET_TYPE(&sock_type, &PyType_Type);
-    m = PyModule_Create(&socketmodule);
-    if (m == NULL) {
-        goto error;
-    }
+    socket_state *state = get_module_state(m);
+    state->defaulttimeout = _PYTIME_FROMSECONDS(-1);
+
+#if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4)
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+    state->accept4_works = -1;
+#endif
+#endif
+
+#ifdef SOCK_CLOEXEC
+    state->sock_cloexec_works = -1;
+#endif
 
 #define ADD_EXC(MOD, NAME, VAR, BASE) do {                  \
     VAR = PyErr_NewException("socket." NAME, BASE, NULL);   \
     if (VAR == NULL) {                                      \
         goto error;                                         \
     }                                                       \
-    int rc = PyModule_AddObjectRef(MOD, NAME, VAR);         \
-    Py_DECREF(VAR);                                         \
-    if (rc < 0) {                                           \
+    if (PyModule_AddObjectRef(MOD, NAME, VAR) < 0) {        \
         goto error;                                         \
     }                                                       \
 } while (0)
 
-    ADD_EXC(m, "herror", socket_herror, PyExc_OSError);
-    ADD_EXC(m, "gaierror", socket_gaierror, PyExc_OSError);
+    ADD_EXC(m, "herror", state->socket_herror, PyExc_OSError);
+    ADD_EXC(m, "gaierror", state->socket_gaierror, PyExc_OSError);
 
 #undef ADD_EXC
 
@@ -7373,10 +7391,16 @@ PyInit__socket(void)
     if (PyModule_AddObjectRef(m, "timeout", PyExc_TimeoutError) < 0) {
         goto error;
     }
-    if (PyModule_AddObjectRef(m, "SocketType", (PyObject *)&sock_type) < 0) {
+
+    PyObject *sock_type = PyType_FromMetaclass(NULL, m, &sock_spec, NULL);
+    if (sock_type == NULL) {
+        goto error;
+    }
+    state->sock_type = (PyTypeObject *)sock_type;
+    if (PyModule_AddObjectRef(m, "SocketType", sock_type) < 0) {
         goto error;
     }
-    if (PyModule_AddType(m, &sock_type) < 0) {
+    if (PyModule_AddType(m, state->sock_type) < 0) {
         goto error;
     }
 
@@ -7391,7 +7415,7 @@ PyInit__socket(void)
     }
 
     /* Export C API */
-    PySocketModule_APIObject *capi = sock_get_api();
+    PySocketModule_APIObject *capi = sock_get_api(state);
     if (capi == NULL) {
         goto error;
     }
@@ -8813,9 +8837,57 @@ PyInit__socket(void)
 #undef ADD_INT_CONST
 #undef ADD_STR_CONST
 
-    return m;
+    return 0;
 
 error:
-    Py_XDECREF(m);
-    return NULL;
+    return -1;
+}
+
+static struct PyModuleDef_Slot socket_slots[] = {
+    {Py_mod_exec, socket_exec},
+    {0, NULL},
+};
+
+static int
+socket_traverse(PyObject *mod, visitproc visit, void *arg)
+{
+    socket_state *state = get_module_state(mod);
+    Py_VISIT(state->sock_type);
+    Py_VISIT(state->socket_herror);
+    Py_VISIT(state->socket_gaierror);
+    return 0;
+}
+
+static int
+socket_clear(PyObject *mod)
+{
+    socket_state *state = get_module_state(mod);
+    Py_CLEAR(state->sock_type);
+    Py_CLEAR(state->socket_herror);
+    Py_CLEAR(state->socket_gaierror);
+    return 0;
+}
+
+static void
+socket_free(void *mod)
+{
+    (void)socket_clear((PyObject *)mod);
+}
+
+static struct PyModuleDef socketmodule = {
+    .m_base = PyModuleDef_HEAD_INIT,
+    .m_name = PySocket_MODULE_NAME,
+    .m_doc = socket_doc,
+    .m_size = sizeof(socket_state),
+    .m_methods = socket_methods,
+    .m_slots = socket_slots,
+    .m_traverse = socket_traverse,
+    .m_clear = socket_clear,
+    .m_free = socket_free,
+};
+
+PyMODINIT_FUNC
+PyInit__socket(void)
+{
+    return PyModuleDef_Init(&socketmodule);
 }
diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h
index f31ba532a6c6..f5ca00450ee9 100644
--- a/Modules/socketmodule.h
+++ b/Modules/socketmodule.h
@@ -322,6 +322,7 @@ typedef struct {
                                         sets a Python exception */
     _PyTime_t sock_timeout;     /* Operation timeout in seconds;
                                         0.0 means non-blocking */
+    struct _socket_state *state;
 } PySocketSockObject;
 
 /* --- C API ----------------------------------------------------*/
diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv
index 0620c7e13925..e36024ad16c2 100644
--- a/Tools/c-analyzer/cpython/globals-to-fix.tsv
+++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv
@@ -392,7 +392,6 @@ Modules/_decimal/_decimal.c	-	PyDecSignalDictMixin_Type	-
 Modules/_decimal/_decimal.c	-	PyDec_Type	-
 Modules/ossaudiodev.c	-	OSSAudioType	-
 Modules/ossaudiodev.c	-	OSSMixerType	-
-Modules/socketmodule.c	-	sock_type	-
 Modules/xxmodule.c	-	Null_Type	-
 Modules/xxmodule.c	-	Str_Type	-
 Modules/xxmodule.c	-	Xxo_Type	-
@@ -416,8 +415,6 @@ Modules/_cursesmodule.c	-	PyCursesError	-
 Modules/_decimal/_decimal.c	-	DecimalException	-
 Modules/_tkinter.c	-	Tkinter_TclError	-
 Modules/ossaudiodev.c	-	OSSAudioError	-
-Modules/socketmodule.c	-	socket_herror	-
-Modules/socketmodule.c	-	socket_gaierror	-
 Modules/xxlimited_35.c	-	ErrorObject	-
 Modules/xxmodule.c	-	ErrorObject	-
 
@@ -514,8 +511,6 @@ Modules/cjkcodecs/cjkcodecs.h	-	mapping_list	-
 Modules/readline.c	-	libedit_append_replace_history_offset	-
 Modules/readline.c	-	using_libedit_emulation	-
 Modules/readline.c	-	libedit_history_start	-
-Modules/socketmodule.c	-	accept4_works	-
-Modules/socketmodule.c	-	sock_cloexec_works	-
 
 ##-----------------------
 ## state
@@ -541,4 +536,3 @@ Modules/readline.c	-	sigwinch_ohandler	-
 Modules/readline.c	-	completed_input_string	-
 Modules/rotatingtree.c	-	random_stream	-
 Modules/rotatingtree.c	-	random_value	-
-Modules/socketmodule.c	-	defaulttimeout	-



More information about the Python-checkins mailing list