diff --git a/Doc/deprecations/c-api-pending-removal-in-3.20.rst b/Doc/deprecations/c-api-pending-removal-in-3.20.rst
index 18623b19a2ab8d..a813cb21dd4dbf 100644
--- a/Doc/deprecations/c-api-pending-removal-in-3.20.rst
+++ b/Doc/deprecations/c-api-pending-removal-in-3.20.rst
@@ -1,6 +1,13 @@
Pending removal in Python 3.20
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+* :c:func:`!_PyObject_CallMethodId`, :c:func:`!_PyObject_GetAttrId` and
+ :c:func:`!_PyUnicode_FromId` are deprecated since 3.15 and will be removed in
+ 3.20. Instead, use :c:func:`PyUnicode_FromString()` and cache the result in
+ the module state, then call :c:func:`PyObject_CallMethod` or
+ :c:func:`PyObject_GetAttr`.
+ (Contributed by Victor Stinner in :gh:`141049`.)
+
* The ``cval`` field in :c:type:`PyComplexObject` (:gh:`128813`).
Use :c:func:`PyComplex_AsCComplex` and :c:func:`PyComplex_FromCComplex`
to convert a Python complex number to/from the C :c:type:`Py_complex`
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index 9d4686f982a99a..1d8b838cd2e7f0 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -1210,6 +1210,13 @@ Deprecated C APIs
use the :c:type:`PyBytesWriter` API instead.
(Contributed by Victor Stinner in :gh:`129813`.)
+* :c:func:`!_PyObject_CallMethodId`, :c:func:`!_PyObject_GetAttrId` and
+ :c:func:`!_PyUnicode_FromId` are deprecated since 3.15 and will be removed in
+ 3.20. Instead, use :c:func:`PyUnicode_FromString()` and cache the result in
+ the module state, then call :c:func:`PyObject_CallMethod` or
+ :c:func:`PyObject_GetAttr`.
+ (Contributed by Victor Stinner in :gh:`141049`.)
+
* Deprecate :c:member:`~PyComplexObject.cval` field of the
:c:type:`PyComplexObject` type.
Use :c:func:`PyComplex_AsCComplex` and :c:func:`PyComplex_FromCComplex`
diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h
index ffd19ccd3500fa..7490ece52e5220 100644
--- a/Include/cpython/abstract.h
+++ b/Include/cpython/abstract.h
@@ -6,7 +6,7 @@
/* Like PyObject_CallMethod(), but expect a _Py_Identifier*
as the method name. */
-PyAPI_FUNC(PyObject*) _PyObject_CallMethodId(
+Py_DEPRECATED(3.15) PyAPI_FUNC(PyObject*) _PyObject_CallMethodId(
PyObject *obj,
_Py_Identifier *name,
const char *format, ...);
diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index 8693390aeda624..85d5edd62e3a72 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -300,7 +300,7 @@ PyAPI_FUNC(void) PyUnstable_Object_Dump(PyObject *);
// Alias for backward compatibility
#define _PyObject_Dump PyUnstable_Object_Dump
-PyAPI_FUNC(PyObject*) _PyObject_GetAttrId(PyObject *, _Py_Identifier *);
+Py_DEPRECATED(3.15) PyAPI_FUNC(PyObject*) _PyObject_GetAttrId(PyObject *, _Py_Identifier *);
PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *);
PyAPI_FUNC(void) PyObject_CallFinalizer(PyObject *);
diff --git a/Include/cpython/unicodeobject.h b/Include/cpython/unicodeobject.h
index 2853d24c34b66e..631a6570658410 100644
--- a/Include/cpython/unicodeobject.h
+++ b/Include/cpython/unicodeobject.h
@@ -778,4 +778,4 @@ static inline int Py_UNICODE_ISALNUM(Py_UCS4 ch) {
// Return an interned Unicode object for an Identifier; may fail if there is no
// memory.
-PyAPI_FUNC(PyObject*) _PyUnicode_FromId(_Py_Identifier*);
+Py_DEPRECATED(3.15) PyAPI_FUNC(PyObject*) _PyUnicode_FromId(_Py_Identifier*);
diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h
index 35659de22b799d..13e58721ebee9e 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -1376,7 +1376,7 @@ _PyOpcode_macro_expansion[256] = {
[CALL_PY_EXACT_ARGS] = { .nuops = 8, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _CHECK_FUNCTION_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _CHECK_STACK_SPACE, OPARG_SIMPLE, 3 }, { _CHECK_RECURSION_REMAINING, OPARG_SIMPLE, 3 }, { _INIT_CALL_PY_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
[CALL_PY_GENERAL] = { .nuops = 6, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _CHECK_RECURSION_REMAINING, OPARG_SIMPLE, 3 }, { _PY_FRAME_GENERAL, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } },
[CALL_STR_1] = { .nuops = 4, .uops = { { _GUARD_NOS_NULL, OPARG_SIMPLE, 3 }, { _GUARD_CALLABLE_STR_1, OPARG_SIMPLE, 3 }, { _CALL_STR_1, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } },
- [CALL_TUPLE_1] = { .nuops = 5, .uops = { { _GUARD_NOS_NULL, OPARG_SIMPLE, 3 }, { _GUARD_CALLABLE_TUPLE_1, OPARG_SIMPLE, 3 }, { _CALL_TUPLE_1, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } },
+ [CALL_TUPLE_1] = { .nuops = 4, .uops = { { _GUARD_NOS_NULL, OPARG_SIMPLE, 3 }, { _GUARD_CALLABLE_TUPLE_1, OPARG_SIMPLE, 3 }, { _CALL_TUPLE_1, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } },
[CALL_TYPE_1] = { .nuops = 3, .uops = { { _GUARD_NOS_NULL, OPARG_SIMPLE, 3 }, { _GUARD_CALLABLE_TYPE_1, OPARG_SIMPLE, 3 }, { _CALL_TYPE_1, OPARG_SIMPLE, 3 } } },
[CHECK_EG_MATCH] = { .nuops = 1, .uops = { { _CHECK_EG_MATCH, OPARG_SIMPLE, 0 } } },
[CHECK_EXC_MATCH] = { .nuops = 1, .uops = { { _CHECK_EXC_MATCH, OPARG_SIMPLE, 0 } } },
diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h
index ce1ad5a4c8a5f6..56928b06685270 100644
--- a/Include/internal/pycore_uop_ids.h
+++ b/Include/internal/pycore_uop_ids.h
@@ -439,7 +439,7 @@ extern "C" {
#define _CALL_METHOD_DESCRIPTOR_O_r01 632
#define _CALL_NON_PY_GENERAL_r01 633
#define _CALL_STR_1_r31 634
-#define _CALL_TUPLE_1_r32 635
+#define _CALL_TUPLE_1_r31 635
#define _CALL_TYPE_1_r31 636
#define _CHECK_AND_ALLOCATE_OBJECT_r00 637
#define _CHECK_ATTR_CLASS_r01 638
diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h
index 1e74588d3aa62e..2ae84bdee0df12 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -2543,7 +2543,7 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = {
{ -1, -1, -1 },
{ -1, -1, -1 },
{ -1, -1, -1 },
- { 2, 0, _CALL_TUPLE_1_r32 },
+ { 1, 0, _CALL_TUPLE_1_r31 },
},
},
[_CHECK_AND_ALLOCATE_OBJECT] = {
@@ -3739,7 +3739,7 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = {
[_GUARD_CALLABLE_TUPLE_1_r13] = _GUARD_CALLABLE_TUPLE_1,
[_GUARD_CALLABLE_TUPLE_1_r23] = _GUARD_CALLABLE_TUPLE_1,
[_GUARD_CALLABLE_TUPLE_1_r33] = _GUARD_CALLABLE_TUPLE_1,
- [_CALL_TUPLE_1_r32] = _CALL_TUPLE_1,
+ [_CALL_TUPLE_1_r31] = _CALL_TUPLE_1,
[_CHECK_AND_ALLOCATE_OBJECT_r00] = _CHECK_AND_ALLOCATE_OBJECT,
[_CREATE_INIT_FRAME_r01] = _CREATE_INIT_FRAME,
[_EXIT_INIT_CHECK_r10] = _EXIT_INIT_CHECK,
@@ -4058,7 +4058,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = {
[_CALL_STR_1] = "_CALL_STR_1",
[_CALL_STR_1_r31] = "_CALL_STR_1_r31",
[_CALL_TUPLE_1] = "_CALL_TUPLE_1",
- [_CALL_TUPLE_1_r32] = "_CALL_TUPLE_1_r32",
+ [_CALL_TUPLE_1_r31] = "_CALL_TUPLE_1_r31",
[_CALL_TYPE_1] = "_CALL_TYPE_1",
[_CALL_TYPE_1_r31] = "_CALL_TYPE_1_r31",
[_CHECK_AND_ALLOCATE_OBJECT] = "_CHECK_AND_ALLOCATE_OBJECT",
diff --git a/Lib/_colorize.py b/Lib/_colorize.py
index 29d7cc67b6e39d..0b7047620b4556 100644
--- a/Lib/_colorize.py
+++ b/Lib/_colorize.py
@@ -169,7 +169,7 @@ class Argparse(ThemeSection):
label: str = ANSIColors.BOLD_YELLOW
action: str = ANSIColors.BOLD_GREEN
default: str = ANSIColors.GREY
- default_value: str = ANSIColors.YELLOW
+ interpolated_value: str = ANSIColors.YELLOW
reset: str = ANSIColors.RESET
error: str = ANSIColors.BOLD_MAGENTA
warning: str = ANSIColors.BOLD_YELLOW
diff --git a/Lib/argparse.py b/Lib/argparse.py
index ed98aa9e974b2a..ee7ebc4696a0f7 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -668,6 +668,10 @@ 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
def _iter_indented_subactions(self, action):
@@ -749,8 +753,8 @@ def _get_help_string(self, action):
default_str = _(" (default: %(default)s)")
prefix, suffix = default_str.split("%(default)s")
help += (
- f" {t.default}{prefix.lstrip()}"
- f"{t.default_value}%(default)s"
+ f" {t.default}{prefix.lstrip()}{t.reset}"
+ f"%(default)s"
f"{t.default}{suffix}{t.reset}"
)
return help
diff --git a/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js
index fb81094521815e..6345320bd2555d 100644
--- a/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js
+++ b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js
@@ -92,7 +92,8 @@ function toggleTheme() {
// Update theme button icon
const btn = document.getElementById('theme-btn');
if (btn) {
- btn.innerHTML = next === 'dark' ? '☼' : '☾'; // sun or moon
+ btn.querySelector('.icon-moon').style.display = next === 'dark' ? 'none' : '';
+ btn.querySelector('.icon-sun').style.display = next === 'dark' ? '' : 'none';
}
// Re-render flamegraph with new theme colors
@@ -160,7 +161,8 @@ function restoreUIState() {
document.documentElement.setAttribute('data-theme', savedTheme);
const btn = document.getElementById('theme-btn');
if (btn) {
- btn.innerHTML = savedTheme === 'dark' ? '☼' : '☾';
+ btn.querySelector('.icon-moon').style.display = savedTheme === 'dark' ? 'none' : '';
+ btn.querySelector('.icon-sun').style.display = savedTheme === 'dark' ? '' : 'none';
}
}
diff --git a/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html b/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html
index 211296a708643f..02855563f83f7c 100644
--- a/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html
+++ b/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html
@@ -39,18 +39,43 @@
class="toolbar-btn"
onclick="resetZoom()"
title="Reset zoom"
- >⌂
+ >
+
+
+ >
+
+
+
+
+
+ >
+
+
+
diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap.js b/Lib/profiling/sampling/_heatmap_assets/heatmap.js
index 038aa44b3df619..8ac4ef43e53b37 100644
--- a/Lib/profiling/sampling/_heatmap_assets/heatmap.js
+++ b/Lib/profiling/sampling/_heatmap_assets/heatmap.js
@@ -24,7 +24,8 @@ function toggleTheme() {
// Update theme button icon
const btn = document.getElementById('theme-btn');
if (btn) {
- btn.innerHTML = next === 'dark' ? '☼' : '☾'; // sun or moon
+ btn.querySelector('.icon-moon').style.display = next === 'dark' ? 'none' : '';
+ btn.querySelector('.icon-sun').style.display = next === 'dark' ? '' : 'none';
}
applyLineColors();
@@ -39,7 +40,8 @@ function restoreUIState() {
document.documentElement.setAttribute('data-theme', savedTheme);
const btn = document.getElementById('theme-btn');
if (btn) {
- btn.innerHTML = savedTheme === 'dark' ? '☼' : '☾';
+ btn.querySelector('.icon-moon').style.display = savedTheme === 'dark' ? 'none' : '';
+ btn.querySelector('.icon-sun').style.display = savedTheme === 'dark' ? '' : 'none';
}
}
}
diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js b/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js
index 4ddacca5173d34..8eb6af0db5335e 100644
--- a/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js
+++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js
@@ -28,7 +28,8 @@ function toggleTheme() {
// Update theme button icon
const btn = document.getElementById('theme-btn');
if (btn) {
- btn.innerHTML = next === 'dark' ? '☼' : '☾'; // sun or moon
+ btn.querySelector('.icon-moon').style.display = next === 'dark' ? 'none' : '';
+ btn.querySelector('.icon-sun').style.display = next === 'dark' ? '' : 'none';
}
applyHeatmapBarColors();
@@ -41,7 +42,8 @@ function restoreUIState() {
document.documentElement.setAttribute('data-theme', savedTheme);
const btn = document.getElementById('theme-btn');
if (btn) {
- btn.innerHTML = savedTheme === 'dark' ? '☼' : '☾';
+ btn.querySelector('.icon-moon').style.display = savedTheme === 'dark' ? 'none' : '';
+ btn.querySelector('.icon-sun').style.display = savedTheme === 'dark' ? '' : 'none';
}
}
}
diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html b/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html
index 98996bdbf5ffb1..3620f8efb8058a 100644
--- a/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html
+++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html
@@ -17,12 +17,29 @@
Heatmap Report
diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html b/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html
index 3fb6d3a6b91dbb..91b629b2628244 100644
--- a/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html
+++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html
@@ -17,14 +17,35 @@
diff --git a/Lib/profiling/sampling/heatmap_collector.py b/Lib/profiling/sampling/heatmap_collector.py
index 45649ce2009bb6..5b4c89283be08c 100644
--- a/Lib/profiling/sampling/heatmap_collector.py
+++ b/Lib/profiling/sampling/heatmap_collector.py
@@ -858,6 +858,7 @@ def _generate_index_html(self, index_path: Path, file_stats: List[FileStats]):
"": f"",
"": f"",
"": self._template_loader.logo_html,
+ "": f"{sys.version_info.major}.{sys.version_info.minor}",
"": str(len(file_stats)),
"": f"{self._total_samples:,}",
"": f"{self.stats.get('duration_sec', 0):.1f}s",
@@ -915,6 +916,7 @@ def _generate_file_html(self, output_path: Path, filename: str,
"": f"",
"": f"",
"": self._template_loader.logo_html,
+ "": f"{sys.version_info.major}.{sys.version_info.minor}",
}
html_content = self._template_loader.file_template
diff --git a/Lib/profiling/sampling/stack_collector.py b/Lib/profiling/sampling/stack_collector.py
index b7aa7f5ff82da3..e437facd8bb94b 100644
--- a/Lib/profiling/sampling/stack_collector.py
+++ b/Lib/profiling/sampling/stack_collector.py
@@ -5,6 +5,7 @@
import json
import linecache
import os
+import sys
from ._css_utils import get_combined_css
from .collector import Collector, extract_lineno
@@ -393,6 +394,9 @@ def _create_flamegraph_html(self, data):
# Let CSS control size; keep markup simple
logo_html = f'
'
html_template = html_template.replace("", logo_html)
+ html_template = html_template.replace(
+ "", f"{sys.version_info.major}.{sys.version_info.minor}"
+ )
d3_js = d3_path.read_text(encoding="utf-8")
fg_css = fg_css_path.read_text(encoding="utf-8")
diff --git a/Lib/test/support/pty_helper.py b/Lib/test/support/pty_helper.py
index 6587fd40333c51..dbe7fa429096fc 100644
--- a/Lib/test/support/pty_helper.py
+++ b/Lib/test/support/pty_helper.py
@@ -15,6 +15,14 @@ def run_pty(script, input=b"dummy input\r", env=None):
output = bytearray()
[master, slave] = pty.openpty()
args = (sys.executable, '-c', script)
+
+ # Isolate readline from personal init files by setting INPUTRC
+ # to an empty file. See also GH-142353.
+ if env is None:
+ env = {**os.environ.copy(), "INPUTRC": os.devnull}
+ else:
+ env.setdefault("INPUTRC", os.devnull)
+
proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave, env=env)
os.close(slave)
with ExitStack() as cleanup:
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 24e8ab1c5cacbb..0f93e8ea740770 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -7308,6 +7308,13 @@ def test_argparse_color(self):
choices=("Aaaaa", "Bbbbb", "Ccccc", "Ddddd"),
help="pick one",
)
+ parser.add_argument(
+ "--optional8",
+ default="A",
+ metavar="X",
+ choices=("A", "B", "C"),
+ help="among %(choices)s, default is %(default)s",
+ )
parser.add_argument("+f")
parser.add_argument("++bar")
@@ -7334,7 +7341,7 @@ def test_argparse_color(self):
label_b = self.theme.label
pos_b = self.theme.action
default = self.theme.default
- default_value = self.theme.default_value
+ interp = self.theme.interpolated_value
reset = self.theme.reset
# Act
@@ -7347,8 +7354,8 @@ def test_argparse_color(self):
f"""\
{heading}usage: {reset}{prog}PROG{reset} [{short}-h{reset}] [{short}-v{reset} | {short}-q{reset}] [{short}-o{reset}] [{long}--optional2 {label}OPTIONAL2{reset}] [{long}--optional3 {label}{{X,Y,Z}}{reset}]
[{long}--optional4 {label}{{X,Y,Z}}{reset}] [{long}--optional5 {label}{{X,Y,Z}}{reset}] [{long}--optional6 {label}{{X,Y,Z}}{reset}]
- [{short}-p {label}{{Aaaaa,Bbbbb,Ccccc,Ddddd}}{reset}] [{short}+f {label}F{reset}] [{long}++bar {label}BAR{reset}] [{long}-+baz {label}BAZ{reset}]
- [{short}-c {label}COUNT{reset}]
+ [{short}-p {label}{{Aaaaa,Bbbbb,Ccccc,Ddddd}}{reset}] [{long}--optional8 {label}X{reset}] [{short}+f {label}F{reset}] [{long}++bar {label}BAR{reset}]
+ [{long}-+baz {label}BAZ{reset}] [{short}-c {label}COUNT{reset}]
{pos}x{reset} {pos}y{reset} {pos}this_indeed_is_a_very_long_action_name{reset} {pos}{{sub1,sub2}} ...{reset}
Colorful help
@@ -7361,17 +7368,18 @@ def test_argparse_color(self):
{heading}options:{reset}
{short_b}-h{reset}, {long_b}--help{reset} show this help message and exit
- {short_b}-v{reset}, {long_b}--verbose{reset} more spam {default}(default: {default_value}False{default}){reset}
- {short_b}-q{reset}, {long_b}--quiet{reset} less spam {default}(default: {default_value}False{default}){reset}
+ {short_b}-v{reset}, {long_b}--verbose{reset} more spam {default}(default: {reset}{interp}False{reset}{default}){reset}
+ {short_b}-q{reset}, {long_b}--quiet{reset} less spam {default}(default: {reset}{interp}False{reset}{default}){reset}
{short_b}-o{reset}, {long_b}--optional1{reset}
{long_b}--optional2{reset} {label_b}OPTIONAL2{reset}
- pick one {default}(default: {default_value}None{default}){reset}
+ pick one {default}(default: {reset}{interp}None{reset}{default}){reset}
{long_b}--optional3{reset} {label_b}{{X,Y,Z}}{reset}
- {long_b}--optional4{reset} {label_b}{{X,Y,Z}}{reset} pick one {default}(default: {default_value}None{default}){reset}
- {long_b}--optional5{reset} {label_b}{{X,Y,Z}}{reset} pick one {default}(default: {default_value}None{default}){reset}
- {long_b}--optional6{reset} {label_b}{{X,Y,Z}}{reset} pick one {default}(default: {default_value}None{default}){reset}
+ {long_b}--optional4{reset} {label_b}{{X,Y,Z}}{reset} pick one {default}(default: {reset}{interp}None{reset}{default}){reset}
+ {long_b}--optional5{reset} {label_b}{{X,Y,Z}}{reset} pick one {default}(default: {reset}{interp}None{reset}{default}){reset}
+ {long_b}--optional6{reset} {label_b}{{X,Y,Z}}{reset} pick one {default}(default: {reset}{interp}None{reset}{default}){reset}
{short_b}-p{reset}, {long_b}--optional7{reset} {label_b}{{Aaaaa,Bbbbb,Ccccc,Ddddd}}{reset}
- pick one {default}(default: {default_value}None{default}){reset}
+ pick one {default}(default: {reset}{interp}None{reset}{default}){reset}
+ {long_b}--optional8{reset} {label_b}X{reset} among {interp}A, B, C{reset}, default is {interp}A{reset}
{short_b}+f{reset} {label_b}F{reset}
{long_b}++bar{reset} {label_b}BAR{reset}
{long_b}-+baz{reset} {label_b}BAZ{reset}
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index 0e7fd62c28a065..e512c08752a02a 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -1938,7 +1938,8 @@ def testfunc(n):
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertIn("_CALL_TUPLE_1", uops)
- self.assertIn("_POP_TOP_NOP", uops)
+ # Re-enable later gh-134584
+ # self.assertIn("_POP_TOP_NOP", uops)
def test_call_str_1(self):
def testfunc(n):
diff --git a/Misc/NEWS.d/next/C_API/2025-12-03-14-41-07.gh-issue-141049.VuAUe2.rst b/Misc/NEWS.d/next/C_API/2025-12-03-14-41-07.gh-issue-141049.VuAUe2.rst
new file mode 100644
index 00000000000000..b0fcd24d414270
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2025-12-03-14-41-07.gh-issue-141049.VuAUe2.rst
@@ -0,0 +1,5 @@
+:c:func:`!_PyObject_CallMethodId`, :c:func:`!_PyObject_GetAttrId` and
+:c:func:`!_PyUnicode_FromId` are deprecated since 3.15 and will be removed in
+3.20. Instead, use :c:func:`PyUnicode_FromString()` and cache the result in
+the module state, then call :c:func:`PyObject_CallMethod` or
+:c:func:`PyObject_GetAttr`. Patch by Victor Stinner.
diff --git a/Misc/NEWS.d/next/Library/2025-11-28-08-25-19.gh-issue-141939.BXPnFj.rst b/Misc/NEWS.d/next/Library/2025-11-28-08-25-19.gh-issue-141939.BXPnFj.rst
new file mode 100644
index 00000000000000..1015d90c501fd0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-11-28-08-25-19.gh-issue-141939.BXPnFj.rst
@@ -0,0 +1 @@
+Add color to all interpolated values in :mod:`argparse` help, like ``%(default)s`` or ``%(choices)s``. Patch by Alex Prengère.
diff --git a/Objects/call.c b/Objects/call.c
index c69015abfb3ed5..41d075caf11ce6 100644
--- a/Objects/call.c
+++ b/Objects/call.c
@@ -708,7 +708,10 @@ _PyObject_CallMethodId(PyObject *obj, _Py_Identifier *name,
return null_error(tstate);
}
+_Py_COMP_DIAG_PUSH
+_Py_COMP_DIAG_IGNORE_DEPR_DECLS
PyObject *callable = _PyObject_GetAttrId(obj, name);
+_Py_COMP_DIAG_POP
if (callable == NULL) {
return NULL;
}
diff --git a/Objects/object.c b/Objects/object.c
index 36a37bb0bbea4d..4fc692bb02940e 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1263,7 +1263,10 @@ PyObject *
_PyObject_GetAttrId(PyObject *v, _Py_Identifier *name)
{
PyObject *result;
+_Py_COMP_DIAG_PUSH
+_Py_COMP_DIAG_IGNORE_DEPR_DECLS
PyObject *oname = _PyUnicode_FromId(name); /* borrowed */
+_Py_COMP_DIAG_POP
if (!oname)
return NULL;
result = PyObject_GetAttr(v, oname);
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index cb39a23d951077..3e7b7d5594f8f7 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -4068,13 +4068,13 @@ dummy_func(
DEOPT_IF(callable_o != (PyObject *)&PyTuple_Type);
}
- op(_CALL_TUPLE_1, (callable, null, arg -- res, a)) {
+ op(_CALL_TUPLE_1, (callable, null, arg -- res)) {
PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg);
assert(oparg == 1);
STAT_INC(CALL, hit);
PyObject *res_o = PySequence_Tuple(arg_o);
- a = arg;
+ PyStackRef_CLOSE(arg);
INPUTS_DEAD();
ERROR_IF(res_o == NULL);
res = PyStackRef_FromPyObjectSteal(res_o);
@@ -4086,7 +4086,6 @@ dummy_func(
_GUARD_NOS_NULL +
_GUARD_CALLABLE_TUPLE_1 +
_CALL_TUPLE_1 +
- POP_TOP +
_CHECK_PERIODIC_AT_END;
op(_CHECK_AND_ALLOCATE_OBJECT, (type_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
@@ -5454,12 +5453,6 @@ dummy_func(
}
}
- label(pop_3_error) {
- stack_pointer -= 3;
- assert(WITHIN_STACK_BOUNDS());
- goto error;
- }
-
label(pop_2_error) {
stack_pointer -= 2;
assert(WITHIN_STACK_BOUNDS());
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 598fea4dfbf692..9c2bffb35abbfb 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -12888,12 +12888,11 @@
break;
}
- case _CALL_TUPLE_1_r32: {
+ case _CALL_TUPLE_1_r31: {
CHECK_CURRENT_CACHED_VALUES(3);
assert(WITHIN_STACK_BOUNDS_WITH_CACHE());
_PyStackRef arg;
_PyStackRef res;
- _PyStackRef a;
_PyStackRef _stack_item_0 = _tos_cache0;
_PyStackRef _stack_item_1 = _tos_cache1;
_PyStackRef _stack_item_2 = _tos_cache2;
@@ -12910,19 +12909,23 @@
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *res_o = PySequence_Tuple(arg_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
- a = arg;
+ stack_pointer += -1;
+ ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ PyStackRef_CLOSE(arg);
+ stack_pointer = _PyFrame_GetStackPointer(frame);
if (res_o == NULL) {
- stack_pointer += -3;
+ stack_pointer += -2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
}
res = PyStackRef_FromPyObjectSteal(res_o);
- _tos_cache1 = a;
_tos_cache0 = res;
+ _tos_cache1 = PyStackRef_ZERO_BITS;
_tos_cache2 = PyStackRef_ZERO_BITS;
- SET_CURRENT_CACHED_VALUES(2);
- stack_pointer += -3;
+ SET_CURRENT_CACHED_VALUES(1);
+ stack_pointer += -2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
assert(WITHIN_STACK_BOUNDS_WITH_CACHE());
break;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index a0e6fd2d7cba27..e72c621f10d340 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -4040,8 +4040,6 @@
_PyStackRef callable;
_PyStackRef arg;
_PyStackRef res;
- _PyStackRef a;
- _PyStackRef value;
/* Skip 1 cache entry */
/* Skip 2 cache entries */
// _GUARD_NOS_NULL
@@ -4072,24 +4070,21 @@
_PyFrame_SetStackPointer(frame, stack_pointer);
PyObject *res_o = PySequence_Tuple(arg_o);
stack_pointer = _PyFrame_GetStackPointer(frame);
- a = arg;
- if (res_o == NULL) {
- JUMP_TO_LABEL(pop_3_error);
- }
- res = PyStackRef_FromPyObjectSteal(res_o);
- }
- // _POP_TOP
- {
- value = a;
- stack_pointer[-3] = res;
- stack_pointer += -2;
+ stack_pointer += -1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
- PyStackRef_XCLOSE(value);
+ PyStackRef_CLOSE(arg);
stack_pointer = _PyFrame_GetStackPointer(frame);
+ if (res_o == NULL) {
+ JUMP_TO_LABEL(pop_2_error);
+ }
+ res = PyStackRef_FromPyObjectSteal(res_o);
}
// _CHECK_PERIODIC_AT_END
{
+ stack_pointer[-2] = res;
+ stack_pointer += -1;
+ ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
int err = check_periodics(tstate);
stack_pointer = _PyFrame_GetStackPointer(frame);
@@ -11863,13 +11858,6 @@ JUMP_TO_LABEL(error);
#endif /* _Py_TAIL_CALL_INTERP */
/* BEGIN LABELS */
- LABEL(pop_3_error)
- {
- stack_pointer -= 3;
- assert(WITHIN_STACK_BOUNDS());
- JUMP_TO_LABEL(error);
- }
-
LABEL(pop_2_error)
{
stack_pointer -= 2;
diff --git a/Python/import.c b/Python/import.c
index 4dd247fac27654..2860ae032dfe29 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -2003,7 +2003,7 @@ import_run_modexport(PyThreadState *tstate, PyModExportFunction ex0,
if (!PyErr_Occurred()) {
PyErr_Format(
PyExc_SystemError,
- "slot export function for module %s failed without setting an exception",
+ "module export hook for module %R failed without setting an exception",
info->name);
}
return NULL;
@@ -2011,7 +2011,7 @@ import_run_modexport(PyThreadState *tstate, PyModExportFunction ex0,
if (PyErr_Occurred()) {
PyErr_Format(
PyExc_SystemError,
- "slot export function for module %s raised unreported exception",
+ "module export hook for module %R raised unreported exception",
info->name);
}
PyObject *result = PyModule_FromSlotsAndSpec(slots, spec);
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 1a8a4392187eca..b2fa7d01e8f6c2 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -522,7 +522,6 @@ static py_tail_call_funcptr instruction_funcptr_handler_table[256];
static py_tail_call_funcptr instruction_funcptr_tracing_table[256];
-Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_3_error(TAIL_CALL_PARAMS);
Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_2_error(TAIL_CALL_PARAMS);
Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_1_error(TAIL_CALL_PARAMS);
Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS);
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 9e767464500d45..61f2dd1e454148 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -1097,7 +1097,7 @@ dummy_func(void) {
}
}
- op(_CALL_TUPLE_1, (callable, null, arg -- res, a)) {
+ op(_CALL_TUPLE_1, (callable, null, arg -- res)) {
if (sym_matches_type(arg, &PyTuple_Type)) {
// e.g. tuple((1, 2)) or tuple(foo) where foo is known to be a tuple
// Note: we must strip the reference information because it goes
@@ -1107,7 +1107,6 @@ dummy_func(void) {
else {
res = sym_new_type(ctx, &PyTuple_Type);
}
- a = arg;
}
op(_GUARD_TOS_LIST, (tos -- tos)) {
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index c3f122215ed7bd..7d95bce7679815 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -2882,7 +2882,6 @@
case _CALL_TUPLE_1: {
JitOptRef arg;
JitOptRef res;
- JitOptRef a;
arg = stack_pointer[-1];
if (sym_matches_type(arg, &PyTuple_Type)) {
res = PyJitRef_StripReferenceInfo(arg);
@@ -2890,11 +2889,9 @@
else {
res = sym_new_type(ctx, &PyTuple_Type);
}
- a = arg;
- CHECK_STACK_BOUNDS(-1);
+ CHECK_STACK_BOUNDS(-2);
stack_pointer[-3] = res;
- stack_pointer[-2] = a;
- stack_pointer += -1;
+ stack_pointer += -2;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
break;
}
diff --git a/Python/remote_debug.h b/Python/remote_debug.h
index 1c02870d3af475..d3932a3fd1e4d6 100644
--- a/Python/remote_debug.h
+++ b/Python/remote_debug.h
@@ -722,6 +722,11 @@ search_linux_map_for_section(proc_handle_t *handle, const char* secname, const c
}
const char *path = line + path_pos;
+ if (path[0] == '[' && path[strlen(path)-1] == ']') {
+ // Skip [heap], [stack], [anon:cpython:pymalloc], etc.
+ continue;
+ }
+
const char *filename = strrchr(path, '/');
if (filename) {
filename++; // Move past the '/'