Skip to content

Commit e453819

Browse files
committed
Merge remote-tracking branch 'upstream/main' into fix-valid-utf8
2 parents 407eef7 + caa2a97 commit e453819

27 files changed

+373
-24
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# https://git-scm.com/docs/gitignore#_pattern_format
66

77
# asyncio
8-
**/*asyncio* @1st1 @asvetlov
8+
**/*asyncio* @1st1 @asvetlov @gvanrossum
99

1010
# Core
1111
**/*context* @1st1

Doc/c-api/init.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,6 +1774,18 @@ Python-level trace functions in previous versions.
17741774
17751775
The caller must hold the :term:`GIL`.
17761776
1777+
.. c:function:: void PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *obj)
1778+
1779+
Like :c:func:`PyEval_SetProfile` but sets the profile function in all running threads
1780+
belonging to the current interpreter instead of the setting it only on the current thread.
1781+
1782+
The caller must hold the :term:`GIL`.
1783+
1784+
As :c:func:`PyEval_SetProfile`, this function ignores any exceptions raised while
1785+
setting the profile functions in all threads.
1786+
1787+
.. versionadded:: 3.12
1788+
17771789
17781790
.. c:function:: void PyEval_SetTrace(Py_tracefunc func, PyObject *obj)
17791791
@@ -1788,6 +1800,18 @@ Python-level trace functions in previous versions.
17881800
17891801
The caller must hold the :term:`GIL`.
17901802
1803+
.. c:function:: void PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *obj)
1804+
1805+
Like :c:func:`PyEval_SetTrace` but sets the tracing function in all running threads
1806+
belonging to the current interpreter instead of the setting it only on the current thread.
1807+
1808+
The caller must hold the :term:`GIL`.
1809+
1810+
As :c:func:`PyEval_SetTrace`, this function ignores any exceptions raised while
1811+
setting the trace functions in all threads.
1812+
1813+
.. versionadded:: 3.12
1814+
17911815
17921816
.. _advanced-debugging:
17931817

Doc/c-api/typeobj.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,9 +2062,9 @@ This results in types that are limited relative to types defined in Python:
20622062
:ref:`sub-interpreters <sub-interpreter-support>`, so they should not
20632063
include any subinterpreter-specific state.
20642064

2065-
Also, since :c:type:`PyTypeObject` is not part of the :ref:`stable ABI <stable>`,
2066-
any extension modules using static types must be compiled for a specific
2067-
Python minor version.
2065+
Also, since :c:type:`PyTypeObject` is only part of the :ref:`Limited API
2066+
<stable>` as an opaque struct, any extension modules using static types must be
2067+
compiled for a specific Python minor version.
20682068

20692069

20702070
.. _heap-types:

Doc/data/refcounts.dat

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,10 +796,18 @@ PyEval_SetProfile:void:::
796796
PyEval_SetProfile:Py_tracefunc:func::
797797
PyEval_SetProfile:PyObject*:obj:+1:
798798

799+
PyEval_SetProfileAllThreads:void:::
800+
PyEval_SetProfileAllThreads:Py_tracefunc:func::
801+
PyEval_SetProfileAllThreads:PyObject*:obj:+1:
802+
799803
PyEval_SetTrace:void:::
800804
PyEval_SetTrace:Py_tracefunc:func::
801805
PyEval_SetTrace:PyObject*:obj:+1:
802806

807+
PyEval_SetTraceAllThreads:void:::
808+
PyEval_SetTraceAllThreads:Py_tracefunc:func::
809+
PyEval_SetTraceAllThreads:PyObject*:obj:+1:
810+
803811
PyEval_EvalCode:PyObject*::+1:
804812
PyEval_EvalCode:PyObject*:co:0:
805813
PyEval_EvalCode:PyObject*:globals:0:

Doc/library/csv.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ The :mod:`csv` module defines the following classes:
167167
All other optional or keyword arguments are passed to the underlying
168168
:class:`reader` instance.
169169

170+
If the argument passed to *fieldnames* is an iterator, it will be coerced to a :class:`list`.
171+
170172
.. versionchanged:: 3.6
171173
Returned rows are now of type :class:`OrderedDict`.
172174

@@ -209,6 +211,8 @@ The :mod:`csv` module defines the following classes:
209211
Note that unlike the :class:`DictReader` class, the *fieldnames* parameter
210212
of the :class:`DictWriter` class is not optional.
211213

214+
If the argument passed to *fieldnames* is an iterator, it will be coerced to a :class:`list`.
215+
212216
A short usage example::
213217

214218
import csv

Doc/library/logging.handlers.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,12 @@ supports sending logging messages to a remote or local Unix syslog.
629629
application needs to run on several platforms). On Windows, you pretty
630630
much have to use the UDP option.
631631

632+
.. note:: On macOS 12.x (Monterey), Apple has changed the behaviour of their
633+
syslog daemon - it no longer listens on a domain socket. Therefore, you cannot
634+
expect :class:`SysLogHandler` to work on this system.
635+
636+
See :gh:`91070` for more information.
637+
632638
.. versionchanged:: 3.2
633639
*socktype* was added.
634640

Doc/library/threading.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,15 @@ This module defines the following functions:
158158
The *func* will be passed to :func:`sys.settrace` for each thread, before its
159159
:meth:`~Thread.run` method is called.
160160

161+
.. function:: settrace_all_threads(func)
162+
163+
Set a trace function for all threads started from the :mod:`threading` module
164+
and all Python threads that are currently executing.
165+
166+
The *func* will be passed to :func:`sys.settrace` for each thread, before its
167+
:meth:`~Thread.run` method is called.
168+
169+
.. versionadded:: 3.12
161170

162171
.. function:: gettrace()
163172

@@ -178,6 +187,15 @@ This module defines the following functions:
178187
The *func* will be passed to :func:`sys.setprofile` for each thread, before its
179188
:meth:`~Thread.run` method is called.
180189

190+
.. function:: setprofile_all_threads(func)
191+
192+
Set a profile function for all threads started from the :mod:`threading` module
193+
and all Python threads that are currently executing.
194+
195+
The *func* will be passed to :func:`sys.setprofile` for each thread, before its
196+
:meth:`~Thread.run` method is called.
197+
198+
.. versionadded:: 3.12
181199

182200
.. function:: getprofile()
183201

Include/cpython/ceval.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
#endif
44

55
PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *);
6+
PyAPI_FUNC(void) PyEval_SetProfileAllThreads(Py_tracefunc, PyObject *);
67
PyAPI_DATA(int) _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg);
78
PyAPI_FUNC(void) PyEval_SetTrace(Py_tracefunc, PyObject *);
9+
PyAPI_FUNC(void) PyEval_SetTraceAllThreads(Py_tracefunc, PyObject *);
810
PyAPI_FUNC(int) _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg);
911

1012
/* Helper to look up a builtin object */

Include/internal/pycore_frame.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ enum _frameowner {
4747

4848
typedef struct _PyInterpreterFrame {
4949
/* "Specials" section */
50-
PyFunctionObject *f_func; /* Strong reference */
50+
PyObject *f_funcobj; /* Strong reference */
5151
PyObject *f_globals; /* Borrowed reference */
5252
PyObject *f_builtins; /* Borrowed reference */
5353
PyObject *f_locals; /* Strong reference, may be NULL */
@@ -101,7 +101,7 @@ _PyFrame_InitializeSpecials(
101101
_PyInterpreterFrame *frame, PyFunctionObject *func,
102102
PyObject *locals, PyCodeObject *code)
103103
{
104-
frame->f_func = func;
104+
frame->f_funcobj = (PyObject *)func;
105105
frame->f_code = (PyCodeObject *)Py_NewRef(code);
106106
frame->f_builtins = func->func_builtins;
107107
frame->f_globals = func->func_globals;

Lib/argparse.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2477,9 +2477,11 @@ def _get_values(self, action, arg_strings):
24772477
not action.option_strings):
24782478
if action.default is not None:
24792479
value = action.default
2480+
self._check_value(action, value)
24802481
else:
2482+
# since arg_strings is always [] at this point
2483+
# there is no need to use self._check_value(action, value)
24812484
value = arg_strings
2482-
self._check_value(action, value)
24832485

24842486
# single argument or optional argument produces a single value
24852487
elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]:

Lib/csv.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class unix_dialect(Dialect):
8181
class DictReader:
8282
def __init__(self, f, fieldnames=None, restkey=None, restval=None,
8383
dialect="excel", *args, **kwds):
84+
if fieldnames is not None and iter(fieldnames) is fieldnames:
85+
fieldnames = list(fieldnames)
8486
self._fieldnames = fieldnames # list of keys for the dict
8587
self.restkey = restkey # key to catch long rows
8688
self.restval = restval # default value for short rows
@@ -133,6 +135,8 @@ def __next__(self):
133135
class DictWriter:
134136
def __init__(self, f, fieldnames, restval="", extrasaction="raise",
135137
dialect="excel", *args, **kwds):
138+
if fieldnames is not None and iter(fieldnames) is fieldnames:
139+
fieldnames = list(fieldnames)
136140
self.fieldnames = fieldnames # list of keys for the dict
137141
self.restval = restval # for writing short dicts
138142
if extrasaction.lower() not in ("raise", "ignore"):

Lib/test/test_argparse.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5230,6 +5230,13 @@ def test_mixed(self):
52305230
self.assertEqual(NS(v=3, spam=True, badger="B"), args)
52315231
self.assertEqual(["C", "--foo", "4"], extras)
52325232

5233+
def test_zero_or_more_optional(self):
5234+
parser = argparse.ArgumentParser()
5235+
parser.add_argument('x', nargs='*', choices=('x', 'y'))
5236+
args = parser.parse_args([])
5237+
self.assertEqual(NS(x=[]), args)
5238+
5239+
52335240
# ===========================
52345241
# parse_intermixed_args tests
52355242
# ===========================

Lib/test/test_csv.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,34 @@ def test_write_field_not_in_field_names_ignore(self):
736736
csv.DictWriter.writerow(writer, dictrow)
737737
self.assertEqual(fileobj.getvalue(), "1,2\r\n")
738738

739+
def test_dict_reader_fieldnames_accepts_iter(self):
740+
fieldnames = ["a", "b", "c"]
741+
f = StringIO()
742+
reader = csv.DictReader(f, iter(fieldnames))
743+
self.assertEqual(reader.fieldnames, fieldnames)
744+
745+
def test_dict_reader_fieldnames_accepts_list(self):
746+
fieldnames = ["a", "b", "c"]
747+
f = StringIO()
748+
reader = csv.DictReader(f, fieldnames)
749+
self.assertEqual(reader.fieldnames, fieldnames)
750+
751+
def test_dict_writer_fieldnames_rejects_iter(self):
752+
fieldnames = ["a", "b", "c"]
753+
f = StringIO()
754+
writer = csv.DictWriter(f, iter(fieldnames))
755+
self.assertEqual(writer.fieldnames, fieldnames)
756+
757+
def test_dict_writer_fieldnames_accepts_list(self):
758+
fieldnames = ["a", "b", "c"]
759+
f = StringIO()
760+
writer = csv.DictWriter(f, fieldnames)
761+
self.assertEqual(writer.fieldnames, fieldnames)
762+
763+
def test_dict_reader_fieldnames_is_optional(self):
764+
f = StringIO()
765+
reader = csv.DictReader(f, fieldnames=None)
766+
739767
def test_read_dict_fields(self):
740768
with TemporaryFile("w+", encoding="utf-8") as fileobj:
741769
fileobj.write("1,2,abc\r\n")

Lib/test/test_socket.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,10 +1398,21 @@ def testStringToIPv6(self):
13981398

13991399
def testSockName(self):
14001400
# Testing getsockname()
1401-
port = socket_helper.find_unused_port()
14021401
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
14031402
self.addCleanup(sock.close)
1404-
sock.bind(("0.0.0.0", port))
1403+
1404+
# Since find_unused_port() is inherently subject to race conditions, we
1405+
# call it a couple times if necessary.
1406+
for i in itertools.count():
1407+
port = socket_helper.find_unused_port()
1408+
try:
1409+
sock.bind(("0.0.0.0", port))
1410+
except OSError as e:
1411+
if e.errno != errno.EADDRINUSE or i == 5:
1412+
raise
1413+
else:
1414+
break
1415+
14051416
name = sock.getsockname()
14061417
# XXX(nnorwitz): http://tinyurl.com/os5jz seems to indicate
14071418
# it reasonable to get the host's addr in addition to 0.0.0.0.

Lib/test/test_threading.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,7 @@ def callback():
853853
callback()
854854
finally:
855855
sys.settrace(old_trace)
856+
threading.settrace(old_trace)
856857

857858
def test_gettrace(self):
858859
def noop_trace(frame, event, arg):
@@ -866,6 +867,35 @@ def noop_trace(frame, event, arg):
866867
finally:
867868
threading.settrace(old_trace)
868869

870+
def test_gettrace_all_threads(self):
871+
def fn(*args): pass
872+
old_trace = threading.gettrace()
873+
first_check = threading.Event()
874+
second_check = threading.Event()
875+
876+
trace_funcs = []
877+
def checker():
878+
trace_funcs.append(sys.gettrace())
879+
first_check.set()
880+
second_check.wait()
881+
trace_funcs.append(sys.gettrace())
882+
883+
try:
884+
t = threading.Thread(target=checker)
885+
t.start()
886+
first_check.wait()
887+
threading.settrace_all_threads(fn)
888+
second_check.set()
889+
t.join()
890+
self.assertEqual(trace_funcs, [None, fn])
891+
self.assertEqual(threading.gettrace(), fn)
892+
self.assertEqual(sys.gettrace(), fn)
893+
finally:
894+
threading.settrace_all_threads(old_trace)
895+
896+
self.assertEqual(threading.gettrace(), old_trace)
897+
self.assertEqual(sys.gettrace(), old_trace)
898+
869899
def test_getprofile(self):
870900
def fn(*args): pass
871901
old_profile = threading.getprofile()
@@ -875,6 +905,35 @@ def fn(*args): pass
875905
finally:
876906
threading.setprofile(old_profile)
877907

908+
def test_getprofile_all_threads(self):
909+
def fn(*args): pass
910+
old_profile = threading.getprofile()
911+
first_check = threading.Event()
912+
second_check = threading.Event()
913+
914+
profile_funcs = []
915+
def checker():
916+
profile_funcs.append(sys.getprofile())
917+
first_check.set()
918+
second_check.wait()
919+
profile_funcs.append(sys.getprofile())
920+
921+
try:
922+
t = threading.Thread(target=checker)
923+
t.start()
924+
first_check.wait()
925+
threading.setprofile_all_threads(fn)
926+
second_check.set()
927+
t.join()
928+
self.assertEqual(profile_funcs, [None, fn])
929+
self.assertEqual(threading.getprofile(), fn)
930+
self.assertEqual(sys.getprofile(), fn)
931+
finally:
932+
threading.setprofile_all_threads(old_profile)
933+
934+
self.assertEqual(threading.getprofile(), old_profile)
935+
self.assertEqual(sys.getprofile(), old_profile)
936+
878937
@cpython_only
879938
def test_shutdown_locks(self):
880939
for daemon in (False, True):

0 commit comments

Comments
 (0)