[Python-checkins] GH-121459: Streamline PyObject* to PyStackRef conversions by disallowing NULL pointers. (GH-124894)

2024-10-07 Thread markshannon
https://github.com/python/cpython/commit/d1453f60c2d289d74d535874e07741745b023c90
commit: d1453f60c2d289d74d535874e07741745b023c90
branch: main
author: Mark Shannon 
committer: markshannon 
date: 2024-10-07T18:13:04+01:00
summary:

GH-121459: Streamline PyObject* to PyStackRef conversions by disallowing NULL 
pointers. (GH-124894)

files:
M Include/internal/pycore_stackref.h
M Python/bytecodes.c
M Python/executor_cases.c.h
M Python/generated_cases.c.h

diff --git a/Include/internal/pycore_stackref.h 
b/Include/internal/pycore_stackref.h
index b5b6993812057d..cf6dd22cfb18d1 100644
--- a/Include/internal/pycore_stackref.h
+++ b/Include/internal/pycore_stackref.h
@@ -111,7 +111,8 @@ PyStackRef_AsPyObjectBorrow(_PyStackRef stackref)
 static inline PyObject *
 PyStackRef_AsPyObjectSteal(_PyStackRef stackref)
 {
-if (!PyStackRef_IsNull(stackref) && PyStackRef_IsDeferred(stackref)) {
+assert(!PyStackRef_IsNull(stackref));
+if (PyStackRef_IsDeferred(stackref)) {
 return Py_NewRef(PyStackRef_AsPyObjectBorrow(stackref));
 }
 return PyStackRef_AsPyObjectBorrow(stackref);
@@ -131,9 +132,10 @@ PyStackRef_AsPyObjectSteal(_PyStackRef stackref)
 static inline _PyStackRef
 _PyStackRef_FromPyObjectSteal(PyObject *obj)
 {
+assert(obj != NULL);
 // Make sure we don't take an already tagged value.
 assert(((uintptr_t)obj & Py_TAG_BITS) == 0);
-unsigned int tag = (obj == NULL || _Py_IsImmortal(obj)) ? 
(Py_TAG_DEFERRED) : Py_TAG_PTR;
+unsigned int tag = _Py_IsImmortal(obj) ? (Py_TAG_DEFERRED) : Py_TAG_PTR;
 return ((_PyStackRef){.bits = ((uintptr_t)(obj)) | tag});
 }
 #   define PyStackRef_FromPyObjectSteal(obj) 
_PyStackRef_FromPyObjectSteal(_PyObject_CAST(obj))
@@ -193,6 +195,7 @@ PyStackRef_FromPyObjectImmortal(PyObject *obj)
 #   define PyStackRef_CLOSE(REF)\
 do {\
 _PyStackRef _close_tmp = (REF); \
+assert(!PyStackRef_IsNull(_close_tmp)); \
 if (!PyStackRef_IsDeferred(_close_tmp)) {   \
 Py_DECREF(PyStackRef_AsPyObjectBorrow(_close_tmp)); \
 }   \
@@ -214,10 +217,11 @@ PyStackRef_FromPyObjectImmortal(PyObject *obj)
 static inline _PyStackRef
 PyStackRef_DUP(_PyStackRef stackref)
 {
+assert(!PyStackRef_IsNull(stackref));
 if (PyStackRef_IsDeferred(stackref)) {
-assert(PyStackRef_IsNull(stackref) ||
-_Py_IsImmortal(PyStackRef_AsPyObjectBorrow(stackref)) ||
-
_PyObject_HasDeferredRefcount(PyStackRef_AsPyObjectBorrow(stackref)));
+assert(_Py_IsImmortal(PyStackRef_AsPyObjectBorrow(stackref)) ||
+   
_PyObject_HasDeferredRefcount(PyStackRef_AsPyObjectBorrow(stackref))
+);
 return stackref;
 }
 Py_INCREF(PyStackRef_AsPyObjectBorrow(stackref));
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index a0edf17d747e77..228d82173e6126 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1922,9 +1922,10 @@ dummy_func(
 DECREF_INPUTS();
 ERROR_IF(super == NULL, error);
 PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2);
-attr = PyStackRef_FromPyObjectSteal(PyObject_GetAttr(super, name));
+PyObject *attr_o = PyObject_GetAttr(super, name);
 Py_DECREF(super);
-ERROR_IF(PyStackRef_IsNull(attr), error);
+ERROR_IF(attr_o == NULL, error);
+attr = PyStackRef_FromPyObjectSteal(attr_o);
 null = PyStackRef_NULL;
 }
 
@@ -2740,9 +2741,10 @@ dummy_func(
 
 inst(GET_ITER, (iterable -- iter)) {
 /* before: [obj]; after [getiter(obj)] */
-iter = 
PyStackRef_FromPyObjectSteal(PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)));
+PyObject *iter_o = 
PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable));
 DECREF_INPUTS();
-ERROR_IF(PyStackRef_IsNull(iter), error);
+ERROR_IF(iter_o == NULL, error);
+iter = PyStackRef_FromPyObjectSteal(iter_o);
 }
 
 inst(GET_YIELD_FROM_ITER, (iterable -- iter)) {
@@ -3052,16 +3054,18 @@ dummy_func(
 PyObject *owner_o = PyStackRef_AsPyObjectSteal(owner);
 PyObject *name = _Py_SpecialMethods[oparg].name;
 PyObject *self_or_null_o;
-attr = 
PyStackRef_FromPyObjectSteal(_PyObject_LookupSpecialMethod(owner_o, name, 
&self_or_null_o));
-if (PyStackRef_IsNull(attr)) {
+PyObject *attr_o = _PyObject_LookupSpecialMethod(owner_o, name, 
&self_or_null_o);
+if (attr_o == NULL) {
 if (!_PyErr_Occurred(tstate)) {
 _PyErr_Format(tstate, PyExc_TypeError,
   _Py_SpecialMethods[oparg].error,
 

[Python-checkins] gh-124956: Use `#undef` for temp macros in `_csv.c` (#124957)

2024-10-07 Thread kumaraditya303
https://github.com/python/cpython/commit/51d426dc033ef9208c0244a569f3e816e4c328c9
commit: 51d426dc033ef9208c0244a569f3e816e4c328c9
branch: main
author: sobolevn 
committer: kumaraditya303 
date: 2024-10-07T23:05:56+05:30
summary:

gh-124956: Use `#undef` for temp macros in `_csv.c` (#124957)

files:
M Modules/_csv.c

diff --git a/Modules/_csv.c b/Modules/_csv.c
index a623ea449da779..913560ce4a0ee3 100644
--- a/Modules/_csv.c
+++ b/Modules/_csv.c
@@ -367,6 +367,8 @@ static struct PyMemberDef Dialect_memberlist[] = {
 { NULL }
 };
 
+#undef D_OFF
+
 static PyGetSetDef Dialect_getsetlist[] = {
 { "delimiter",  (getter)Dialect_get_delimiter},
 { "escapechar", (getter)Dialect_get_escapechar},
@@ -502,6 +504,7 @@ dialect_new(PyTypeObject *type, PyObject *args, PyObject 
*kwargs)
 DIALECT_GETATTR(skipinitialspace, "skipinitialspace");
 DIALECT_GETATTR(strict, "strict");
 }
+#undef DIALECT_GETATTR
 
 /* check types and convert to C values */
 #define DIASET(meth, name, target, src, dflt) \
@@ -515,6 +518,7 @@ dialect_new(PyTypeObject *type, PyObject *args, PyObject 
*kwargs)
 DIASET(_set_int, "quoting", &self->quoting, quoting, QUOTE_MINIMAL);
 DIASET(_set_bool, "skipinitialspace", &self->skipinitialspace, 
skipinitialspace, false);
 DIASET(_set_bool, "strict", &self->strict, strict, false);
+#undef DIASET
 
 /* validate options */
 if (dialect_check_quoting(self->quoting))
@@ -1026,6 +1030,8 @@ static struct PyMemberDef Reader_memberlist[] = {
 { NULL }
 };
 
+#undef R_OFF
+
 
 static PyType_Slot Reader_Type_slots[] = {
 {Py_tp_doc, (char*)Reader_Type_doc},
@@ -1441,6 +1447,8 @@ static struct PyMemberDef Writer_memberlist[] = {
 { NULL }
 };
 
+#undef W_OFF
+
 static int
 Writer_traverse(WriterObj *self, visitproc visit, void *arg)
 {

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-124182: Explain naming rules for struct sequence types (#124335)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/3287c834e5370294e310450115290979aac06efa
commit: 3287c834e5370294e310450115290979aac06efa
branch: main
author: ffelixg <[email protected]>
committer: vstinner 
date: 2024-10-07T19:52:34+02:00
summary:

gh-124182: Explain naming rules for struct sequence types (#124335)

files:
M Doc/c-api/tuple.rst

diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst
index 7a8a6134282ade..815afddad19df1 100644
--- a/Doc/c-api/tuple.rst
+++ b/Doc/c-api/tuple.rst
@@ -167,7 +167,8 @@ type.
 
.. c:member:: const char *name
 
-  Name of the struct sequence type.
+  Fully qualified name of the type; null-terminated UTF-8 encoded.
+  The name must contain the module name.
 
.. c:member:: const char *doc
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] Use _PyLong_GetOne() and _PyLong_GetZero() in long_invmod() (#125044)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/03775472cc69e150ced22dc30334a7a202fc0380
commit: 03775472cc69e150ced22dc30334a7a202fc0380
branch: main
author: Victor Stinner 
committer: vstinner 
date: 2024-10-07T19:54:42+02:00
summary:

Use _PyLong_GetOne() and _PyLong_GetZero() in long_invmod() (#125044)

These functions cannot fail.

files:
M Objects/longobject.c

diff --git a/Objects/longobject.c b/Objects/longobject.c
index 6ca8d449bcf4a2..4e948940485730 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -4828,21 +4828,12 @@ long_divmod(PyObject *a, PyObject *b)
 static PyLongObject *
 long_invmod(PyLongObject *a, PyLongObject *n)
 {
-PyLongObject *b, *c;
-
 /* Should only ever be called for positive n */
 assert(_PyLong_IsPositive(n));
 
-b = (PyLongObject *)PyLong_FromLong(1L);
-if (b == NULL) {
-return NULL;
-}
-c = (PyLongObject *)PyLong_FromLong(0L);
-if (c == NULL) {
-Py_DECREF(b);
-return NULL;
-}
 Py_INCREF(a);
+PyLongObject *b = (PyLongObject *)Py_NewRef(_PyLong_GetOne());
+PyLongObject *c = (PyLongObject *)Py_NewRef(_PyLong_GetZero());
 Py_INCREF(n);
 
 /* references now owned: a, b, c, n */

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.12] gh-125018: Add importlib.metadata semantic link targets (GH-125027) (#125048)

2024-10-07 Thread ncoghlan
https://github.com/python/cpython/commit/fa32f007f0ee702fbad970961a0258e8a3e92de8
commit: fa32f007f0ee702fbad970961a0258e8a3e92de8
branch: 3.12
author: Alyssa Coghlan 
committer: ncoghlan 
date: 2024-10-07T14:24:45Z
summary:

[3.12] gh-125018: Add importlib.metadata semantic link targets (GH-125027) 
(#125048)

This allows direct intersphinx references to APIs via references
like `` :func:`importlib.metadata.version` ``.

-

(cherry picked from commit cda3b5a576412a8671bbe4c68bb792ec14f1a4b1)

Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) 

Co-authored-by: Adam Turner <[email protected]>

files:
A Misc/NEWS.d/next/Documentation/2024-10-07-00-31-17.gh-issue-125018.yKnymn.rst
M Doc/library/importlib.metadata.rst

diff --git a/Doc/library/importlib.metadata.rst 
b/Doc/library/importlib.metadata.rst
index 3f407ba48a4d8e..d85b3c68626a89 100644
--- a/Doc/library/importlib.metadata.rst
+++ b/Doc/library/importlib.metadata.rst
@@ -100,6 +100,13 @@ You can also get a :ref:`distribution's version number 
`, list its
 :ref:`requirements`.
 
 
+.. exception:: PackageNotFoundError
+
+   Subclass of :class:`ModuleNotFoundError` raised by several functions in this
+   module when queried for a distribution package which is not installed in the
+   current Python environment.
+
+
 Functional API
 ==
 
@@ -111,31 +118,53 @@ This package provides the following functionality via its 
public API.
 Entry points
 
 
-The ``entry_points()`` function returns a collection of entry points.
-Entry points are represented by ``EntryPoint`` instances;
-each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and
-a ``.load()`` method to resolve the value.  There are also ``.module``,
-``.attr``, and ``.extras`` attributes for getting the components of the
-``.value`` attribute.
+.. function:: entry_points(**select_params)
+
+   Returns a :class:`EntryPoints` instance describing entry points for the
+   current environment. Any given keyword parameters are passed to the
+   :meth:`!~EntryPoints.select` method for comparison to the attributes of
+   the individual entry point definitions.
+
+   Note: it is not currently possible to query for entry points based on
+   their :attr:`!EntryPoint.dist` attribute (as different 
:class:`!Distribution`
+   instances do not currently compare equal, even if they have the same 
attributes)
+
+.. class:: EntryPoints
+
+   Details of a collection of installed entry points.
+
+   Also provides a ``.groups`` attribute that reports all identifed entry
+   point groups, and a ``.names`` attribute that reports all identified entry
+   point names.
+
+.. class:: EntryPoint
+
+   Details of an installed entry point.
+
+   Each :class:`!EntryPoint` instance has ``.name``, ``.group``, and ``.value``
+   attributes and a ``.load()`` method to resolve the value. There are also
+   ``.module``, ``.attr``, and ``.extras`` attributes for getting the
+   components of the ``.value`` attribute, and ``.dist`` for obtaining
+   information regarding the distribution package that provides the entry 
point.
 
 Query all entry points::
 
 >>> eps = entry_points()  # doctest: +SKIP
 
-The ``entry_points()`` function returns an ``EntryPoints`` object,
-a collection of all ``EntryPoint`` objects with ``names`` and ``groups``
+The :func:`!entry_points` function returns a :class:`!EntryPoints` object,
+a collection of all :class:`!EntryPoint` objects with ``names`` and ``groups``
 attributes for convenience::
 
 >>> sorted(eps.groups)  # doctest: +SKIP
 ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 
'egg_info.writers', 'setuptools.installation']
 
-``EntryPoints`` has a ``select`` method to select entry points
+:class:`!EntryPoints` has a :meth:`!~EntryPoints.select` method to select 
entry points
 matching specific properties. Select entry points in the
 ``console_scripts`` group::
 
 >>> scripts = eps.select(group='console_scripts')  # doctest: +SKIP
 
-Equivalently, since ``entry_points`` passes keyword arguments
+Equivalently, since :func:`!entry_points` passes keyword arguments
 through to select::
 
 >>> scripts = entry_points(group='console_scripts')  # doctest: +SKIP
@@ -187,31 +216,41 @@ for compatibility options.
 Distribution metadata
 -
 
-Every `Distribution Package 
`_ 
includes some metadata,
-which you can extract using the
-``metadata()`` function::
+.. function:: metadata(distribution_name)
+
+   Return the distribution metadata corresponding to the named
+   distribution package as a :class:`PackageMetadata` instance.
+
+   Raises :exc:`PackageNotFoundError` if the named distribution
+   package is not installed in the current Python environment.
+
+.. class:: PackageMetadata
+
+   A concrete implementation of the
+`PackageMetadata protocol 


[Python-checkins] [3.13] gh-112804: Clamping timeout value for _PySemaphore_PlatformWait (gh-124914) (gh-124991)

2024-10-07 Thread corona10
https://github.com/python/cpython/commit/80ba17a3dd383882622cf303a2f2fb19cbaa0ee6
commit: 80ba17a3dd383882622cf303a2f2fb19cbaa0ee6
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: corona10 
date: 2024-10-07T16:53:32Z
summary:

[3.13] gh-112804: Clamping timeout value for _PySemaphore_PlatformWait 
(gh-124914) (gh-124991)

gh-112804: Clamping timeout value for _PySemaphore_PlatformWait (gh-124914)

* gh-112804: Clamping timeout value for _PySemaphore_PlatformWait

* Address code review

* nit
(cherry picked from commit a5fc50994a3fae46d0c3d496c4e1d5e00548a1b8)

Co-authored-by: Donghee Na 

files:
M Python/parking_lot.c

diff --git a/Python/parking_lot.c b/Python/parking_lot.c
index 841b1d71ea16cb..a7e9760e35d87a 100644
--- a/Python/parking_lot.c
+++ b/Python/parking_lot.c
@@ -102,7 +102,14 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, PyTime_t 
timeout)
 millis = INFINITE;
 }
 else {
-millis = (DWORD) (timeout / 100);
+PyTime_t div = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT);
+// Prevent overflow with clamping the result
+if ((PyTime_t)PY_DWORD_MAX < div) {
+millis = PY_DWORD_MAX;
+}
+else {
+millis = (DWORD) div;
+}
 }
 wait = WaitForSingleObjectEx(sema->platform_sem, millis, FALSE);
 if (wait == WAIT_OBJECT_0) {

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.12] gh-125018: Fix role syntax (GH-125050) (#125055)

2024-10-07 Thread AA-Turner
https://github.com/python/cpython/commit/d2b307171ce899f6bd17f6ba570f5172ff9a390a
commit: d2b307171ce899f6bd17f6ba570f5172ff9a390a
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: AA-Turner <[email protected]>
date: 2024-10-07T16:58:47Z
summary:

[3.12] gh-125018: Fix role syntax (GH-125050) (#125055)

gh-125018: Fix role syntax (GH-125050)
(cherry picked from commit 10094a533a947b72d01ed8195dcf540f2e7820ea)

Co-authored-by: Adam Turner <[email protected]>

files:
M Doc/library/importlib.metadata.rst

diff --git a/Doc/library/importlib.metadata.rst 
b/Doc/library/importlib.metadata.rst
index d85b3c68626a89..14414d973f20c1 100644
--- a/Doc/library/importlib.metadata.rst
+++ b/Doc/library/importlib.metadata.rst
@@ -122,7 +122,7 @@ Entry points
 
Returns a :class:`EntryPoints` instance describing entry points for the
current environment. Any given keyword parameters are passed to the
-   :meth:`!~EntryPoints.select` method for comparison to the attributes of
+   :meth:`!select` method for comparison to the attributes of
the individual entry point definitions.
 
Note: it is not currently possible to query for entry points based on
@@ -158,7 +158,7 @@ attributes for convenience::
 >>> sorted(eps.groups)  # doctest: +SKIP
 ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 
'egg_info.writers', 'setuptools.installation']
 
-:class:`!EntryPoints` has a :meth:`!~EntryPoints.select` method to select 
entry points
+:class:`!EntryPoints` has a :meth:`!select` method to select entry points
 matching specific properties. Select entry points in the
 ``console_scripts`` group::
 
@@ -230,7 +230,7 @@ Distribution metadata
 `PackageMetadata protocol 
`_.
 
 In addition to providing the defined protocol methods and attributes, 
subscripting
-the instance is equivalent to calling the :meth:`!~PackageMetadata.get` 
method.
+the instance is equivalent to calling the :meth:`!get` method.
 
 Every `Distribution Package 
`_
 includes some metadata, which you can extract using the :func:`!metadata` 
function::
@@ -243,7 +243,7 @@ the values are returned unparsed from the distribution 
metadata::
 >>> wheel_metadata['Requires-Python']  # doctest: +SKIP
 '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
 
-:class:`PackageMetadata` also presents a :attr:`!~PackageMetadata.json` 
attribute that returns
+:class:`PackageMetadata` also presents a :attr:`!json` attribute that returns
 all the metadata in a JSON-compatible form per :PEP:`566`::
 
 >>> wheel_metadata.json['requires_python']
@@ -329,7 +329,7 @@ Once you have the file, you can also read its contents::
 return s.encode('utf-8')
 return s
 
-You can also use the :meth:`!~PackagePath.locate` method to get the absolute
+You can also use the :meth:`!locate` method to get the absolute
 path to the file::
 
 >>> util.locate()  # doctest: +SKIP

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.12] gh-122392: IDLE - Fix overlapping lines in browsers (GH-122392) (GH-124975) (#125062)

2024-10-07 Thread terryjreedy
https://github.com/python/cpython/commit/79b4c9d99a070a6cdcb810148e514864d8958bc1
commit: 79b4c9d99a070a6cdcb810148e514864d8958bc1
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: terryjreedy 
date: 2024-10-07T19:23:08Z
summary:

[3.12] gh-122392: IDLE - Fix overlapping lines in browsers (GH-122392) 
(GH-124975) (#125062)

gh-122392: IDLE - Fix overlapping lines in browsers (GH-122392) (GH-124975)

Increase currently inadequate vertical spacing for the IDLE browsers (path,
module, and stack) on high-resolution monitors.
-

(cherry picked from commit c5df1cb7bde7e86f046196b0e34a0b90f8fc11de)

Co-authored-by: Zhikang Yan <[email protected]>
Co-authored-by: Terry Jan Reedy 

files:
A Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst
M Lib/idlelib/tree.py

diff --git a/Lib/idlelib/tree.py b/Lib/idlelib/tree.py
index 0726d7e23660f6..182ce7189614da 100644
--- a/Lib/idlelib/tree.py
+++ b/Lib/idlelib/tree.py
@@ -83,6 +83,8 @@ def wheel_event(event, widget=None):
 
 class TreeNode:
 
+dy = 0
+
 def __init__(self, canvas, parent, item):
 self.canvas = canvas
 self.parent = parent
@@ -199,23 +201,22 @@ def update(self):
 
 def draw(self, x, y):
 # XXX This hard-codes too many geometry constants!
-dy = 20
 self.x, self.y = x, y
 self.drawicon()
 self.drawtext()
 if self.state != 'expanded':
-return y + dy
+return y + TreeNode.dy
 # draw children
 if not self.children:
 sublist = self.item._GetSubList()
 if not sublist:
 # _IsExpandable() was mistaken; that's allowed
-return y+17
+return y + TreeNode.dy
 for item in sublist:
 child = self.__class__(self.canvas, self, item)
 self.children.append(child)
 cx = x+20
-cy = y + dy
+cy = y + TreeNode.dy
 cylast = 0
 for child in self.children:
 cylast = cy
@@ -289,6 +290,11 @@ def drawtext(self):
 self.label.bind("", lambda e: wheel_event(e, 
self.canvas))
 self.label.bind("", lambda e: wheel_event(e, 
self.canvas))
 self.text_id = id
+if TreeNode.dy == 0:
+# The first row doesn't matter what the dy is, just measure its
+# size to get the value of the subsequent dy
+coords = self.canvas.bbox(id)
+TreeNode.dy = max(20, coords[3] - coords[1] - 3)
 
 def select_or_edit(self, event=None):
 if self.selected and self.item.IsEditable():
diff --git 
a/Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst 
b/Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst
new file mode 100644
index 00..541f6212794ef2
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst
@@ -0,0 +1,2 @@
+Increase currently inadequate vertical spacing for the IDLE browsers (path,
+module, and stack) on high-resolution monitors.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-95468: Add more tests for "--" (double dash) in test_argparse (GH-124274) (GH-125068)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/2a40dda89de418cd563c3fc2144f3831b6feece7
commit: 2a40dda89de418cd563c3fc2144f3831b6feece7
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-07T21:40:47Z
summary:

[3.13] gh-95468: Add more tests for "--" (double dash) in test_argparse 
(GH-124274) (GH-125068)

(cherry picked from commit baa3550bc3a119f41cc4eaed5373f9d695208e8e)

Co-authored-by: Serhiy Storchaka 

files:
M Lib/test/test_argparse.py

diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index fe71e8a2a95154..39023a8789267f 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -5993,7 +5993,9 @@ def test_zero_or_more_optional(self):
 args = parser.parse_args([])
 self.assertEqual(NS(x=[]), args)
 
-def test_double_dash(self):
+
+class TestDoubleDash(TestCase):
+def test_single_argument_option(self):
 parser = argparse.ArgumentParser(exit_on_error=False)
 parser.add_argument('-f', '--foo')
 parser.add_argument('bar', nargs='*')
@@ -6017,6 +6019,7 @@ def test_double_dash(self):
 args = parser.parse_args(['a', '--', 'b', '--', 'c', '--foo', 'd'])
 self.assertEqual(NS(foo=None, bar=['a', 'b', '--', 'c', '--foo', 
'd']), args)
 
+def test_multiple_argument_option(self):
 parser = argparse.ArgumentParser(exit_on_error=False)
 parser.add_argument('-f', '--foo', nargs='*')
 parser.add_argument('bar', nargs='*')
@@ -6039,6 +6042,7 @@ def test_double_dash(self):
 self.assertEqual(NS(foo=['c'], bar=['a', 'b']), args)
 self.assertEqual(argv, ['--', 'd'])
 
+def test_multiple_double_dashes(self):
 parser = argparse.ArgumentParser(exit_on_error=False)
 parser.add_argument('foo')
 parser.add_argument('bar', nargs='*')
@@ -6054,9 +6058,10 @@ def test_double_dash(self):
 args = parser.parse_args(['--', '--', 'a', '--', 'b', 'c'])
 self.assertEqual(NS(foo='--', bar=['a', '--', 'b', 'c']), args)
 
+def test_remainder(self):
 parser = argparse.ArgumentParser(exit_on_error=False)
 parser.add_argument('foo')
-parser.add_argument('bar', nargs=argparse.REMAINDER)
+parser.add_argument('bar', nargs='...')
 
 args = parser.parse_args(['--', 'a', 'b', 'c'])
 self.assertEqual(NS(foo='a', bar=['b', 'c']), args)
@@ -6067,6 +6072,40 @@ def test_double_dash(self):
 args = parser.parse_args(['a', '--', 'b', '--', 'c'])
 self.assertEqual(NS(foo='a', bar=['b', '--', 'c']), args)
 
+parser = argparse.ArgumentParser(exit_on_error=False)
+parser.add_argument('--foo')
+parser.add_argument('bar', nargs='...')
+args = parser.parse_args(['--foo', 'a', '--', 'b', '--', 'c'])
+self.assertEqual(NS(foo='a', bar=['--', 'b', '--', 'c']), args)
+
+def test_subparser(self):
+parser = argparse.ArgumentParser(exit_on_error=False)
+parser.add_argument('foo')
+subparsers = parser.add_subparsers()
+parser1 = subparsers.add_parser('run')
+parser1.add_argument('-f')
+parser1.add_argument('bar', nargs='*')
+
+args = parser.parse_args(['x', 'run', 'a', 'b', '-f', 'c'])
+self.assertEqual(NS(foo='x', f='c', bar=['a', 'b']), args)
+args = parser.parse_args(['x', 'run', 'a', 'b', '--', '-f', 'c'])
+self.assertEqual(NS(foo='x', f=None, bar=['a', 'b', '-f', 'c']), args)
+args = parser.parse_args(['x', 'run', 'a', '--', 'b', '-f', 'c'])
+self.assertEqual(NS(foo='x', f=None, bar=['a', 'b', '-f', 'c']), args)
+args = parser.parse_args(['x', 'run', '--', 'a', 'b', '-f', 'c'])
+self.assertEqual(NS(foo='x', f=None, bar=['a', 'b', '-f', 'c']), args)
+args = parser.parse_args(['x', '--', 'run', 'a', 'b', '-f', 'c'])
+self.assertEqual(NS(foo='x', f='c', bar=['a', 'b']), args)
+args = parser.parse_args(['--', 'x', 'run', 'a', 'b', '-f', 'c'])
+self.assertEqual(NS(foo='x', f='c', bar=['a', 'b']), args)
+args = parser.parse_args(['x', 'run', '--', 'a', '--', 'b'])
+self.assertEqual(NS(foo='x', f=None, bar=['a', '--', 'b']), args)
+args = parser.parse_args(['x', '--', 'run', '--', 'a', '--', 'b'])
+self.assertEqual(NS(foo='x', f=None, bar=['a', '--', 'b']), args)
+self.assertRaisesRegex(argparse.ArgumentError,
+"invalid choice: '--'",
+parser.parse_args, ['--', 'x', '--', 'run', 'a', 'b'])
+
 
 # ===
 # parse_intermixed_args tests

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] Fix typos (#123775) (#123866)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/d432fa43b9078784a9306848354941bd0eee158f
commit: d432fa43b9078784a9306848354941bd0eee158f
branch: 3.13
author: Victor Stinner 
committer: vstinner 
date: 2024-10-07T23:44:31+02:00
summary:

[3.13] Fix typos (#123775) (#123866)

Fix typos (#123775)

(cherry picked from commit 9017b95ff2dcff16bcb0b0a609ed2b0daa845943)

Co-authored-by: algonell 

files:
M Android/testbed/app/src/main/c/main_activity.c
M Lib/enum.py
M Lib/test/decimaltestdata/ddFMA.decTest
M Lib/test/decimaltestdata/ddQuantize.decTest
M Lib/test/decimaltestdata/ddRemainder.decTest
M Lib/test/decimaltestdata/ddRemainderNear.decTest
M Lib/test/decimaltestdata/dqRemainder.decTest
M Lib/test/decimaltestdata/dqRemainderNear.decTest
M Lib/test/decimaltestdata/exp.decTest
M Lib/test/decimaltestdata/remainder.decTest
M Lib/test/decimaltestdata/remainderNear.decTest
M Lib/test/libregrtest/main.py
M Lib/test/libregrtest/utils.py
M Lib/test/mathdata/ieee754.txt
M Lib/test/pickletester.py
M Lib/test/test_class.py
M Lib/test/test_concurrent_futures/test_deadlock.py
M Lib/test/test_enum.py
M Lib/test/test_fractions.py
M Lib/test/test_free_threading/test_monitoring.py
M Lib/test/test_fstring.py
M Lib/test/test_getpath.py
M Lib/test/test_logging.py
M Lib/test/test_math.py
M Lib/test/test_msvcrt.py
M Lib/test/test_posix.py
M Lib/test/test_property.py
M Lib/test/test_queue.py
M Lib/test/test_socket.py
M Lib/test/test_string_literals.py
M Lib/test/test_zipfile/test_core.py
M Lib/test/test_zipimport.py
M Lib/turtledemo/sorting_animate.py
M Makefile.pre.in
M Misc/coverity_model.c
M Modules/_threadmodule.c
M Objects/mimalloc/bitmap.h
M Objects/mimalloc/init.c
M Objects/mimalloc/segment-map.c
M Tools/c-analyzer/c_parser/preprocessor/gcc.py
M configure
M configure.ac

diff --git a/Android/testbed/app/src/main/c/main_activity.c 
b/Android/testbed/app/src/main/c/main_activity.c
index 534709048990c6..69251332d48890 100644
--- a/Android/testbed/app/src/main/c/main_activity.c
+++ b/Android/testbed/app/src/main/c/main_activity.c
@@ -100,7 +100,7 @@ JNIEXPORT void JNICALL 
Java_org_python_testbed_PythonTestRunner_redirectStdioToL
 }
 
 
-// --- Python intialization 

+// --- Python initialization 
---
 
 static PyStatus set_config_string(
 JNIEnv *env, PyConfig *config, wchar_t **config_str, jstring value
diff --git a/Lib/enum.py b/Lib/enum.py
index c36fc75a24a239..0c2135f9040bac 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -442,7 +442,7 @@ def __setitem__(self, key, value):
 # accepts iterable as multiple arguments?
 value = t(auto_valued)
 except TypeError:
-# then pass them in singlely
+# then pass them in singly
 value = t(*auto_valued)
 self._member_names[key] = None
 if non_auto_store:
diff --git a/Lib/test/decimaltestdata/ddFMA.decTest 
b/Lib/test/decimaltestdata/ddFMA.decTest
index 9094fc015bde18..7f2e52303747f8 100644
--- a/Lib/test/decimaltestdata/ddFMA.decTest
+++ b/Lib/test/decimaltestdata/ddFMA.decTest
@@ -1663,7 +1663,7 @@ ddfma375087 fma  1  12345678 1E-33   -> 
12345678.0001 Inexac
 ddfma375088 fma  1  12345678 1E-34   -> 12345678.0001 
Inexact Rounded
 ddfma375089 fma  1  12345678 1E-35   -> 12345678.0001 
Inexact Rounded
 
--- desctructive subtraction (from remainder tests)
+-- destructive subtraction (from remainder tests)
 
 -- +++ some of these will be off-by-one remainder vs remainderNear
 
diff --git a/Lib/test/decimaltestdata/ddQuantize.decTest 
b/Lib/test/decimaltestdata/ddQuantize.decTest
index 91776201694dd6..e1c5674d9ac042 100644
--- a/Lib/test/decimaltestdata/ddQuantize.decTest
+++ b/Lib/test/decimaltestdata/ddQuantize.decTest
@@ -462,7 +462,7 @@ ddqua520 quantize   1.2341e359 -> 0E+359 Inexact Rounded
 ddqua521 quantize 123.4561e359 -> 0E+359 Inexact Rounded
 ddqua522 quantize   1.2341e359 -> 0E+359 Inexact Rounded
 ddqua523 quantize 123.4561e359 -> 0E+359 Inexact Rounded
--- next four are "won't fit" overfl
+-- next four are "won't fit" overflow
 ddqua526 quantize   1.234   1e-299 -> NaN Invalid_operation
 ddqua527 quantize 123.456   1e-299 -> NaN Invalid_operation
 ddqua528 quantize   1.234   1e-299 -> NaN Invalid_operation
diff --git a/Lib/test/decimaltestdata/ddRemainder.decTest 
b/Lib/test/decimaltestdata/ddRemainder.decTest
index 5bd1e32d01ef03..b1866d39a2868c 100644
--- a/Lib/test/decimaltestdata/ddRemainder.decTest
+++ b/Lib/test/decimaltestdata/ddRemainder.decTest
@@ -422,7 +422,7 @@ ddrem757 remainder  1sNaN   ->  NaN  Invalid_operation
 ddrem758 remainder  1000 sNaN   ->  NaN  Invalid_operation
 ddrem759 remainder  Inf -sNaN   -> -NaN  Invalid_operation
 
--- propaging NaNs
+-- propagating NaNs
 ddrem760 remainder  NaN1   NaN7   ->  NaN1
 ddrem761 rem

[Python-checkins] [3.13] gh-124217, ipaddress: Add RFC 9637 reserved IPv6 block `3fff::/20` (GH-124240) (#124282)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/a93716f6958f90590ffed329a04fd3acbbd7e6f2
commit: a93716f6958f90590ffed329a04fd3acbbd7e6f2
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: vstinner 
date: 2024-10-07T23:45:59+02:00
summary:

[3.13] gh-124217, ipaddress: Add RFC 9637 reserved IPv6 block `3fff::/20` 
(GH-124240) (#124282)

gh-124217, ipaddress: Add RFC 9637 reserved IPv6 block `3fff::/20` (GH-124240)
(cherry picked from commit db6eb3640a7d98db6fea17cf9da4cb14504e5571)

Signed-off-by: y5c4l3 
Co-authored-by: Y5 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2024-09-19-20-15-00.gh-issue-124217.j0KlQB.rst
M Lib/ipaddress.py
M Lib/test/test_ipaddress.py

diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py
index c165505a53330c..da8677ad6805de 100644
--- a/Lib/ipaddress.py
+++ b/Lib/ipaddress.py
@@ -2383,6 +2383,8 @@ class _IPv6Constants:
 IPv6Network('2001:db8::/32'),
 # IANA says N/A, let's consider it not globally reachable to be safe
 IPv6Network('2002::/16'),
+# RFC 9637: https://www.rfc-editor.org/rfc/rfc9637.html#section-6-2.2
+IPv6Network('3fff::/20'),
 IPv6Network('fc00::/7'),
 IPv6Network('fe80::/10'),
 ]
diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py
index bd8d5d220e9131..5837fc2c43d544 100644
--- a/Lib/test/test_ipaddress.py
+++ b/Lib/test/test_ipaddress.py
@@ -2427,6 +2427,8 @@ def testReservedIpv6(self):
 self.assertTrue(ipaddress.ip_address('2001:30::').is_global)
 self.assertFalse(ipaddress.ip_address('2001:40::').is_global)
 self.assertFalse(ipaddress.ip_address('2002::').is_global)
+# gh-124217: conform with RFC 9637
+self.assertFalse(ipaddress.ip_address('3fff::').is_global)
 
 # some generic IETF reserved addresses
 self.assertEqual(True, ipaddress.ip_address('100::').is_reserved)
diff --git 
a/Misc/NEWS.d/next/Library/2024-09-19-20-15-00.gh-issue-124217.j0KlQB.rst 
b/Misc/NEWS.d/next/Library/2024-09-19-20-15-00.gh-issue-124217.j0KlQB.rst
new file mode 100644
index 00..46f9866f8d427c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-09-19-20-15-00.gh-issue-124217.j0KlQB.rst
@@ -0,0 +1 @@
+Add RFC 9637 reserved IPv6 block ``3fff::/20`` in :mod:`ipaddress` module.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] Support the "pager" binary in _pyrepl (GH-122878) (#124242)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/9e218afc8591e3687855a591ffd5bcb7d9bf1bc7
commit: 9e218afc8591e3687855a591ffd5bcb7d9bf1bc7
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: vstinner 
date: 2024-10-07T23:45:43+02:00
summary:

[3.13] Support the "pager" binary in _pyrepl (GH-122878) (#124242)

Support the "pager" binary in _pyrepl (GH-122878)

Debian (and derivatives) provide a /usr/bin/pager binary, managed by the
alternatives system, that always points to an available pager utility.
Allow _pyrepl to use it, to follow system policy.

This is a very trivial change, from a patch that Debian has been
carrying since 2.7 era. Seems appropriate to upstream.
https://bugs.debian.org/799555
(cherry picked from commit 426569eb8ca1edaa68026aa2bab6b8d1c9105f93)

Co-authored-by: Stefano Rivera 
Co-authored-by: T. Wouters 

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2024-09-19-13-17-31.gh-issue-122878.4iFpsB.rst
M Lib/_pyrepl/pager.py

diff --git a/Lib/_pyrepl/pager.py b/Lib/_pyrepl/pager.py
index 66dcd99111adfc..1fddc63e3ee3ad 100644
--- a/Lib/_pyrepl/pager.py
+++ b/Lib/_pyrepl/pager.py
@@ -36,6 +36,8 @@ def get_pager() -> Pager:
 return plain_pager
 if sys.platform == 'win32':
 return lambda text, title='': tempfile_pager(plain(text), 'more <')
+if hasattr(os, 'system') and os.system('(pager) 2>/dev/null') == 0:
+return lambda text, title='': pipe_pager(text, 'pager', title)
 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
 return lambda text, title='': pipe_pager(text, 'less', title)
 
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2024-09-19-13-17-31.gh-issue-122878.4iFpsB.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-19-13-17-31.gh-issue-122878.4iFpsB.rst
new file mode 100644
index 00..85dd0fd769be68
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-19-13-17-31.gh-issue-122878.4iFpsB.rst
@@ -0,0 +1 @@
+Use the ``pager`` binary, if available (e.g. on Debian and derivatives), to 
display REPL ``help()``.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] GH-116968: Remove branch from advance_backoff_counter (GH-124469)

2024-10-07 Thread markshannon
https://github.com/python/cpython/commit/f55273b3b7124dc570911724107c2440f37905fc
commit: f55273b3b7124dc570911724107c2440f37905fc
branch: main
author: Mark Shannon 
committer: markshannon 
date: 2024-10-07T11:46:33+01:00
summary:

GH-116968: Remove branch from advance_backoff_counter (GH-124469)

files:
M Include/internal/pycore_backoff.h
M Python/bytecodes.c
M Python/executor_cases.c.h
M Python/instrumentation.c

diff --git a/Include/internal/pycore_backoff.h 
b/Include/internal/pycore_backoff.h
index a9d1bce127e63d..20436a68b69677 100644
--- a/Include/internal/pycore_backoff.h
+++ b/Include/internal/pycore_backoff.h
@@ -15,13 +15,7 @@ extern "C" {
 
 
 typedef struct {
-union {
-struct {
-uint16_t backoff : 4;
-uint16_t value : 12;
-};
-uint16_t as_counter;  // For printf("%#x", ...)
-};
+uint16_t value_and_backoff;
 } _Py_BackoffCounter;
 
 
@@ -38,17 +32,19 @@ typedef struct {
and a 4-bit 'backoff' field. When resetting the counter, the
backoff field is incremented (until it reaches a limit) and the
value is set to a bit mask representing the value 2**backoff - 1.
-   The maximum backoff is 12 (the number of value bits).
+   The maximum backoff is 12 (the number of bits in the value).
 
There is an exceptional value which must not be updated, 0x.
 */
 
-#define UNREACHABLE_BACKOFF 0x
+#define BACKOFF_BITS 4
+#define MAX_BACKOFF 12
+#define UNREACHABLE_BACKOFF 15
 
 static inline bool
 is_unreachable_backoff_counter(_Py_BackoffCounter counter)
 {
-return counter.as_counter == UNREACHABLE_BACKOFF;
+return counter.value_and_backoff == UNREACHABLE_BACKOFF;
 }
 
 static inline _Py_BackoffCounter
@@ -57,8 +53,7 @@ make_backoff_counter(uint16_t value, uint16_t backoff)
 assert(backoff <= 15);
 assert(value <= 0xFFF);
 _Py_BackoffCounter result;
-result.value = value;
-result.backoff = backoff;
+result.value_and_backoff = (value << BACKOFF_BITS) | backoff;
 return result;
 }
 
@@ -66,7 +61,7 @@ static inline _Py_BackoffCounter
 forge_backoff_counter(uint16_t counter)
 {
 _Py_BackoffCounter result;
-result.as_counter = counter;
+result.value_and_backoff = counter;
 return result;
 }
 
@@ -74,35 +69,36 @@ static inline _Py_BackoffCounter
 restart_backoff_counter(_Py_BackoffCounter counter)
 {
 assert(!is_unreachable_backoff_counter(counter));
-if (counter.backoff < 12) {
-return make_backoff_counter((1 << (counter.backoff + 1)) - 1, 
counter.backoff + 1);
+int backoff = counter.value_and_backoff & 15;
+if (backoff < MAX_BACKOFF) {
+return make_backoff_counter((1 << (backoff + 1)) - 1, backoff + 1);
 }
 else {
-return make_backoff_counter((1 << 12) - 1, 12);
+return make_backoff_counter((1 << MAX_BACKOFF) - 1, MAX_BACKOFF);
 }
 }
 
 static inline _Py_BackoffCounter
 pause_backoff_counter(_Py_BackoffCounter counter)
 {
-return make_backoff_counter(counter.value | 1, counter.backoff);
+_Py_BackoffCounter result;
+result.value_and_backoff = counter.value_and_backoff | (1 << BACKOFF_BITS);
+return result;
 }
 
 static inline _Py_BackoffCounter
 advance_backoff_counter(_Py_BackoffCounter counter)
 {
-if (!is_unreachable_backoff_counter(counter)) {
-return make_backoff_counter((counter.value - 1) & 0xFFF, 
counter.backoff);
-}
-else {
-return counter;
-}
+_Py_BackoffCounter result;
+result.value_and_backoff = counter.value_and_backoff - (1 << BACKOFF_BITS);
+return result;
 }
 
 static inline bool
 backoff_counter_triggers(_Py_BackoffCounter counter)
 {
-return counter.value == 0;
+/* Test whether the value is zero and the backoff is not 
UNREACHABLE_BACKOFF */
+return counter.value_and_backoff < UNREACHABLE_BACKOFF;
 }
 
 /* Initial JUMP_BACKWARD counter.
@@ -136,7 +132,7 @@ initial_temperature_backoff_counter(void)
 static inline _Py_BackoffCounter
 initial_unreachable_backoff_counter(void)
 {
-return forge_backoff_counter(UNREACHABLE_BACKOFF);
+return make_backoff_counter(0, UNREACHABLE_BACKOFF);
 }
 
 #ifdef __cplusplus
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index c712c772201e10..f251b79e00ebe7 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -4705,7 +4705,7 @@ dummy_func(
 printf("SIDE EXIT: [UOp ");
 _PyUOpPrint(&next_uop[-1]);
 printf(", exit %u, temp %d, target %d -> %s]\n",
-exit - current_executor->exits, 
exit->temperature.as_counter,
+exit - current_executor->exits, 
exit->temperature.value_and_backoff,
 (int)(target - _PyCode_CODE(code)),
 _PyOpcode_OpName[target->op.code]);
 }
@@ -4794,7 +4794,7 @@ dummy_func(
 printf("DYNAMIC EXIT: [UOp ");
 _PyUOpPrint(&next_uop[-1]);
 printf(", exit %u, temp %d, target %d -> %s]\n",
-

[Python-checkins] gh-121249: Support _Complex types in the struct module (#121613)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/7487db4c7af629f0a81b2127a3eea288cefc
commit: 7487db4c7af629f0a81b2127a3eea288cefc
branch: main
author: Sergey B Kirpichev 
committer: vstinner 
date: 2024-10-07T13:53:02+02:00
summary:

gh-121249: Support _Complex types in the struct module (#121613)

Co-authored-by: Peter Bierma 
Co-authored-by: Bénédikt Tran <[email protected]>
Co-authored-by: Victor Stinner 

files:
A Misc/NEWS.d/next/Library/2024-07-10-08-13-34.gh-issue-121249.W9Gd09.rst
M Doc/library/struct.rst
M Lib/ctypes/__init__.py
M Lib/test/test_struct.py
M Modules/_struct.c

diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst
index 4769affdf1d666..3ea9e5ba071289 100644
--- a/Doc/library/struct.rst
+++ b/Doc/library/struct.rst
@@ -267,12 +267,26 @@ platform-dependent.
 | ``P``  | :c:expr:`void \*`| integer|| 
\(5)   |
 
++--++++
 
+Additionally, if IEC 60559 compatible complex arithmetic (Annex G of the
+C11 standard) is supported, the following format characters are available:
+
+++--++++
+| Format | C Type   | Python type| Standard size  | 
Notes  |
+++==++++
+| ``E``  | :c:expr:`float complex`  | complex| 8  | 
\(10)  |
+++--++++
+| ``C``  | :c:expr:`double complex` | complex| 16 | 
\(10)  |
+++--++++
+
 .. versionchanged:: 3.3
Added support for the ``'n'`` and ``'N'`` formats.
 
 .. versionchanged:: 3.6
Added support for the ``'e'`` format.
 
+.. versionchanged:: 3.14
+   Added support for the ``'E'`` and ``'C'`` formats.
+
 
 Notes:
 
@@ -349,6 +363,11 @@ Notes:
of bytes.  As a special case, ``'0s'`` means a single, empty string (while
``'0c'`` means 0 characters).
 
+(10)
+   For the ``'E'`` and ``'C'`` format characters, the packed representation 
uses
+   the IEEE 754 binary32 and binary64 format for components of the complex
+   number, regardless of the floating-point format used by the platform.
+
 A format character may be preceded by an integral repeat count.  For example,
 the format string ``'4h'`` means exactly the same as ``''``.
 
diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py
index cb3a61287bfe5d..4a368f02d851c5 100644
--- a/Lib/ctypes/__init__.py
+++ b/Lib/ctypes/__init__.py
@@ -208,8 +208,10 @@ class c_longdouble(_SimpleCData):
 try:
 class c_double_complex(_SimpleCData):
 _type_ = "C"
+_check_size(c_double_complex)
 class c_float_complex(_SimpleCData):
 _type_ = "E"
+_check_size(c_float_complex)
 class c_longdouble_complex(_SimpleCData):
 _type_ = "F"
 except AttributeError:
diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py
index bdbf8800cfd8f6..e3193c7863fbae 100644
--- a/Lib/test/test_struct.py
+++ b/Lib/test/test_struct.py
@@ -1,4 +1,5 @@
 from collections import abc
+from itertools import combinations
 import array
 import gc
 import math
@@ -11,12 +12,22 @@
 from test import support
 from test.support import import_helper, suppress_immortalization
 from test.support.script_helper import assert_python_ok
+from test.support.testcase import ComplexesAreIdenticalMixin
 
 ISBIGENDIAN = sys.byteorder == "big"
 
 integer_codes = 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'n', 'N'
 byteorders = '', '@', '=', '<', '>', '!'
 
+INF = float('inf')
+NAN = float('nan')
+
+try:
+struct.pack('C', 1j)
+have_c_complex = True
+except struct.error:
+have_c_complex = False
+
 def iter_integer_formats(byteorders=byteorders):
 for code in integer_codes:
 for byteorder in byteorders:
@@ -33,7 +44,7 @@ def bigendian_to_native(value):
 else:
 return string_reverse(value)
 
-class StructTest(unittest.TestCase):
+class StructTest(ComplexesAreIdenticalMixin, unittest.TestCase):
 def test_isbigendian(self):
 self.assertEqual((struct.pack('=i', 1)[0] == 0), ISBIGENDIAN)
 
@@ -783,6 +794,30 @@ def test_repr(self):
 s = struct.Struct('=i2H')
 self.assertEqual(repr(s), f'Struct({s.format!r})')
 
[email protected](have_c_complex, "requires C11 complex type support")
+def test_c_complex_round_trip(self):
+values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2,
+ -3, INF, -INF, NAN], 2)]
+for z in values:
+for f in ['E', 'C', '>E', '>C', '   // offsetof()
 
 /*[clinic input]
@@ -80,6 +83,10 @@ typedef struct { char c; int x; } st_int;
 typedef struct { char c; long x; } st_long;
 typedef st

[Python-checkins] gh-125018: Add importlib.metadata semantic link targets (#125027)

2024-10-07 Thread ncoghlan
https://github.com/python/cpython/commit/cda3b5a576412a8671bbe4c68bb792ec14f1a4b1
commit: cda3b5a576412a8671bbe4c68bb792ec14f1a4b1
branch: main
author: Alyssa Coghlan 
committer: ncoghlan 
date: 2024-10-07T23:44:18+10:00
summary:

gh-125018: Add importlib.metadata semantic link targets (#125027)

This allows direct intersphinx references to APIs via references
like `` :func:`importlib.metadata.version` ``.

-

Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) 

Co-authored-by: Adam Turner <[email protected]>

files:
A Misc/NEWS.d/next/Documentation/2024-10-07-00-31-17.gh-issue-125018.yKnymn.rst
M Doc/library/importlib.metadata.rst

diff --git a/Doc/library/importlib.metadata.rst 
b/Doc/library/importlib.metadata.rst
index 66ba621084c898..ddb72784694fd9 100644
--- a/Doc/library/importlib.metadata.rst
+++ b/Doc/library/importlib.metadata.rst
@@ -100,6 +100,13 @@ You can also get a :ref:`distribution's version number 
`, list its
 :ref:`requirements`.
 
 
+.. exception:: PackageNotFoundError
+
+   Subclass of :class:`ModuleNotFoundError` raised by several functions in this
+   module when queried for a distribution package which is not installed in the
+   current Python environment.
+
+
 Functional API
 ==
 
@@ -111,31 +118,53 @@ This package provides the following functionality via its 
public API.
 Entry points
 
 
-The ``entry_points()`` function returns a collection of entry points.
-Entry points are represented by ``EntryPoint`` instances;
-each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and
-a ``.load()`` method to resolve the value. There are also ``.module``,
-``.attr``, and ``.extras`` attributes for getting the components of the
-``.value`` attribute.
+.. function:: entry_points(**select_params)
+
+   Returns a :class:`EntryPoints` instance describing entry points for the
+   current environment. Any given keyword parameters are passed to the
+   :meth:`!~EntryPoints.select` method for comparison to the attributes of
+   the individual entry point definitions.
+
+   Note: it is not currently possible to query for entry points based on
+   their :attr:`!EntryPoint.dist` attribute (as different 
:class:`!Distribution`
+   instances do not currently compare equal, even if they have the same 
attributes)
+
+.. class:: EntryPoints
+
+   Details of a collection of installed entry points.
+
+   Also provides a ``.groups`` attribute that reports all identifed entry
+   point groups, and a ``.names`` attribute that reports all identified entry
+   point names.
+
+.. class:: EntryPoint
+
+   Details of an installed entry point.
+
+   Each :class:`!EntryPoint` instance has ``.name``, ``.group``, and ``.value``
+   attributes and a ``.load()`` method to resolve the value. There are also
+   ``.module``, ``.attr``, and ``.extras`` attributes for getting the
+   components of the ``.value`` attribute, and ``.dist`` for obtaining
+   information regarding the distribution package that provides the entry 
point.
 
 Query all entry points::
 
 >>> eps = entry_points()  # doctest: +SKIP
 
-The ``entry_points()`` function returns an ``EntryPoints`` object,
-a collection of all ``EntryPoint`` objects with ``names`` and ``groups``
+The :func:`!entry_points` function returns a :class:`!EntryPoints` object,
+a collection of all :class:`!EntryPoint` objects with ``names`` and ``groups``
 attributes for convenience::
 
 >>> sorted(eps.groups)  # doctest: +SKIP
 ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 
'egg_info.writers', 'setuptools.installation']
 
-``EntryPoints`` has a ``select`` method to select entry points
+:class:`!EntryPoints` has a :meth:`!~EntryPoints.select` method to select 
entry points
 matching specific properties. Select entry points in the
 ``console_scripts`` group::
 
 >>> scripts = eps.select(group='console_scripts')  # doctest: +SKIP
 
-Equivalently, since ``entry_points`` passes keyword arguments
+Equivalently, since :func:`!entry_points` passes keyword arguments
 through to select::
 
 >>> scripts = entry_points(group='console_scripts')  # doctest: +SKIP
@@ -189,31 +218,41 @@ for more information on entry points, their definition, 
and usage.
 Distribution metadata
 -
 
-Every `Distribution Package 
`_ 
includes some metadata,
-which you can extract using the
-``metadata()`` function::
+.. function:: metadata(distribution_name)
+
+   Return the distribution metadata corresponding to the named
+   distribution package as a :class:`PackageMetadata` instance.
+
+   Raises :exc:`PackageNotFoundError` if the named distribution
+   package is not installed in the current Python environment.
+
+.. class:: PackageMetadata
+
+   A concrete implementation of the
+`PackageMetadata protocol 


[Python-checkins] gh-125018: Fix role syntax (#125050)

2024-10-07 Thread AA-Turner
https://github.com/python/cpython/commit/10094a533a947b72d01ed8195dcf540f2e7820ea
commit: 10094a533a947b72d01ed8195dcf540f2e7820ea
branch: main
author: Adam Turner <[email protected]>
committer: AA-Turner <[email protected]>
date: 2024-10-07T17:51:53+01:00
summary:

gh-125018: Fix role syntax (#125050)

files:
M Doc/library/importlib.metadata.rst

diff --git a/Doc/library/importlib.metadata.rst 
b/Doc/library/importlib.metadata.rst
index ddb72784694fd9..85d5a2d684d6eb 100644
--- a/Doc/library/importlib.metadata.rst
+++ b/Doc/library/importlib.metadata.rst
@@ -122,7 +122,7 @@ Entry points
 
Returns a :class:`EntryPoints` instance describing entry points for the
current environment. Any given keyword parameters are passed to the
-   :meth:`!~EntryPoints.select` method for comparison to the attributes of
+   :meth:`!select` method for comparison to the attributes of
the individual entry point definitions.
 
Note: it is not currently possible to query for entry points based on
@@ -158,7 +158,7 @@ attributes for convenience::
 >>> sorted(eps.groups)  # doctest: +SKIP
 ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 
'egg_info.writers', 'setuptools.installation']
 
-:class:`!EntryPoints` has a :meth:`!~EntryPoints.select` method to select 
entry points
+:class:`!EntryPoints` has a :meth:`!select` method to select entry points
 matching specific properties. Select entry points in the
 ``console_scripts`` group::
 
@@ -232,7 +232,7 @@ Distribution metadata
 `PackageMetadata protocol 
`_.
 
 In addition to providing the defined protocol methods and attributes, 
subscripting
-the instance is equivalent to calling the :meth:`!~PackageMetadata.get` 
method.
+the instance is equivalent to calling the :meth:`!get` method.
 
 Every `Distribution Package 
`_
 includes some metadata, which you can extract using the :func:`!metadata` 
function::
@@ -245,7 +245,7 @@ the values are returned unparsed from the distribution 
metadata::
 >>> wheel_metadata['Requires-Python']  # doctest: +SKIP
 '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
 
-:class:`PackageMetadata` also presents a :attr:`!~PackageMetadata.json` 
attribute that returns
+:class:`PackageMetadata` also presents a :attr:`!json` attribute that returns
 all the metadata in a JSON-compatible form per :PEP:`566`::
 
 >>> wheel_metadata.json['requires_python']
@@ -331,7 +331,7 @@ Once you have the file, you can also read its contents::
 return s.encode('utf-8')
 return s
 
-You can also use the :meth:`!~PackagePath.locate` method to get the absolute
+You can also use the :meth:`!locate` method to get the absolute
 path to the file::
 
 >>> util.locate()  # doctest: +SKIP

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-120762: make_ssl_certs: Don't set extensions for the temporary CSR (GH-125045)

2024-10-07 Thread encukou
https://github.com/python/cpython/commit/744caa8ef42ab67c6aa20cd691e078721e72e22a
commit: 744caa8ef42ab67c6aa20cd691e078721e72e22a
branch: main
author: Petr Viktorin 
committer: encukou 
date: 2024-10-07T17:37:52+02:00
summary:

gh-120762: make_ssl_certs: Don't set extensions for the temporary CSR 
(GH-125045)

gh-120762: make_ssl_certs: Don't set extensions for the CSR

`openssl req` fails with openssl 3.2.2 because the config line

authorityKeyIdentifier = keyid:always,issuer:always

is not supported for certificate signing requests (since the issuing
certificate authority is not known).

David von Oheimb, the OpenSSL dev that made the change, commented in:
https://github.com/openssl/openssl/issues/22966#issuecomment-1858396738 :

> This problem did not show up in older OpenSSL versions because of a bug:
> the `req` app ignored the `-extensions` option unless `-x505` is given,
> which I fixed in https://github.com/openssl/openssl/pull/16865.

(I assume `-x505` is a typo for `-x509`.)

In our `make_cert_key` function:

If `sign` is true:
- We don't pass `-x509` to `req`, so in this case it should be safe to
  omit the `-extensions` argument. (Old OpenSSL ignores it, new OpenSSL
  fails on it.)
- The extensions are passed to the `ca` call later in the function.
  There they take effect, and `authorityKeyIdentifier` is valid.

If `sign` is false, this commit has no effect except rearranging the
CLI arguments.

files:
M Lib/test/certdata/make_ssl_certs.py

diff --git a/Lib/test/certdata/make_ssl_certs.py 
b/Lib/test/certdata/make_ssl_certs.py
index 48f980124e1198..198c64035c5044 100644
--- a/Lib/test/certdata/make_ssl_certs.py
+++ b/Lib/test/certdata/make_ssl_certs.py
@@ -139,7 +139,6 @@ def make_cert_key(cmdlineargs, hostname, sign=False, 
extra_san='',
 f.write(req)
 args = ['req', '-new', '-nodes', '-days', cmdlineargs.days,
 '-newkey', key, '-keyout', key_file,
-'-extensions', ext,
 '-config', req_file]
 if sign:
 with tempfile.NamedTemporaryFile(delete=False) as f:
@@ -148,7 +147,7 @@ def make_cert_key(cmdlineargs, hostname, sign=False, 
extra_san='',
 args += ['-out', reqfile ]
 
 else:
-args += ['-x509', '-out', cert_file ]
+args += ['-extensions', ext, '-x509', '-out', cert_file ]
 check_call(['openssl'] + args)
 
 if sign:

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-111178: Fix function signatures in fileio.c (#125043)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/d8f707420b3b0975567e0d9cab60c978d6cb7111
commit: d8f707420b3b0975567e0d9cab60c978d6cb7111
branch: main
author: Victor Stinner 
committer: vstinner 
date: 2024-10-07T15:27:36+02:00
summary:

gh-78: Fix function signatures in fileio.c (#125043)

* Add "fileio_" prefix to getter functions.
* Small refactoring.

files:
M Modules/_io/fileio.c

diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
index 8dae465fd20f8b..d9597f860b9429 100644
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -86,6 +86,7 @@ typedef struct {
 } fileio;
 
 #define PyFileIO_Check(state, op) (PyObject_TypeCheck((op), 
state->PyFileIO_Type))
+#define _PyFileIO_CAST(op) _Py_CAST(fileio*, (op))
 
 /* Forward declarations */
 static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence, 
bool suppress_pipe_error);
@@ -93,15 +94,16 @@ static PyObject* portable_lseek(fileio *self, PyObject 
*posobj, int whence, bool
 int
 _PyFileIO_closed(PyObject *self)
 {
-return ((fileio *)self)->fd < 0;
+return (_PyFileIO_CAST(self)->fd < 0);
 }
 
 /* Because this can call arbitrary code, it shouldn't be called when
the refcount is 0 (that is, not directly from tp_dealloc unless
the refcount has been temporarily re-incremented). */
 static PyObject *
-fileio_dealloc_warn(fileio *self, PyObject *source)
+fileio_dealloc_warn(PyObject *op, PyObject *source)
 {
+fileio *self = _PyFileIO_CAST(op);
 if (self->fd >= 0 && self->closefd) {
 PyObject *exc = PyErr_GetRaisedException();
 if (PyErr_ResourceWarning(source, 1, "unclosed file %R", source)) {
@@ -171,7 +173,7 @@ _io_FileIO_close_impl(fileio *self, PyTypeObject *cls)
 exc = PyErr_GetRaisedException();
 }
 if (self->finalizing) {
-PyObject *r = fileio_dealloc_warn(self, (PyObject *) self);
+PyObject *r = fileio_dealloc_warn((PyObject*)self, (PyObject *) self);
 if (r) {
 Py_DECREF(r);
 }
@@ -192,23 +194,22 @@ _io_FileIO_close_impl(fileio *self, PyTypeObject *cls)
 static PyObject *
 fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
-fileio *self;
-
 assert(type != NULL && type->tp_alloc != NULL);
 
-self = (fileio *) type->tp_alloc(type, 0);
-if (self != NULL) {
-self->fd = -1;
-self->created = 0;
-self->readable = 0;
-self->writable = 0;
-self->appending = 0;
-self->seekable = -1;
-self->stat_atopen = NULL;
-self->closefd = 1;
-self->weakreflist = NULL;
+fileio *self = (fileio *) type->tp_alloc(type, 0);
+if (self == NULL) {
+return NULL;
 }
 
+self->fd = -1;
+self->created = 0;
+self->readable = 0;
+self->writable = 0;
+self->appending = 0;
+self->seekable = -1;
+self->stat_atopen = NULL;
+self->closefd = 1;
+self->weakreflist = NULL;
 return (PyObject *) self;
 }
 
@@ -539,36 +540,43 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, 
const char *mode,
 }
 
 static int
-fileio_traverse(fileio *self, visitproc visit, void *arg)
+fileio_traverse(PyObject *op, visitproc visit, void *arg)
 {
+fileio *self = _PyFileIO_CAST(op);
 Py_VISIT(Py_TYPE(self));
 Py_VISIT(self->dict);
 return 0;
 }
 
 static int
-fileio_clear(fileio *self)
+fileio_clear(PyObject *op)
 {
+fileio *self = _PyFileIO_CAST(op);
 Py_CLEAR(self->dict);
 return 0;
 }
 
 static void
-fileio_dealloc(fileio *self)
+fileio_dealloc(PyObject *op)
 {
-PyTypeObject *tp = Py_TYPE(self);
+fileio *self = _PyFileIO_CAST(op);
 self->finalizing = 1;
-if (_PyIOBase_finalize((PyObject *) self) < 0)
+if (_PyIOBase_finalize(op) < 0) {
 return;
+}
+
 _PyObject_GC_UNTRACK(self);
 if (self->stat_atopen != NULL) {
 PyMem_Free(self->stat_atopen);
 self->stat_atopen = NULL;
 }
-if (self->weakreflist != NULL)
-PyObject_ClearWeakRefs((PyObject *) self);
-(void)fileio_clear(self);
-tp->tp_free((PyObject *)self);
+if (self->weakreflist != NULL) {
+PyObject_ClearWeakRefs(op);
+}
+(void)fileio_clear(op);
+
+PyTypeObject *tp = Py_TYPE(op);
+tp->tp_free(op);
 Py_DECREF(tp);
 }
 
@@ -1151,18 +1159,20 @@ mode_string(fileio *self)
 }
 
 static PyObject *
-fileio_repr(fileio *self)
+fileio_repr(PyObject *op)
 {
-PyObject *nameobj, *res;
-const char *type_name = Py_TYPE((PyObject *) self)->tp_name;
+fileio *self = _PyFileIO_CAST(op);
+const char *type_name = Py_TYPE(self)->tp_name;
 
 if (self->fd < 0) {
 return PyUnicode_FromFormat("<%.100s [closed]>", type_name);
 }
 
+PyObject *nameobj;
 if (PyObject_GetOptionalAttr((PyObject *) self, &_Py_ID(name), &nameobj) < 
0) {
 return NULL;
 }
+PyObject *res;
 if (nameobj == NULL) {
 res = PyUnicode_FromFormat(
 "<%.100s fd=%d mode='%s' closefd=%s>",
@@ -1224,7 +1234,7 @@ static PyMe

[Python-checkins] Python 3.13.0

2024-10-07 Thread Yhg1s
https://github.com/python/cpython/commit/60403a5409ff2c3f3b07dd2ca91a7a3e096839c7
commit: 60403a5409ff2c3f3b07dd2ca91a7a3e096839c7
branch: 3.13
author: Thomas Wouters 
committer: Yhg1s 
date: 2024-10-07T07:02:14+02:00
summary:

Python 3.13.0

files:
A Misc/NEWS.d/3.13.0.rst
D Misc/NEWS.d/next/Core and 
Builtins/2024-10-03-22-26-39.gh-issue-124871.tAMF47.rst
D 
Misc/NEWS.d/next/Core_and_Builtins/2024-10-05-23-53-06.gh-issue-125008.ETANpd.rst
M Include/patchlevel.h
M Lib/pydoc_data/topics.py
M README.rst

diff --git a/Include/patchlevel.h b/Include/patchlevel.h
index 22bb119b66908b..2f13bb9e89c83e 100644
--- a/Include/patchlevel.h
+++ b/Include/patchlevel.h
@@ -19,11 +19,11 @@
 #define PY_MAJOR_VERSION3
 #define PY_MINOR_VERSION13
 #define PY_MICRO_VERSION0
-#define PY_RELEASE_LEVELPY_RELEASE_LEVEL_GAMMA
-#define PY_RELEASE_SERIAL   3
+#define PY_RELEASE_LEVELPY_RELEASE_LEVEL_FINAL
+#define PY_RELEASE_SERIAL   0
 
 /* Version as a string */
-#define PY_VERSION  "3.13.0rc3+"
+#define PY_VERSION  "3.13.0"
 /*--end constants--*/
 
 /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.
diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py
index 81c0b680b83113..dbfeb55998c1b0 100644
--- a/Lib/pydoc_data/topics.py
+++ b/Lib/pydoc_data/topics.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Autogenerated by Sphinx on Tue Oct  1 04:00:06 2024
+# Autogenerated by Sphinx on Mon Oct  7 06:59:36 2024
 # as part of the release process.
 topics = {'assert': 'The "assert" statement\n'
'**\n'
diff --git a/Misc/NEWS.d/3.13.0.rst b/Misc/NEWS.d/3.13.0.rst
new file mode 100644
index 00..b0f43d8f8ab947
--- /dev/null
+++ b/Misc/NEWS.d/3.13.0.rst
@@ -0,0 +1,18 @@
+.. date: 2024-10-05-23-53-06
+.. gh-issue: 125008
+.. nonce: ETANpd
+.. release date: 2024-10-07
+.. section: Core and Builtins
+
+Fix :func:`tokenize.untokenize` producing invalid syntax for double braces
+preceded by certain escape characters.
+
+..
+
+.. date: 2024-10-03-22-26-39
+.. gh-issue: 124871
+.. nonce: tAMF47
+.. section: Core and Builtins
+
+Fix compiler bug (in some versions of 3.13) where an assertion fails during
+reachability analysis.
diff --git a/Misc/NEWS.d/next/Core and 
Builtins/2024-10-03-22-26-39.gh-issue-124871.tAMF47.rst b/Misc/NEWS.d/next/Core 
and Builtins/2024-10-03-22-26-39.gh-issue-124871.tAMF47.rst
deleted file mode 100644
index 185cb3048fadf5..00
--- a/Misc/NEWS.d/next/Core and 
Builtins/2024-10-03-22-26-39.gh-issue-124871.tAMF47.rst 
+++ /dev/null
@@ -1,2 +0,0 @@
-Fix compiler bug (in some versions of 3.13) where an assertion fails during 
reachability
-analysis.
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-05-23-53-06.gh-issue-125008.ETANpd.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-05-23-53-06.gh-issue-125008.ETANpd.rst
deleted file mode 100644
index 8971e052860225..00
--- 
a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-05-23-53-06.gh-issue-125008.ETANpd.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-Fix :func:`tokenize.untokenize` producing invalid syntax for
-double braces preceded by certain escape characters.
diff --git a/README.rst b/README.rst
index 3f2b4d5f033892..7c1e463273b418 100644
--- a/README.rst
+++ b/README.rst
@@ -1,5 +1,5 @@
-This is Python version 3.13.0 release candidate 3
-=
+This is Python version 3.13.0
+=
 
 .. image:: https://github.com/python/cpython/workflows/Tests/badge.svg
:alt: CPython build status on GitHub Actions

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.12] gh-124182: Explain naming rules for struct sequence types (GH-124335) (#125056)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/a62d2d3318fcd5a9ac5c062360f73e511a4375e1
commit: a62d2d3318fcd5a9ac5c062360f73e511a4375e1
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: vstinner 
date: 2024-10-07T17:59:31Z
summary:

[3.12] gh-124182: Explain naming rules for struct sequence types (GH-124335) 
(#125056)

gh-124182: Explain naming rules for struct sequence types (GH-124335)
(cherry picked from commit 3287c834e5370294e310450115290979aac06efa)

Co-authored-by: ffelixg <[email protected]>

files:
M Doc/c-api/tuple.rst

diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst
index 82ef4bcd14784d..e20c5a66189be0 100644
--- a/Doc/c-api/tuple.rst
+++ b/Doc/c-api/tuple.rst
@@ -158,7 +158,8 @@ type.
 
.. c:member:: const char *name
 
-  Name of the struct sequence type.
+  Fully qualified name of the type; null-terminated UTF-8 encoded.
+  The name must contain the module name.
 
.. c:member:: const char *doc
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-124653: Relax (again) detection of queue API for logging handlers (GH-124897)

2024-10-07 Thread vsajip
https://github.com/python/cpython/commit/7ffe94fb242fd51bb07c7f0d31e94efeea3619d4
commit: 7ffe94fb242fd51bb07c7f0d31e94efeea3619d4
branch: main
author: Bénédikt Tran <[email protected]>
committer: vsajip 
date: 2024-10-07T19:42:19+01:00
summary:

gh-124653: Relax (again) detection of queue API for logging handlers  
(GH-124897)

files:
A Misc/NEWS.d/next/Library/2024-10-02-15-05-45.gh-issue-124653.tqsTu9.rst
M Doc/library/logging.config.rst
M Lib/logging/config.py
M Lib/test/test_logging.py

diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst
index 317ca8728248c8..0e9dc33ae2123a 100644
--- a/Doc/library/logging.config.rst
+++ b/Doc/library/logging.config.rst
@@ -753,16 +753,17 @@ The ``queue`` and ``listener`` keys are optional.
 
 If the ``queue`` key is present, the corresponding value can be one of the 
following:
 
-* An object implementing the :class:`queue.Queue` public API. For instance,
-  this may be an actual instance of :class:`queue.Queue` or a subclass thereof,
-  or a proxy obtained by :meth:`multiprocessing.managers.SyncManager.Queue`.
+* An object implementing the :meth:`Queue.put_nowait `
+  and :meth:`Queue.get ` public API. For instance, this may be
+  an actual instance of :class:`queue.Queue` or a subclass thereof, or a proxy
+  obtained by :meth:`multiprocessing.managers.SyncManager.Queue`.
 
   This is of course only possible if you are constructing or modifying
   the configuration dictionary in code.
 
 * A string that resolves to a callable which, when called with no arguments, 
returns
-  the :class:`queue.Queue` instance to use. That callable could be a
-  :class:`queue.Queue` subclass or a function which returns a suitable queue 
instance,
+  the queue instance to use. That callable could be a :class:`queue.Queue` 
subclass
+  or a function which returns a suitable queue instance,
   such as ``my.module.queue_factory()``.
 
 * A dict with a ``'()'`` key which is constructed in the usual way as 
discussed in
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index 3781cb1aeb9ae2..6a6a7f726f7e0c 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -499,7 +499,7 @@ def as_tuple(self, value):
 
 def _is_queue_like_object(obj):
 """Check that *obj* implements the Queue API."""
-if isinstance(obj, queue.Queue):
+if isinstance(obj, (queue.Queue, queue.SimpleQueue)):
 return True
 # defer importing multiprocessing as much as possible
 from multiprocessing.queues import Queue as MPQueue
@@ -516,13 +516,13 @@ def _is_queue_like_object(obj):
 # Ideally, we would have wanted to simply use strict type checking
 # instead of a protocol-based type checking since the latter does
 # not check the method signatures.
-queue_interface = [
-'empty', 'full', 'get', 'get_nowait',
-'put', 'put_nowait', 'join', 'qsize',
-'task_done',
-]
+#
+# Note that only 'put_nowait' and 'get' are required by the logging
+# queue handler and queue listener (see gh-124653) and that other
+# methods are either optional or unused.
+minimal_queue_interface = ['put_nowait', 'get']
 return all(callable(getattr(obj, method, None))
-   for method in queue_interface)
+   for method in minimal_queue_interface)
 
 class DictConfigurator(BaseConfigurator):
 """
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 230ba954cd286d..d4ceb7c8dc0b41 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -2377,16 +2377,22 @@ def __getattr__(self, attribute):
 return getattr(queue, attribute)
 
 class CustomQueueFakeProtocol(CustomQueueProtocol):
-# An object implementing the Queue API (incorrect signatures).
+# An object implementing the minimial Queue API for
+# the logging module but with incorrect signatures.
+#
 # The object will be considered a valid queue class since we
 # do not check the signatures (only callability of methods)
 # but will NOT be usable in production since a TypeError will
-# be raised due to a missing argument.
-def empty(self, x):
+# be raised due to the extra argument in 'put_nowait'.
+def put_nowait(self):
 pass
 
 class CustomQueueWrongProtocol(CustomQueueProtocol):
-empty = None
+put_nowait = None
+
+class MinimalQueueProtocol:
+def put_nowait(self, x): pass
+def get(self): pass
 
 def queueMaker():
 return queue.Queue()
@@ -3946,56 +3952,70 @@ def test_config_queue_handler(self):
 msg = str(ctx.exception)
 self.assertEqual(msg, "Unable to configure handler 'ah'")
 
+def _apply_simple_queue_listener_configuration(self, qspec):
+self.apply_config({
+"version": 1,
+"handlers": {
+"queue_listener": {
+"class": "logging.handlers.QueueHandler",
+"queue": qspec,
+},
+  

[Python-checkins] [3.12] GH-109975: Announce final release in What's New in Python 3.13 (GH-125007) (#125034)

2024-10-07 Thread Yhg1s
https://github.com/python/cpython/commit/a01970d6a2add5a07af58b0aa3ca280bad62b9fd
commit: a01970d6a2add5a07af58b0aa3ca280bad62b9fd
branch: 3.12
author: T. Wouters 
committer: Yhg1s 
date: 2024-10-07T11:51:07-07:00
summary:

[3.12] GH-109975: Announce final release in What's New in Python 3.13 
(GH-125007) (#125034)

Prepare What's New in Python 3.13 for final release
(cherry picked from commit 31516c98dd7097047ba10da8dcf728c3d580f3d6)

Co-authored-by: Adam Turner <[email protected]>

files:
M Doc/whatsnew/3.12.rst

diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 09f47b7041e9e8..5c36e2d4bcfb49 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -59,7 +59,7 @@ Summary -- Release highlights
 .. This section singles out the most important changes in Python 3.12.
Brevity is key.
 
-Python 3.12 is the latest stable release of the Python programming language,
+Python 3.12 is a stable release of the Python programming language,
 with a mix of changes to the language and the standard library.
 The library changes focus on cleaning up deprecated APIs, usability, and 
correctness.
 Of note, the :mod:`!distutils` package has been removed from the standard 
library.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-122392: IDLE - Fix overlapping lines in browsers (#122392) (#124975)

2024-10-07 Thread terryjreedy
https://github.com/python/cpython/commit/c5df1cb7bde7e86f046196b0e34a0b90f8fc11de
commit: c5df1cb7bde7e86f046196b0e34a0b90f8fc11de
branch: main
author: Zhikang Yan <[email protected]>
committer: terryjreedy 
date: 2024-10-07T14:52:09-04:00
summary:

gh-122392: IDLE - Fix overlapping lines in browsers (#122392) (#124975)

Increase currently inadequate vertical spacing for the IDLE browsers (path,
module, and stack) on high-resolution monitors.
-

Co-authored-by: Terry Jan Reedy 

files:
A Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst
M Lib/idlelib/tree.py

diff --git a/Lib/idlelib/tree.py b/Lib/idlelib/tree.py
index 0726d7e23660f6..182ce7189614da 100644
--- a/Lib/idlelib/tree.py
+++ b/Lib/idlelib/tree.py
@@ -83,6 +83,8 @@ def wheel_event(event, widget=None):
 
 class TreeNode:
 
+dy = 0
+
 def __init__(self, canvas, parent, item):
 self.canvas = canvas
 self.parent = parent
@@ -199,23 +201,22 @@ def update(self):
 
 def draw(self, x, y):
 # XXX This hard-codes too many geometry constants!
-dy = 20
 self.x, self.y = x, y
 self.drawicon()
 self.drawtext()
 if self.state != 'expanded':
-return y + dy
+return y + TreeNode.dy
 # draw children
 if not self.children:
 sublist = self.item._GetSubList()
 if not sublist:
 # _IsExpandable() was mistaken; that's allowed
-return y+17
+return y + TreeNode.dy
 for item in sublist:
 child = self.__class__(self.canvas, self, item)
 self.children.append(child)
 cx = x+20
-cy = y + dy
+cy = y + TreeNode.dy
 cylast = 0
 for child in self.children:
 cylast = cy
@@ -289,6 +290,11 @@ def drawtext(self):
 self.label.bind("", lambda e: wheel_event(e, 
self.canvas))
 self.label.bind("", lambda e: wheel_event(e, 
self.canvas))
 self.text_id = id
+if TreeNode.dy == 0:
+# The first row doesn't matter what the dy is, just measure its
+# size to get the value of the subsequent dy
+coords = self.canvas.bbox(id)
+TreeNode.dy = max(20, coords[3] - coords[1] - 3)
 
 def select_or_edit(self, event=None):
 if self.selected and self.item.IsEditable():
diff --git 
a/Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst 
b/Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst
new file mode 100644
index 00..541f6212794ef2
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst
@@ -0,0 +1,2 @@
+Increase currently inadequate vertical spacing for the IDLE browsers (path,
+module, and stack) on high-resolution monitors.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-122392: IDLE - Fix overlapping lines in browsers (GH-122392) (GH-124975) (#125061)

2024-10-07 Thread terryjreedy
https://github.com/python/cpython/commit/6fe746d702bfeeafbde1e9df2b1b5e061808fbb5
commit: 6fe746d702bfeeafbde1e9df2b1b5e061808fbb5
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: terryjreedy 
date: 2024-10-07T19:25:26Z
summary:

[3.13] gh-122392: IDLE - Fix overlapping lines in browsers (GH-122392) 
(GH-124975) (#125061)

gh-122392: IDLE - Fix overlapping lines in browsers (GH-122392) (GH-124975)

Increase currently inadequate vertical spacing for the IDLE browsers (path,
module, and stack) on high-resolution monitors.
-

(cherry picked from commit c5df1cb7bde7e86f046196b0e34a0b90f8fc11de)

Co-authored-by: Zhikang Yan <[email protected]>
Co-authored-by: Terry Jan Reedy 

files:
A Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst
M Lib/idlelib/tree.py

diff --git a/Lib/idlelib/tree.py b/Lib/idlelib/tree.py
index 0726d7e23660f6..182ce7189614da 100644
--- a/Lib/idlelib/tree.py
+++ b/Lib/idlelib/tree.py
@@ -83,6 +83,8 @@ def wheel_event(event, widget=None):
 
 class TreeNode:
 
+dy = 0
+
 def __init__(self, canvas, parent, item):
 self.canvas = canvas
 self.parent = parent
@@ -199,23 +201,22 @@ def update(self):
 
 def draw(self, x, y):
 # XXX This hard-codes too many geometry constants!
-dy = 20
 self.x, self.y = x, y
 self.drawicon()
 self.drawtext()
 if self.state != 'expanded':
-return y + dy
+return y + TreeNode.dy
 # draw children
 if not self.children:
 sublist = self.item._GetSubList()
 if not sublist:
 # _IsExpandable() was mistaken; that's allowed
-return y+17
+return y + TreeNode.dy
 for item in sublist:
 child = self.__class__(self.canvas, self, item)
 self.children.append(child)
 cx = x+20
-cy = y + dy
+cy = y + TreeNode.dy
 cylast = 0
 for child in self.children:
 cylast = cy
@@ -289,6 +290,11 @@ def drawtext(self):
 self.label.bind("", lambda e: wheel_event(e, 
self.canvas))
 self.label.bind("", lambda e: wheel_event(e, 
self.canvas))
 self.text_id = id
+if TreeNode.dy == 0:
+# The first row doesn't matter what the dy is, just measure its
+# size to get the value of the subsequent dy
+coords = self.canvas.bbox(id)
+TreeNode.dy = max(20, coords[3] - coords[1] - 3)
 
 def select_or_edit(self, event=None):
 if self.selected and self.item.IsEditable():
diff --git 
a/Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst 
b/Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst
new file mode 100644
index 00..541f6212794ef2
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2024-10-04-15-34-34.gh-issue-122392.V8K3w2.rst
@@ -0,0 +1,2 @@
+Increase currently inadequate vertical spacing for the IDLE browsers (path,
+module, and stack) on high-resolution monitors.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] Docs: make a tutorial example more precise (#125066)

2024-10-07 Thread nedbat
https://github.com/python/cpython/commit/6e3c70c61bf961e55e9912a31ca11f61c8e2cd0c
commit: 6e3c70c61bf961e55e9912a31ca11f61c8e2cd0c
branch: main
author: Ned Batchelder 
committer: nedbat 
date: 2024-10-07T19:41:13-04:00
summary:

Docs: make a tutorial example more precise (#125066)

Based on discussion here:

https://discuss.python.org/t/omission-in-the-documentation/66816

files:
M Doc/tutorial/introduction.rst

diff --git a/Doc/tutorial/introduction.rst b/Doc/tutorial/introduction.rst
index 054bac59c955d5..65e3b1938bca9c 100644
--- a/Doc/tutorial/introduction.rst
+++ b/Doc/tutorial/introduction.rst
@@ -197,21 +197,19 @@ and workarounds.
 String literals can span multiple lines.  One way is using triple-quotes:
 ``"""..."""`` or ``'''...'''``.  End of lines are automatically
 included in the string, but it's possible to prevent this by adding a ``\`` at
-the end of the line.  The following example::
-
-   print("""\
+the end of the line.  In the following example, the initial newline is not
+included::
+
+   >>> print("""\
+   ... Usage: thingy [OPTIONS]
+   ...  -hDisplay this usage message
+   ...  -H hostname   Hostname to connect to
+   ... """)
Usage: thingy [OPTIONS]
 -hDisplay this usage message
 -H hostname   Hostname to connect to
-   """)
-
-produces the following output (note that the initial newline is not included):
 
-.. code-block:: text
-
-   Usage: thingy [OPTIONS]
--hDisplay this usage message
--H hostname   Hostname to connect to
+   >>>
 
 Strings can be concatenated (glued together) with the ``+`` operator, and
 repeated with ``*``::

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-102511: Change the `os.path.splitroot` param name from `path` back to `p` (GH-124097) (#124919)

2024-10-07 Thread JelleZijlstra
https://github.com/python/cpython/commit/761c3b280b3a4401a4a4224cbe7094c36a999bfd
commit: 761c3b280b3a4401a4a4224cbe7094c36a999bfd
branch: 3.13
author: Jelle Zijlstra 
committer: JelleZijlstra 
date: 2024-10-07T18:42:49-07:00
summary:

[3.13] gh-102511: Change the `os.path.splitroot` param name from `path` back to 
`p` (GH-124097) (#124919)

(cherry picked from commit 3b6bfa77aa4da2ce1f3a15e39831f8b85882698c)

Co-authored-by: sobolevn 

files:
M Modules/clinic/posixmodule.c.h
M Modules/posixmodule.c

diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h
index 14a6efb9ac96d8..d7af87dc946aeb 100644
--- a/Modules/clinic/posixmodule.c.h
+++ b/Modules/clinic/posixmodule.c.h
@@ -2367,7 +2367,7 @@ os__path_isjunction(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
 #endif /* defined(MS_WINDOWS) */
 
 PyDoc_STRVAR(os__path_splitroot_ex__doc__,
-"_path_splitroot_ex($module, /, path)\n"
+"_path_splitroot_ex($module, /, p)\n"
 "--\n"
 "\n"
 "Split a pathname into drive, root and tail.\n"
@@ -2393,7 +2393,7 @@ os__path_splitroot_ex(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs,
 PyObject *ob_item[NUM_KEYWORDS];
 } _kwtuple = {
 .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
-.ob_item = { &_Py_ID(path), },
+.ob_item = { _Py_LATIN1_CHR('p'), },
 };
 #undef NUM_KEYWORDS
 #define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -2402,7 +2402,7 @@ os__path_splitroot_ex(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs,
 #  define KWTUPLE NULL
 #endif  // !Py_BUILD_CORE
 
-static const char * const _keywords[] = {"path", NULL};
+static const char * const _keywords[] = {"p", NULL};
 static _PyArg_Parser _parser = {
 .keywords = _keywords,
 .fname = "_path_splitroot_ex",
@@ -12819,4 +12819,4 @@ os__is_inputhook_installed(PyObject *module, PyObject 
*Py_UNUSED(ignored))
 #ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF
 #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF
 #endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */
-/*[clinic end generated code: output=b2ffb856bcada7c9 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=ee0e3772de1bf216 input=a9049054013a1b77]*/
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 51e34b5f4b74fc..1b5dc278e9d41b 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -5503,7 +5503,7 @@ os__path_isjunction_impl(PyObject *module, path_t *path)
 /*[clinic input]
 os._path_splitroot_ex
 
-path: path_t(make_wide=True, nonstrict=True)
+p as path: path_t(make_wide=True, nonstrict=True)
 
 Split a pathname into drive, root and tail.
 
@@ -5512,7 +5512,7 @@ The tail contains anything after the root.
 
 static PyObject *
 os__path_splitroot_ex_impl(PyObject *module, path_t *path)
-/*[clinic end generated code: output=4b0072b6cdf4b611 input=6eb76e9173412c92]*/
+/*[clinic end generated code: output=4b0072b6cdf4b611 input=4556b615c7cc13f2]*/
 {
 Py_ssize_t drvsize, rootsize;
 PyObject *drv = NULL, *root = NULL, *tail = NULL, *result = NULL;

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-125018: Add importlib.metadata semantic link targets (GH-125027) (#125047)

2024-10-07 Thread AA-Turner
https://github.com/python/cpython/commit/b0d01a15131f2df64272057de0c1bd0fd8d2c251
commit: b0d01a15131f2df64272057de0c1bd0fd8d2c251
branch: 3.13
author: Alyssa Coghlan 
committer: AA-Turner <[email protected]>
date: 2024-10-08T02:16:10+01:00
summary:

[3.13] gh-125018: Add importlib.metadata semantic link targets (GH-125027) 
(#125047)

gh-125018: Add importlib.metadata semantic link targets (#125027)

This allows direct intersphinx references to APIs via references
like `` :func:`importlib.metadata.version` ``.

(cherry picked from commit cda3b5a576412a8671bbe4c68bb792ec14f1a4b1)

-

Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) 

Co-authored-by: Adam Turner <[email protected]>

files:
A Misc/NEWS.d/next/Documentation/2024-10-07-00-31-17.gh-issue-125018.yKnymn.rst
M Doc/library/importlib.metadata.rst

diff --git a/Doc/library/importlib.metadata.rst 
b/Doc/library/importlib.metadata.rst
index 9c0879f5ca850f..5aa95593b2f1fe 100644
--- a/Doc/library/importlib.metadata.rst
+++ b/Doc/library/importlib.metadata.rst
@@ -100,6 +100,13 @@ You can also get a :ref:`distribution's version number 
`, list its
 :ref:`requirements`.
 
 
+.. exception:: PackageNotFoundError
+
+   Subclass of :class:`ModuleNotFoundError` raised by several functions in this
+   module when queried for a distribution package which is not installed in the
+   current Python environment.
+
+
 Functional API
 ==
 
@@ -111,31 +118,53 @@ This package provides the following functionality via its 
public API.
 Entry points
 
 
-The ``entry_points()`` function returns a collection of entry points.
-Entry points are represented by ``EntryPoint`` instances;
-each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and
-a ``.load()`` method to resolve the value.  There are also ``.module``,
-``.attr``, and ``.extras`` attributes for getting the components of the
-``.value`` attribute.
+.. function:: entry_points(**select_params)
+
+   Returns a :class:`EntryPoints` instance describing entry points for the
+   current environment. Any given keyword parameters are passed to the
+   :meth:`!~EntryPoints.select` method for comparison to the attributes of
+   the individual entry point definitions.
+
+   Note: it is not currently possible to query for entry points based on
+   their :attr:`!EntryPoint.dist` attribute (as different 
:class:`!Distribution`
+   instances do not currently compare equal, even if they have the same 
attributes)
+
+.. class:: EntryPoints
+
+   Details of a collection of installed entry points.
+
+   Also provides a ``.groups`` attribute that reports all identifed entry
+   point groups, and a ``.names`` attribute that reports all identified entry
+   point names.
+
+.. class:: EntryPoint
+
+   Details of an installed entry point.
+
+   Each :class:`!EntryPoint` instance has ``.name``, ``.group``, and ``.value``
+   attributes and a ``.load()`` method to resolve the value. There are also
+   ``.module``, ``.attr``, and ``.extras`` attributes for getting the
+   components of the ``.value`` attribute, and ``.dist`` for obtaining
+   information regarding the distribution package that provides the entry 
point.
 
 Query all entry points::
 
 >>> eps = entry_points()  # doctest: +SKIP
 
-The ``entry_points()`` function returns an ``EntryPoints`` object,
-a collection of all ``EntryPoint`` objects with ``names`` and ``groups``
+The :func:`!entry_points` function returns a :class:`!EntryPoints` object,
+a collection of all :class:`!EntryPoint` objects with ``names`` and ``groups``
 attributes for convenience::
 
 >>> sorted(eps.groups)  # doctest: +SKIP
 ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 
'egg_info.writers', 'setuptools.installation']
 
-``EntryPoints`` has a ``select`` method to select entry points
+:class:`!EntryPoints` has a :meth:`!~EntryPoints.select` method to select 
entry points
 matching specific properties. Select entry points in the
 ``console_scripts`` group::
 
 >>> scripts = eps.select(group='console_scripts')  # doctest: +SKIP
 
-Equivalently, since ``entry_points`` passes keyword arguments
+Equivalently, since :func:`!entry_points` passes keyword arguments
 through to select::
 
 >>> scripts = entry_points(group='console_scripts')  # doctest: +SKIP
@@ -189,31 +218,41 @@ for more information on entry points, their definition, 
and usage.
 Distribution metadata
 -
 
-Every `Distribution Package 
`_ 
includes some metadata,
-which you can extract using the
-``metadata()`` function::
+.. function:: metadata(distribution_name)
+
+   Return the distribution metadata corresponding to the named
+   distribution package as a :class:`PackageMetadata` instance.
+
+   Raises :exc:`PackageNotFoundError` if the named distribution
+   package is not installed in the current P

[Python-checkins] [3.13] Docs: make a tutorial example more precise (GH-125066) (#125078)

2024-10-07 Thread AA-Turner
https://github.com/python/cpython/commit/12bb9ed27923576bfbc9af572e983e6f77040ac7
commit: 12bb9ed27923576bfbc9af572e983e6f77040ac7
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: AA-Turner <[email protected]>
date: 2024-10-08T02:15:42+01:00
summary:

[3.13] Docs: make a tutorial example more precise (GH-125066) (#125078)

(cherry picked from commit 6e3c70c61bf961e55e9912a31ca11f61c8e2cd0c)
Co-authored-by: Ned Batchelder 

files:
M Doc/tutorial/introduction.rst

diff --git a/Doc/tutorial/introduction.rst b/Doc/tutorial/introduction.rst
index 054bac59c955d5..65e3b1938bca9c 100644
--- a/Doc/tutorial/introduction.rst
+++ b/Doc/tutorial/introduction.rst
@@ -197,21 +197,19 @@ and workarounds.
 String literals can span multiple lines.  One way is using triple-quotes:
 ``"""..."""`` or ``'''...'''``.  End of lines are automatically
 included in the string, but it's possible to prevent this by adding a ``\`` at
-the end of the line.  The following example::
-
-   print("""\
+the end of the line.  In the following example, the initial newline is not
+included::
+
+   >>> print("""\
+   ... Usage: thingy [OPTIONS]
+   ...  -hDisplay this usage message
+   ...  -H hostname   Hostname to connect to
+   ... """)
Usage: thingy [OPTIONS]
 -hDisplay this usage message
 -H hostname   Hostname to connect to
-   """)
-
-produces the following output (note that the initial newline is not included):
 
-.. code-block:: text
-
-   Usage: thingy [OPTIONS]
--hDisplay this usage message
--H hostname   Hostname to connect to
+   >>>
 
 Strings can be concatenated (glued together) with the ``+`` operator, and
 repeated with ``*``::

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-125072: Add label for assignment expressions; update tracked section for assignment expression topic (GH-125074) (#125077)

2024-10-07 Thread AA-Turner
https://github.com/python/cpython/commit/9a1b9d52546cbb0903f9cef4a1f83e3528479e85
commit: 9a1b9d52546cbb0903f9cef4a1f83e3528479e85
branch: 3.13
author: Emily Morehouse 
committer: AA-Turner <[email protected]>
date: 2024-10-08T02:14:34+01:00
summary:

[3.13] gh-125072: Add label for assignment expressions; update tracked section 
for assignment expression topic (GH-125074) (#125077)

(cherry picked from commit 447a15190d6d766004b77619ba43e44256e348e2)

files:
M Doc/reference/expressions.rst
M Doc/tools/extensions/pyspecific.py

diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst
index ab72ad49d041e1..decde0d297cf59 100644
--- a/Doc/reference/expressions.rst
+++ b/Doc/reference/expressions.rst
@@ -1809,6 +1809,8 @@ returns a boolean value regardless of the type of its 
argument
single: named expression
pair: assignment; expression
 
+.. _assignment-expressions:
+
 Assignment expressions
 ==
 
diff --git a/Doc/tools/extensions/pyspecific.py 
b/Doc/tools/extensions/pyspecific.py
index 6138246ccb4a31..5d27c8ff574036 100644
--- a/Doc/tools/extensions/pyspecific.py
+++ b/Doc/tools/extensions/pyspecific.py
@@ -334,8 +334,8 @@ def run(self):
 # Support for building "topic help" for pydoc
 
 pydoc_topic_labels = [
-'assert', 'assignment', 'async', 'atom-identifiers', 'atom-literals',
-'attribute-access', 'attribute-references', 'augassign', 'await',
+'assert', 'assignment', 'assignment-expressions', 'async',  
'atom-identifiers',
+'atom-literals', 'attribute-access', 'attribute-references', 'augassign', 
'await',
 'binary', 'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object',
 'bltin-null-object', 'bltin-type-objects', 'booleans',
 'break', 'callable-types', 'calls', 'class', 'comparisons', 'compound',

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-125018: Fix role syntax (GH-125050) (#125080)

2024-10-07 Thread AA-Turner
https://github.com/python/cpython/commit/d869d54962102552a70a769d684394ff2ac99564
commit: d869d54962102552a70a769d684394ff2ac99564
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: AA-Turner <[email protected]>
date: 2024-10-08T01:23:19Z
summary:

[3.13] gh-125018: Fix role syntax (GH-125050) (#125080)

gh-125018: Fix role syntax (GH-125050)
(cherry picked from commit 10094a533a947b72d01ed8195dcf540f2e7820ea)

Co-authored-by: Adam Turner <[email protected]>

files:
M Doc/library/importlib.metadata.rst

diff --git a/Doc/library/importlib.metadata.rst 
b/Doc/library/importlib.metadata.rst
index 5aa95593b2f1fe..b27537a9fa13a3 100644
--- a/Doc/library/importlib.metadata.rst
+++ b/Doc/library/importlib.metadata.rst
@@ -122,7 +122,7 @@ Entry points
 
Returns a :class:`EntryPoints` instance describing entry points for the
current environment. Any given keyword parameters are passed to the
-   :meth:`!~EntryPoints.select` method for comparison to the attributes of
+   :meth:`!select` method for comparison to the attributes of
the individual entry point definitions.
 
Note: it is not currently possible to query for entry points based on
@@ -158,7 +158,7 @@ attributes for convenience::
 >>> sorted(eps.groups)  # doctest: +SKIP
 ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 
'egg_info.writers', 'setuptools.installation']
 
-:class:`!EntryPoints` has a :meth:`!~EntryPoints.select` method to select 
entry points
+:class:`!EntryPoints` has a :meth:`!select` method to select entry points
 matching specific properties. Select entry points in the
 ``console_scripts`` group::
 
@@ -232,7 +232,7 @@ Distribution metadata
 `PackageMetadata protocol 
`_.
 
 In addition to providing the defined protocol methods and attributes, 
subscripting
-the instance is equivalent to calling the :meth:`!~PackageMetadata.get` 
method.
+the instance is equivalent to calling the :meth:`!get` method.
 
 Every `Distribution Package 
`_
 includes some metadata, which you can extract using the :func:`!metadata` 
function::
@@ -245,7 +245,7 @@ the values are returned unparsed from the distribution 
metadata::
 >>> wheel_metadata['Requires-Python']  # doctest: +SKIP
 '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
 
-:class:`PackageMetadata` also presents a :attr:`!~PackageMetadata.json` 
attribute that returns
+:class:`PackageMetadata` also presents a :attr:`!json` attribute that returns
 all the metadata in a JSON-compatible form per :PEP:`566`::
 
 >>> wheel_metadata.json['requires_python']
@@ -331,7 +331,7 @@ Once you have the file, you can also read its contents::
 return s.encode('utf-8')
 return s
 
-You can also use the :meth:`!~PackagePath.locate` method to get the absolute
+You can also use the :meth:`!locate` method to get the absolute
 path to the file::
 
 >>> util.locate()  # doctest: +SKIP

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-81691: Fix handling of multiple "--" (double dashes) in argparse (GH-124233) (GH-124266)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/63870162f4d80359025762aec802d46ab0ecdb6b
commit: 63870162f4d80359025762aec802d46ab0ecdb6b
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T00:12:28+03:00
summary:

[3.13] gh-81691: Fix handling of multiple "--" (double dashes) in argparse 
(GH-124233) (GH-124266)

Only the first one has now been removed, all subsequent ones are now
taken literally.
(cherry picked from commit aae126748ff3d442fdbcd07933855ffd7ae6f59c)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-09-19-10-36-18.gh-issue-81691.Hyhp_U.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index b0f96565eba594..7bdaf3155a208d 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -2097,6 +2097,11 @@ def consume_positionals(start_index):
 # and add the Positional and its args to the list
 for action, arg_count in zip(positionals, arg_counts):
 args = arg_strings[start_index: start_index + arg_count]
+# Strip out the first '--' if it is not in PARSER or REMAINDER 
arg.
+if (action.nargs not in [PARSER, REMAINDER]
+and arg_strings_pattern.find('-', start_index,
+ start_index + arg_count) >= 
0):
+args.remove('--')
 start_index += arg_count
 if args and action.deprecated and action.dest not in warned:
 self._warning(_("argument '%(argument_name)s' is 
deprecated") %
@@ -2497,13 +2502,6 @@ def parse_known_intermixed_args(self, args=None, 
namespace=None):
 # Value conversion methods
 # 
 def _get_values(self, action, arg_strings):
-# for everything but PARSER, REMAINDER args, strip out first '--'
-if not action.option_strings and action.nargs not in [PARSER, 
REMAINDER]:
-try:
-arg_strings.remove('--')
-except ValueError:
-pass
-
 # optional argument produces a default when not present
 if not arg_strings and action.nargs == OPTIONAL:
 if action.option_strings:
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index eb744468b55602..5d3476feb47798 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -5784,7 +5784,30 @@ def test_zero_or_more_optional(self):
 self.assertEqual(NS(x=[]), args)
 
 def test_double_dash(self):
-parser = argparse.ArgumentParser()
+parser = argparse.ArgumentParser(exit_on_error=False)
+parser.add_argument('-f', '--foo')
+parser.add_argument('bar', nargs='*')
+
+args = parser.parse_args(['--foo=--'])
+self.assertEqual(NS(foo='--', bar=[]), args)
+self.assertRaisesRegex(argparse.ArgumentError,
+'argument -f/--foo: expected one argument',
+parser.parse_args, ['--foo', '--'])
+args = parser.parse_args(['-f--'])
+self.assertEqual(NS(foo='--', bar=[]), args)
+self.assertRaisesRegex(argparse.ArgumentError,
+'argument -f/--foo: expected one argument',
+parser.parse_args, ['-f', '--'])
+args = parser.parse_args(['--foo', 'a', '--', 'b', 'c'])
+self.assertEqual(NS(foo='a', bar=['b', 'c']), args)
+args = parser.parse_args(['a', 'b', '--foo', 'c'])
+self.assertEqual(NS(foo='c', bar=['a', 'b']), args)
+args = parser.parse_args(['a', '--', 'b', '--foo', 'c'])
+self.assertEqual(NS(foo=None, bar=['a', 'b', '--foo', 'c']), args)
+args = parser.parse_args(['a', '--', 'b', '--', 'c', '--foo', 'd'])
+self.assertEqual(NS(foo=None, bar=['a', 'b', '--', 'c', '--foo', 
'd']), args)
+
+parser = argparse.ArgumentParser(exit_on_error=False)
 parser.add_argument('-f', '--foo', nargs='*')
 parser.add_argument('bar', nargs='*')
 
@@ -5798,6 +5821,41 @@ def test_double_dash(self):
 self.assertEqual(NS(foo=[], bar=[]), args)
 args = parser.parse_args(['--foo', 'a', 'b', '--', 'c', 'd'])
 self.assertEqual(NS(foo=['a', 'b'], bar=['c', 'd']), args)
+args = parser.parse_args(['a', 'b', '--foo', 'c', 'd'])
+self.assertEqual(NS(foo=['c', 'd'], bar=['a', 'b']), args)
+args = parser.parse_args(['a', '--', 'b', '--foo', 'c', 'd'])
+self.assertEqual(NS(foo=None, bar=['a', 'b', '--foo', 'c', 'd']), args)
+args, argv = parser.parse_known_args(['a', 'b', '--foo', 'c', '--', 
'd'])
+self.assertEqual(NS(foo=['c'], bar=['a', 'b']), args)
+self.assertEqual(argv, ['--', 'd'])
+
+parser = argparse.ArgumentParser(exit_on_error=False)
+parser.add_argument('foo')
+parser.add_argument('bar', nargs='*')
+
+args = parser.parse_args(['--', 'a', 'b', 'c'])
+self.ass

[Python-checkins] [3.13] gh-63143: Fix parsing mutually exclusive arguments in argparse (GH-124307) (GH-124418)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/167d8d2f072505a85c6db72fdaf4edffd6d17f3a
commit: 167d8d2f072505a85c6db72fdaf4edffd6d17f3a
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T00:20:31+03:00
summary:

[3.13] gh-63143: Fix parsing mutually exclusive arguments in argparse 
(GH-124307) (GH-124418)

Arguments with the value identical to the default value (e.g. booleans,
small integers, empty or 1-character strings) are no longer considered
"not present".
(cherry picked from commit 3094cd17b0e5ba69309c54964744c797a70aa11b)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-09-21-23-56-41.gh-issue-63143.YKu-LQ.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index 7bdaf3155a208d..95be2b0a51360c 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -1977,9 +1977,8 @@ def take_action(action, argument_strings, 
option_string=None):
 argument_values = self._get_values(action, argument_strings)
 
 # error if this argument is not allowed with other previously
-# seen arguments, assuming that actions that use the default
-# value don't really count as "present"
-if argument_values is not action.default:
+# seen arguments
+if action.option_strings or argument_strings:
 seen_non_default_actions.add(action)
 for conflict_action in action_conflicts.get(action, []):
 if conflict_action in seen_non_default_actions:
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 5d3476feb47798..8841c118685355 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -2900,26 +2900,30 @@ def test_failures_when_not_required(self):
 parse_args = self.get_parser(required=False).parse_args
 error = ArgumentParserError
 for args_string in self.failures:
-self.assertRaises(error, parse_args, args_string.split())
+with self.subTest(args=args_string):
+self.assertRaises(error, parse_args, args_string.split())
 
 def test_failures_when_required(self):
 parse_args = self.get_parser(required=True).parse_args
 error = ArgumentParserError
 for args_string in self.failures + ['']:
-self.assertRaises(error, parse_args, args_string.split())
+with self.subTest(args=args_string):
+self.assertRaises(error, parse_args, args_string.split())
 
 def test_successes_when_not_required(self):
 parse_args = self.get_parser(required=False).parse_args
 successes = self.successes + self.successes_when_not_required
 for args_string, expected_ns in successes:
-actual_ns = parse_args(args_string.split())
-self.assertEqual(actual_ns, expected_ns)
+with self.subTest(args=args_string):
+actual_ns = parse_args(args_string.split())
+self.assertEqual(actual_ns, expected_ns)
 
 def test_successes_when_required(self):
 parse_args = self.get_parser(required=True).parse_args
 for args_string, expected_ns in self.successes:
-actual_ns = parse_args(args_string.split())
-self.assertEqual(actual_ns, expected_ns)
+with self.subTest(args=args_string):
+actual_ns = parse_args(args_string.split())
+self.assertEqual(actual_ns, expected_ns)
 
 def test_usage_when_not_required(self):
 format_usage = self.get_parser(required=False).format_usage
@@ -3306,6 +3310,111 @@ def get_parser(self, required):
 test_successes_when_not_required = None
 test_successes_when_required = None
 
+
+class TestMutuallyExclusiveOptionalOptional(MEMixin, TestCase):
+def get_parser(self, required=None):
+parser = ErrorRaisingArgumentParser(prog='PROG')
+group = parser.add_mutually_exclusive_group(required=required)
+group.add_argument('--foo')
+group.add_argument('--bar', nargs='?')
+return parser
+
+failures = [
+'--foo X --bar Y',
+'--foo X --bar',
+]
+successes = [
+('--foo X', NS(foo='X', bar=None)),
+('--bar X', NS(foo=None, bar='X')),
+('--bar', NS(foo=None, bar=None)),
+]
+successes_when_not_required = [
+('', NS(foo=None, bar=None)),
+]
+usage_when_required = '''\
+usage: PROG [-h] (--foo FOO | --bar [BAR])
+'''
+usage_when_not_required = '''\
+usage: PROG [-h] [--foo FOO | --bar [BAR]]
+'''
+help = '''\
+
+options:
+  -h, --help   show this help message and exit
+  --foo FOO
+  --bar [BAR]
+'''
+
+
+class TestMutuallyExclusiveOptionalWithDefault(MEMixin, TestCase):
+def get_parser(self, required=None):
+parser = ErrorRaisingArgumentPars

[Python-checkins] [3.13] gh-124188: Fix PyErr_ProgramTextObject() (GH-124189) (GH-124423)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/03ae82d0d34649d1fcb755015aa567fe3978d4f9
commit: 03ae82d0d34649d1fcb755015aa567fe3978d4f9
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T00:23:49+03:00
summary:

[3.13] gh-124188: Fix PyErr_ProgramTextObject() (GH-124189) (GH-124423)

* Detect source file encoding.
* Use the "replace" error handler even for UTF-8 (default) encoding.
* Remove the BOM.
* Fix detection of too long lines if they contain NUL.
* Return the head rather than the tail for truncated long lines.
(cherry picked from commit e2f710792b0418b8ca1ca3b8cdf39588c7268495)

Co-authored-by: Serhiy Storchaka 

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2024-09-17-22-06-01.gh-issue-124188.aFqNAB.rst
M Lib/test/support/script_helper.py
M Lib/test/test_compiler_codegen.py
M Lib/test/test_eof.py
M Lib/test/test_exceptions.py
M Python/errors.c

diff --git a/Lib/test/support/script_helper.py 
b/Lib/test/support/script_helper.py
index 65e0bc199e7f0b..04458077d514b6 100644
--- a/Lib/test/support/script_helper.py
+++ b/Lib/test/support/script_helper.py
@@ -232,9 +232,13 @@ def make_script(script_dir, script_basename, source, 
omit_suffix=False):
 if not omit_suffix:
 script_filename += os.extsep + 'py'
 script_name = os.path.join(script_dir, script_filename)
-# The script should be encoded to UTF-8, the default string encoding
-with open(script_name, 'w', encoding='utf-8') as script_file:
-script_file.write(source)
+if isinstance(source, str):
+# The script should be encoded to UTF-8, the default string encoding
+with open(script_name, 'w', encoding='utf-8') as script_file:
+script_file.write(source)
+else:
+with open(script_name, 'wb') as script_file:
+script_file.write(source)
 importlib.invalidate_caches()
 return script_name
 
diff --git a/Lib/test/test_compiler_codegen.py 
b/Lib/test/test_compiler_codegen.py
index d82fb85ed259ab..8a15c400a449e1 100644
--- a/Lib/test/test_compiler_codegen.py
+++ b/Lib/test/test_compiler_codegen.py
@@ -152,5 +152,8 @@ def g():
 
 def test_syntax_error__return_not_in_function(self):
 snippet = "return 42"
-with self.assertRaisesRegex(SyntaxError, "'return' outside function"):
+with self.assertRaisesRegex(SyntaxError, "'return' outside function") 
as cm:
 self.codegen_test(snippet, None)
+self.assertIsNone(cm.exception.text)
+self.assertEqual(cm.exception.offset, 1)
+self.assertEqual(cm.exception.end_offset, 10)
diff --git a/Lib/test/test_eof.py b/Lib/test/test_eof.py
index be4fd73bfdc36b..e377383450e19d 100644
--- a/Lib/test/test_eof.py
+++ b/Lib/test/test_eof.py
@@ -1,6 +1,7 @@
 """test script for a few new invalid token catches"""
 
 import sys
+from codecs import BOM_UTF8
 from test import support
 from test.support import os_helper
 from test.support import script_helper
@@ -11,67 +12,158 @@ class EOFTestCase(unittest.TestCase):
 def test_EOF_single_quote(self):
 expect = "unterminated string literal (detected at line 1) (, 
line 1)"
 for quote in ("'", "\""):
-try:
+with self.assertRaises(SyntaxError) as cm:
 eval(f"""{quote}this is a test\
 """)
-except SyntaxError as msg:
-self.assertEqual(str(msg), expect)
-self.assertEqual(msg.offset, 1)
-else:
-raise support.TestFailed
+self.assertEqual(str(cm.exception), expect)
+self.assertEqual(cm.exception.offset, 1)
 
 def test_EOFS(self):
-expect = ("unterminated triple-quoted string literal (detected at line 
1) (, line 1)")
-try:
-eval("""'''this is a test""")
-except SyntaxError as msg:
-self.assertEqual(str(msg), expect)
-self.assertEqual(msg.offset, 1)
-else:
-raise support.TestFailed
+expect = ("unterminated triple-quoted string literal (detected at line 
3) (, line 1)")
+with self.assertRaises(SyntaxError) as cm:
+eval("""ä = '''thîs is \na \ntest""")
+self.assertEqual(str(cm.exception), expect)
+self.assertEqual(cm.exception.text, "ä = '''thîs is ")
+self.assertEqual(cm.exception.offset, 5)
+
+with self.assertRaises(SyntaxError) as cm:
+eval("""ä = '''thîs is \na \ntest""".encode())
+self.assertEqual(str(cm.exception), expect)
+self.assertEqual(cm.exception.text, "ä = '''thîs is ")
+self.assertEqual(cm.exception.offset, 5)
+
+with self.assertRaises(SyntaxError) as cm:
+eval(BOM_UTF8 + """ä = '''thîs is \na \ntest""".encode())
+self.assertEqual(str(cm.exception), expect)
+self.assertEqual(cm.exception.text, "ä = '''thîs is ")
+self.assertEqual(cm.exception.offset, 5)
+
+with se

[Python-checkins] [3.13] gh-59317: Improve parsing optional positional arguments in argparse (GH-124303) (GH-124436)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/16127de2eb1a3dae56326560952c9b49f547cb3e
commit: 16127de2eb1a3dae56326560952c9b49f547cb3e
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T00:24:31+03:00
summary:

[3.13] gh-59317: Improve parsing optional positional arguments in argparse 
(GH-124303) (GH-124436)

Fix parsing positional argument with nargs equal to '?' or '*' if it is
preceded by an option and another positional argument.
(cherry picked from commit 4a5e4aade420c594c5b3fe0589e9e6b444bd6ee5)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-09-21-19-02-37.gh-issue-59317.OAhNZZ.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index 95be2b0a51360c..04a535e42bed01 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -2252,18 +2252,19 @@ def _match_argument(self, action, arg_strings_pattern):
 def _match_arguments_partial(self, actions, arg_strings_pattern):
 # progressively shorten the actions list by slicing off the
 # final actions until we find a match
-result = []
 for i in range(len(actions), 0, -1):
 actions_slice = actions[:i]
 pattern = ''.join([self._get_nargs_pattern(action)
for action in actions_slice])
 match = _re.match(pattern, arg_strings_pattern)
 if match is not None:
-result.extend([len(string) for string in match.groups()])
-break
-
-# return the list of arg string counts
-return result
+result = [len(string) for string in match.groups()]
+if (match.end() < len(arg_strings_pattern)
+and arg_strings_pattern[match.end()] == 'O'):
+while result and not result[-1]:
+del result[-1]
+return result
+return []
 
 def _parse_optional(self, arg_string):
 # if it's an empty string, it was meant to be a positional
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 8841c118685355..24260bec0cae36 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -280,16 +280,18 @@ def test_failures(self, tester):
 parser = self._get_parser(tester)
 for args_str in tester.failures:
 args = args_str.split()
-with tester.assertRaises(ArgumentParserError, msg=args):
-parser.parse_args(args)
+with tester.subTest(args=args):
+with tester.assertRaises(ArgumentParserError, 
msg=args):
+parser.parse_args(args)
 
 def test_successes(self, tester):
 parser = self._get_parser(tester)
 for args, expected_ns in tester.successes:
 if isinstance(args, str):
 args = args.split()
-result_ns = self._parse_args(parser, args)
-tester.assertEqual(expected_ns, result_ns)
+with tester.subTest(args=args):
+result_ns = self._parse_args(parser, args)
+tester.assertEqual(expected_ns, result_ns)
 
 # add tests for each combination of an optionals adding method
 # and an arg parsing method
@@ -1132,57 +1134,87 @@ class TestPositionalsNargs2None(ParserTestCase):
 class TestPositionalsNargsNoneZeroOrMore(ParserTestCase):
 """Test a Positional with no nargs followed by one with unlimited"""
 
-argument_signatures = [Sig('foo'), Sig('bar', nargs='*')]
-failures = ['', '--foo']
+argument_signatures = [Sig('-x'), Sig('foo'), Sig('bar', nargs='*')]
+failures = ['', '--foo', 'a b -x X c']
 successes = [
-('a', NS(foo='a', bar=[])),
-('a b', NS(foo='a', bar=['b'])),
-('a b c', NS(foo='a', bar=['b', 'c'])),
+('a', NS(x=None, foo='a', bar=[])),
+('a b', NS(x=None, foo='a', bar=['b'])),
+('a b c', NS(x=None, foo='a', bar=['b', 'c'])),
+('-x X a', NS(x='X', foo='a', bar=[])),
+('a -x X', NS(x='X', foo='a', bar=[])),
+('-x X a b', NS(x='X', foo='a', bar=['b'])),
+('a -x X b', NS(x='X', foo='a', bar=['b'])),
+('a b -x X', NS(x='X', foo='a', bar=['b'])),
+('-x X a b c', NS(x='X', foo='a', bar=['b', 'c'])),
+('a -x X b c', NS(x='X', foo='a', bar=['b', 'c'])),
+('a b c -x X', NS(x='X', foo='a', bar=['b', 'c'])),
 ]
 
 
 class TestPositionalsNargsNoneOneOrMore(ParserTestCase):
 """Test a Positional with no nargs followed by one with one or more"""
 
-argument_signatures = [Sig('foo'), Sig('bar', nargs='+')]
-failures = ['', '--foo', 'a']
+argument_signatures = [Sig('-x'), Sig('foo'), Sig('bar', nargs='+')]
+failures = ['', '--foo', 'a', 

[Python-checkins] gh-124502: Add PyUnicode_Equal() function (#124504)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/a7f0727ca575fef4d8891b5ebfe71ef2a774868b
commit: a7f0727ca575fef4d8891b5ebfe71ef2a774868b
branch: main
author: Victor Stinner 
committer: vstinner 
date: 2024-10-07T21:24:53Z
summary:

gh-124502: Add PyUnicode_Equal() function (#124504)

files:
A Misc/NEWS.d/next/C_API/2024-09-25-11-44-02.gh-issue-124502.qWuDjT.rst
M Doc/c-api/unicode.rst
M Doc/data/stable_abi.dat
M Doc/whatsnew/3.14.rst
M Include/unicodeobject.h
M Lib/test/test_capi/test_unicode.py
M Lib/test/test_stable_abi_ctypes.py
M Misc/stable_abi.toml
M Modules/_testlimitedcapi/unicode.c
M Objects/unicodeobject.c
M PC/python3dll.c

diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst
index b2ac0c903c2bd7..f5704cffa199a5 100644
--- a/Doc/c-api/unicode.rst
+++ b/Doc/c-api/unicode.rst
@@ -1438,6 +1438,31 @@ They all return ``NULL`` or ``-1`` if an exception 
occurs.
This function returns ``-1`` upon failure, so one should call
:c:func:`PyErr_Occurred` to check for errors.
 
+   .. seealso::
+
+  The :c:func:`PyUnicode_Equal` function.
+
+
+.. c:function:: int PyUnicode_Equal(PyObject *a, PyObject *b)
+
+   Test if two strings are equal:
+
+   * Return ``1`` if *a* is equal to *b*.
+   * Return ``0`` if *a* is not equal to *b*.
+   * Set a :exc:`TypeError` exception and return ``-1`` if *a* or *b* is not a
+ :class:`str` object.
+
+   The function always succeeds if *a* and *b* are :class:`str` objects.
+
+   The function works for :class:`str` subclasses, but does not honor custom
+   ``__eq__()`` method.
+
+   .. seealso::
+
+  The :c:func:`PyUnicode_Compare` function.
+
+   .. versionadded:: 3.14
+
 
 .. c:function:: int PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char 
*string, Py_ssize_t size)
 
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 19dc71a345b474..9314facd2ad873 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -783,6 +783,7 @@ func,PyUnicode_DecodeUnicodeEscape,3.2,,
 func,PyUnicode_EncodeCodePage,3.7,on Windows,
 func,PyUnicode_EncodeFSDefault,3.2,,
 func,PyUnicode_EncodeLocale,3.7,,
+func,PyUnicode_Equal,3.14,,
 func,PyUnicode_EqualToUTF8,3.13,,
 func,PyUnicode_EqualToUTF8AndSize,3.13,,
 func,PyUnicode_FSConverter,3.2,,
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 67d8d389b58082..f1f78ed843f313 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -687,6 +687,10 @@ New Features
   `__ mentioned in :pep:`630`
   (:gh:`124153`).
 
+* Add :c:func:`PyUnicode_Equal` function to the limited C API:
+  test if two strings are equal.
+  (Contributed by Victor Stinner in :gh:`124502`.)
+
 
 Porting to Python 3.14
 --
diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h
index dee00715b3c51d..2ce3a008b7129e 100644
--- a/Include/unicodeobject.h
+++ b/Include/unicodeobject.h
@@ -966,6 +966,10 @@ PyAPI_FUNC(int) PyUnicode_EqualToUTF8(PyObject *, const 
char *);
 PyAPI_FUNC(int) PyUnicode_EqualToUTF8AndSize(PyObject *, const char *, 
Py_ssize_t);
 #endif
 
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e
+PyAPI_FUNC(int) PyUnicode_Equal(PyObject *str1, PyObject *str2);
+#endif
+
 /* Rich compare two strings and return one of the following:
 
- NULL in case an exception was raised
diff --git a/Lib/test/test_capi/test_unicode.py 
b/Lib/test/test_capi/test_unicode.py
index e6f85427214958..65d8242ad3fc60 100644
--- a/Lib/test/test_capi/test_unicode.py
+++ b/Lib/test/test_capi/test_unicode.py
@@ -1903,6 +1903,39 @@ def test_recover_error(self):
 
 self.assertEqual(writer.finish(), 'Hello World.')
 
+def test_unicode_equal(self):
+unicode_equal = _testlimitedcapi.unicode_equal
+
+def copy(text):
+return text.encode().decode()
+
+self.assertTrue(unicode_equal("", ""))
+self.assertTrue(unicode_equal("abc", "abc"))
+self.assertTrue(unicode_equal("abc", copy("abc")))
+self.assertTrue(unicode_equal("\u20ac", copy("\u20ac")))
+self.assertTrue(unicode_equal("\U0010", copy("\U0010")))
+
+self.assertFalse(unicode_equal("abc", "abcd"))
+self.assertFalse(unicode_equal("\u20ac", "\u20ad"))
+self.assertFalse(unicode_equal("\U0010", "\U0010fffe"))
+
+# str subclass
+self.assertTrue(unicode_equal("abc", Str("abc")))
+self.assertTrue(unicode_equal(Str("abc"), "abc"))
+self.assertFalse(unicode_equal("abc", Str("abcd")))
+self.assertFalse(unicode_equal(Str("abc"), "abcd"))
+
+# invalid type
+for invalid_type in (b'bytes', 123, ("tuple",)):
+with self.subTest(invalid_type=invalid_type):
+with self.assertRaises(TypeError):
+unicode_equal("abc", invalid_type)
+with self.assertRaises(TypeError):
+unicode_equal(invalid_type, "abc")
+
+# CRASHES unicode_equal("abc", NULL)
+

[Python-checkins] [3.13] gh-104860: Fix allow_abbrev=False for single-dash long options (GH-124340) (GH-124749)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/aa648c21e982c142d24471f4d4b52127402904f6
commit: aa648c21e982c142d24471f4d4b52127402904f6
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T00:25:13+03:00
summary:

[3.13] gh-104860: Fix allow_abbrev=False for single-dash long options 
(GH-124340) (GH-124749)

(cherry picked from commit 49e105f9488de18d3d92948232fcbd956cbe0c6e)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-09-23-17-33-47.gh-issue-104860.O86OSc.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index 04a535e42bed01..7c237063706336 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -2353,7 +2353,7 @@ def _get_option_tuples(self, option_string):
 action = self._option_string_actions[option_string]
 tup = action, option_string, '', short_explicit_arg
 result.append(tup)
-elif option_string.startswith(option_prefix):
+elif self.allow_abbrev and 
option_string.startswith(option_prefix):
 action = self._option_string_actions[option_string]
 tup = action, option_string, None, None
 result.append(tup)
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 24260bec0cae36..81daa4299c32ae 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -957,6 +957,23 @@ class 
TestOptionalsDisallowLongAbbreviationPrefixChars(ParserTestCase):
 ]
 
 
+class TestOptionalsDisallowSingleDashLongAbbreviation(ParserTestCase):
+"""Do not allow abbreviations of long options at all"""
+
+parser_signature = Sig(allow_abbrev=False)
+argument_signatures = [
+Sig('-foo'),
+Sig('-foodle', action='store_true'),
+Sig('-foonly'),
+]
+failures = ['-foon 3', '-food', '-food -foo 2']
+successes = [
+('', NS(foo=None, foodle=False, foonly=None)),
+('-foo 3', NS(foo='3', foodle=False, foonly=None)),
+('-foonly 7 -foodle -foo 2', NS(foo='2', foodle=True, foonly='7')),
+]
+
+
 class TestDisallowLongAbbreviationAllowsShortGrouping(ParserTestCase):
 """Do not allow abbreviations of long options at all"""
 
diff --git 
a/Misc/NEWS.d/next/Library/2024-09-23-17-33-47.gh-issue-104860.O86OSc.rst 
b/Misc/NEWS.d/next/Library/2024-09-23-17-33-47.gh-issue-104860.O86OSc.rst
new file mode 100644
index 00..707c4d651cb5e6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-09-23-17-33-47.gh-issue-104860.O86OSc.rst
@@ -0,0 +1,2 @@
+Fix disallowing abbreviation of single-dash long options in :mod:`argparse`
+with ``allow_abbrev=False``.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-80259: Fix conflict between type and default=SUPPRESS in argparse (GH-124519) (GH-124751)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/f28906e58e003c3eb52db6f3f68e110263fb08c8
commit: f28906e58e003c3eb52db6f3f68e110263fb08c8
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T00:26:00+03:00
summary:

[3.13] gh-80259: Fix conflict between type and default=SUPPRESS in argparse 
(GH-124519) (GH-124751)

type() no longer called for SUPPRESS.

This only affects positional arguments with nargs='?'.
(cherry picked from commit 9bcadf589ab6f7b9d309290de7a80156b6905d35)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-09-25-18-08-29.gh-issue-80259.kO5Tw7.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index 7c237063706336..810586f379c14c 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -2508,7 +2508,7 @@ def _get_values(self, action, arg_strings):
 value = action.const
 else:
 value = action.default
-if isinstance(value, str):
+if isinstance(value, str) and value is not SUPPRESS:
 value = self._get_value(action, value)
 self._check_value(action, value)
 
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 81daa4299c32ae..97641db5de255b 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -1630,18 +1630,24 @@ class TestDefaultSuppress(ParserTestCase):
 """Test actions with suppressed defaults"""
 
 argument_signatures = [
-Sig('foo', nargs='?', default=argparse.SUPPRESS),
-Sig('bar', nargs='*', default=argparse.SUPPRESS),
+Sig('foo', nargs='?', type=int, default=argparse.SUPPRESS),
+Sig('bar', nargs='*', type=int, default=argparse.SUPPRESS),
 Sig('--baz', action='store_true', default=argparse.SUPPRESS),
+Sig('--qux', nargs='?', type=int, default=argparse.SUPPRESS),
+Sig('--quux', nargs='*', type=int, default=argparse.SUPPRESS),
 ]
-failures = ['-x']
+failures = ['-x', 'a', '1 a']
 successes = [
 ('', NS()),
-('a', NS(foo='a')),
-('a b', NS(foo='a', bar=['b'])),
+('1', NS(foo=1)),
+('1 2', NS(foo=1, bar=[2])),
 ('--baz', NS(baz=True)),
-('a --baz', NS(foo='a', baz=True)),
-('--baz a b', NS(foo='a', bar=['b'], baz=True)),
+('1 --baz', NS(foo=1, baz=True)),
+('--baz 1 2', NS(foo=1, bar=[2], baz=True)),
+('--qux', NS(qux=None)),
+('--qux 1', NS(qux=1)),
+('--quux', NS(quux=[])),
+('--quux 1 2', NS(quux=[1, 2])),
 ]
 
 
diff --git 
a/Misc/NEWS.d/next/Library/2024-09-25-18-08-29.gh-issue-80259.kO5Tw7.rst 
b/Misc/NEWS.d/next/Library/2024-09-25-18-08-29.gh-issue-80259.kO5Tw7.rst
new file mode 100644
index 00..bb451cdd9ae44c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-09-25-18-08-29.gh-issue-80259.kO5Tw7.rst
@@ -0,0 +1,2 @@
+Fix :mod:`argparse` support of positional arguments with ``nargs='?'``,
+``default=argparse.SUPPRESS`` and specified ``type``.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-124345: Support abbreviated single-dash long options with = in argparse (GH-124428) (GH-124753)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/597b6211ab6ddbf1bc5bdf15d404c1a9a94d04af
commit: 597b6211ab6ddbf1bc5bdf15d404c1a9a94d04af
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T00:26:37+03:00
summary:

[3.13] gh-124345: Support abbreviated single-dash long options with = in 
argparse (GH-124428) (GH-124753)

(cherry picked from commit 61180446eee2aef07b042c7e8892c45afabd1499)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-09-24-12-34-48.gh-issue-124345.s3vKql.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index 810586f379c14c..ac08a809e93f2e 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -2344,7 +2344,9 @@ def _get_option_tuples(self, option_string):
 # but multiple character options always have to have their argument
 # separate
 elif option_string[0] in chars and option_string[1] not in chars:
-option_prefix = option_string
+option_prefix, sep, explicit_arg = option_string.partition('=')
+if not sep:
+sep = explicit_arg = None
 short_option_prefix = option_string[:2]
 short_explicit_arg = option_string[2:]
 
@@ -2355,7 +2357,7 @@ def _get_option_tuples(self, option_string):
 result.append(tup)
 elif self.allow_abbrev and 
option_string.startswith(option_prefix):
 action = self._option_string_actions[option_string]
-tup = action, option_string, None, None
+tup = action, option_string, sep, explicit_arg
 result.append(tup)
 
 # shouldn't ever get here
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 97641db5de255b..18640a189430ef 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -380,15 +380,22 @@ class TestOptionalsSingleDashAmbiguous(ParserTestCase):
 """Test Optionals that partially match but are not subsets"""
 
 argument_signatures = [Sig('-foobar'), Sig('-foorab')]
-failures = ['-f', '-f a', '-fa', '-foa', '-foo', '-fo', '-foo b']
+failures = ['-f', '-f a', '-fa', '-foa', '-foo', '-fo', '-foo b',
+'-f=a', '-foo=b']
 successes = [
 ('', NS(foobar=None, foorab=None)),
 ('-foob a', NS(foobar='a', foorab=None)),
+('-foob=a', NS(foobar='a', foorab=None)),
 ('-foor a', NS(foobar=None, foorab='a')),
+('-foor=a', NS(foobar=None, foorab='a')),
 ('-fooba a', NS(foobar='a', foorab=None)),
+('-fooba=a', NS(foobar='a', foorab=None)),
 ('-foora a', NS(foobar=None, foorab='a')),
+('-foora=a', NS(foobar=None, foorab='a')),
 ('-foobar a', NS(foobar='a', foorab=None)),
+('-foobar=a', NS(foobar='a', foorab=None)),
 ('-foorab a', NS(foobar=None, foorab='a')),
+('-foorab=a', NS(foobar=None, foorab='a')),
 ]
 
 
@@ -918,7 +925,9 @@ class TestOptionalsAllowLongAbbreviation(ParserTestCase):
 successes = [
 ('', NS(foo=None, foobaz=None, fooble=False)),
 ('--foo 7', NS(foo='7', foobaz=None, fooble=False)),
+('--foo=7', NS(foo='7', foobaz=None, fooble=False)),
 ('--fooba a', NS(foo=None, foobaz='a', fooble=False)),
+('--fooba=a', NS(foo=None, foobaz='a', fooble=False)),
 ('--foobl --foo g', NS(foo='g', foobaz=None, fooble=True)),
 ]
 
diff --git 
a/Misc/NEWS.d/next/Library/2024-09-24-12-34-48.gh-issue-124345.s3vKql.rst 
b/Misc/NEWS.d/next/Library/2024-09-24-12-34-48.gh-issue-124345.s3vKql.rst
new file mode 100644
index 00..dff902d8c6139a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-09-24-12-34-48.gh-issue-124345.s3vKql.rst
@@ -0,0 +1,2 @@
+:mod:`argparse` vim supports abbreviated single-dash long options separated
+by ``=`` from its value.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-116850: Fix argparse for namespaces with not directly writable dict (GH-124667) (GH-124757)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/11d4b54b88063efbeb8968263dfa77dcfb816f8c
commit: 11d4b54b88063efbeb8968263dfa77dcfb816f8c
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T00:27:11+03:00
summary:

[3.13] gh-116850: Fix argparse for namespaces with not directly writable dict 
(GH-124667) (GH-124757)

It now always uses setattr() instead of setting the dict item to modify
the namespace. This allows to use a class as a namespace.
(cherry picked from commit 95e92ef6c74e973ea13d15180190d0fa2af82fbf)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-09-27-15-16-04.gh-issue-116850.dBkR0-.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index ac08a809e93f2e..5d6a065da9e6c5 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -1252,7 +1252,8 @@ def __call__(self, parser, namespace, values, 
option_string=None):
 setattr(namespace, key, value)
 
 if arg_strings:
-vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, [])
+if not hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR):
+setattr(namespace, _UNRECOGNIZED_ARGS_ATTR, [])
 getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings)
 
 class _ExtendAction(_AppendAction):
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 18640a189430ef..7a55dc2f941c6f 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -2365,6 +2365,18 @@ def test_parse_known_args(self):
 (NS(foo=False, bar=0.5, w=7, x='b'), ['-W', '-X', 'Y', 'Z']),
 )
 
+def test_parse_known_args_to_class_namespace(self):
+class C:
+pass
+self.assertEqual(
+self.parser.parse_known_args('0.5 1 b -w 7 -p'.split(), 
namespace=C),
+(C, ['-p']),
+)
+self.assertIs(C.foo, False)
+self.assertEqual(C.bar, 0.5)
+self.assertEqual(C.w, 7)
+self.assertEqual(C.x, 'b')
+
 def test_parse_known_args_with_single_dash_option(self):
 parser = ErrorRaisingArgumentParser()
 parser.add_argument('-k', '--known', action='count', default=0)
diff --git 
a/Misc/NEWS.d/next/Library/2024-09-27-15-16-04.gh-issue-116850.dBkR0-.rst 
b/Misc/NEWS.d/next/Library/2024-09-27-15-16-04.gh-issue-116850.dBkR0-.rst
new file mode 100644
index 00..62639a16c52aa0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-09-27-15-16-04.gh-issue-116850.dBkR0-.rst
@@ -0,0 +1,2 @@
+Fix :mod:`argparse` for namespaces with not directly writable dict (e.g.
+classes).

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-61181: Fix support of choices with string value in argparse (GH-124578) (GH-124755)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/4a9a359f32d3f42c509bb9be770370ba21634fc6
commit: 4a9a359f32d3f42c509bb9be770370ba21634fc6
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T00:28:17+03:00
summary:

[3.13] gh-61181: Fix support of choices with string value in argparse 
(GH-124578) (GH-124755)

Substrings of the specified string no longer considered valid values.
(cherry picked from commit f1a2417b9e2993e584610851ac004c8b0599b323)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-09-26-09-18-09.gh-issue-61181.dwjmch.rst
M Doc/library/argparse.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index 53ecc97d5659f4..95ee7574610001 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -1804,7 +1804,7 @@ Sub-commands
  >>>
  >>> # create the parser for the "b" command
  >>> parser_b = subparsers.add_parser('b', help='b help')
- >>> parser_b.add_argument('--baz', choices='XYZ', help='baz help')
+ >>> parser_b.add_argument('--baz', choices=('X', 'Y', 'Z'), help='baz 
help')
  >>>
  >>> # parse some argument lists
  >>> parser.parse_args(['a', '12'])
diff --git a/Lib/argparse.py b/Lib/argparse.py
index 5d6a065da9e6c5..926907ad0f3221 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -2582,11 +2582,15 @@ def _get_value(self, action, arg_string):
 
 def _check_value(self, action, value):
 # converted value must be one of the choices (if specified)
-if action.choices is not None and value not in action.choices:
-args = {'value': value,
-'choices': ', '.join(map(repr, action.choices))}
-msg = _('invalid choice: %(value)r (choose from %(choices)s)')
-raise ArgumentError(action, msg % args)
+choices = action.choices
+if choices is not None:
+if isinstance(choices, str):
+choices = iter(choices)
+if value not in choices:
+args = {'value': value,
+'choices': ', '.join(map(repr, action.choices))}
+msg = _('invalid choice: %(value)r (choose from %(choices)s)')
+raise ArgumentError(action, msg % args)
 
 # ===
 # Help-formatting methods
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 7a55dc2f941c6f..fe71e8a2a95154 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -686,7 +686,7 @@ class TestOptionalsChoices(ParserTestCase):
 argument_signatures = [
 Sig('-f', choices='abc'),
 Sig('-g', type=int, choices=range(5))]
-failures = ['a', '-f d', '-fad', '-ga', '-g 6']
+failures = ['a', '-f d', '-f ab', '-fad', '-ga', '-g 6']
 successes = [
 ('', NS(f=None, g=None)),
 ('-f a', NS(f='a', g=None)),
@@ -2291,14 +2291,14 @@ def _get_parser(self, subparser_help=False, 
prefix_chars=None,
 parser1_kwargs['aliases'] = ['1alias1', '1alias2']
 parser1 = subparsers.add_parser('1', **parser1_kwargs)
 parser1.add_argument('-w', type=int, help='w help')
-parser1.add_argument('x', choices='abc', help='x help')
+parser1.add_argument('x', choices=['a', 'b', 'c'], help='x help')
 
 # add second sub-parser
 parser2_kwargs = dict(description='2 description')
 if subparser_help:
 parser2_kwargs['help'] = '2 help'
 parser2 = subparsers.add_parser('2', **parser2_kwargs)
-parser2.add_argument('-y', choices='123', help='y help')
+parser2.add_argument('-y', choices=['1', '2', '3'], help='y help')
 parser2.add_argument('z', type=complex, nargs='*', help='z help')
 
 # add third sub-parser
@@ -4628,7 +4628,7 @@ class TestHelpVariableExpansion(HelpTestCase):
 help='x %(prog)s %(default)s %(type)s %%'),
 Sig('-y', action='store_const', default=42, const='XXX',
 help='y %(prog)s %(default)s %(const)s'),
-Sig('--foo', choices='abc',
+Sig('--foo', choices=['a', 'b', 'c'],
 help='foo %(prog)s %(default)s %(choices)s'),
 Sig('--bar', default='baz', choices=[1, 2], metavar='BBB',
 help='bar %(prog)s %(default)s %(dest)s'),
@@ -5291,7 +5291,7 @@ def test_no_argument_actions(self):
 for action in ['store_const', 'store_true', 'store_false',
'append_const', 'count']:
 for attrs in [dict(type=int), dict(nargs='+'),
-  dict(choices='ab')]:
+  dict(choices=['a', 'b'])]:
 self.assertTypeError('-x', action=action, **attrs)
 
 def test_no_argument_no_const_actions(self):
diff --git 
a/Misc/NEWS.d/next/Library/2024-09-26-09-18-09.gh-issue-61181.dwjmch.rst 
b/Misc/NEWS.d/next/Library/2024-09-26-09-18-09.gh-issue-61

[Python-checkins] [3.13] gh-124842: Fix test.support.import_helper.make_legacy_pyc() (GH-124843) (GH-124853)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/970f3b73d20e792653d690152edccd7b6079ee95
commit: 970f3b73d20e792653d690152edccd7b6079ee95
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-08T00:29:54+03:00
summary:

[3.13] gh-124842: Fix test.support.import_helper.make_legacy_pyc() (GH-124843) 
(GH-124853)

For source file "path/to/file.py" it created file with incorrect path
"/absolute/path/to/path/to/file.pyc" instead of "path/to/file.pyc".
(cherry picked from commit 60ff67d010078eca15a74b1429caf779ac4f9c74)

Co-authored-by: Serhiy Storchaka 

files:
M Lib/test/support/import_helper.py

diff --git a/Lib/test/support/import_helper.py 
b/Lib/test/support/import_helper.py
index edcd2b9a35bbd9..2b91bdcf9cd859 100644
--- a/Lib/test/support/import_helper.py
+++ b/Lib/test/support/import_helper.py
@@ -58,8 +58,8 @@ def make_legacy_pyc(source):
 :return: The file system path to the legacy pyc file.
 """
 pyc_file = importlib.util.cache_from_source(source)
-up_one = os.path.dirname(os.path.abspath(source))
-legacy_pyc = os.path.join(up_one, source + 'c')
+assert source.endswith('.py')
+legacy_pyc = source + 'c'
 shutil.move(pyc_file, legacy_pyc)
 return legacy_pyc
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] gh-125072: Add label for assignment expressions; update tracked section for assignment expression topic (#125074)

2024-10-07 Thread emilyemorehouse
https://github.com/python/cpython/commit/447a15190d6d766004b77619ba43e44256e348e2
commit: 447a15190d6d766004b77619ba43e44256e348e2
branch: main
author: Emily Morehouse 
committer: emilyemorehouse 
date: 2024-10-07T22:51:14Z
summary:

gh-125072: Add label for assignment expressions; update tracked section for 
assignment expression topic (#125074)

files:
M Doc/reference/expressions.rst
M Doc/tools/extensions/pyspecific.py
M Lib/pydoc_data/topics.py

diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst
index ab72ad49d041e1..decde0d297cf59 100644
--- a/Doc/reference/expressions.rst
+++ b/Doc/reference/expressions.rst
@@ -1809,6 +1809,8 @@ returns a boolean value regardless of the type of its 
argument
single: named expression
pair: assignment; expression
 
+.. _assignment-expressions:
+
 Assignment expressions
 ==
 
diff --git a/Doc/tools/extensions/pyspecific.py 
b/Doc/tools/extensions/pyspecific.py
index c89b1693343b4e..b6623a2b8e01f1 100644
--- a/Doc/tools/extensions/pyspecific.py
+++ b/Doc/tools/extensions/pyspecific.py
@@ -353,8 +353,8 @@ def run(self):
 # Support for building "topic help" for pydoc
 
 pydoc_topic_labels = [
-'assert', 'assignment', 'async', 'atom-identifiers', 'atom-literals',
-'attribute-access', 'attribute-references', 'augassign', 'await',
+'assert', 'assignment', 'assignment-expressions', 'async',  
'atom-identifiers',
+'atom-literals', 'attribute-access', 'attribute-references', 'augassign', 
'await',
 'binary', 'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object',
 'bltin-null-object', 'bltin-type-objects', 'booleans',
 'break', 'callable-types', 'calls', 'class', 'comparisons', 'compound',
diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py
index 97bb4eb52f4386..ae56c136608472 100644
--- a/Lib/pydoc_data/topics.py
+++ b/Lib/pydoc_data/topics.py
@@ -417,33 +417,43 @@
'caused a\n'
'syntax error.\n',
  'assignment-expressions': 'Assignment expressions\n'
-  '**\n'
-  '\n'
-  'An assignment expression (sometimes also called a “named 
expression”'
-  '\nor “walrus”) assigns an expression to an identifier, while also\n'
-  'returning the value of the expression.\n'
-  '\n'
-  'One common use case is when handling matched regular expressions:\n'
-  '\n'
-  '   if matching := pattern.search(data):\n'
-  '  do_something(matching)\n'
-  '\n'
-  'Or, when processing a file stream in chunks:\n'
-  '\n'
-  '   while chunk := file.read(9000):\n'
-  '  process(chunk)\n'
-  '\n'
-  'Assignment expressions must be surrounded by parentheses when used 
as\n'
-  'expression statements and when used as sub-expressions in 
slicing,\n'
-  'conditional, lambda, keyword-argument, and comprehension-if\n'
-  'expressions and in assert, with, and assignment statements. In 
all\n'
-  'other places where they can be used, parentheses are not 
required,\n'
-  'including in if and while statements.\n'
-  '\n'
-  'Added in version 3.8.\n'
-  'See also:\n'
-  '\n'
-  '  **PEP 572** - Assignment Expressions\n',
+   '**\n'
+   '\n'
+   '   assignment_expression ::= [identifier ":="] '
+   'expression\n'
+   '\n'
+   'An assignment expression (sometimes also called a '
+   '“named expression”\n'
+   'or “walrus”) assigns an "expression" to an '
+   '"identifier", while also\n'
+   'returning the value of the "expression".\n'
+   '\n'
+   'One common use case is when handling matched '
+   'regular expressions:\n'
+   '\n'
+   '   if matching := pattern.search(data):\n'
+   '   do_something(matching)\n'
+   '\n'
+   'Or, when processing a file stream in chunks:\n'
+   '\n'
+   '   while chunk := file.read(9000):\n'
+   '   process(chunk)\n'
+   '\n'
+   'Assignment expressions must be surrounded by '
+   'parentheses when used as\n'
+   'expression statements and when used as '
+   'sub-expressions in slicing,\n'
+   'conditional, lambda, keyword-argument, and '
+   'comprehension-if\n'
+   'expressions and in "assert", "with", and '
+   

[Python-checkins] [3.13] gh-72795: Make positional arguments with nargs='*' or REMAINDER non-required (GH-124306) (#124421)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/0a046771c0b274356ff3d550401b009a9eff3a9a
commit: 0a046771c0b274356ff3d550401b009a9eff3a9a
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-07T22:51:35Z
summary:

[3.13] gh-72795: Make positional arguments with nargs='*' or REMAINDER 
non-required (GH-124306) (#124421)

This allows to use positional argument with nargs='*' and without default
in mutually exclusive group and improves error message about required
arguments.
(cherry picked from commit 3c83f9958c14cd62ad8951c53536f7788745b0ba)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-09-21-22-32-21.gh-issue-72795.naLmkX.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index 454fe42b8e8be5..980f02de9a0910 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -1561,9 +1561,8 @@ def _get_positional_kwargs(self, dest, **kwargs):
 
 # mark positional arguments as required if at least one is
 # always required
-if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]:
-kwargs['required'] = True
-if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs:
+nargs = kwargs.get('nargs')
+if nargs not in [OPTIONAL, ZERO_OR_MORE, REMAINDER, SUPPRESS, 0]:
 kwargs['required'] = True
 
 # return the keyword arguments with no option strings
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index de65a7d24d5bff..3bf90866388bec 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -3201,7 +3201,7 @@ def get_parser(self, required):
 group = parser.add_mutually_exclusive_group(required=required)
 group.add_argument('--foo', action='store_true', help='FOO')
 group.add_argument('--spam', help='SPAM')
-group.add_argument('badger', nargs='*', default='X', help='BADGER')
+group.add_argument('badger', nargs='*', help='BADGER')
 return parser
 
 failures = [
@@ -3212,13 +3212,13 @@ def get_parser(self, required):
 '--foo X Y',
 ]
 successes = [
-('--foo', NS(foo=True, spam=None, badger='X')),
-('--spam S', NS(foo=False, spam='S', badger='X')),
+('--foo', NS(foo=True, spam=None, badger=[])),
+('--spam S', NS(foo=False, spam='S', badger=[])),
 ('X', NS(foo=False, spam=None, badger=['X'])),
 ('X Y Z', NS(foo=False, spam=None, badger=['X', 'Y', 'Z'])),
 ]
 successes_when_not_required = [
-('', NS(foo=False, spam=None, badger='X')),
+('', NS(foo=False, spam=None, badger=[])),
 ]
 
 usage_when_not_required = '''\
@@ -6491,7 +6491,28 @@ def test_required_args(self):
 self.parser.add_argument('bar')
 self.parser.add_argument('baz')
 self.assertRaisesRegex(argparse.ArgumentError,
-   'the following arguments are required: bar, 
baz',
+   'the following arguments are required: bar, 
baz$',
+   self.parser.parse_args, [])
+
+def test_required_args_optional(self):
+self.parser.add_argument('bar')
+self.parser.add_argument('baz', nargs='?')
+self.assertRaisesRegex(argparse.ArgumentError,
+   'the following arguments are required: bar$',
+   self.parser.parse_args, [])
+
+def test_required_args_zero_or_more(self):
+self.parser.add_argument('bar')
+self.parser.add_argument('baz', nargs='*')
+self.assertRaisesRegex(argparse.ArgumentError,
+   'the following arguments are required: bar$',
+   self.parser.parse_args, [])
+
+def test_required_args_remainder(self):
+self.parser.add_argument('bar')
+self.parser.add_argument('baz', nargs='...')
+self.assertRaisesRegex(argparse.ArgumentError,
+   'the following arguments are required: bar$',
self.parser.parse_args, [])
 
 def test_required_mutually_exclusive_args(self):
diff --git 
a/Misc/NEWS.d/next/Library/2024-09-21-22-32-21.gh-issue-72795.naLmkX.rst 
b/Misc/NEWS.d/next/Library/2024-09-21-22-32-21.gh-issue-72795.naLmkX.rst
new file mode 100644
index 00..15c0918097367f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-09-21-22-32-21.gh-issue-72795.naLmkX.rst
@@ -0,0 +1,4 @@
+Positional arguments with :ref:`nargs` equal to ``'*'`` or
+:data:`!argparse.REMAINDER` are no longer required. This allows to use
+positional argument with ``nargs='*'`` and without ``default`` in mutually
+exclusive group and improves error message about required arguments.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to python-checkins-le...@python.

[Python-checkins] [3.13] gh-53780: Ignore the first "--" (double dash) between an option and command in argparse (GH-124275) (GH-125073)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/db3ccd8b62c7ff9f1ce5a7d1b8e7d68eed09517f
commit: db3ccd8b62c7ff9f1ce5a7d1b8e7d68eed09517f
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-07T22:51:07Z
summary:

[3.13] gh-53780: Ignore the first "--" (double dash) between an option and 
command in argparse (GH-124275) (GH-125073)

(cherry picked from commit c578271366176a1d1b0941897efefb6e4d6508b4)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-09-20-12-23-11.gh-issue-53780.mrV1zi.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index 926907ad0f3221..454fe42b8e8be5 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -2097,11 +2097,15 @@ def consume_positionals(start_index):
 # and add the Positional and its args to the list
 for action, arg_count in zip(positionals, arg_counts):
 args = arg_strings[start_index: start_index + arg_count]
-# Strip out the first '--' if it is not in PARSER or REMAINDER 
arg.
-if (action.nargs not in [PARSER, REMAINDER]
-and arg_strings_pattern.find('-', start_index,
+# Strip out the first '--' if it is not in REMAINDER arg.
+if action.nargs == PARSER:
+if arg_strings_pattern[start_index] == '-':
+assert args[0] == '--'
+args.remove('--')
+elif action.nargs != REMAINDER:
+if (arg_strings_pattern.find('-', start_index,
  start_index + arg_count) >= 
0):
-args.remove('--')
+args.remove('--')
 start_index += arg_count
 if args and action.deprecated and action.dest not in warned:
 self._warning(_("argument '%(argument_name)s' is 
deprecated") %
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 39023a8789267f..de65a7d24d5bff 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -6106,6 +6106,20 @@ def test_subparser(self):
 "invalid choice: '--'",
 parser.parse_args, ['--', 'x', '--', 'run', 'a', 'b'])
 
+def test_subparser_after_multiple_argument_option(self):
+parser = argparse.ArgumentParser(exit_on_error=False)
+parser.add_argument('--foo', nargs='*')
+subparsers = parser.add_subparsers()
+parser1 = subparsers.add_parser('run')
+parser1.add_argument('-f')
+parser1.add_argument('bar', nargs='*')
+
+args = parser.parse_args(['--foo', 'x', 'y', '--', 'run', 'a', 'b', 
'-f', 'c'])
+self.assertEqual(NS(foo=['x', 'y'], f='c', bar=['a', 'b']), args)
+self.assertRaisesRegex(argparse.ArgumentError,
+"invalid choice: '--'",
+parser.parse_args, ['--foo', 'x', '--', '--', 'run', 'a', 'b'])
+
 
 # ===
 # parse_intermixed_args tests
diff --git 
a/Misc/NEWS.d/next/Library/2024-09-20-12-23-11.gh-issue-53780.mrV1zi.rst 
b/Misc/NEWS.d/next/Library/2024-09-20-12-23-11.gh-issue-53780.mrV1zi.rst
new file mode 100644
index 00..fb700c722c8a8b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-09-20-12-23-11.gh-issue-53780.mrV1zi.rst
@@ -0,0 +1 @@
+:mod:`argparse` now ignores the first ``"--"`` (double dash) between an option 
and command.

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-58573: Fix conflicts between abbreviated long options in the parent parser and subparsers in argparse (GH-124631) (GH-124760)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/6925e5b5c778d0c7deab58c62545b69b4a3d3844
commit: 6925e5b5c778d0c7deab58c62545b69b4a3d3844
branch: 3.13
author: Serhiy Storchaka 
committer: serhiy-storchaka 
date: 2024-10-07T22:55:27Z
summary:

[3.13] gh-58573: Fix conflicts between abbreviated long options in the parent 
parser and subparsers in argparse (GH-124631) (GH-124760)

Check for ambiguous options if the option is consumed, not when it is
parsed.
(cherry picked from commit 3f27153e077d7e9448e2f081275931968b40cc74)

files:
A Misc/NEWS.d/next/Library/2024-09-26-22-14-12.gh-issue-58573.hozbm9.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index 980f02de9a0910..3c14d58d55343e 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -1956,11 +1956,11 @@ def _parse_known_args(self, arg_strings, namespace):
 # otherwise, add the arg to the arg strings
 # and note the index if it was an option
 else:
-option_tuple = self._parse_optional(arg_string)
-if option_tuple is None:
+option_tuples = self._parse_optional(arg_string)
+if option_tuples is None:
 pattern = 'A'
 else:
-option_string_indices[i] = option_tuple
+option_string_indices[i] = option_tuples
 pattern = 'O'
 arg_string_pattern_parts.append(pattern)
 
@@ -1995,8 +1995,16 @@ def take_action(action, argument_strings, 
option_string=None):
 def consume_optional(start_index):
 
 # get the optional identified at this index
-option_tuple = option_string_indices[start_index]
-action, option_string, sep, explicit_arg = option_tuple
+option_tuples = option_string_indices[start_index]
+# if multiple actions match, the option string was ambiguous
+if len(option_tuples) > 1:
+options = ', '.join([option_string
+for action, option_string, sep, explicit_arg in 
option_tuples])
+args = {'option': arg_string, 'matches': options}
+msg = _('ambiguous option: %(option)s could match %(matches)s')
+raise ArgumentError(None, msg % args)
+
+action, option_string, sep, explicit_arg = option_tuples[0]
 
 # identify additional optionals in the same arg string
 # (e.g. -xyz is the same as -x -y -z if no args are required)
@@ -2282,7 +2290,7 @@ def _parse_optional(self, arg_string):
 # if the option string is present in the parser, return the action
 if arg_string in self._option_string_actions:
 action = self._option_string_actions[arg_string]
-return action, arg_string, None, None
+return [(action, arg_string, None, None)]
 
 # if it's just a single character, it was meant to be positional
 if len(arg_string) == 1:
@@ -2292,25 +2300,14 @@ def _parse_optional(self, arg_string):
 option_string, sep, explicit_arg = arg_string.partition('=')
 if sep and option_string in self._option_string_actions:
 action = self._option_string_actions[option_string]
-return action, option_string, sep, explicit_arg
+return [(action, option_string, sep, explicit_arg)]
 
 # search through all possible prefixes of the option string
 # and all actions in the parser for possible interpretations
 option_tuples = self._get_option_tuples(arg_string)
 
-# if multiple actions match, the option string was ambiguous
-if len(option_tuples) > 1:
-options = ', '.join([option_string
-for action, option_string, sep, explicit_arg in option_tuples])
-args = {'option': arg_string, 'matches': options}
-msg = _('ambiguous option: %(option)s could match %(matches)s')
-raise ArgumentError(None, msg % args)
-
-# if exactly one action matched, this segmentation is good,
-# so return the parsed action
-elif len(option_tuples) == 1:
-option_tuple, = option_tuples
-return option_tuple
+if option_tuples:
+return option_tuples
 
 # if it was not found as an option, but it looks like a negative
 # number, it was meant to be positional
@@ -2325,7 +2322,7 @@ def _parse_optional(self, arg_string):
 
 # it was meant to be an optional but there is no such option
 # in this parser (though it might be a valid option in a subparser)
-return None, arg_string, None, None
+return [(None, arg_string, None, None)]
 
 def _get_option_tuples(self, option_string):
 result = []
@@ -2375,43 +2372,40 @@ def _get_nargs_pattern(self, action):
 # in all examples below, we have to allow for '--' args
 # which are represented as '-' in the pat

[Python-checkins] [3.13] Small improvements to the itertools docs (GH-123885) (#125075)

2024-10-07 Thread rhettinger
https://github.com/python/cpython/commit/84efcecf03897f278dc8673980e3e0a4b2a439ca
commit: 84efcecf03897f278dc8673980e3e0a4b2a439ca
branch: 3.13
author: Raymond Hettinger 
committer: rhettinger 
date: 2024-10-07T23:08:09Z
summary:

[3.13] Small improvements to the itertools docs (GH-123885) (#125075)

files:
M Doc/library/itertools.rst
M Lib/test/test_itertools.py

diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 43e665c3f0d5e3..ceaab2f3e72454 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -474,7 +474,7 @@ loops that truncate the stream.
If *start* is zero or ``None``, iteration starts at zero.  Otherwise,
elements from the iterable are skipped until *start* is reached.
 
-   If *stop* is ``None``, iteration continues until the iterator is
+   If *stop* is ``None``, iteration continues until the iterable is
exhausted, if at all.  Otherwise, it stops at the specified position.
 
If *step* is ``None``, the step defaults to one.  Elements are returned
@@ -503,6 +503,10 @@ loops that truncate the stream.
   yield element
   next_i += step
 
+   If the input is an iterator, then fully consuming the *islice*
+   advances the input iterator by ``max(start, stop)`` steps regardless
+   of the *step* value.
+
 
 .. function:: pairwise(iterable)
 
@@ -601,6 +605,8 @@ loops that truncate the stream.
# product('ABCD', 'xy') → Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) → 000 001 010 011 100 101 110 111
 
+   if repeat < 0:
+   raise ValueError('repeat argument cannot be negative')
pools = [tuple(pool) for pool in iterables] * repeat
 
result = [[]]
@@ -684,6 +690,8 @@ loops that truncate the stream.
Roughly equivalent to::
 
 def tee(iterable, n=2):
+if n < 0:
+raise ValueError('n must be >= 0')
 iterator = iter(iterable)
 shared_link = [None, None]
 return tuple(_tee(iterator, shared_link) for _ in range(n))
@@ -703,6 +711,12 @@ loops that truncate the stream.
used anywhere else; otherwise, the *iterable* could get advanced without
the tee objects being informed.
 
+   When the input *iterable* is already a tee iterator object, all
+   members of the return tuple are constructed as if they had been
+   produced by the upstream :func:`tee` call.  This "flattening step"
+   allows nested :func:`tee` calls to share the same underlying data
+   chain and to have a single update step rather than a chain of calls.
+
``tee`` iterators are not threadsafe. A :exc:`RuntimeError` may be
raised when simultaneously using iterators returned by the same :func:`tee`
call, even if the original *iterable* is threadsafe.
diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index 2c92d880c10cb3..2c8752d215dc69 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -1288,12 +1288,16 @@ def product1(*args, **kwds):
 else:
 return
 
-def product2(*args, **kwds):
+def product2(*iterables, repeat=1):
 'Pure python version used in docs'
-pools = list(map(tuple, args)) * kwds.get('repeat', 1)
+if repeat < 0:
+raise ValueError('repeat argument cannot be negative')
+pools = [tuple(pool) for pool in iterables] * repeat
+
 result = [[]]
 for pool in pools:
 result = [x+[y] for x in result for y in pool]
+
 for prod in result:
 yield tuple(prod)
 
@@ -2062,6 +2066,161 @@ def test_islice_recipe(self):
 self.assertEqual(next(c), 3)
 
 
+def test_tee_recipe(self):
+
+# Begin tee() recipe ###
+
+def tee(iterable, n=2):
+if n < 0:
+raise ValueError('n must be >= 0')
+iterator = iter(iterable)
+shared_link = [None, None]
+return tuple(_tee(iterator, shared_link) for _ in range(n))
+
+def _tee(iterator, link):
+try:
+while True:
+if link[1] is None:
+link[0] = next(iterator)
+link[1] = [None, None]
+value, link = link
+yield value
+except StopIteration:
+return
+
+# End tee() recipe #
+
+n = 200
+
+a, b = tee([])# test empty iterator
+self.assertEqual(list(a), [])
+self.assertEqual(list(b), [])
+
+a, b = tee(irange(n)) # test 100% interleaved
+self.assertEqual(lzip(a,b), lzip(range(n), range(n)))
+
+a, b = tee(irange(n)) # test 0% interleaved
+self.assertEqual(list(a), list(range(n)))
+self.assertEqual(list(b), list(range(n)))
+
+a, b =

[Python-checkins] [3.13] gh-116810: fix memory leak in ssl module (GH-123249) (#124800)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/f5f1d45f1704a31c85d92b88dfe36c05946baf8a
commit: f5f1d45f1704a31c85d92b88dfe36c05946baf8a
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: vstinner 
date: 2024-10-07T23:46:29+02:00
summary:

[3.13] gh-116810: fix memory leak in ssl module (GH-123249) (#124800)

gh-116810: fix memory leak in ssl module (GH-123249)

Resolve a memory leak introduced in CPython 3.10's :mod:`ssl` when the 
:attr:`ssl.SSLSocket.session` property was accessed. Speeds up read and write 
access to said property by no longer unnecessarily cloning session objects via 
serialization.

(cherry picked from commit 7e7223e18f58ec48fb36a68fb75b5c5b7a45042a)

Co-authored-by: Jeffrey R. Van Voorst 
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Peter Bierma 
Co-authored-by: Gregory P. Smith 
Co-authored-by: Antoine Pitrou 

files:
A Misc/NEWS.d/next/Library/2024-08-23-15-49-10.gh-issue-116810.QLBUU8.rst
M Modules/_ssl.c

diff --git 
a/Misc/NEWS.d/next/Library/2024-08-23-15-49-10.gh-issue-116810.QLBUU8.rst 
b/Misc/NEWS.d/next/Library/2024-08-23-15-49-10.gh-issue-116810.QLBUU8.rst
new file mode 100644
index 00..0e5256e7151c5a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-08-23-15-49-10.gh-issue-116810.QLBUU8.rst
@@ -0,0 +1,4 @@
+Resolve a memory leak introduced in CPython 3.10's :mod:`ssl` when the
+:attr:`ssl.SSLSocket.session` property was accessed.  Speeds up read and
+write access to said property by no longer unnecessarily cloning session
+objects via serialization.
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 9d50b576ba337f..54900894cc8831 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -2245,6 +2245,17 @@ PySSL_dealloc(PySSLSocket *self)
 PyTypeObject *tp = Py_TYPE(self);
 PyObject_GC_UnTrack(self);
 if (self->ssl) {
+// If we free the SSL socket object without having called SSL_shutdown,
+// OpenSSL will invalidate the linked SSL session object. While this
+// behavior is strictly RFC-compliant, it makes session reuse less
+// likely and it would also break compatibility with older stdlib
+// versions (which used an ugly workaround of duplicating the
+// SSL_SESSION object).
+// Therefore, we ensure the socket is marked as shutdown in any case.
+//
+// See elaborate explanation at
+// https://github.com/python/cpython/pull/123249#discussion_r1766164530
+SSL_set_shutdown(self->ssl, SSL_SENT_SHUTDOWN | 
SSL_get_shutdown(self->ssl));
 SSL_free(self->ssl);
 }
 Py_XDECREF(self->Socket);
@@ -2789,48 +2800,6 @@ 
_ssl__SSLSocket_verify_client_post_handshake_impl(PySSLSocket *self)
 #endif
 }
 
-static SSL_SESSION*
-_ssl_session_dup(SSL_SESSION *session) {
-SSL_SESSION *newsession = NULL;
-int slen;
-unsigned char *senc = NULL, *p;
-const unsigned char *const_p;
-
-if (session == NULL) {
-PyErr_SetString(PyExc_ValueError, "Invalid session");
-goto error;
-}
-
-/* get length */
-slen = i2d_SSL_SESSION(session, NULL);
-if (slen == 0 || slen > 0xFF00) {
-PyErr_SetString(PyExc_ValueError, "i2d() failed");
-goto error;
-}
-if ((senc = PyMem_Malloc(slen)) == NULL) {
-PyErr_NoMemory();
-goto error;
-}
-p = senc;
-if (!i2d_SSL_SESSION(session, &p)) {
-PyErr_SetString(PyExc_ValueError, "i2d() failed");
-goto error;
-}
-const_p = senc;
-newsession = d2i_SSL_SESSION(NULL, &const_p, slen);
-if (newsession == NULL) {
-PyErr_SetString(PyExc_ValueError, "d2i() failed");
-goto error;
-}
-PyMem_Free(senc);
-return newsession;
-  error:
-if (senc != NULL) {
-PyMem_Free(senc);
-}
-return NULL;
-}
-
 static PyObject *
 PySSL_get_session(PySSLSocket *self, void *closure) {
 /* get_session can return sessions from a server-side connection,
@@ -2838,15 +2807,6 @@ PySSL_get_session(PySSLSocket *self, void *closure) {
 PySSLSession *pysess;
 SSL_SESSION *session;
 
-/* duplicate session as workaround for session bug in OpenSSL 1.1.0,
- * https://github.com/openssl/openssl/issues/1550 */
-session = SSL_get0_session(self->ssl);  /* borrowed reference */
-if (session == NULL) {
-Py_RETURN_NONE;
-}
-if ((session = _ssl_session_dup(session)) == NULL) {
-return NULL;
-}
 session = SSL_get1_session(self->ssl);
 if (session == NULL) {
 Py_RETURN_NONE;
@@ -2865,11 +2825,8 @@ PySSL_get_session(PySSLSocket *self, void *closure) {
 }
 
 static int PySSL_set_session(PySSLSocket *self, PyObject *value,
- void *closure)
-  {
+ void *closure) {
 PySSLSession *pysess;
-SSL_SESSION *session;
-int result;
 
 if (!Py_IS_TYPE(value, get_state_sock(self)->PySS

[Python-checkins] [3.13] gh-124182: Explain naming rules for struct sequence types (GH-124335) (#125057)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/566983dd1250c897404db8fab3257a0d1b54753d
commit: 566983dd1250c897404db8fab3257a0d1b54753d
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: vstinner 
date: 2024-10-07T23:47:30+02:00
summary:

[3.13] gh-124182: Explain naming rules for struct sequence types (GH-124335) 
(#125057)

gh-124182: Explain naming rules for struct sequence types (GH-124335)
(cherry picked from commit 3287c834e5370294e310450115290979aac06efa)

Co-authored-by: ffelixg <[email protected]>

files:
M Doc/c-api/tuple.rst

diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst
index a2c3a75daa6e48..2944b8d342c9a8 100644
--- a/Doc/c-api/tuple.rst
+++ b/Doc/c-api/tuple.rst
@@ -161,7 +161,8 @@ type.
 
.. c:member:: const char *name
 
-  Name of the struct sequence type.
+  Fully qualified name of the type; null-terminated UTF-8 encoded.
+  The name must contain the module name.
 
.. c:member:: const char *doc
 

___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]


[Python-checkins] [3.13] gh-120378: Fix crash caused by integer overflow in `curses` (GH-124555) (#124905)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/2d3087b3cf01f26679c3cc53f39630955a3a56be
commit: 2d3087b3cf01f26679c3cc53f39630955a3a56be
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: vstinner 
date: 2024-10-07T23:46:57+02:00
summary:

[3.13] gh-120378: Fix crash caused by integer overflow in `curses` (GH-124555) 
(#124905)

gh-120378: Fix crash caused by integer overflow in `curses` (GH-124555)

This is actually an upstream problem in curses, and has been reported
to them already:
https://lists.gnu.org/archive/html/bug-ncurses/2024-09/msg00101.html

This is a nice workaround in the meantime to prevent the segfault.

(cherry picked from commit c2ba931318280796a6dcc33d1a5c5c02ad4d035b)

Co-authored-by: Peter Bierma 
Co-authored-by: Bénédikt Tran <[email protected]>

files:
A Misc/NEWS.d/next/Library/2024-09-25-18-07-51.gh-issue-120378.NlBSz_.rst
M Lib/test/test_curses.py
M Modules/_cursesmodule.c
M Modules/clinic/_cursesmodule.c.h

diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index 83d10dd8579074..cc3aa561cd4c42 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -1081,6 +1081,14 @@ def test_resize_term(self):
 self.assertEqual(curses.LINES, lines)
 self.assertEqual(curses.COLS, cols)
 
+with self.assertRaises(OverflowError):
+curses.resize_term(35000, 1)
+with self.assertRaises(OverflowError):
+curses.resize_term(1, 35000)
+# GH-120378: Overflow failure in resize_term() causes refresh to fail
+tmp = curses.initscr()
+tmp.erase()
+
 @requires_curses_func('resizeterm')
 def test_resizeterm(self):
 curses.update_lines_cols()
@@ -1095,6 +1103,14 @@ def test_resizeterm(self):
 self.assertEqual(curses.LINES, lines)
 self.assertEqual(curses.COLS, cols)
 
+with self.assertRaises(OverflowError):
+curses.resizeterm(35000, 1)
+with self.assertRaises(OverflowError):
+curses.resizeterm(1, 35000)
+# GH-120378: Overflow failure in resizeterm() causes refresh to fail
+tmp = curses.initscr()
+tmp.erase()
+
 def test_ungetch(self):
 curses.ungetch(b'A')
 self.assertEqual(self.stdscr.getkey(), 'A')
diff --git 
a/Misc/NEWS.d/next/Library/2024-09-25-18-07-51.gh-issue-120378.NlBSz_.rst 
b/Misc/NEWS.d/next/Library/2024-09-25-18-07-51.gh-issue-120378.NlBSz_.rst
new file mode 100644
index 00..1a8c1427b6b9b9
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-09-25-18-07-51.gh-issue-120378.NlBSz_.rst
@@ -0,0 +1,2 @@
+Fix a crash related to an integer overflow in :func:`curses.resizeterm`
+and :func:`curses.resize_term`.
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index d32cff66144790..f926fa2f9c0093 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -4071,9 +4071,9 @@ NoArgNoReturnFunctionBody(resetty)
 /*[clinic input]
 _curses.resizeterm
 
-nlines: int
+nlines: short
 Height.
-ncols: int
+ncols: short
 Width.
 /
 
@@ -4084,8 +4084,8 @@ window dimensions (in particular the SIGWINCH handler).
 [clinic start generated code]*/
 
 static PyObject *
-_curses_resizeterm_impl(PyObject *module, int nlines, int ncols)
-/*[clinic end generated code: output=56d6bcc5194ad055 input=0fca02ebad5ffa82]*/
+_curses_resizeterm_impl(PyObject *module, short nlines, short ncols)
+/*[clinic end generated code: output=4de3abab50c67f02 input=414e92a63e3e9899]*/
 {
 PyObject *result;
 
@@ -4107,9 +4107,9 @@ _curses_resizeterm_impl(PyObject *module, int nlines, int 
ncols)
 /*[clinic input]
 _curses.resize_term
 
-nlines: int
+nlines: short
 Height.
-ncols: int
+ncols: short
 Width.
 /
 
@@ -4123,8 +4123,8 @@ without additional interaction with the application.
 [clinic start generated code]*/
 
 static PyObject *
-_curses_resize_term_impl(PyObject *module, int nlines, int ncols)
-/*[clinic end generated code: output=9e26d8b9ea311ed2 input=2197edd05b049ed4]*/
+_curses_resize_term_impl(PyObject *module, short nlines, short ncols)
+/*[clinic end generated code: output=46c6d749fa291dbd input=276afa43d8ea7091]*/
 {
 PyObject *result;
 
diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h
index f7e0aaf7b23649..0b52308f10243e 100644
--- a/Modules/clinic/_cursesmodule.c.h
+++ b/Modules/clinic/_cursesmodule.c.h
@@ -3693,25 +3693,55 @@ PyDoc_STRVAR(_curses_resizeterm__doc__,
 {"resizeterm", _PyCFunction_CAST(_curses_resizeterm), METH_FASTCALL, 
_curses_resizeterm__doc__},
 
 static PyObject *
-_curses_resizeterm_impl(PyObject *module, int nlines, int ncols);
+_curses_resizeterm_impl(PyObject *module, short nlines, short ncols);
 
 static PyObject *
 _curses_resizeterm(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
 {
 PyObject *return_value = NULL;
-int nlines;
-int ncols;
+short 

[Python-checkins] [3.13] gh-124130: Increase test coverage for \b and \B in regular expressions (GH-124330) (GH-124413)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/a380dc68360f18e07eceb10d52c7169194564dca
commit: a380dc68360f18e07eceb10d52c7169194564dca
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-10-07T21:58:17Z
summary:

[3.13] gh-124130: Increase test coverage for \b and \B in regular expressions 
(GH-124330) (GH-124413)

(cherry picked from commit b82f07653e1e15a48ebaf8de324f52559e470254)

Co-authored-by: Serhiy Storchaka 

files:
M Lib/test/test_re.py

diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py
index 2a46e058e95f39..084c87bc768308 100644
--- a/Lib/test/test_re.py
+++ b/Lib/test/test_re.py
@@ -884,31 +884,137 @@ def test_named_unicode_escapes(self):
 self.checkPatternError(br'\N{LESS-THAN SIGN}', r'bad escape \N', 0)
 self.checkPatternError(br'[\N{LESS-THAN SIGN}]', r'bad escape \N', 1)
 
-def test_string_boundaries(self):
+def test_word_boundaries(self):
 # See http://bugs.python.org/issue10713
-self.assertEqual(re.search(r"\b(abc)\b", "abc").group(1),
- "abc")
+self.assertEqual(re.search(r"\b(abc)\b", "abc").group(1), "abc")
+self.assertEqual(re.search(r"\b(abc)\b", "abc", re.ASCII).group(1), 
"abc")
+self.assertEqual(re.search(br"\b(abc)\b", b"abc").group(1), b"abc")
+self.assertEqual(re.search(br"\b(abc)\b", b"abc", re.LOCALE).group(1), 
b"abc")
+self.assertEqual(re.search(r"\b(ьюя)\b", "ьюя").group(1), "ьюя")
+self.assertIsNone(re.search(r"\b(ьюя)\b", "ьюя", re.ASCII))
+# There's a word boundary between a word and a non-word.
+self.assertTrue(re.match(r".\b", "a="))
+self.assertTrue(re.match(r".\b", "a=", re.ASCII))
+self.assertTrue(re.match(br".\b", b"a="))
+self.assertTrue(re.match(br".\b", b"a=", re.LOCALE))
+self.assertTrue(re.match(r".\b", "я="))
+self.assertIsNone(re.match(r".\b", "я=", re.ASCII))
+# There's a word boundary between a non-word and a word.
+self.assertTrue(re.match(r".\b", "=a"))
+self.assertTrue(re.match(r".\b", "=a", re.ASCII))
+self.assertTrue(re.match(br".\b", b"=a"))
+self.assertTrue(re.match(br".\b", b"=a", re.LOCALE))
+self.assertTrue(re.match(r".\b", "=я"))
+self.assertIsNone(re.match(r".\b", "=я", re.ASCII))
+# There is no word boundary inside a word.
+self.assertIsNone(re.match(r".\b", "ab"))
+self.assertIsNone(re.match(r".\b", "ab", re.ASCII))
+self.assertIsNone(re.match(br".\b", b"ab"))
+self.assertIsNone(re.match(br".\b", b"ab", re.LOCALE))
+self.assertIsNone(re.match(r".\b", "юя"))
+self.assertIsNone(re.match(r".\b", "юя", re.ASCII))
+# There is no word boundary between a non-word characters.
+self.assertIsNone(re.match(r".\b", "=-"))
+self.assertIsNone(re.match(r".\b", "=-", re.ASCII))
+self.assertIsNone(re.match(br".\b", b"=-"))
+self.assertIsNone(re.match(br".\b", b"=-", re.LOCALE))
+# There is no non-boundary match between a word and a non-word.
+self.assertIsNone(re.match(r".\B", "a="))
+self.assertIsNone(re.match(r".\B", "a=", re.ASCII))
+self.assertIsNone(re.match(br".\B", b"a="))
+self.assertIsNone(re.match(br".\B", b"a=", re.LOCALE))
+self.assertIsNone(re.match(r".\B", "я="))
+self.assertTrue(re.match(r".\B", "я=", re.ASCII))
+# There is no non-boundary match between a non-word and a word.
+self.assertIsNone(re.match(r".\B", "=a"))
+self.assertIsNone(re.match(r".\B", "=a", re.ASCII))
+self.assertIsNone(re.match(br".\B", b"=a"))
+self.assertIsNone(re.match(br".\B", b"=a", re.LOCALE))
+self.assertIsNone(re.match(r".\B", "=я"))
+self.assertTrue(re.match(r".\B", "=я", re.ASCII))
+# There's a non-boundary match inside a word.
+self.assertTrue(re.match(r".\B", "ab"))
+self.assertTrue(re.match(r".\B", "ab", re.ASCII))
+self.assertTrue(re.match(br".\B", b"ab"))
+self.assertTrue(re.match(br".\B", b"ab", re.LOCALE))
+self.assertTrue(re.match(r".\B", "юя"))
+self.assertTrue(re.match(r".\B", "юя", re.ASCII))
+# There's a non-boundary match between a non-word characters.
+self.assertTrue(re.match(r".\B", "=-"))
+self.assertTrue(re.match(r".\B", "=-", re.ASCII))
+self.assertTrue(re.match(br".\B", b"=-"))
+self.assertTrue(re.match(br".\B", b"=-", re.LOCALE))
 # There's a word boundary at the start of a string.
 self.assertTrue(re.match(r"\b", "abc"))
+self.assertTrue(re.match(r"\b", "abc", re.ASCII))
+self.assertTrue(re.match(br"\b", b"abc"))
+self.assertTrue(re.match(br"\b", b"abc", re.LOCALE))
+self.assertTrue(re.match(r"\b", "ьюя"))
+self.assertIsNone(re.match(r"\b", "ьюя", re.ASCII))
+# There's a word bound

[Python-checkins] [3.13] gh-58282: Fix support of tuple metavar for positional arguments in argparse (GH-124782) (GH-124882)

2024-10-07 Thread serhiy-storchaka
https://github.com/python/cpython/commit/e28f2c670541710ecafc0218d025431125d87fc8
commit: e28f2c670541710ecafc0218d025431125d87fc8
branch: 3.13
author: Serhiy Storchaka 
committer: serhiy-storchaka 
date: 2024-10-08T06:36:38Z
summary:

[3.13] gh-58282: Fix support of tuple metavar for positional arguments in 
argparse (GH-124782) (GH-124882)

Previously, formatting help output or error message for positional argument
with a tuple metavar raised exception.

(cherry picked from commit 9b31a2d83fa7cb0fe4d75ce7cf6a2c9ea2ce0728)

Co-authored-by: Cyker Way 

files:
A Misc/NEWS.d/next/Library/2018-12-04-07-36-27.bpo-14074.fMLKCu.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index 3c14d58d55343e..71d0c8fc8703ea 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -527,8 +527,7 @@ def _format_action(self, action):
 def _format_action_invocation(self, action):
 if not action.option_strings:
 default = self._get_default_metavar_for_positional(action)
-metavar, = self._metavar_formatter(action, default)(1)
-return metavar
+return ' '.join(self._metavar_formatter(action, default)(1))
 
 else:
 
@@ -703,7 +702,15 @@ def _get_action_name(argument):
 elif argument.option_strings:
 return '/'.join(argument.option_strings)
 elif argument.metavar not in (None, SUPPRESS):
-return argument.metavar
+metavar = argument.metavar
+if not isinstance(metavar, tuple):
+return metavar
+if argument.nargs == ZERO_OR_MORE and len(metavar) == 2:
+return '%s[, %s]' % metavar
+elif argument.nargs == ONE_OR_MORE:
+return '%s[, %s]' % metavar
+else:
+return ', '.join(metavar)
 elif argument.dest not in (None, SUPPRESS):
 return argument.dest
 elif argument.choices:
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 3c7dc0622c1763..0084df8ec64706 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -4893,7 +4893,7 @@ class TestHelpNone(HelpTestCase):
 version = ''
 
 
-class TestHelpTupleMetavar(HelpTestCase):
+class TestHelpTupleMetavarOptional(HelpTestCase):
 """Test specifying metavar as a tuple"""
 
 parser_signature = Sig(prog='PROG')
@@ -4920,6 +4920,34 @@ class TestHelpTupleMetavar(HelpTestCase):
 version = ''
 
 
+class TestHelpTupleMetavarPositional(HelpTestCase):
+"""Test specifying metavar on a Positional as a tuple"""
+
+parser_signature = Sig(prog='PROG')
+argument_signatures = [
+Sig('w', help='w help', nargs='+', metavar=('W1', 'W2')),
+Sig('x', help='x help', nargs='*', metavar=('X1', 'X2')),
+Sig('y', help='y help', nargs=3, metavar=('Y1', 'Y2', 'Y3')),
+Sig('z', help='z help', nargs='?', metavar=('Z1',)),
+]
+argument_group_signatures = []
+usage = '''\
+usage: PROG [-h] W1 [W2 ...] [X1 [X2 ...]] Y1 Y2 Y3 [Z1]
+'''
+help = usage + '''\
+
+positional arguments:
+  W1 W2   w help
+  X1 X2   x help
+  Y1 Y2 Y3y help
+  Z1  z help
+
+options:
+  -h, --help  show this help message and exit
+'''
+version = ''
+
+
 class TestHelpRawText(HelpTestCase):
 """Test the RawTextHelpFormatter"""
 
@@ -6516,6 +6544,27 @@ def test_required_args(self):
'the following arguments are required: bar, 
baz$',
self.parser.parse_args, [])
 
+def test_required_args_with_metavar(self):
+self.parser.add_argument('bar')
+self.parser.add_argument('baz', metavar='BaZ')
+self.assertRaisesRegex(argparse.ArgumentError,
+   'the following arguments are required: bar, 
BaZ$',
+   self.parser.parse_args, [])
+
+def test_required_args_n(self):
+self.parser.add_argument('bar')
+self.parser.add_argument('baz', nargs=3)
+self.assertRaisesRegex(argparse.ArgumentError,
+   'the following arguments are required: bar, 
baz$',
+   self.parser.parse_args, [])
+
+def test_required_args_n_with_metavar(self):
+self.parser.add_argument('bar')
+self.parser.add_argument('baz', nargs=3, metavar=('B', 'A', 'Z'))
+self.assertRaisesRegex(argparse.ArgumentError,
+   'the following arguments are required: bar, B, 
A, Z$',
+   self.parser.parse_args, [])
+
 def test_required_args_optional(self):
 self.parser.add_argument('bar')
 self.parser.add_argument('baz', nargs='?')
@@ -6530,6 +6579,20 @@ def test_required_args_zero_or_more(self):
'the following arguments are required: bar$',
self.parser.parse_args, [])
 
+def test_req

[Python-checkins] gh-90102: Remove isatty call during regular open (#124922)

2024-10-07 Thread vstinner
https://github.com/python/cpython/commit/cc9b9bebb2c09c475cb0f278268cce03c115eaea
commit: cc9b9bebb2c09c475cb0f278268cce03c115eaea
branch: main
author: Cody Maloney 
committer: vstinner 
date: 2024-10-08T08:50:42+02:00
summary:

gh-90102: Remove isatty call during regular open (#124922)

Co-authored-by: Victor Stinner 

files:
A Misc/NEWS.d/next/Library/2024-10-02-22-53-48.gh-issue-90102.4qX52R.rst
M Include/internal/pycore_global_objects_fini_generated.h
M Include/internal/pycore_global_strings.h
M Include/internal/pycore_runtime_init_generated.h
M Include/internal/pycore_unicodeobject_generated.h
M Lib/_pyio.py
M Modules/_io/_iomodule.c
M Modules/_io/fileio.c
M Modules/_io/winconsoleio.c

diff --git a/Include/internal/pycore_global_objects_fini_generated.h 
b/Include/internal/pycore_global_objects_fini_generated.h
index 28a76c36801b4b..3140a75a47c5ee 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -757,6 +757,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
 _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_initializing));
 _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_io));
 _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_is_text_encoding));
+_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_isatty_open_only));
 _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_length_));
 _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_limbo));
 _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_lock_unlock_module));
diff --git a/Include/internal/pycore_global_strings.h 
b/Include/internal/pycore_global_strings.h
index ac789b06fb8a61..1591cb0a3f114f 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -246,6 +246,7 @@ struct _Py_global_strings {
 STRUCT_FOR_ID(_initializing)
 STRUCT_FOR_ID(_io)
 STRUCT_FOR_ID(_is_text_encoding)
+STRUCT_FOR_ID(_isatty_open_only)
 STRUCT_FOR_ID(_length_)
 STRUCT_FOR_ID(_limbo)
 STRUCT_FOR_ID(_lock_unlock_module)
diff --git a/Include/internal/pycore_runtime_init_generated.h 
b/Include/internal/pycore_runtime_init_generated.h
index 7847a5c63ebf3f..c9d20d0b5aacdb 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -755,6 +755,7 @@ extern "C" {
 INIT_ID(_initializing), \
 INIT_ID(_io), \
 INIT_ID(_is_text_encoding), \
+INIT_ID(_isatty_open_only), \
 INIT_ID(_length_), \
 INIT_ID(_limbo), \
 INIT_ID(_lock_unlock_module), \
diff --git a/Include/internal/pycore_unicodeobject_generated.h 
b/Include/internal/pycore_unicodeobject_generated.h
index a688f70a2ba36f..d335373e88ee74 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -784,6 +784,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
 _PyUnicode_InternStatic(interp, &string);
 assert(_PyUnicode_CheckConsistency(string, 1));
 assert(PyUnicode_GET_LENGTH(string) != 1);
+string = &_Py_ID(_isatty_open_only);
+_PyUnicode_InternStatic(interp, &string);
+assert(_PyUnicode_CheckConsistency(string, 1));
+assert(PyUnicode_GET_LENGTH(string) != 1);
 string = &_Py_ID(_length_);
 _PyUnicode_InternStatic(interp, &string);
 assert(_PyUnicode_CheckConsistency(string, 1));
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index 18849b309b8605..2a1d2a33d02960 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -238,7 +238,7 @@ def open(file, mode="r", buffering=-1, encoding=None, 
errors=None,
 result = raw
 try:
 line_buffering = False
-if buffering == 1 or buffering < 0 and raw.isatty():
+if buffering == 1 or buffering < 0 and raw._isatty_open_only():
 buffering = -1
 line_buffering = True
 if buffering < 0:
@@ -1794,6 +1794,21 @@ def isatty(self):
 self._checkClosed()
 return os.isatty(self._fd)
 
+def _isatty_open_only(self):
+"""Checks whether the file is a TTY using an open-only optimization.
+
+TTYs are always character devices. If the interpreter knows a file is
+not a character device when it would call ``isatty``, can skip that
+call. Inside ``open()``  there is a fresh stat result that contains 
that
+information. Use the stat result to skip a system call. Outside of that
+context TOCTOU issues (the fd could be arbitrarily modified by
+surrounding code).
+"""
+if (self._stat_atopen is not None
+and not stat.S_ISCHR(self._stat_atopen.st_mode)):
+return True
+return os.isatty(self._fd)
+
 @property
 def closefd(self):
 """True if the file descriptor will be closed by close()."""
diff --git 
a/Misc/NEWS.d/next/Library/2024-10-02-22-53-48.gh-issue-90102.4qX52R.rst 
b/Misc/NEWS.d/next/Library/2024-10-02-22-53-48.gh-is

[Python-checkins] [3.12] gh-124653: Relax (again) detection of queue API for logging handlers (GH-124897) (GH-125060)

2024-10-07 Thread vsajip
https://github.com/python/cpython/commit/bc237ed9a8d631675a4962d6627d0571dfa4c04f
commit: bc237ed9a8d631675a4962d6627d0571dfa4c04f
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: vsajip 
date: 2024-10-08T07:24:09+01:00
summary:

[3.12] gh-124653: Relax (again) detection of queue API for logging handlers  
(GH-124897) (GH-125060)

(cherry picked from commit 7ffe94fb242fd51bb07c7f0d31e94efeea3619d4)

files:
A Misc/NEWS.d/next/Library/2024-10-02-15-05-45.gh-issue-124653.tqsTu9.rst
M Doc/library/logging.config.rst
M Lib/logging/config.py
M Lib/test/test_logging.py

diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst
index 5060250ee0..bc85f4301f9ba0 100644
--- a/Doc/library/logging.config.rst
+++ b/Doc/library/logging.config.rst
@@ -752,16 +752,17 @@ The ``queue`` and ``listener`` keys are optional.
 
 If the ``queue`` key is present, the corresponding value can be one of the 
following:
 
-* An object implementing the :class:`queue.Queue` public API. For instance,
-  this may be an actual instance of :class:`queue.Queue` or a subclass thereof,
-  or a proxy obtained by :meth:`multiprocessing.managers.SyncManager.Queue`.
+* An object implementing the :meth:`Queue.put_nowait `
+  and :meth:`Queue.get ` public API. For instance, this may be
+  an actual instance of :class:`queue.Queue` or a subclass thereof, or a proxy
+  obtained by :meth:`multiprocessing.managers.SyncManager.Queue`.
 
   This is of course only possible if you are constructing or modifying
   the configuration dictionary in code.
 
 * A string that resolves to a callable which, when called with no arguments, 
returns
-  the :class:`queue.Queue` instance to use. That callable could be a
-  :class:`queue.Queue` subclass or a function which returns a suitable queue 
instance,
+  the queue instance to use. That callable could be a :class:`queue.Queue` 
subclass
+  or a function which returns a suitable queue instance,
   such as ``my.module.queue_factory()``.
 
 * A dict with a ``'()'`` key which is constructed in the usual way as 
discussed in
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index ac90b537d8a396..c128bc7a61e34e 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -502,7 +502,7 @@ def as_tuple(self, value):
 
 def _is_queue_like_object(obj):
 """Check that *obj* implements the Queue API."""
-if isinstance(obj, queue.Queue):
+if isinstance(obj, (queue.Queue, queue.SimpleQueue)):
 return True
 # defer importing multiprocessing as much as possible
 from multiprocessing.queues import Queue as MPQueue
@@ -519,13 +519,13 @@ def _is_queue_like_object(obj):
 # Ideally, we would have wanted to simply use strict type checking
 # instead of a protocol-based type checking since the latter does
 # not check the method signatures.
-queue_interface = [
-'empty', 'full', 'get', 'get_nowait',
-'put', 'put_nowait', 'join', 'qsize',
-'task_done',
-]
+#
+# Note that only 'put_nowait' and 'get' are required by the logging
+# queue handler and queue listener (see gh-124653) and that other
+# methods are either optional or unused.
+minimal_queue_interface = ['put_nowait', 'get']
 return all(callable(getattr(obj, method, None))
-   for method in queue_interface)
+   for method in minimal_queue_interface)
 
 class DictConfigurator(BaseConfigurator):
 """
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 78bcd065ad5d72..5112c2e7d60f0b 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -2391,16 +2391,22 @@ def __getattr__(self, attribute):
 return getattr(queue, attribute)
 
 class CustomQueueFakeProtocol(CustomQueueProtocol):
-# An object implementing the Queue API (incorrect signatures).
+# An object implementing the minimial Queue API for
+# the logging module but with incorrect signatures.
+#
 # The object will be considered a valid queue class since we
 # do not check the signatures (only callability of methods)
 # but will NOT be usable in production since a TypeError will
-# be raised due to a missing argument.
-def empty(self, x):
+# be raised due to the extra argument in 'put_nowait'.
+def put_nowait(self):
 pass
 
 class CustomQueueWrongProtocol(CustomQueueProtocol):
-empty = None
+put_nowait = None
+
+class MinimalQueueProtocol:
+def put_nowait(self, x): pass
+def get(self): pass
 
 def queueMaker():
 return queue.Queue()
@@ -3914,56 +3920,70 @@ def test_config_queue_handler(self):
 msg = str(ctx.exception)
 self.assertEqual(msg, "Unable to configure handler 'ah'")
 
+def _apply_simple_queue_listener_configuration(self, qspec):
+self.apply_config({
+"version": 1,
+"handlers": {
+"queue_listener": {
+"class"

[Python-checkins] [3.13] gh-124653: Relax (again) detection of queue API for logging handlers (GH-124897) (GH-125059)

2024-10-07 Thread vsajip
https://github.com/python/cpython/commit/1e820e63e7a48e8516e9c5d5d4e2dfb6cc9df552
commit: 1e820e63e7a48e8516e9c5d5d4e2dfb6cc9df552
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: vsajip 
date: 2024-10-08T07:23:40+01:00
summary:

[3.13] gh-124653: Relax (again) detection of queue API for logging handlers  
(GH-124897) (GH-125059)

(cherry picked from commit 7ffe94fb242fd51bb07c7f0d31e94efeea3619d4)

files:
A Misc/NEWS.d/next/Library/2024-10-02-15-05-45.gh-issue-124653.tqsTu9.rst
M Doc/library/logging.config.rst
M Lib/logging/config.py
M Lib/test/test_logging.py

diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst
index 317ca8728248c8..0e9dc33ae2123a 100644
--- a/Doc/library/logging.config.rst
+++ b/Doc/library/logging.config.rst
@@ -753,16 +753,17 @@ The ``queue`` and ``listener`` keys are optional.
 
 If the ``queue`` key is present, the corresponding value can be one of the 
following:
 
-* An object implementing the :class:`queue.Queue` public API. For instance,
-  this may be an actual instance of :class:`queue.Queue` or a subclass thereof,
-  or a proxy obtained by :meth:`multiprocessing.managers.SyncManager.Queue`.
+* An object implementing the :meth:`Queue.put_nowait `
+  and :meth:`Queue.get ` public API. For instance, this may be
+  an actual instance of :class:`queue.Queue` or a subclass thereof, or a proxy
+  obtained by :meth:`multiprocessing.managers.SyncManager.Queue`.
 
   This is of course only possible if you are constructing or modifying
   the configuration dictionary in code.
 
 * A string that resolves to a callable which, when called with no arguments, 
returns
-  the :class:`queue.Queue` instance to use. That callable could be a
-  :class:`queue.Queue` subclass or a function which returns a suitable queue 
instance,
+  the queue instance to use. That callable could be a :class:`queue.Queue` 
subclass
+  or a function which returns a suitable queue instance,
   such as ``my.module.queue_factory()``.
 
 * A dict with a ``'()'`` key which is constructed in the usual way as 
discussed in
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index 735bffeaa09884..190b4f922590ca 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -499,7 +499,7 @@ def as_tuple(self, value):
 
 def _is_queue_like_object(obj):
 """Check that *obj* implements the Queue API."""
-if isinstance(obj, queue.Queue):
+if isinstance(obj, (queue.Queue, queue.SimpleQueue)):
 return True
 # defer importing multiprocessing as much as possible
 from multiprocessing.queues import Queue as MPQueue
@@ -516,13 +516,13 @@ def _is_queue_like_object(obj):
 # Ideally, we would have wanted to simply use strict type checking
 # instead of a protocol-based type checking since the latter does
 # not check the method signatures.
-queue_interface = [
-'empty', 'full', 'get', 'get_nowait',
-'put', 'put_nowait', 'join', 'qsize',
-'task_done',
-]
+#
+# Note that only 'put_nowait' and 'get' are required by the logging
+# queue handler and queue listener (see gh-124653) and that other
+# methods are either optional or unused.
+minimal_queue_interface = ['put_nowait', 'get']
 return all(callable(getattr(obj, method, None))
-   for method in queue_interface)
+   for method in minimal_queue_interface)
 
 class DictConfigurator(BaseConfigurator):
 """
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index ec6854b4bb18ed..678c23dad67faa 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -2376,16 +2376,22 @@ def __getattr__(self, attribute):
 return getattr(queue, attribute)
 
 class CustomQueueFakeProtocol(CustomQueueProtocol):
-# An object implementing the Queue API (incorrect signatures).
+# An object implementing the minimial Queue API for
+# the logging module but with incorrect signatures.
+#
 # The object will be considered a valid queue class since we
 # do not check the signatures (only callability of methods)
 # but will NOT be usable in production since a TypeError will
-# be raised due to a missing argument.
-def empty(self, x):
+# be raised due to the extra argument in 'put_nowait'.
+def put_nowait(self):
 pass
 
 class CustomQueueWrongProtocol(CustomQueueProtocol):
-empty = None
+put_nowait = None
+
+class MinimalQueueProtocol:
+def put_nowait(self, x): pass
+def get(self): pass
 
 def queueMaker():
 return queue.Queue()
@@ -3945,56 +3951,70 @@ def test_config_queue_handler(self):
 msg = str(ctx.exception)
 self.assertEqual(msg, "Unable to configure handler 'ah'")
 
+def _apply_simple_queue_listener_configuration(self, qspec):
+self.apply_config({
+"version": 1,
+"handlers": {
+"queue_listener": {
+"class"