diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index a24589fd0a5af3..755d681b9df2be 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -768,7 +768,7 @@ not have to be) the original ``STACK[-2]``. end = STACK.pop() start = STACK.pop() container = STACK.pop() - values = STACK.pop() + value = STACK.pop() container[start:end] = value .. versionadded:: 3.12 diff --git a/Lib/argparse.py b/Lib/argparse.py index 633fec69ea4615..0494b545f2f1d3 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -688,11 +688,41 @@ def _expand_help(self, action): params[name] = value.__name__ if params.get('choices') is not None: params['choices'] = ', '.join(map(str, params['choices'])) - # Before interpolating, wrap the values with color codes + t = self._theme - for name, value in params.items(): - params[name] = f"{t.interpolated_value}{value}{t.reset}" - return help_string % params + + result = help_string % params + + if not t.reset: + return result + + # Match format specifiers like: %s, %d, %(key)s, etc. + fmt_spec = r''' + % + (?: + % # %% escape + | + (?:\((?P[^)]*)\))? # key + [-#0\ +]* # flags + (?:\*|\d+)? # width + (?:\.(?:\*|\d+))? # precision + [hlL]? # length modifier + [diouxXeEfFgGcrsa] # conversion type + ) + ''' + + def colorize(match): + spec, key = match.group(0, 'key') + if spec == '%%': + return '%' + if key is not None: + # %(key)... - format and colorize + formatted = spec % {key: params[key]} + return f'{t.interpolated_value}{formatted}{t.reset}' + # bare %s etc. - format with full params dict, no colorization + return spec % params + + return _re.sub(fmt_spec, colorize, help_string, flags=_re.VERBOSE) def _iter_indented_subactions(self, action): try: diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 758af98d5cb046..77170244675474 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -7663,6 +7663,38 @@ def test_backtick_markup_special_regex_chars(self): help_text = parser.format_help() self.assertIn(f'{prog_extra}grep "foo.*bar" | sort{reset}', help_text) + def test_help_with_format_specifiers(self): + # GH-142950: format specifiers like %x should work with color=True + parser = argparse.ArgumentParser(prog='PROG', color=True) + parser.add_argument('--hex', type=int, default=255, + help='hex: %(default)x, alt: %(default)#x') + parser.add_argument('--zero', type=int, default=7, + help='zero: %(default)05d') + parser.add_argument('--str', default='test', + help='str: %(default)s') + parser.add_argument('--pct', type=int, default=50, + help='pct: %(default)d%%') + parser.add_argument('--literal', help='literal: 100%%') + parser.add_argument('--prog', help='prog: %(prog)s') + parser.add_argument('--type', type=int, help='type: %(type)s') + parser.add_argument('--choices', choices=['a', 'b'], + help='choices: %(choices)s') + + help_text = parser.format_help() + + interp = self.theme.interpolated_value + reset = self.theme.reset + + self.assertIn(f'hex: {interp}ff{reset}', help_text) + self.assertIn(f'alt: {interp}0xff{reset}', help_text) + self.assertIn(f'zero: {interp}00007{reset}', help_text) + self.assertIn(f'str: {interp}test{reset}', help_text) + self.assertIn(f'pct: {interp}50{reset}%', help_text) + self.assertIn('literal: 100%', help_text) + self.assertIn(f'prog: {interp}PROG{reset}', help_text) + self.assertIn(f'type: {interp}int{reset}', help_text) + self.assertIn(f'choices: {interp}a, b{reset}', help_text) + def test_print_help_uses_target_file_for_color_decision(self): parser = argparse.ArgumentParser(prog='PROG', color=True) parser.add_argument('--opt') diff --git a/Misc/NEWS.d/next/Library/2025-12-18-22-58-46.gh-issue-142950.EJ8w-T.rst b/Misc/NEWS.d/next/Library/2025-12-18-22-58-46.gh-issue-142950.EJ8w-T.rst new file mode 100644 index 00000000000000..219930c638384f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-18-22-58-46.gh-issue-142950.EJ8w-T.rst @@ -0,0 +1 @@ +Fix regression in :mod:`argparse` where format specifiers in help strings raised :exc:`ValueError`. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8db1de6ca384bb..300683a9402784 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1349,7 +1349,7 @@ dummy_func( PyObject *receiver_o = PyStackRef_AsPyObjectBorrow(receiver); PyObject *retval_o; assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - if ((tstate->interp->eval_frame == NULL) && + if (!IS_PEP523_HOOKED(tstate) && (Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) && gen_try_set_executing((PyGenObject *)receiver_o)) { @@ -2596,7 +2596,7 @@ dummy_func( PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert((oparg & 1) == 0); - DEOPT_IF(tstate->interp->eval_frame); + DEOPT_IF(IS_PEP523_HOOKED(tstate)); PyTypeObject *cls = Py_TYPE(owner_o); assert(type_version != 0); DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(cls->tp_version_tag) != type_version); @@ -3746,7 +3746,7 @@ dummy_func( } // Check if the call can be inlined or not if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -3938,7 +3938,7 @@ dummy_func( } op(_CHECK_PEP_523, (--)) { - DEOPT_IF(tstate->interp->eval_frame); + DEOPT_IF(IS_PEP523_HOOKED(tstate)); } op(_CHECK_FUNCTION_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { @@ -3974,7 +3974,7 @@ dummy_func( } op(_PUSH_FRAME, (new_frame -- )) { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); DEAD(new_frame); SYNC_SP(); @@ -4607,7 +4607,7 @@ dummy_func( int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); // Check if the call can be inlined or not if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -4851,7 +4851,7 @@ dummy_func( } else { if (Py_TYPE(func) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); assert(PyTuple_CheckExact(callargs)); diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 697466d14a689f..c70d6f4ba00650 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -489,6 +489,8 @@ do { \ #define CHECK_CURRENT_CACHED_VALUES(N) ((void)0) #endif +#define IS_PEP523_HOOKED(tstate) (tstate->interp->eval_frame != NULL) + static inline int check_periodics(PyThreadState *tstate) { _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index e78c37611ce6b8..fb8a919336bcf4 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -13100,7 +13100,7 @@ case _CHECK_PEP_523_r00: { CHECK_CURRENT_CACHED_VALUES(0); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UOP_STAT_INC(uopcode, miss); SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); @@ -13114,7 +13114,7 @@ CHECK_CURRENT_CACHED_VALUES(1); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef _stack_item_0 = _tos_cache0; - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UOP_STAT_INC(uopcode, miss); _tos_cache0 = _stack_item_0; SET_CURRENT_CACHED_VALUES(1); @@ -13131,7 +13131,7 @@ assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UOP_STAT_INC(uopcode, miss); _tos_cache1 = _stack_item_1; _tos_cache0 = _stack_item_0; @@ -13151,7 +13151,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UOP_STAT_INC(uopcode, miss); _tos_cache2 = _stack_item_2; _tos_cache1 = _stack_item_1; @@ -13457,7 +13457,7 @@ _PyStackRef new_frame; _PyStackRef _stack_item_0 = _tos_cache0; new_frame = _stack_item_0; - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ece1146924bc1f..4de75b54c9e0a1 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -680,7 +680,7 @@ /* Skip 5 cache entries */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); @@ -730,7 +730,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); stack_pointer += -2; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); @@ -1750,7 +1750,7 @@ total_args++; } if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -1827,7 +1827,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -1913,7 +1913,7 @@ // _PUSH_FRAME { new_frame = init_frame; - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -1946,7 +1946,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -2054,7 +2054,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); stack_pointer += -2 - oparg; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); @@ -2089,7 +2089,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -2184,7 +2184,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -2626,7 +2626,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL_FUNCTION_EX); assert(_PyOpcode_Deopt[opcode] == (CALL_FUNCTION_EX)); JUMP_TO_PREDICTED(CALL_FUNCTION_EX); @@ -2713,7 +2713,7 @@ // _PUSH_FRAME { new_frame = ex_frame; - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -2842,7 +2842,7 @@ } else { if (Py_TYPE(func) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); assert(PyTuple_CheckExact(callargs)); @@ -3122,7 +3122,7 @@ } int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -3193,7 +3193,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL_KW); assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); JUMP_TO_PREDICTED(CALL_KW); @@ -3289,7 +3289,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -3399,7 +3399,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL_KW); assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); JUMP_TO_PREDICTED(CALL_KW); @@ -3476,7 +3476,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -4147,7 +4147,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -4226,7 +4226,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); stack_pointer += -2 - oparg; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); @@ -4260,7 +4260,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -4328,7 +4328,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -5804,7 +5804,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(FOR_ITER); assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); JUMP_TO_PREDICTED(FOR_ITER); @@ -5836,7 +5836,7 @@ // _PUSH_FRAME { new_frame = gen_frame; - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -6432,7 +6432,7 @@ total_args++; } if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -6583,7 +6583,7 @@ } else { if (Py_TYPE(func) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); assert(PyTuple_CheckExact(callargs)); @@ -6728,7 +6728,7 @@ } int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -8028,7 +8028,7 @@ PyObject *getattribute = read_obj(&this_instr[6].cache); PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert((oparg & 1) == 0); - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); @@ -8528,7 +8528,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); @@ -8590,7 +8590,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); @@ -10476,7 +10476,7 @@ PyObject *receiver_o = PyStackRef_AsPyObjectBorrow(receiver); PyObject *retval_o; assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - if ((tstate->interp->eval_frame == NULL) && + if (!IS_PEP523_HOOKED(tstate) && (Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) && gen_try_set_executing((PyGenObject *)receiver_o)) { @@ -10561,7 +10561,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(SEND); assert(_PyOpcode_Deopt[opcode] == (SEND)); JUMP_TO_PREDICTED(SEND); @@ -10595,7 +10595,7 @@ // _PUSH_FRAME { new_frame = gen_frame; - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 16fb43ea191439..88dbdb6d139c5f 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2435,7 +2435,7 @@ new_interpreter(PyThreadState **tstate_p, /* Issue #10915, #15751: The GIL API doesn't work with multiple interpreters: disable PyGILState_Check(). */ - runtime->gilstate.check_enabled = 0; + _Py_atomic_store_int_relaxed(&runtime->gilstate.check_enabled, 0); // XXX Might new_interpreter() have been called without the GIL held? PyThreadState *save_tstate = _PyThreadState_GET(); diff --git a/Python/pystate.c b/Python/pystate.c index f605527598a86d..23853f69792450 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2832,7 +2832,7 @@ int PyGILState_Check(void) { _PyRuntimeState *runtime = &_PyRuntime; - if (!runtime->gilstate.check_enabled) { + if (!_Py_atomic_load_int_relaxed(&runtime->gilstate.check_enabled)) { return 1; } diff --git a/Tools/tsan/suppressions.txt b/Tools/tsan/suppressions.txt index 6bda5ecd570889..7a32f4e0ba26c5 100644 --- a/Tools/tsan/suppressions.txt +++ b/Tools/tsan/suppressions.txt @@ -1,5 +1,10 @@ # This file contains suppressions for the default (with GIL) build. -# reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions +# +# Reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions +# +# When adding a suppression, include a comment referencing a GitHub issue +# that describes how to reproduce the race and includes the relevant TSan +# output. -# https://gist.github.com/mpage/daaf32b39180c1989572957b943eb665 -thread:pthread_create +# There are currently no active suppressions. This file is kept so tooling +# that expects it can still find it. diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index 581e9ef26f3c61..f7ce11792e51b9 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -1,18 +1,12 @@ -# This file contains suppressions for the free-threaded build. It contains the -# suppressions for the default build and additional suppressions needed only in +# This file contains suppressions for the free-threaded build. It includes the +# default build suppressions plus any additional suppressions needed only in # the free-threaded build. # -# reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions - -## Free-threaded suppressions - - -# These entries are for warnings that trigger in a library function, as called -# by a CPython function. - -# These warnings trigger directly in a CPython function. - -race_top:_PyObject_TryGetInstanceAttribute +# Reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions +# +# When adding a suppression, include a comment referencing a GitHub issue +# that describes how to reproduce the race and includes the relevant TSan +# output. -# https://gist.github.com/mpage/6962e8870606cfc960e159b407a0cb40 -thread:pthread_create +# There are currently no active suppressions. This file is kept so tooling +# that expects it can still find it.