Skip to content

Commit 41a86a6

Browse files
merge fixes for socket
2 parents 847da18 + bb9d955 commit 41a86a6

File tree

11 files changed

+106
-59
lines changed

11 files changed

+106
-59
lines changed

Doc/about.rst

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
=====================
2-
About these documents
3-
=====================
1+
========================
2+
About this documentation
3+
========================
44

55

6-
These documents are generated from `reStructuredText`_ sources by `Sphinx`_, a
7-
document processor specifically written for the Python documentation.
6+
Python's documentation is generated from `reStructuredText`_ sources
7+
using `Sphinx`_, a documentation generator originally created for Python
8+
and now maintained as an independent project.
89

910
.. _reStructuredText: https://docutils.sourceforge.io/rst.html
1011
.. _Sphinx: https://www.sphinx-doc.org/
@@ -20,14 +21,14 @@ volunteers are always welcome!
2021
Many thanks go to:
2122

2223
* Fred L. Drake, Jr., the creator of the original Python documentation toolset
23-
and writer of much of the content;
24+
and author of much of the content;
2425
* the `Docutils <https://docutils.sourceforge.io/>`_ project for creating
2526
reStructuredText and the Docutils suite;
2627
* Fredrik Lundh for his Alternative Python Reference project from which Sphinx
2728
got many good ideas.
2829

2930

30-
Contributors to the Python Documentation
31+
Contributors to the Python documentation
3132
----------------------------------------
3233

3334
Many people have contributed to the Python language, the Python standard

Doc/deprecations/pending-removal-in-3.16.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,8 @@ Pending removal in Python 3.16
7979

8080
* The undocumented and unused :attr:`!TarFile.tarfile` attribute
8181
has been deprecated since Python 3.13.
82+
83+
* :mod:`functools`:
84+
85+
* Calling the Python implementation of :func:`functools.reduce` with *function*
86+
or *sequence* as keyword arguments has been deprecated since Python 3.14.

Doc/whatsnew/3.14.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,11 @@ Deprecated
744744
as a single positional argument.
745745
(Contributed by Serhiy Storchaka in :gh:`109218`.)
746746

747+
* :mod:`functools`:
748+
Calling the Python implementation of :func:`functools.reduce` with *function*
749+
or *sequence* as keyword arguments is now deprecated.
750+
(Contributed by Kirill Podoprigora in :gh:`121676`.)
751+
747752
* :mod:`os`:
748753
:term:`Soft deprecate <soft deprecated>` :func:`os.popen` and
749754
:func:`os.spawn* <os.spawnl>` functions. They should no longer be used to

Lib/functools.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,6 @@ def reduce(function, sequence, initial=_initial_missing):
264264

265265
return value
266266

267-
try:
268-
from _functools import reduce
269-
except ImportError:
270-
pass
271-
272267

273268
################################################################################
274269
### partial() argument application
@@ -1124,3 +1119,31 @@ def __get__(self, instance, owner=None):
11241119
return val
11251120

11261121
__class_getitem__ = classmethod(GenericAlias)
1122+
1123+
def _warn_python_reduce_kwargs(py_reduce):
1124+
@wraps(py_reduce)
1125+
def wrapper(*args, **kwargs):
1126+
if 'function' in kwargs or 'sequence' in kwargs:
1127+
import os
1128+
import warnings
1129+
warnings.warn(
1130+
'Calling functools.reduce with keyword arguments '
1131+
'"function" or "sequence" '
1132+
'is deprecated in Python 3.14 and will be '
1133+
'forbidden in Python 3.16.',
1134+
DeprecationWarning,
1135+
skip_file_prefixes=(os.path.dirname(__file__),))
1136+
return py_reduce(*args, **kwargs)
1137+
return wrapper
1138+
1139+
reduce = _warn_python_reduce_kwargs(reduce)
1140+
del _warn_python_reduce_kwargs
1141+
1142+
# The import of the C accelerated version of reduce() has been moved
1143+
# here due to gh-121676. In Python 3.16, _warn_python_reduce_kwargs()
1144+
# should be removed and the import block should be moved back right
1145+
# after the definition of reduce().
1146+
try:
1147+
from _functools import reduce
1148+
except ImportError:
1149+
pass

Lib/test/test_functools.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,12 @@ class TestReduceC(TestReduce, unittest.TestCase):
10451045
class TestReducePy(TestReduce, unittest.TestCase):
10461046
reduce = staticmethod(py_functools.reduce)
10471047

1048+
def test_reduce_with_kwargs(self):
1049+
with self.assertWarns(DeprecationWarning):
1050+
self.reduce(function=lambda x, y: x + y, sequence=[1, 2, 3, 4, 5], initial=1)
1051+
with self.assertWarns(DeprecationWarning):
1052+
self.reduce(lambda x, y: x + y, sequence=[1, 2, 3, 4, 5], initial=1)
1053+
10481054

10491055
class TestCmpToKey:
10501056

Lib/test/test_socket.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7075,6 +7075,26 @@ def close_fds(fds):
70757075
self.assertEqual(data, str(index).encode())
70767076

70777077

7078+
class FreeThreadingTests(unittest.TestCase):
7079+
7080+
def test_close_detach_race(self):
7081+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
7082+
7083+
def close():
7084+
for _ in range(1000):
7085+
s.close()
7086+
7087+
def detach():
7088+
for _ in range(1000):
7089+
s.detach()
7090+
7091+
t1 = threading.Thread(target=close)
7092+
t2 = threading.Thread(target=detach)
7093+
7094+
with threading_helper.start_threads([t1, t2]):
7095+
pass
7096+
7097+
70787098
def setUpModule():
70797099
thread_info = threading_helper.threading_setup()
70807100
unittest.addModuleCleanup(threading_helper.threading_cleanup, *thread_info)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Deprecate calling the Python implementation of :meth:`functools.reduce`
2+
with a ``function`` or ``sequence`` as a :term:`keyword argument`.
3+
This will be forbidden in Python 3.16 in order to match the C implementation.

Modules/clinic/socketmodule.c.h

Lines changed: 2 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/socketmodule.c

Lines changed: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -550,20 +550,20 @@ typedef struct _socket_state {
550550

551551
/* Default timeout for new sockets */
552552
PyTime_t defaulttimeout;
553+
} socket_state;
553554

554555
#if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4)
555556
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
556-
/* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
557-
int accept4_works;
557+
/* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
558+
static int accept4_works = -1;
558559
#endif
559560
#endif
560561

561562
#ifdef SOCK_CLOEXEC
562-
/* socket() and socketpair() fail with EINVAL on Linux kernel older
563-
* than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */
564-
int sock_cloexec_works;
563+
/* socket() and socketpair() fail with EINVAL on Linux kernel older
564+
* than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */
565+
static int sock_cloexec_works = -1;
565566
#endif
566-
} socket_state;
567567

568568
static inline void
569569
set_sock_fd(PySocketSockObject *s, SOCKET_T fd)
@@ -2904,16 +2904,15 @@ sock_accept_impl(PySocketSockObject *s, void *data)
29042904
#endif
29052905

29062906
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
2907-
socket_state *state = s->state;
2908-
if (state->accept4_works != 0) {
2907+
if (_Py_atomic_load_int_relaxed(&accept4_works) != 0) {
29092908
ctx->result = accept4(get_sock_fd(s), addr, paddrlen,
29102909
SOCK_CLOEXEC);
2911-
if (ctx->result == INVALID_SOCKET && state->accept4_works == -1) {
2910+
if (ctx->result == INVALID_SOCKET && _Py_atomic_load_int_relaxed(&accept4_works) == -1) {
29122911
/* On Linux older than 2.6.28, accept4() fails with ENOSYS */
2913-
state->accept4_works = (errno != ENOSYS);
2912+
_Py_atomic_store_int_relaxed(&accept4_works, errno != ENOSYS);
29142913
}
29152914
}
2916-
if (state->accept4_works == 0)
2915+
if (_Py_atomic_load_int_relaxed(&accept4_works) == 0)
29172916
ctx->result = accept(get_sock_fd(s), addr, paddrlen);
29182917
#else
29192918
ctx->result = accept(get_sock_fd(s), addr, paddrlen);
@@ -2966,8 +2965,7 @@ sock_accept(PySocketSockObject *s, PyObject *Py_UNUSED(ignored))
29662965
#else
29672966

29682967
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
2969-
socket_state *state = s->state;
2970-
if (!state->accept4_works)
2968+
if (!_Py_atomic_load_int_relaxed(&accept4_works))
29712969
#endif
29722970
{
29732971
if (_Py_set_inheritable(newfd, 0, NULL) < 0) {
@@ -3388,7 +3386,6 @@ sockets the address is a tuple (ifname, proto [,pkttype [,hatype [,addr]]])");
33883386
will surely fail. */
33893387

33903388
/*[clinic input]
3391-
@critical_section
33923389
_socket.socket.close
33933390
self as s: self(type="PySocketSockObject *")
33943391
@@ -3399,7 +3396,7 @@ Close the socket. It cannot be used after this call.
33993396

34003397
static PyObject *
34013398
_socket_socket_close_impl(PySocketSockObject *s)
3402-
/*[clinic end generated code: output=038b2418e07f6f6c input=9839a261e05bcb97]*/
3399+
/*[clinic end generated code: output=038b2418e07f6f6c input=dc487e470e55a83c]*/
34033400
{
34043401
SOCKET_T fd;
34053402
int res;
@@ -5428,7 +5425,7 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
54285425

54295426
#ifndef MS_WINDOWS
54305427
#ifdef SOCK_CLOEXEC
5431-
int *atomic_flag_works = &state->sock_cloexec_works;
5428+
int *atomic_flag_works = &sock_cloexec_works;
54325429
#else
54335430
int *atomic_flag_works = NULL;
54345431
#endif
@@ -5583,15 +5580,16 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
55835580
/* UNIX */
55845581
Py_BEGIN_ALLOW_THREADS
55855582
#ifdef SOCK_CLOEXEC
5586-
if (state->sock_cloexec_works != 0) {
5583+
if (_Py_atomic_load_int_relaxed(&sock_cloexec_works) != 0) {
55875584
fd = socket(family, type | SOCK_CLOEXEC, proto);
5588-
if (state->sock_cloexec_works == -1) {
5585+
if (_Py_atomic_load_int_relaxed(&sock_cloexec_works) == -1) {
55895586
if (fd >= 0) {
5590-
state->sock_cloexec_works = 1;
5587+
_Py_atomic_store_int_relaxed(&sock_cloexec_works, 1);
55915588
}
5589+
55925590
else if (errno == EINVAL) {
55935591
/* Linux older than 2.6.27 does not support SOCK_CLOEXEC */
5594-
state->sock_cloexec_works = 0;
5592+
_Py_atomic_store_int_relaxed(&sock_cloexec_works, 0);
55955593
fd = socket(family, type, proto);
55965594
}
55975595
}
@@ -6332,7 +6330,7 @@ socket_socketpair(PyObject *self, PyObject *args)
63326330
PyObject *res = NULL;
63336331
socket_state *state = get_module_state(self);
63346332
#ifdef SOCK_CLOEXEC
6335-
int *atomic_flag_works = &state->sock_cloexec_works;
6333+
int *atomic_flag_works = &sock_cloexec_works;
63366334
#else
63376335
int *atomic_flag_works = NULL;
63386336
#endif
@@ -6350,15 +6348,15 @@ socket_socketpair(PyObject *self, PyObject *args)
63506348
/* Create a pair of socket fds */
63516349
Py_BEGIN_ALLOW_THREADS
63526350
#ifdef SOCK_CLOEXEC
6353-
if (state->sock_cloexec_works != 0) {
6351+
if (_Py_atomic_load_int_relaxed(&sock_cloexec_works) != 0) {
63546352
ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv);
6355-
if (state->sock_cloexec_works == -1) {
6353+
if (_Py_atomic_load_int_relaxed(&sock_cloexec_works) == -1) {
63566354
if (ret >= 0) {
6357-
state->sock_cloexec_works = 1;
6355+
_Py_atomic_store_int_relaxed(&sock_cloexec_works, 1);
63586356
}
63596357
else if (errno == EINVAL) {
63606358
/* Linux older than 2.6.27 does not support SOCK_CLOEXEC */
6361-
state->sock_cloexec_works = 0;
6359+
_Py_atomic_store_int_relaxed(&sock_cloexec_works, 0);
63626360
ret = socketpair(family, type, proto, sv);
63636361
}
63646362
}
@@ -7466,17 +7464,8 @@ socket_exec(PyObject *m)
74667464
}
74677465

74687466
socket_state *state = get_module_state(m);
7469-
state->defaulttimeout = _PYTIME_FROMSECONDS(-1);
7470-
7471-
#if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4)
7472-
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
7473-
state->accept4_works = -1;
7474-
#endif
7475-
#endif
74767467

7477-
#ifdef SOCK_CLOEXEC
7478-
state->sock_cloexec_works = -1;
7479-
#endif
7468+
_Py_atomic_store_int64_relaxed(&state->defaulttimeout, _PYTIME_FROMSECONDS(-1));
74807469

74817470
#define ADD_EXC(MOD, NAME, VAR, BASE) do { \
74827471
VAR = PyErr_NewException("socket." NAME, BASE, NULL); \

Python/fileutils.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,14 +1468,14 @@ set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works)
14681468
assert(!(atomic_flag_works != NULL && inheritable));
14691469

14701470
if (atomic_flag_works != NULL && !inheritable) {
1471-
if (*atomic_flag_works == -1) {
1471+
if (_Py_atomic_load_int_relaxed(atomic_flag_works) == -1) {
14721472
int isInheritable = get_inheritable(fd, raise);
14731473
if (isInheritable == -1)
14741474
return -1;
1475-
*atomic_flag_works = !isInheritable;
1475+
_Py_atomic_store_int_relaxed(atomic_flag_works, !isInheritable);
14761476
}
14771477

1478-
if (*atomic_flag_works)
1478+
if (_Py_atomic_load_int_relaxed(atomic_flag_works))
14791479
return 0;
14801480
}
14811481

0 commit comments

Comments
 (0)