Skip to content

Commit 2b37218

Browse files
authored
Merge branch 'main' into gh-158166-xml-parseerror-difference
2 parents 6b5cdcf + ee1bbf0 commit 2b37218

36 files changed

Lines changed: 1059 additions & 1021 deletions

‎Doc/howto/free-threading-python.rst‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,7 @@ The free-threaded build of CPython aims to provide similar thread-safety
7979
behavior at the Python level to the default GIL-enabled build. Built-in
8080
types like :class:`dict`, :class:`list`, and :class:`set` use internal locks
8181
to protect against concurrent modifications in ways that behave similarly to
82-
the GIL. However, Python has not historically guaranteed specific behavior for
83-
concurrent modifications to these built-in types, so this should be treated
84-
as a description of the current implementation, not a guarantee of current or
85-
future behavior.
82+
the GIL. See :ref:`threadsafety` for the guarantees provided by built-in types.
8683

8784
.. note::
8885

‎Doc/library/argparse.rst‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2099,7 +2099,7 @@ Mutual exclusion
20992099
>>> group.add_argument('--bar', action='store_false')
21002100
>>> parser.parse_args([])
21012101
usage: PROG [-h] (--foo | --bar)
2102-
PROG: error: one of the arguments --foo --bar is required
2102+
PROG: error: one of the following arguments is required: --foo, --bar
21032103

21042104
Note that currently mutually exclusive argument groups do not support the
21052105
*title* and *description* arguments of

‎Grammar/python.gram‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,7 @@ compare_op_bitwise_or_pair[CmpopExprPair*]:
789789
| (tok='!=' { _PyPegen_check_barry_as_flufl(p, tok) ? NULL : tok }) a=bitwise_or {
790790
_PyPegen_cmpop_expr_pair(p, NotEq, a) }
791791
| '<=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, LtE, a) }
792+
| invalid_noteq
792793
| '<' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Lt, a) }
793794
| '>=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, GtE, a) }
794795
| '>' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Gt, a) }
@@ -1615,3 +1616,10 @@ invalid_bitwise_or:
16151616
? RAISE_SYNTAX_ERROR_KNOWN_RANGE(b, c, "invalid syntax. Maybe you meant 'or' or '|' instead of '||'?")
16161617
: NULL
16171618
}
1619+
1620+
invalid_noteq:
1621+
| a='<' b='>' {
1622+
_PyPegen_tokens_are_adjacent(a, b)
1623+
? RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Maybe you meant '!=' instead of '<>'?")
1624+
: NULL
1625+
}

‎Include/cpython/pystate.h‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,7 @@ struct _ts {
118118

119119
int _whence;
120120

121-
/* Thread state (_Py_THREAD_ATTACHED, _Py_THREAD_DETACHED, _Py_THREAD_SUSPENDED).
122-
See Include/internal/pycore_pystate.h for more details. */
121+
/* Thread state. See Include/internal/pycore_pystate.h for details. */
123122
int state;
124123

125124
int py_recursion_remaining;

‎Include/internal/pycore_pystate.h‎

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,32 @@ extern "C" {
2121
// interpreter at the same time. Only the "bound" thread may perform the
2222
// transitions between "attached" and "detached" on its own PyThreadState.
2323
//
24-
// The "suspended" state is used to implement stop-the-world pauses, such as
25-
// for cyclic garbage collection. It is only used in `--disable-gil` builds.
26-
// The "suspended" state is similar to the "detached" state in that in both
27-
// states the thread is not allowed to call most Python APIs. However, unlike
28-
// the "detached" state, a thread may not transition itself out from the
29-
// "suspended" state. Only the thread performing a stop-the-world pause may
30-
// transition a thread from the "suspended" state back to the "detached" state.
24+
// The "suspended" states are used to implement stop-the-world pauses and to
25+
// merge biased reference counts on behalf of detached threads. They are only
26+
// used in `--disable-gil` builds.
27+
// They are similar to the "detached" state in that the thread is not allowed
28+
// to call most Python APIs. A suspended thread trying to attach marks itself
29+
// as "suspended-waiting". Only the thread responsible for suspending it may
30+
// resume it, moving it to "detached" or "detached-waiting".
31+
// A "detached-waiting" thread must attach before it can be suspended again.
3132
//
3233
// The "shutting down" state is used when the interpreter is being finalized.
3334
// Threads in this state can't do anything other than block the OS thread.
3435
// (See _PyThreadState_HangThread).
3536
//
36-
// State transition diagram:
37-
//
38-
// (bound thread) (stop-the-world thread)
39-
// [attached] <-> [detached] <-> [suspended]
40-
// | ^
41-
// +---------------------------->---------------------------+
42-
// (bound thread)
43-
//
44-
// The (bound thread) and (stop-the-world thread) labels indicate which thread
45-
// is allowed to perform the transition.
46-
#define _Py_THREAD_DETACHED 0
47-
#define _Py_THREAD_ATTACHED 1
48-
#define _Py_THREAD_SUSPENDED 2
49-
#define _Py_THREAD_SHUTTING_DOWN 3
37+
// State transitions:
38+
// Bound thread: attached <-> detached
39+
// attached -> suspended
40+
// suspended -> suspended-waiting
41+
// detached-waiting -> attached
42+
// Suspending thread: detached <-> suspended
43+
// suspended-waiting -> detached-waiting
44+
#define _Py_THREAD_DETACHED 0
45+
#define _Py_THREAD_ATTACHED 1
46+
#define _Py_THREAD_SUSPENDED 2
47+
#define _Py_THREAD_SHUTTING_DOWN 3
48+
#define _Py_THREAD_SUSPENDED_WAITING 4
49+
#define _Py_THREAD_DETACHED_WAITING 5
5050

5151

5252
/* Check if the current thread is the main thread.
@@ -162,8 +162,9 @@ extern void _PyThreadState_Suspend(PyThreadState *tstate);
162162
// Returns 1 on success, 0 if the thread was not in the "detached" state.
163163
extern int _PyThreadState_TrySuspendDetached(PyThreadState *tstate);
164164

165-
// Undo a successful _PyThreadState_TrySuspendDetached(): switch the thread
166-
// back to "detached" and wake it if it is waiting to attach.
165+
// Resume a thread suspended by _PyThreadState_TrySuspendDetached() or a
166+
// stop-the-world pause: switch it back to "detached" or "detached-waiting"
167+
// and wake it if it is waiting to attach.
167168
extern void _PyThreadState_ResumeDetached(PyThreadState *tstate);
168169
#endif
169170

‎Include/internal/pycore_token.h‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Lib/argparse.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2499,8 +2499,8 @@ def consume_positionals(start_index):
24992499
names = [_get_action_name(action)
25002500
for action in group._group_actions
25012501
if action.help is not SUPPRESS]
2502-
msg = _('one of the arguments %s is required')
2503-
raise ArgumentError(None, msg % ' '.join(names))
2502+
msg = _('one of the following arguments is required: %s')
2503+
raise ArgumentError(None, msg % ', '.join(names))
25042504

25052505
# return the updated namespace and the extra arguments
25062506
return namespace, extras

‎Lib/idlelib/run.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ def main(del_exitfunc=False):
164164
).start()
165165

166166
while True:
167+
request = None
167168
try:
168169
if exit_now:
169170
try:
@@ -174,9 +175,9 @@ def main(del_exitfunc=False):
174175
try:
175176
request = rpc.request_queue.get(block=True, timeout=0.05)
176177
except queue.Empty:
177-
request = None
178178
# Issue 32207: calling handle_tk_events here adds spurious
179179
# queue.Empty traceback to event handling exceptions.
180+
pass
180181
if request:
181182
seq, (method, args, kwargs) = request
182183
ret = method(*args, **kwargs)
@@ -186,6 +187,10 @@ def main(del_exitfunc=False):
186187
except KeyboardInterrupt:
187188
if quitting:
188189
exit_now = True
190+
elif request:
191+
# Interrupted while executing a request, as when debugging.
192+
print_exception()
193+
rpc.response_queue.put((seq, None))
189194
continue
190195
except SystemExit:
191196
capture_warnings(False)

‎Lib/test/pythoninfo.py‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -917,18 +917,17 @@ def collect_support_threading_helper(info_add):
917917
copy_attributes(info_add, threading_helper, 'support_threading_helper.%s', attributes)
918918

919919

920-
def collect_cc(info_add):
920+
def get_compiler_version(sysconfig_var):
921921
import sysconfig
922-
923-
CC = sysconfig.get_config_var('CC')
924-
if not CC:
922+
program = sysconfig.get_config_var(sysconfig_var)
923+
if not program:
925924
return
926925

927926
try:
928927
import shlex
929-
args = shlex.split(CC)
928+
args = shlex.split(program)
930929
except ImportError:
931-
args = CC.split()
930+
args = program.split()
932931
args.append('--version')
933932

934933
stdout = run_command(args)
@@ -942,7 +941,21 @@ def collect_cc(info_add):
942941

943942
text = first_line(stdout)
944943
text = normalize_text(text)
945-
info_add('CC.version', text)
944+
if text:
945+
text = f'[{program}] {text}'
946+
return text
947+
948+
949+
def collect_cc(info_add):
950+
# C compiler
951+
version = get_compiler_version('CC')
952+
if version:
953+
info_add('CC.version', version)
954+
955+
# C++ compiler
956+
version = get_compiler_version('CXX')
957+
if version:
958+
info_add('CXX.version', version)
946959

947960

948961
def collect_gdbm(info_add):

‎Lib/test/test_argparse.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6866,7 +6866,7 @@ def test_required_exclusive(self):
68666866
args = parser.parse_intermixed_args('1 --foo 2'.split())
68676867
self.assertEqual(NS(badger=['1', '2'], foo=True, spam=None), args)
68686868
self.assertRaisesRegex(argparse.ArgumentError,
6869-
'one of the arguments --foo --spam is required',
6869+
'one of the following arguments is required: --foo, --spam',
68706870
parser.parse_intermixed_args, '1 2'.split())
68716871
self.assertEqual(group.required, True)
68726872

@@ -6882,7 +6882,7 @@ def test_required_exclusive_with_positional(self):
68826882
args = parser.parse_intermixed_args(['a', 'b'])
68836883
self.assertEqual(NS(foo=False, spam=None, badger=['a', 'b']), args)
68846884
self.assertRaisesRegex(argparse.ArgumentError,
6885-
'one of the arguments --foo --spam badger is required',
6885+
'one of the following arguments is required: --foo, --spam, badger',
68866886
parser.parse_intermixed_args, [])
68876887
self.assertRaisesRegex(argparse.ArgumentError,
68886888
'argument badger: not allowed with argument --foo',
@@ -7258,7 +7258,7 @@ def test_required_mutually_exclusive_args(self):
72587258
group.add_argument('--bar')
72597259
group.add_argument('--baz')
72607260
self.assertRaisesRegex(argparse.ArgumentError,
7261-
'one of the arguments --bar --baz is required',
7261+
'one of the following arguments is required: --bar, --baz',
72627262
self.parser.parse_args, [])
72637263

72647264
def test_conflicting_mutually_exclusive_args_optional_with_metavar(self):

0 commit comments

Comments
 (0)