[Python-checkins] [3.12] gh-113692: skip a test if multiprocessing isn't available. (GH-113704) (GH-113844)

2024-01-09 Thread vsajip
https://github.com/python/cpython/commit/cdd703d1313b546823882663ca94cf536f025f4a
commit: cdd703d1313b546823882663ca94cf536f025f4a
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: vsajip 
date: 2024-01-09T08:13:41Z
summary:

[3.12] gh-113692: skip a test if multiprocessing isn't available. (GH-113704) 
(GH-113844)

(cherry picked from commit 842b738129021f52293dc053e014ecb4fe095baa)

files:
M Lib/test/test_logging.py

diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index df08ccd362a073..635dd7c26f6eed 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -43,6 +43,7 @@
 import tempfile
 from test.support.script_helper import assert_python_ok, assert_python_failure
 from test import support
+from test.support import import_helper
 from test.support import os_helper
 from test.support import socket_helper
 from test.support import threading_helper
@@ -3894,7 +3895,8 @@ def test_90195(self):
 
 def test_111615(self):
 # See gh-111615
-import multiprocessing as mp
+import_helper.import_module('_multiprocessing')  # see gh-113692
+mp = import_helper.import_module('multiprocessing')
 
 config = {
 'version': 1,

___
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-101100: Fix Sphinx warnings for 2.6 port-specific deprecations (#113752)

2024-01-09 Thread hugovk
https://github.com/python/cpython/commit/2e17cad2b8899126eb2024bf75db331b871bd5bc
commit: 2e17cad2b8899126eb2024bf75db331b871bd5bc
branch: main
author: Hugo van Kemenade <[email protected]>
committer: hugovk <[email protected]>
date: 2024-01-09T10:18:15+02:00
summary:

gh-101100: Fix Sphinx warnings for 2.6 port-specific deprecations (#113752)

files:
M Doc/whatsnew/2.0.rst
M Doc/whatsnew/2.6.rst

diff --git a/Doc/whatsnew/2.0.rst b/Doc/whatsnew/2.0.rst
index 620eb7829d559a..f4a9d23699de53 100644
--- a/Doc/whatsnew/2.0.rst
+++ b/Doc/whatsnew/2.0.rst
@@ -130,7 +130,7 @@ Guidelines":
 Read the rest of :pep:`1` for the details of the PEP editorial process, style, 
and
 format.  PEPs are kept in the Python CVS tree on SourceForge, though they're 
not
 part of the Python 2.0 distribution, and are also available in HTML form from
-https://peps.python.org/.  As of September 2000, there are 25 PEPS, ranging
+https://peps.python.org/.  As of September 2000, there are 25 PEPs, ranging
 from :pep:`201`, "Lockstep Iteration", to PEP 225, "Elementwise/Objectwise
 Operators".
 
diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst
index 93ddc7a027c324..c6bab93b7efdda 100644
--- a/Doc/whatsnew/2.6.rst
+++ b/Doc/whatsnew/2.6.rst
@@ -4,8 +4,6 @@
   What's New in Python 2.6
 
 
-.. XXX add trademark info for Apple, Microsoft, SourceForge.
-
 :Author: A.M. Kuchling (amk at amk.ca)
 
 .. $Id$
@@ -128,7 +126,7 @@ and to C extension code as :c:data:`!Py_Py3kWarningFlag`.
The 3\ *xxx* series of PEPs, which contains proposals for Python 3.0.
:pep:`3000` describes the development process for Python 3.0.
Start with :pep:`3100` that describes the general goals for Python
-   3.0, and then explore the higher-numbered PEPS that propose
+   3.0, and then explore the higher-numbered PEPs that propose
specific features.
 
 
@@ -1051,8 +1049,6 @@ the :mod:`io` module:
   sockets, but Python 2.6 hasn't restructured its file and socket objects
   in this way.
 
-  .. XXX should 2.6 register them in io.py?
-
 * :class:`BufferedIOBase` is an abstract base class that
   buffers data in memory to reduce the number of
   system calls used, making I/O processing more efficient.
@@ -1133,8 +1129,6 @@ while an external caller could be modifying the contents,
 so there's a corresponding ``PyBuffer_Release(Py_buffer *view)`` to
 indicate that the external caller is done.
 
-.. XXX PyObject_GetBuffer not documented in c-api
-
 The *flags* argument to :c:func:`PyObject_GetBuffer` specifies
 constraints upon the memory returned.  Some examples are:
 
@@ -3110,8 +3104,8 @@ Port-Specific Changes: Windows
 
 * The :mod:`msvcrt` module now supports
   both the normal and wide char variants of the console I/O
-  API.  The :func:`getwch` function reads a keypress and returns a Unicode
-  value, as does the :func:`getwche` function.  The :func:`putwch` function
+  API.  The :func:`~msvcrt.getwch` function reads a keypress and returns a 
Unicode
+  value, as does the :func:`~msvcrt.getwche` function.  The 
:func:`~msvcrt.putwch` function
   takes a Unicode character and writes it to the console.
   (Contributed by Christian Heimes.)
 
@@ -3120,24 +3114,24 @@ Port-Specific Changes: Windows
   directory path.  (Contributed by Josiah Carlson; :issue:`957650`.)
 
 * The :mod:`socket` module's socket objects now have an
-  :meth:`ioctl` method that provides a limited interface to the
+  :meth:`~socket.socket.ioctl` method that provides a limited interface to the
   :c:func:`WSAIoctl` system interface.
 
-* The :mod:`_winreg` module now has a function,
-  :func:`ExpandEnvironmentStrings`,
+* The :mod:`_winreg ` module now has a function,
+  :func:`~winreg.ExpandEnvironmentStrings`,
   that expands environment variable references such as ``%NAME%``
   in an input string.  The handle objects provided by this
   module now support the context protocol, so they can be used
   in :keyword:`with` statements. (Contributed by Christian Heimes.)
 
-  :mod:`_winreg` also has better support for x64 systems,
-  exposing the :func:`DisableReflectionKey`, :func:`EnableReflectionKey`,
-  and :func:`QueryReflectionKey` functions, which enable and disable
+  :mod:`_winreg ` also has better support for x64 systems,
+  exposing the :func:`~winreg.DisableReflectionKey`, 
:func:`~winreg.EnableReflectionKey`,
+  and :func:`~winreg.QueryReflectionKey` functions, which enable and disable
   registry reflection for 32-bit processes running on 64-bit systems.
   (:issue:`1753245`)
 
-* The :mod:`!msilib` module's :class:`Record` object
-  gained :meth:`GetInteger` and :meth:`GetString` methods that
+* The :mod:`!msilib` module's :class:`!Record` object
+  gained :meth:`!GetInteger` and :meth:`!GetString` methods that
   return field values as an integer or a string.
   (Contributed by Floris Bruynooghe; :issue:`2125`.)
 
@@ -3151,49 +3145,49 @@ Port-Specific Changes: Mac OS X
   :option:`!--with-framewo

[Python-checkins] [3.12] gh-101100: Fix Sphinx warnings for 2.6 port-specific deprecations (GH-113752) (#113846)

2024-01-09 Thread hugovk
https://github.com/python/cpython/commit/7e894a4baf80a13d7f23071f2e417bed8a2a3a1d
commit: 7e894a4baf80a13d7f23071f2e417bed8a2a3a1d
branch: 3.12
author: Hugo van Kemenade <[email protected]>
committer: hugovk <[email protected]>
date: 2024-01-09T11:58:55+02:00
summary:

[3.12] gh-101100: Fix Sphinx warnings for 2.6 port-specific deprecations 
(GH-113752) (#113846)

files:
M Doc/whatsnew/2.0.rst
M Doc/whatsnew/2.6.rst

diff --git a/Doc/whatsnew/2.0.rst b/Doc/whatsnew/2.0.rst
index a9a1c9f0997090..d8ed59501860ff 100644
--- a/Doc/whatsnew/2.0.rst
+++ b/Doc/whatsnew/2.0.rst
@@ -130,7 +130,7 @@ Guidelines":
 Read the rest of :pep:`1` for the details of the PEP editorial process, style, 
and
 format.  PEPs are kept in the Python CVS tree on SourceForge, though they're 
not
 part of the Python 2.0 distribution, and are also available in HTML form from
-https://peps.python.org/.  As of September 2000, there are 25 PEPS, ranging
+https://peps.python.org/.  As of September 2000, there are 25 PEPs, ranging
 from :pep:`201`, "Lockstep Iteration", to PEP 225, "Elementwise/Objectwise
 Operators".
 
diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst
index 1160c8ccf174ec..5c8404611f060d 100644
--- a/Doc/whatsnew/2.6.rst
+++ b/Doc/whatsnew/2.6.rst
@@ -4,8 +4,6 @@
   What's New in Python 2.6
 
 
-.. XXX add trademark info for Apple, Microsoft, SourceForge.
-
 :Author: A.M. Kuchling (amk at amk.ca)
 
 .. $Id$
@@ -128,7 +126,7 @@ and to C extension code as :c:data:`Py_Py3kWarningFlag`.
The 3\ *xxx* series of PEPs, which contains proposals for Python 3.0.
:pep:`3000` describes the development process for Python 3.0.
Start with :pep:`3100` that describes the general goals for Python
-   3.0, and then explore the higher-numbered PEPS that propose
+   3.0, and then explore the higher-numbered PEPs that propose
specific features.
 
 
@@ -1051,8 +1049,6 @@ the :mod:`io` module:
   sockets, but Python 2.6 hasn't restructured its file and socket objects
   in this way.
 
-  .. XXX should 2.6 register them in io.py?
-
 * :class:`BufferedIOBase` is an abstract base class that
   buffers data in memory to reduce the number of
   system calls used, making I/O processing more efficient.
@@ -1133,8 +1129,6 @@ while an external caller could be modifying the contents,
 so there's a corresponding ``PyBuffer_Release(Py_buffer *view)`` to
 indicate that the external caller is done.
 
-.. XXX PyObject_GetBuffer not documented in c-api
-
 The *flags* argument to :c:func:`PyObject_GetBuffer` specifies
 constraints upon the memory returned.  Some examples are:
 
@@ -3110,8 +3104,8 @@ Port-Specific Changes: Windows
 
 * The :mod:`msvcrt` module now supports
   both the normal and wide char variants of the console I/O
-  API.  The :func:`getwch` function reads a keypress and returns a Unicode
-  value, as does the :func:`getwche` function.  The :func:`putwch` function
+  API.  The :func:`~msvcrt.getwch` function reads a keypress and returns a 
Unicode
+  value, as does the :func:`~msvcrt.getwche` function.  The 
:func:`~msvcrt.putwch` function
   takes a Unicode character and writes it to the console.
   (Contributed by Christian Heimes.)
 
@@ -3120,24 +3114,24 @@ Port-Specific Changes: Windows
   directory path.  (Contributed by Josiah Carlson; :issue:`957650`.)
 
 * The :mod:`socket` module's socket objects now have an
-  :meth:`ioctl` method that provides a limited interface to the
+  :meth:`~socket.socket.ioctl` method that provides a limited interface to the
   :c:func:`WSAIoctl` system interface.
 
-* The :mod:`_winreg` module now has a function,
-  :func:`ExpandEnvironmentStrings`,
+* The :mod:`_winreg ` module now has a function,
+  :func:`~winreg.ExpandEnvironmentStrings`,
   that expands environment variable references such as ``%NAME%``
   in an input string.  The handle objects provided by this
   module now support the context protocol, so they can be used
   in :keyword:`with` statements. (Contributed by Christian Heimes.)
 
-  :mod:`_winreg` also has better support for x64 systems,
-  exposing the :func:`DisableReflectionKey`, :func:`EnableReflectionKey`,
-  and :func:`QueryReflectionKey` functions, which enable and disable
+  :mod:`_winreg ` also has better support for x64 systems,
+  exposing the :func:`~winreg.DisableReflectionKey`, 
:func:`~winreg.EnableReflectionKey`,
+  and :func:`~winreg.QueryReflectionKey` functions, which enable and disable
   registry reflection for 32-bit processes running on 64-bit systems.
   (:issue:`1753245`)
 
-* The :mod:`msilib` module's :class:`Record` object
-  gained :meth:`GetInteger` and :meth:`GetString` methods that
+* The :mod:`msilib` module's :class:`!Record` object
+  gained :meth:`~msilib.Record.GetInteger` and 
:meth:`~msilib.Record.GetString` methods that
   return field values as an integer or a string.
   (Contributed by Floris Bruynooghe; :issue:`2125`.)
 
@@ -3151,49 +3145,49 @@ Port-Specific

[Python-checkins] [3.11] gh-101100: Fix Sphinx warnings for 2.6 port-specific deprecations (GH-113752) (#113847)

2024-01-09 Thread hugovk
https://github.com/python/cpython/commit/f20c69299e026813de2a97ddb32e89155f08fdff
commit: f20c69299e026813de2a97ddb32e89155f08fdff
branch: 3.11
author: Hugo van Kemenade <[email protected]>
committer: hugovk <[email protected]>
date: 2024-01-09T11:58:59+02:00
summary:

[3.11] gh-101100: Fix Sphinx warnings for 2.6 port-specific deprecations 
(GH-113752) (#113847)

files:
M Doc/whatsnew/2.0.rst
M Doc/whatsnew/2.6.rst

diff --git a/Doc/whatsnew/2.0.rst b/Doc/whatsnew/2.0.rst
index 8aae3ae9701f7b..6711364cd74c85 100644
--- a/Doc/whatsnew/2.0.rst
+++ b/Doc/whatsnew/2.0.rst
@@ -130,7 +130,7 @@ Guidelines":
 Read the rest of :pep:`1` for the details of the PEP editorial process, style, 
and
 format.  PEPs are kept in the Python CVS tree on SourceForge, though they're 
not
 part of the Python 2.0 distribution, and are also available in HTML form from
-https://peps.python.org/.  As of September 2000, there are 25 PEPS, ranging
+https://peps.python.org/.  As of September 2000, there are 25 PEPs, ranging
 from :pep:`201`, "Lockstep Iteration", to PEP 225, "Elementwise/Objectwise
 Operators".
 
diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst
index 996cc6efaabdef..ce47d92df46517 100644
--- a/Doc/whatsnew/2.6.rst
+++ b/Doc/whatsnew/2.6.rst
@@ -4,8 +4,6 @@
   What's New in Python 2.6
 
 
-.. XXX add trademark info for Apple, Microsoft, SourceForge.
-
 :Author: A.M. Kuchling (amk at amk.ca)
 
 .. $Id$
@@ -128,7 +126,7 @@ and to C extension code as :c:data:`Py_Py3kWarningFlag`.
The 3\ *xxx* series of PEPs, which contains proposals for Python 3.0.
:pep:`3000` describes the development process for Python 3.0.
Start with :pep:`3100` that describes the general goals for Python
-   3.0, and then explore the higher-numbered PEPS that propose
+   3.0, and then explore the higher-numbered PEPs that propose
specific features.
 
 
@@ -1051,8 +1049,6 @@ the :mod:`io` module:
   sockets, but Python 2.6 hasn't restructured its file and socket objects
   in this way.
 
-  .. XXX should 2.6 register them in io.py?
-
 * :class:`BufferedIOBase` is an abstract base class that
   buffers data in memory to reduce the number of
   system calls used, making I/O processing more efficient.
@@ -1133,8 +1129,6 @@ while an external caller could be modifying the contents,
 so there's a corresponding ``PyBuffer_Release(Py_buffer *view)`` to
 indicate that the external caller is done.
 
-.. XXX PyObject_GetBuffer not documented in c-api
-
 The *flags* argument to :c:func:`PyObject_GetBuffer` specifies
 constraints upon the memory returned.  Some examples are:
 
@@ -3110,8 +3104,8 @@ Port-Specific Changes: Windows
 
 * The :mod:`msvcrt` module now supports
   both the normal and wide char variants of the console I/O
-  API.  The :func:`getwch` function reads a keypress and returns a Unicode
-  value, as does the :func:`getwche` function.  The :func:`putwch` function
+  API.  The :func:`~msvcrt.getwch` function reads a keypress and returns a 
Unicode
+  value, as does the :func:`~msvcrt.getwche` function.  The 
:func:`~msvcrt.putwch` function
   takes a Unicode character and writes it to the console.
   (Contributed by Christian Heimes.)
 
@@ -3120,24 +3114,24 @@ Port-Specific Changes: Windows
   directory path.  (Contributed by Josiah Carlson; :issue:`957650`.)
 
 * The :mod:`socket` module's socket objects now have an
-  :meth:`ioctl` method that provides a limited interface to the
+  :meth:`~socket.socket.ioctl` method that provides a limited interface to the
   :c:func:`WSAIoctl` system interface.
 
-* The :mod:`_winreg` module now has a function,
-  :func:`ExpandEnvironmentStrings`,
+* The :mod:`_winreg ` module now has a function,
+  :func:`~winreg.ExpandEnvironmentStrings`,
   that expands environment variable references such as ``%NAME%``
   in an input string.  The handle objects provided by this
   module now support the context protocol, so they can be used
   in :keyword:`with` statements. (Contributed by Christian Heimes.)
 
-  :mod:`_winreg` also has better support for x64 systems,
-  exposing the :func:`DisableReflectionKey`, :func:`EnableReflectionKey`,
-  and :func:`QueryReflectionKey` functions, which enable and disable
+  :mod:`_winreg ` also has better support for x64 systems,
+  exposing the :func:`~winreg.DisableReflectionKey`, 
:func:`~winreg.EnableReflectionKey`,
+  and :func:`~winreg.QueryReflectionKey` functions, which enable and disable
   registry reflection for 32-bit processes running on 64-bit systems.
   (:issue:`1753245`)
 
-* The :mod:`msilib` module's :class:`Record` object
-  gained :meth:`GetInteger` and :meth:`GetString` methods that
+* The :mod:`msilib` module's :class:`!Record` object
+  gained :meth:`~msilib.Record.GetInteger` and 
:meth:`~msilib.Record.GetString` methods that
   return field values as an integer or a string.
   (Contributed by Floris Bruynooghe; :issue:`2125`.)
 
@@ -3151,49 +3145,49 @@ Port-Specific

[Python-checkins] gh-113842: Add missing error check for PyIter_Next() in Python/symtable.c (GH-113843)

2024-01-09 Thread serhiy-storchaka
https://github.com/python/cpython/commit/fda901a1ff94ea6cc338b74928acdbc5ee165ed7
commit: fda901a1ff94ea6cc338b74928acdbc5ee165ed7
branch: main
author: Yan Yanchii 
committer: serhiy-storchaka 
date: 2024-01-09T12:43:58+02:00
summary:

gh-113842: Add missing error check for PyIter_Next() in Python/symtable.c 
(GH-113843)

files:
M Python/symtable.c

diff --git a/Python/symtable.c b/Python/symtable.c
index 52d5932896b263..83137b491f282c 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -977,6 +977,12 @@ update_symbols(PyObject *symbols, PyObject *scopes,
 }
 Py_DECREF(name);
 }
+
+/* Check if loop ended because of exception in PyIter_Next */
+if (PyErr_Occurred()) {
+goto error;
+}
+
 Py_DECREF(itr);
 Py_DECREF(v_free);
 return 1;

___
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-113842: Add missing error check for PyIter_Next() in Python/symtable.c (GH-113843) (GH-113851)

2024-01-09 Thread serhiy-storchaka
https://github.com/python/cpython/commit/6b6f91ec88488e9e1ac71cf5635c79c92beeff0f
commit: 6b6f91ec88488e9e1ac71cf5635c79c92beeff0f
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-01-09T11:00:07Z
summary:

[3.12] gh-113842: Add missing error check for PyIter_Next() in 
Python/symtable.c (GH-113843) (GH-113851)

(cherry picked from commit fda901a1ff94ea6cc338b74928acdbc5ee165ed7)

Co-authored-by: Yan Yanchii 

files:
M Python/symtable.c

diff --git a/Python/symtable.c b/Python/symtable.c
index 70b6eacd4ac071..a5c6b465b71ddd 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -872,6 +872,12 @@ update_symbols(PyObject *symbols, PyObject *scopes,
 }
 Py_DECREF(name);
 }
+
+/* Check if loop ended because of exception in PyIter_Next */
+if (PyErr_Occurred()) {
+goto error;
+}
+
 Py_DECREF(itr);
 Py_DECREF(v_free);
 return 1;

___
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.11] gh-113842: Add missing error check for PyIter_Next() in Python/symtable.c (GH-113843) (GH-113852)

2024-01-09 Thread serhiy-storchaka
https://github.com/python/cpython/commit/50efd7db20a3073affd877ecd2edd1dcf2a2c8b9
commit: 50efd7db20a3073affd877ecd2edd1dcf2a2c8b9
branch: 3.11
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-01-09T11:09:32Z
summary:

[3.11] gh-113842: Add missing error check for PyIter_Next() in 
Python/symtable.c (GH-113843) (GH-113852)

(cherry picked from commit fda901a1ff94ea6cc338b74928acdbc5ee165ed7)

Co-authored-by: Yan Yanchii 

files:
M Python/symtable.c

diff --git a/Python/symtable.c b/Python/symtable.c
index 37e5c697405b1a..3519f62098425c 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -728,6 +728,12 @@ update_symbols(PyObject *symbols, PyObject *scopes,
 }
 Py_DECREF(name);
 }
+
+/* Check if loop ended because of exception in PyIter_Next */
+if (PyErr_Occurred()) {
+goto error;
+}
+
 Py_DECREF(itr);
 Py_DECREF(v_free);
 return 1;

___
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-87868: Sort and remove duplicates in getenvironment() (GH-102731)

2024-01-09 Thread zooba
https://github.com/python/cpython/commit/c31be58da8577ef140e83d4e46502c7bb1eb9abf
commit: c31be58da8577ef140e83d4e46502c7bb1eb9abf
branch: main
author: AN Long 
committer: zooba 
date: 2024-01-09T15:58:26Z
summary:

gh-87868: Sort and remove duplicates in getenvironment() (GH-102731)

Co-authored-by: Alex Waygood 
Co-authored-by: Pieter Eendebak 
Co-authored-by: Erlend E. Aasland 

files:
A Misc/NEWS.d/next/Windows/2023-03-15-23-53-45.gh-issue-87868.4C36oQ.rst
M Lib/test/test_subprocess.py
M Modules/_winapi.c

diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 6d3228bf92f8ca..102e697ba7a90d 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -791,6 +791,19 @@ def test_env(self):
 stdout, stderr = p.communicate()
 self.assertEqual(stdout, b"orange")
 
[email protected](sys.platform == "win32", "Windows only issue")
+def test_win32_duplicate_envs(self):
+newenv = os.environ.copy()
+newenv["fRUit"] = "cherry"
+newenv["fruit"] = "lemon"
+newenv["FRUIT"] = "orange"
+newenv["frUit"] = "banana"
+with subprocess.Popen(["CMD", "/c", "SET", "fruit"],
+  stdout=subprocess.PIPE,
+  env=newenv) as p:
+stdout, _ = p.communicate()
+self.assertEqual(stdout.strip(), b"frUit=banana")
+
 # Windows requires at least the SYSTEMROOT environment variable to start
 # Python
 @unittest.skipIf(sys.platform == 'win32',
@@ -822,6 +835,17 @@ def is_env_var_to_ignore(n):
if not is_env_var_to_ignore(k)]
 self.assertEqual(child_env_names, [])
 
+def test_one_environment_variable(self):
+newenv = {'fruit': 'orange'}
+cmd = [sys.executable, '-c',
+   'import sys,os;'
+   'sys.stdout.write("fruit="+os.getenv("fruit"))']
+if sys.platform == "win32":
+cmd = ["CMD", "/c", "SET", "fruit"]
+with subprocess.Popen(cmd, stdout=subprocess.PIPE, env=newenv) as p:
+stdout, _ = p.communicate()
+self.assertTrue(stdout.startswith(b"fruit=orange"))
+
 def test_invalid_cmd(self):
 # null character in the command name
 cmd = sys.executable + '\0'
@@ -862,6 +886,19 @@ def test_invalid_env(self):
 stdout, stderr = p.communicate()
 self.assertEqual(stdout, b"orange=lemon")
 
[email protected](sys.platform == "win32", "Windows only issue")
+def test_win32_invalid_env(self):
+# '=' in the environment variable name
+newenv = os.environ.copy()
+newenv["FRUIT=VEGETABLE"] = "cabbage"
+with self.assertRaises(ValueError):
+subprocess.Popen(ZERO_RETURN_CMD, env=newenv)
+
+newenv = os.environ.copy()
+newenv["==FRUIT"] = "cabbage"
+with self.assertRaises(ValueError):
+subprocess.Popen(ZERO_RETURN_CMD, env=newenv)
+
 def test_communicate_stdin(self):
 p = subprocess.Popen([sys.executable, "-c",
   'import sys;'
diff --git 
a/Misc/NEWS.d/next/Windows/2023-03-15-23-53-45.gh-issue-87868.4C36oQ.rst 
b/Misc/NEWS.d/next/Windows/2023-03-15-23-53-45.gh-issue-87868.4C36oQ.rst
new file mode 100644
index 00..37e8103c9ec34b
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2023-03-15-23-53-45.gh-issue-87868.4C36oQ.rst
@@ -0,0 +1,2 @@
+Correctly sort and remove duplicate environment variables in
+:py:func:`!_winapi.CreateProcess`.
diff --git a/Modules/_winapi.c b/Modules/_winapi.c
index 8c48b6f3ec6ef6..a26850e825b492 100644
--- a/Modules/_winapi.c
+++ b/Modules/_winapi.c
@@ -774,12 +774,157 @@ gethandle(PyObject* obj, const char* name)
 return ret;
 }
 
+static PyObject *
+sortenvironmentkey(PyObject *module, PyObject *item)
+{
+return _winapi_LCMapStringEx_impl(NULL, LOCALE_NAME_INVARIANT,
+  LCMAP_UPPERCASE, item);
+}
+
+static PyMethodDef sortenvironmentkey_def = {
+"sortenvironmentkey", _PyCFunction_CAST(sortenvironmentkey), METH_O, "",
+};
+
+static int
+sort_environment_keys(PyObject *keys)
+{
+PyObject *keyfunc = PyCFunction_New(&sortenvironmentkey_def, NULL);
+if (keyfunc == NULL) {
+return -1;
+}
+PyObject *kwnames = Py_BuildValue("(s)", "key");
+if (kwnames == NULL) {
+Py_DECREF(keyfunc);
+return -1;
+}
+PyObject *args[] = { keys, keyfunc };
+PyObject *ret = PyObject_VectorcallMethod(&_Py_ID(sort), args, 1, kwnames);
+Py_DECREF(keyfunc);
+Py_DECREF(kwnames);
+if (ret == NULL) {
+return -1;
+}
+Py_DECREF(ret);
+
+return 0;
+}
+
+static int
+compare_string_ordinal(PyObject *str1, PyObject *str2, int *result)
+{
+wchar_t *s1 = PyUnicode_AsWideCharString(str1, NULL);
+if (s1 == NULL) {
+return -1;
+}
+wchar_t *s2 = PyUnicode_AsWideCharString(str2, NU

[Python-checkins] gh-103092: Test _ctypes type hierarchy and features (#113727)

2024-01-09 Thread erlend-aasland
https://github.com/python/cpython/commit/be89ee5649031e08f191bf596fa20a09c5698079
commit: be89ee5649031e08f191bf596fa20a09c5698079
branch: main
author: AN Long 
committer: erlend-aasland 
date: 2024-01-09T18:28:43+01:00
summary:

gh-103092: Test _ctypes type hierarchy and features (#113727)

Test the following features for _ctypes types:
- disallow instantiation
- inheritance (MRO)
- immutability
- type name

The following _ctypes types are tested:
- Array
- CField
- COMError
- PyCArrayType
- PyCFuncPtrType
- PyCPointerType
- PyCSimpleType
- PyCStructType
- Structure
- Union
- UnionType
- _CFuncPtr
- _Pointer
- _SimpleCData

Co-authored-by: Erlend E. Aasland 

files:
A Lib/test/test_ctypes/_support.py
A Lib/test/test_ctypes/test_unions.py
M Lib/test/test_ctypes/test_arrays.py
M Lib/test/test_ctypes/test_funcptr.py
M Lib/test/test_ctypes/test_pointers.py
M Lib/test/test_ctypes/test_simplesubclasses.py
M Lib/test/test_ctypes/test_struct_fields.py
M Lib/test/test_ctypes/test_structures.py
M Lib/test/test_ctypes/test_win32.py

diff --git a/Lib/test/test_ctypes/_support.py b/Lib/test/test_ctypes/_support.py
new file mode 100644
index 00..e4c2b33825ae8f
--- /dev/null
+++ b/Lib/test/test_ctypes/_support.py
@@ -0,0 +1,24 @@
+# Some classes and types are not export to _ctypes module directly.
+
+import ctypes
+from _ctypes import Structure, Union, _Pointer, Array, _SimpleCData, CFuncPtr
+
+
+_CData = Structure.__base__
+assert _CData.__name__ == "_CData"
+
+class _X(Structure):
+_fields_ = [("x", ctypes.c_int)]
+CField = type(_X.x)
+
+# metaclasses
+PyCStructType = type(Structure)
+UnionType = type(Union)
+PyCPointerType = type(_Pointer)
+PyCArrayType = type(Array)
+PyCSimpleType = type(_SimpleCData)
+PyCFuncPtrType = type(CFuncPtr)
+
+# type flags
+Py_TPFLAGS_DISALLOW_INSTANTIATION = 1 << 7
+Py_TPFLAGS_IMMUTABLETYPE = 1 << 8
diff --git a/Lib/test/test_ctypes/test_arrays.py 
b/Lib/test/test_ctypes/test_arrays.py
index 6b6cebd3e20285..774316e227ff73 100644
--- a/Lib/test/test_ctypes/test_arrays.py
+++ b/Lib/test/test_ctypes/test_arrays.py
@@ -7,6 +7,8 @@
 c_char, c_wchar, c_byte, c_ubyte, c_short, c_ushort, 
c_int, c_uint,
 c_long, c_ulonglong, c_float, c_double, c_longdouble)
 from test.support import bigmemtest, _2G
+from ._support import (_CData, PyCArrayType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
+   Py_TPFLAGS_IMMUTABLETYPE)
 
 
 formats = "bBhHiIlLqQfd"
@@ -23,6 +25,18 @@ def ARRAY(*args):
 
 
 class ArrayTestCase(unittest.TestCase):
+def test_inheritance_hierarchy(self):
+self.assertEqual(Array.mro(), [Array, _CData, object])
+
+self.assertEqual(PyCArrayType.__name__, "PyCArrayType")
+self.assertEqual(type(PyCArrayType), type)
+
+def test_type_flags(self):
+for cls in Array, PyCArrayType:
+with self.subTest(cls=cls):
+self.assertTrue(cls.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
+self.assertFalse(cls.__flags__ & 
Py_TPFLAGS_DISALLOW_INSTANTIATION)
+
 def test_simple(self):
 # create classes holding simple numeric types, and check
 # various properties.
diff --git a/Lib/test/test_ctypes/test_funcptr.py 
b/Lib/test/test_ctypes/test_funcptr.py
index 2ad40647e0cfbb..0eed39484fb39e 100644
--- a/Lib/test/test_ctypes/test_funcptr.py
+++ b/Lib/test/test_ctypes/test_funcptr.py
@@ -3,6 +3,8 @@
 import unittest
 from ctypes import (CDLL, Structure, CFUNCTYPE, sizeof, _CFuncPtr,
 c_void_p, c_char_p, c_char, c_int, c_uint, c_long)
+from ._support import (_CData, PyCFuncPtrType, 
Py_TPFLAGS_DISALLOW_INSTANTIATION,
+   Py_TPFLAGS_IMMUTABLETYPE)
 
 
 try:
@@ -15,6 +17,18 @@
 
 
 class CFuncPtrTestCase(unittest.TestCase):
+def test_inheritance_hierarchy(self):
+self.assertEqual(_CFuncPtr.mro(), [_CFuncPtr, _CData, object])
+
+self.assertEqual(PyCFuncPtrType.__name__, "PyCFuncPtrType")
+self.assertEqual(type(PyCFuncPtrType), type)
+
+def test_type_flags(self):
+for cls in _CFuncPtr, PyCFuncPtrType:
+with self.subTest(cls=cls):
+self.assertTrue(_CFuncPtr.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
+self.assertFalse(_CFuncPtr.__flags__ & 
Py_TPFLAGS_DISALLOW_INSTANTIATION)
+
 def test_basic(self):
 X = WINFUNCTYPE(c_int, c_int, c_int)
 
diff --git a/Lib/test/test_ctypes/test_pointers.py 
b/Lib/test/test_ctypes/test_pointers.py
index 8410174358c19d..8cf2114c282cab 100644
--- a/Lib/test/test_ctypes/test_pointers.py
+++ b/Lib/test/test_ctypes/test_pointers.py
@@ -10,6 +10,8 @@
 c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
 c_long, c_ulong, c_longlong, c_ulonglong,
 c_float, c_double)
+from ._support import (_CData, PyCPointerType, 
Py_TPFLAGS_DISALLOW_INSTANTIATION,
+   Py_TPFLAGS_IMMUTABLETYPE)
 
 
 ctype_types = [c_byte, c_ubyte, c_short, c_ushort,

[Python-checkins] gh-113650: Add workaround option for MSVC ARM64 bug affecting string encoding (GH-113836)

2024-01-09 Thread zooba
https://github.com/python/cpython/commit/ad849b4ba008bf4ff97151651e619259ddb4fc18
commit: ad849b4ba008bf4ff97151651e619259ddb4fc18
branch: main
author: Steve Dower 
committer: zooba 
date: 2024-01-09T17:32:22Z
summary:

gh-113650: Add workaround option for MSVC ARM64 bug affecting string encoding 
(GH-113836)

files:
M PCbuild/pyproject.props

diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props
index 06c695783ced12..16ad91ef0278c8 100644
--- a/PCbuild/pyproject.props
+++ b/PCbuild/pyproject.props
@@ -29,6 +29,7 @@
 
 
 true
+true
   
 
   
@@ -62,6 +63,7 @@
   -Wno-deprecated-non-prototype -Wno-unused-label -Wno-pointer-sign 
-Wno-incompatible-pointer-types-discards-qualifiers -Wno-unused-function 
%(AdditionalOptions)
   -flto %(AdditionalOptions)
   -d2pattern-opt-disable:-932189325 
%(AdditionalOptions)
+  -d2ssa-patterns-all- 
%(AdditionalOptions)
   /sourceDependencies "$(IntDir.Trim(`\`))" 
%(AdditionalOptions)
 
 

___
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] Fix opcode name printing in debug mode (#113870)

2024-01-09 Thread gvanrossum
https://github.com/python/cpython/commit/65f8eb71190f870c66fb00da29a670ee232a3fd5
commit: 65f8eb71190f870c66fb00da29a670ee232a3fd5
branch: main
author: Guido van Rossum 
committer: gvanrossum 
date: 2024-01-09T18:18:11Z
summary:

Fix opcode name printing in debug mode (#113870)

Fix a few places where the lltrace debug output printed ``(null)`` instead of 
an opcode name, because it was calling ``_PyUOpName()`` on a Tier-1 opcode.

files:
M Python/optimizer.c

diff --git a/Python/optimizer.c b/Python/optimizer.c
index f27af14d967cd3..ad5b4994318d44 100644
--- a/Python/optimizer.c
+++ b/Python/optimizer.c
@@ -2,7 +2,7 @@
 #include "opcode.h"
 #include "pycore_interp.h"
 #include "pycore_bitutils.h"// _Py_popcount32()
-#include "pycore_opcode_metadata.h" // _PyOpcode_OpName()
+#include "pycore_opcode_metadata.h" // _PyOpcode_OpName[]
 #include "pycore_opcode_utils.h"  // MAX_REAL_OPCODE
 #include "pycore_optimizer.h" // _Py_uop_analyze_and_optimize()
 #include "pycore_pystate.h"   // _PyInterpreterState_GET()
@@ -563,7 +563,7 @@ translate_bytecode_to_trace(
 uint32_t uopcode = BRANCH_TO_GUARD[opcode - 
POP_JUMP_IF_FALSE][jump_likely];
 _Py_CODEUNIT *next_instr = instr + 1 + 
_PyOpcode_Caches[_PyOpcode_Deopt[opcode]];
 DPRINTF(2, "%s(%d): counter=%x, bitcount=%d, likely=%d, 
confidence=%d, uopcode=%s\n",
-_PyUOpName(opcode), oparg,
+_PyOpcode_OpName[opcode], oparg,
 counter, bitcount, jump_likely, confidence, 
_PyUOpName(uopcode));
 ADD_TO_TRACE(uopcode, max_length, 0, target);
 if (jump_likely) {
@@ -722,7 +722,7 @@ translate_bytecode_to_trace(
 }
 break;
 }
-DPRINTF(2, "Unsupported opcode %s\n", _PyUOpName(opcode));
+DPRINTF(2, "Unsupported opcode %s\n", 
_PyOpcode_OpName[opcode]);
 OPT_UNSUPPORTED_OPCODE(opcode);
 goto done;  // Break out of loop
 }  // End default

___
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] Simplify binomial approximation example with random.binomialvariate() (gh-113871)

2024-01-09 Thread rhettinger
https://github.com/python/cpython/commit/2fd2e747930987eb8ed4929cf0132e85db759dab
commit: 2fd2e747930987eb8ed4929cf0132e85db759dab
branch: main
author: Raymond Hettinger 
committer: rhettinger 
date: 2024-01-09T13:02:07-06:00
summary:

Simplify binomial approximation example with random.binomialvariate() 
(gh-113871)

files:
M Doc/library/statistics.rst

diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst
index 5c8ad3a7dd7380..588c9c0be4ea02 100644
--- a/Doc/library/statistics.rst
+++ b/Doc/library/statistics.rst
@@ -1026,19 +1026,16 @@ probability that the Python room will stay within its 
capacity limits?
 >>> round(NormalDist(mu=n*p, sigma=sqrt(n*p*q)).cdf(k + 0.5), 4)
 0.8402
 
->>> # Solution using the cumulative binomial distribution
+>>> # Exact solution using the cumulative binomial distribution
 >>> from math import comb, fsum
 >>> round(fsum(comb(n, r) * p**r * q**(n-r) for r in range(k+1)), 4)
 0.8402
 
 >>> # Approximation using a simulation
->>> from random import seed, choices
+>>> from random import seed, binomialvariate
 >>> seed(8675309)
->>> def trial():
-... return choices(('Python', 'Ruby'), (p, q), k=n).count('Python')
-...
->>> mean(trial() <= k for i in range(10_000))
-0.8398
+>>> mean(binomialvariate(n, p) <= k for i in range(10_000))
+0.8406
 
 
 Naive bayesian classifier

___
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-113528: Deoptimise `pathlib._abc.PathBase._make_child_relpath()` (#113532)

2024-01-09 Thread barneygale
https://github.com/python/cpython/commit/9100fc407e8c7038e7214b600b4ae568ae5510e3
commit: 9100fc407e8c7038e7214b600b4ae568ae5510e3
branch: main
author: Barney Gale 
committer: barneygale 
date: 2024-01-09T19:11:17Z
summary:

GH-113528: Deoptimise `pathlib._abc.PathBase._make_child_relpath()` (#113532)

Call straight through to `joinpath()` in `PathBase._make_child_relpath()`.
Move optimised/caching code to `pathlib.Path._make_child_relpath()`

files:
M Lib/pathlib/__init__.py
M Lib/pathlib/_abc.py

diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py
index a432d45bfed3a9..749c68d2999bdc 100644
--- a/Lib/pathlib/__init__.py
+++ b/Lib/pathlib/__init__.py
@@ -405,6 +405,22 @@ def _make_child_entry(self, entry):
 path._tail_cached = self._tail + [entry.name]
 return path
 
+def _make_child_relpath(self, name):
+path_str = str(self)
+tail = self._tail
+if tail:
+path_str = f'{path_str}{self.pathmod.sep}{name}'
+elif path_str != '.':
+path_str = f'{path_str}{name}'
+else:
+path_str = name
+path = self.with_segments(path_str)
+path._str = path_str
+path._drv = self.drive
+path._root = self.root
+path._tail_cached = tail + [name]
+return path
+
 def glob(self, pattern, *, case_sensitive=None, follow_symlinks=None):
 """Iterate over this subtree and yield all existing files (of any
 kind, including directories) matching the given relative pattern.
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index be22ecef4d214e..0e442ae4809c36 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -753,20 +753,7 @@ def _make_child_entry(self, entry):
 return entry
 
 def _make_child_relpath(self, name):
-path_str = str(self)
-tail = self._tail
-if tail:
-path_str = f'{path_str}{self.pathmod.sep}{name}'
-elif path_str != '.':
-path_str = f'{path_str}{name}'
-else:
-path_str = name
-path = self.with_segments(path_str)
-path._str = path_str
-path._drv = self.drive
-path._root = self.root
-path._tail_cached = tail + [name]
-return path
+return self.joinpath(name)
 
 def glob(self, pattern, *, case_sensitive=None, follow_symlinks=None):
 """Iterate over this subtree and yield all existing files (of any

___
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] Simplify binomial approximation example with random.binomialvariate() (gh-113871) (gh-113872)

2024-01-09 Thread rhettinger
https://github.com/python/cpython/commit/9a6b99ee8b750c8891b488a6bd60696bc164c6fa
commit: 9a6b99ee8b750c8891b488a6bd60696bc164c6fa
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: rhettinger 
date: 2024-01-09T13:24:55-06:00
summary:

[3.12] Simplify binomial approximation example with random.binomialvariate() 
(gh-113871) (gh-113872)

files:
M Doc/library/statistics.rst

diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst
index 318e5d74611426..36f47b92ee3df5 100644
--- a/Doc/library/statistics.rst
+++ b/Doc/library/statistics.rst
@@ -1016,19 +1016,16 @@ probability that the Python room will stay within its 
capacity limits?
 >>> round(NormalDist(mu=n*p, sigma=sqrt(n*p*q)).cdf(k + 0.5), 4)
 0.8402
 
->>> # Solution using the cumulative binomial distribution
+>>> # Exact solution using the cumulative binomial distribution
 >>> from math import comb, fsum
 >>> round(fsum(comb(n, r) * p**r * q**(n-r) for r in range(k+1)), 4)
 0.8402
 
 >>> # Approximation using a simulation
->>> from random import seed, choices
+>>> from random import seed, binomialvariate
 >>> seed(8675309)
->>> def trial():
-... return choices(('Python', 'Ruby'), (p, q), k=n).count('Python')
-...
->>> mean(trial() <= k for i in range(10_000))
-0.8398
+>>> mean(binomialvariate(n, p) <= k for i in range(10_000))
+0.8406
 
 
 Naive bayesian classifier

___
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-113848: Use PyErr_GivenExceptionMatches() for check for CancelledError (GH-113849)

2024-01-09 Thread serhiy-storchaka
https://github.com/python/cpython/commit/5273655bea050432756098641b9fda72361bf983
commit: 5273655bea050432756098641b9fda72361bf983
branch: main
author: Serhiy Storchaka 
committer: serhiy-storchaka 
date: 2024-01-09T21:41:02+02:00
summary:

gh-113848: Use PyErr_GivenExceptionMatches() for check for CancelledError 
(GH-113849)

files:
M Modules/_asynciomodule.c

diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 3a11cdc926f138..b929e6d9922b4e 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -2754,7 +2754,6 @@ gen_status_from_result(PyObject **result)
 static PyObject *
 task_step_impl(asyncio_state *state, TaskObj *task, PyObject *exc)
 {
-int res;
 int clear_exc = 0;
 PyObject *result = NULL;
 PyObject *coro;
@@ -2771,20 +2770,7 @@ task_step_impl(asyncio_state *state, TaskObj *task, 
PyObject *exc)
 if (task->task_must_cancel) {
 assert(exc != Py_None);
 
-if (exc) {
-/* Check if exc is a CancelledError */
-res = PyObject_IsInstance(exc, state->asyncio_CancelledError);
-if (res == -1) {
-/* An error occurred, abort */
-goto fail;
-}
-if (res == 0) {
-/* exc is not CancelledError; reset it to NULL */
-exc = NULL;
-}
-}
-
-if (!exc) {
+if (!exc || !PyErr_GivenExceptionMatches(exc, 
state->asyncio_CancelledError)) {
 /* exc was not a CancelledError */
 exc = create_cancelled_error(state, (FutureObj*)task);
 

___
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-113848: Handle CancelledError subclasses in asyncio TaskGroup() and timeout() (GH-113850)

2024-01-09 Thread serhiy-storchaka
https://github.com/python/cpython/commit/a5db6a3351b440a875a5af84a8b2447981356e34
commit: a5db6a3351b440a875a5af84a8b2447981356e34
branch: main
author: Serhiy Storchaka 
committer: serhiy-storchaka 
date: 2024-01-09T21:41:31+02:00
summary:

gh-113848: Handle CancelledError subclasses in asyncio TaskGroup() and 
timeout() (GH-113850)

files:
A Misc/NEWS.d/next/Library/2024-01-09-12-19-55.gh-issue-113848.kXoCy0.rst
M Lib/asyncio/taskgroups.py
M Lib/asyncio/timeouts.py

diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py
index cb9c1ce4d7d1d2..e1c56d140bef7d 100644
--- a/Lib/asyncio/taskgroups.py
+++ b/Lib/asyncio/taskgroups.py
@@ -73,8 +73,10 @@ async def __aexit__(self, et, exc, tb):
 self._base_error is None):
 self._base_error = exc
 
-propagate_cancellation_error = \
-exc if et is exceptions.CancelledError else None
+if et is not None and issubclass(et, exceptions.CancelledError):
+propagate_cancellation_error = exc
+else:
+propagate_cancellation_error = None
 if self._parent_cancel_requested:
 # If this flag is set we *must* call uncancel().
 if self._parent_task.uncancel() == 0:
@@ -133,7 +135,7 @@ async def __aexit__(self, et, exc, tb):
 if propagate_cancellation_error and not self._errors:
 raise propagate_cancellation_error
 
-if et is not None and et is not exceptions.CancelledError:
+if et is not None and not issubclass(et, exceptions.CancelledError):
 self._errors.append(exc)
 
 if self._errors:
diff --git a/Lib/asyncio/timeouts.py b/Lib/asyncio/timeouts.py
index 30042abb3ad804..2c5dd295ff5ade 100644
--- a/Lib/asyncio/timeouts.py
+++ b/Lib/asyncio/timeouts.py
@@ -109,10 +109,11 @@ async def __aexit__(
 if self._state is _State.EXPIRING:
 self._state = _State.EXPIRED
 
-if self._task.uncancel() <= self._cancelling and exc_type is 
exceptions.CancelledError:
-# Since there are no new cancel requests, we're
-# handling this.
-raise TimeoutError from exc_val
+if self._task.uncancel() <= self._cancelling and exc_type is not 
None:
+if issubclass(exc_type, exceptions.CancelledError):
+# Since there are no new cancel requests, we're
+# handling this.
+raise TimeoutError from exc_val
 elif self._state is _State.ENTERED:
 self._state = _State.EXITED
 
diff --git 
a/Misc/NEWS.d/next/Library/2024-01-09-12-19-55.gh-issue-113848.kXoCy0.rst 
b/Misc/NEWS.d/next/Library/2024-01-09-12-19-55.gh-issue-113848.kXoCy0.rst
new file mode 100644
index 00..8d5032ab0201f9
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-01-09-12-19-55.gh-issue-113848.kXoCy0.rst
@@ -0,0 +1,3 @@
+:func:`asyncio.TaskGroup()` and :func:`asyncio.timeout()` context managers
+now handle :exc:`~asyncio.CancelledError` subclasses as well as exact
+:exc:`!CancelledError`.

___
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-113781: Silence AttributeError in warning module during Python finalization (GH-113813)

2024-01-09 Thread serhiy-storchaka
https://github.com/python/cpython/commit/0297418cacf998e778bc0517aa11eaac827b8c0f
commit: 0297418cacf998e778bc0517aa11eaac827b8c0f
branch: main
author: Serhiy Storchaka 
committer: serhiy-storchaka 
date: 2024-01-09T21:44:05+02:00
summary:

gh-113781: Silence AttributeError in warning module during Python finalization 
(GH-113813)

The tracemalloc module can already be cleared.

files:
A Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst
M Lib/warnings.py

diff --git a/Lib/warnings.py b/Lib/warnings.py
index b8ff078569d2ce..4ad6ad027192e8 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -58,15 +58,16 @@ def _formatwarnmsg_impl(msg):
 # catch Exception, not only ImportError and RecursionError.
 except Exception:
 # don't suggest to enable tracemalloc if it's not available
-tracing = True
+suggest_tracemalloc = False
 tb = None
 else:
-tracing = tracemalloc.is_tracing()
 try:
+suggest_tracemalloc = not tracemalloc.is_tracing()
 tb = tracemalloc.get_object_traceback(msg.source)
 except Exception:
 # When a warning is logged during Python shutdown, tracemalloc
 # and the import machinery don't work anymore
+suggest_tracemalloc = False
 tb = None
 
 if tb is not None:
@@ -85,7 +86,7 @@ def _formatwarnmsg_impl(msg):
 if line:
 line = line.strip()
 s += '%s\n' % line
-elif not tracing:
+elif suggest_tracemalloc:
 s += (f'{category}: Enable tracemalloc to get the object '
   f'allocation traceback\n')
 return s
diff --git 
a/Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst 
b/Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst
new file mode 100644
index 00..141230b066e22e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst
@@ -0,0 +1,2 @@
+Silence unraisable AttributeError when warnings are emitted during Python
+finalization.

___
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-113661: unittest runner: Don't exit 5 if tests were skipped (#113856)

2024-01-09 Thread gpshead
https://github.com/python/cpython/commit/3a9096c337c16c9335e0d4eba8d1d4196258af72
commit: 3a9096c337c16c9335e0d4eba8d1d4196258af72
branch: main
author: Stefano Rivera 
committer: gpshead 
date: 2024-01-09T19:50:01Z
summary:

GH-113661: unittest runner: Don't exit 5 if tests were skipped (#113856)

The intention of exiting 5 was to detect issues where the test suite
wasn't discovered at all. If we skipped tests, it was correctly
discovered.

files:
A Misc/NEWS.d/next/Library/2024-01-09-08-59-43.gh-issue-113661.asvXSx.rst
M Doc/library/unittest.rst
M Lib/test/test_unittest/test_program.py
M Lib/unittest/main.py
M Lib/unittest/runner.py

diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
index 70b4c84c05f818..491009769f5aa6 100644
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -2290,7 +2290,7 @@ Loading and running tests
The *testRunner* argument can either be a test runner class or an already
created instance of it. By default ``main`` calls :func:`sys.exit` with
an exit code indicating success (0) or failure (1) of the tests run.
-   An exit code of 5 indicates that no tests were run.
+   An exit code of 5 indicates that no tests were run or skipped.
 
The *testLoader* argument has to be a :class:`TestLoader` instance,
and defaults to :data:`defaultTestLoader`.
diff --git a/Lib/test/test_unittest/test_program.py 
b/Lib/test/test_unittest/test_program.py
index f6d52f93e4a25f..d8f5d3692a5088 100644
--- a/Lib/test/test_unittest/test_program.py
+++ b/Lib/test/test_unittest/test_program.py
@@ -167,6 +167,18 @@ def test_ExitAsDefault(self):
 'expected failures=1, unexpected successes=1)\n')
 self.assertTrue(out.endswith(expected))
 
+def test_ExitSkippedSuite(self):
+stream = BufferedWriter()
+with self.assertRaises(SystemExit) as cm:
+unittest.main(
+argv=["foobar", "-k", "testSkipped"],
+testRunner=unittest.TextTestRunner(stream=stream),
+testLoader=self.TestLoader(self.FooBar))
+self.assertEqual(cm.exception.code, 0)
+out = stream.getvalue()
+expected = '\n\nOK (skipped=1)\n'
+self.assertTrue(out.endswith(expected))
+
 def test_ExitEmptySuite(self):
 stream = BufferedWriter()
 with self.assertRaises(SystemExit) as cm:
diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py
index d29a9f91fcca42..c3869de3f6f18e 100644
--- a/Lib/unittest/main.py
+++ b/Lib/unittest/main.py
@@ -269,7 +269,7 @@ def runTests(self):
 testRunner = self.testRunner
 self.result = testRunner.run(self.test)
 if self.exit:
-if self.result.testsRun == 0:
+if self.result.testsRun == 0 and len(self.result.skipped) == 0:
 sys.exit(_NO_TESTS_EXITCODE)
 elif self.result.wasSuccessful():
 sys.exit(0)
diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py
index e3c020e0ace96d..2bcadf0c998bd9 100644
--- a/Lib/unittest/runner.py
+++ b/Lib/unittest/runner.py
@@ -274,7 +274,7 @@ def run(self, test):
 infos.append("failures=%d" % failed)
 if errored:
 infos.append("errors=%d" % errored)
-elif run == 0:
+elif run == 0 and not skipped:
 self.stream.write("NO TESTS RAN")
 else:
 self.stream.write("OK")
diff --git 
a/Misc/NEWS.d/next/Library/2024-01-09-08-59-43.gh-issue-113661.asvXSx.rst 
b/Misc/NEWS.d/next/Library/2024-01-09-08-59-43.gh-issue-113661.asvXSx.rst
new file mode 100644
index 00..f4a4f1a9841d1a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-01-09-08-59-43.gh-issue-113661.asvXSx.rst
@@ -0,0 +1,3 @@
+unittest runner: Don't exit 5 if tests were skipped. The intention of
+exiting 5 was to detect issues where the test suite wasn't discovered at
+all. If we skipped tests, it was correctly discovered.

___
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-113528: Deoptimise `pathlib._abc.PathBase.resolve()` (#113782)

2024-01-09 Thread barneygale
https://github.com/python/cpython/commit/1092cfb20179ac7dd6a2c3c6f8a57ecc1732c777
commit: 1092cfb20179ac7dd6a2c3c6f8a57ecc1732c777
branch: main
author: Barney Gale 
committer: barneygale 
date: 2024-01-09T19:50:23Z
summary:

GH-113528: Deoptimise `pathlib._abc.PathBase.resolve()` (#113782)

Replace use of `_from_parsed_parts()` with `with_segments()` in
`resolve()`.

No effect on `Path.resolve()`, which uses `os.path.realpath()`.

files:
M Lib/pathlib/_abc.py

diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index 0e442ae4809c36..caa84fc40559f7 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -406,6 +406,23 @@ def __rtruediv__(self, key):
 except TypeError:
 return NotImplemented
 
+@property
+def _stack(self):
+"""
+Split the path into a 2-tuple (anchor, parts), where *anchor* is the
+uppermost parent of the path (equivalent to path.parents[-1]), and
+*parts* is a reversed list of parts following the anchor.
+"""
+split = self.pathmod.split
+path = str(self)
+parent, name = split(path)
+names = []
+while path != parent:
+names.append(name)
+path = parent
+parent, name = split(path)
+return path, names
+
 @property
 def parent(self):
 """The logical parent of the path."""
@@ -911,16 +928,6 @@ def readlink(self):
 self._unsupported("readlink")
 readlink._supported = False
 
-def _split_stack(self):
-"""
-Split the path into a 2-tuple (anchor, parts), where *anchor* is the
-uppermost parent of the path (equivalent to path.parents[-1]), and
-*parts* is a reversed list of parts following the anchor.
-"""
-if not self._tail:
-return self, []
-return self._from_parsed_parts(self.drive, self.root, []), 
self._tail[::-1]
-
 def resolve(self, strict=False):
 """
 Make the path absolute, resolving all symlinks on the way and also
@@ -928,11 +935,15 @@ def resolve(self, strict=False):
 """
 if self._resolving:
 return self
-path, parts = self._split_stack()
+path_root, parts = self._stack
+path = self.with_segments(path_root)
 try:
 path = path.absolute()
 except UnsupportedOperation:
-pass
+path_tail = []
+else:
+path_root, path_tail = path._stack
+path_tail.reverse()
 
 # If the user has *not* overridden the `readlink()` method, then 
symlinks are unsupported
 # and (in non-strict mode) we can improve performance by not calling 
`stat()`.
@@ -940,31 +951,37 @@ def resolve(self, strict=False):
 link_count = 0
 while parts:
 part = parts.pop()
+if not part or part == '.':
+continue
 if part == '..':
-if not path._tail:
-if path.root:
+if not path_tail:
+if path_root:
 # Delete '..' segment immediately following root
 continue
-elif path._tail[-1] != '..':
+elif path_tail[-1] != '..':
 # Delete '..' segment and its predecessor
-path = path.parent
+path_tail.pop()
 continue
-next_path = path._make_child_relpath(part)
+path_tail.append(part)
 if querying and part != '..':
-next_path._resolving = True
+path = self.with_segments(path_root + 
self.pathmod.sep.join(path_tail))
+path._resolving = True
 try:
-st = next_path.stat(follow_symlinks=False)
+st = path.stat(follow_symlinks=False)
 if S_ISLNK(st.st_mode):
 # Like Linux and macOS, raise OSError(errno.ELOOP) if 
too many symlinks are
 # encountered during resolution.
 link_count += 1
 if link_count >= self._max_symlinks:
 raise OSError(ELOOP, "Too many symbolic links in 
path", str(self))
-target, target_parts = 
next_path.readlink()._split_stack()
+target_root, target_parts = path.readlink()._stack
 # If the symlink target is absolute (like 
'/etc/hosts'), set the current
 # path to its uppermost parent (like '/').
-if target.root:
-path = target
+if target_root:
+path_root = target_root
+path_tail.clear()
+else:
+path_tail.pop()
 # Add the symlink target's rever

[Python-checkins] gh-66060: Use actual class name in _io type's __repr__ (#30824)

2024-01-09 Thread erlend-aasland
https://github.com/python/cpython/commit/623b338adf2645b09c546e7a17f2648d3a900620
commit: 623b338adf2645b09c546e7a17f2648d3a900620
branch: main
author: AN Long 
committer: erlend-aasland 
date: 2024-01-09T21:39:36+01:00
summary:

gh-66060: Use actual class name in _io type's __repr__ (#30824)

Use the object's actual class name in the following _io type's __repr__:
- FileIO
- TextIOWrapper
- _WindowsConsoleIO

files:
A Misc/NEWS.d/next/Core and Builtins/2022-01-23-18-00-10.bpo-21861.N8E1zw.rst
M Lib/test/test_fileio.py
M Lib/test/test_io.py
M Lib/test/test_winconsoleio.py
M Modules/_io/fileio.c
M Modules/_io/textio.c
M Modules/_io/winconsoleio.c

diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py
index f490485cdaf3eb..06d9b454add34c 100644
--- a/Lib/test/test_fileio.py
+++ b/Lib/test/test_fileio.py
@@ -174,6 +174,16 @@ def testRepr(self):
 self.assertEqual(repr(self.f),
  "<%s.FileIO [closed]>" % (self.modulename,))
 
+def test_subclass_repr(self):
+class TestSubclass(self.FileIO):
+pass
+
+f = TestSubclass(TESTFN)
+with f:
+self.assertIn(TestSubclass.__name__, repr(f))
+
+self.assertIn(TestSubclass.__name__, repr(f))
+
 def testReprNoCloseFD(self):
 fd = os.open(TESTFN, os.O_RDONLY)
 try:
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index ca31b9dad2631a..936edea3cad70c 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -2806,6 +2806,13 @@ def test_recursive_repr(self):
 with self.assertRaises(RuntimeError):
 repr(t)  # Should not crash
 
+def test_subclass_repr(self):
+class TestSubclass(self.TextIOWrapper):
+pass
+
+f = TestSubclass(self.StringIO())
+self.assertIn(TestSubclass.__name__, repr(f))
+
 def test_line_buffering(self):
 r = self.BytesIO()
 b = self.BufferedWriter(r, 1000)
diff --git a/Lib/test/test_winconsoleio.py b/Lib/test/test_winconsoleio.py
index 70a85552cc03b0..72ff9606908ed5 100644
--- a/Lib/test/test_winconsoleio.py
+++ b/Lib/test/test_winconsoleio.py
@@ -98,6 +98,16 @@ def test_open_name(self):
 self.assertIsInstance(f, ConIO)
 f.close()
 
+def test_subclass_repr(self):
+class TestSubclass(ConIO):
+pass
+
+f = TestSubclass("CON")
+with f:
+self.assertIn(TestSubclass.__name__, repr(f))
+
+self.assertIn(TestSubclass.__name__, repr(f))
+
 @unittest.skipIf(sys.getwindowsversion()[:2] <= (6, 1),
 "test does not work on Windows 7 and earlier")
 def test_conin_conout_names(self):
diff --git a/Misc/NEWS.d/next/Core and 
Builtins/2022-01-23-18-00-10.bpo-21861.N8E1zw.rst b/Misc/NEWS.d/next/Core and 
Builtins/2022-01-23-18-00-10.bpo-21861.N8E1zw.rst
new file mode 100644
index 00..5d99845912caf3
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and 
Builtins/2022-01-23-18-00-10.bpo-21861.N8E1zw.rst   
@@ -0,0 +1,3 @@
+Use the object's actual class name in :meth:`_io.FileIO.__repr__`,
+:meth:`_io._WindowsConsoleIO` and :meth:`_io.TextIOWrapper.__repr__`, to
+make these methods subclass friendly.
diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c
index 8a73ea0365b7a3..af4375c3640679 100644
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -1100,31 +1100,32 @@ static PyObject *
 fileio_repr(fileio *self)
 {
 PyObject *nameobj, *res;
+const char *type_name = Py_TYPE((PyObject *) self)->tp_name;
 
-if (self->fd < 0)
-return PyUnicode_FromFormat("<_io.FileIO [closed]>");
+if (self->fd < 0) {
+return PyUnicode_FromFormat("<%.100s [closed]>", type_name);
+}
 
 if (PyObject_GetOptionalAttr((PyObject *) self, &_Py_ID(name), &nameobj) < 
0) {
 return NULL;
 }
 if (nameobj == NULL) {
 res = PyUnicode_FromFormat(
-"<_io.FileIO fd=%d mode='%s' closefd=%s>",
-self->fd, mode_string(self), self->closefd ? "True" : "False");
+"<%.100s fd=%d mode='%s' closefd=%s>",
+type_name, self->fd, mode_string(self), self->closefd ? "True" : 
"False");
 }
 else {
 int status = Py_ReprEnter((PyObject *)self);
 res = NULL;
 if (status == 0) {
 res = PyUnicode_FromFormat(
-"<_io.FileIO name=%R mode='%s' closefd=%s>",
-nameobj, mode_string(self), self->closefd ? "True" : "False");
+"<%.100s name=%R mode='%s' closefd=%s>",
+type_name, nameobj, mode_string(self), self->closefd ? "True" 
: "False");
 Py_ReprLeave((PyObject *)self);
 }
 else if (status > 0) {
 PyErr_Format(PyExc_RuntimeError,
- "reentrant call inside %s.__repr__",
- Py_TYPE(self)->tp_name);
+ "reentrant call inside %.100s.__repr__", type_name);
 }
 Py_DECREF(nameobj);
 }
diff 

[Python-checkins] [3.12] gh-113781: Silence AttributeError in warning module during Python finalization (GH-113813) (GH-113873)

2024-01-09 Thread serhiy-storchaka
https://github.com/python/cpython/commit/85cf360d2994514e2eae6f851750cff5d3d32235
commit: 85cf360d2994514e2eae6f851750cff5d3d32235
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-01-09T22:40:30+02:00
summary:

[3.12] gh-113781: Silence AttributeError in warning module during Python 
finalization (GH-113813) (GH-113873)

The tracemalloc module can already be cleared.
(cherry picked from commit 0297418cacf998e778bc0517aa11eaac827b8c0f)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst
M Lib/warnings.py

diff --git a/Lib/warnings.py b/Lib/warnings.py
index 98ae708ca3401d..391a501f7282eb 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -58,15 +58,16 @@ def _formatwarnmsg_impl(msg):
 # catch Exception, not only ImportError and RecursionError.
 except Exception:
 # don't suggest to enable tracemalloc if it's not available
-tracing = True
+suggest_tracemalloc = False
 tb = None
 else:
-tracing = tracemalloc.is_tracing()
 try:
+suggest_tracemalloc = not tracemalloc.is_tracing()
 tb = tracemalloc.get_object_traceback(msg.source)
 except Exception:
 # When a warning is logged during Python shutdown, tracemalloc
 # and the import machinery don't work anymore
+suggest_tracemalloc = False
 tb = None
 
 if tb is not None:
@@ -85,7 +86,7 @@ def _formatwarnmsg_impl(msg):
 if line:
 line = line.strip()
 s += '%s\n' % line
-elif not tracing:
+elif suggest_tracemalloc:
 s += (f'{category}: Enable tracemalloc to get the object '
   f'allocation traceback\n')
 return s
diff --git 
a/Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst 
b/Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst
new file mode 100644
index 00..141230b066e22e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst
@@ -0,0 +1,2 @@
+Silence unraisable AttributeError when warnings are emitted during Python
+finalization.

___
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.11] gh-113781: Silence AttributeError in warning module during Python finalization (GH-113813) (GH-113874)

2024-01-09 Thread serhiy-storchaka
https://github.com/python/cpython/commit/86b004358e892f6fbe0ba51f9c3e0e3ea53d7e44
commit: 86b004358e892f6fbe0ba51f9c3e0e3ea53d7e44
branch: 3.11
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka 
date: 2024-01-09T22:41:02+02:00
summary:

[3.11] gh-113781: Silence AttributeError in warning module during Python 
finalization (GH-113813) (GH-113874)

The tracemalloc module can already be cleared.
(cherry picked from commit 0297418cacf998e778bc0517aa11eaac827b8c0f)

Co-authored-by: Serhiy Storchaka 

files:
A Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst
M Lib/warnings.py

diff --git a/Lib/warnings.py b/Lib/warnings.py
index 7d8c4400127f7f..7c8a0943b8179e 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -58,15 +58,16 @@ def _formatwarnmsg_impl(msg):
 # catch Exception, not only ImportError and RecursionError.
 except Exception:
 # don't suggest to enable tracemalloc if it's not available
-tracing = True
+suggest_tracemalloc = False
 tb = None
 else:
-tracing = tracemalloc.is_tracing()
 try:
+suggest_tracemalloc = not tracemalloc.is_tracing()
 tb = tracemalloc.get_object_traceback(msg.source)
 except Exception:
 # When a warning is logged during Python shutdown, tracemalloc
 # and the import machinery don't work anymore
+suggest_tracemalloc = False
 tb = None
 
 if tb is not None:
@@ -85,7 +86,7 @@ def _formatwarnmsg_impl(msg):
 if line:
 line = line.strip()
 s += '%s\n' % line
-elif not tracing:
+elif suggest_tracemalloc:
 s += (f'{category}: Enable tracemalloc to get the object '
   f'allocation traceback\n')
 return s
diff --git 
a/Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst 
b/Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst
new file mode 100644
index 00..141230b066e22e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-01-08-14-57-09.gh-issue-113781.IoTnwi.rst
@@ -0,0 +1,2 @@
+Silence unraisable AttributeError when warnings are emitted during Python
+finalization.

___
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-113661: unittest runner: Don't exit 5 if tests were skipped (GH-113856) (#113875)

2024-01-09 Thread gpshead
https://github.com/python/cpython/commit/159e3db1f7697b9aecdf674bb833fbb87f3dcad3
commit: 159e3db1f7697b9aecdf674bb833fbb87f3dcad3
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: gpshead 
date: 2024-01-09T13:34:38-08:00
summary:

[3.12] GH-113661: unittest runner: Don't exit 5 if tests were skipped 
(GH-113856) (#113875)

GH-113661: unittest runner: Don't exit 5 if tests were skipped (GH-113856)

The intention of exiting 5 was to detect issues where the test suite
wasn't discovered at all. If we skipped tests, it was correctly
discovered.
(cherry picked from commit 3a9096c337c16c9335e0d4eba8d1d4196258af72)

Co-authored-by: Stefano Rivera 

files:
A Misc/NEWS.d/next/Library/2024-01-09-08-59-43.gh-issue-113661.asvXSx.rst
M Doc/library/unittest.rst
M Lib/test/test_unittest/test_program.py
M Lib/unittest/main.py
M Lib/unittest/runner.py

diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
index f821688d145b4f..7b45f33bff100c 100644
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -2282,7 +2282,7 @@ Loading and running tests
The *testRunner* argument can either be a test runner class or an already
created instance of it. By default ``main`` calls :func:`sys.exit` with
an exit code indicating success (0) or failure (1) of the tests run.
-   An exit code of 5 indicates that no tests were run.
+   An exit code of 5 indicates that no tests were run or skipped.
 
The *testLoader* argument has to be a :class:`TestLoader` instance,
and defaults to :data:`defaultTestLoader`.
diff --git a/Lib/test/test_unittest/test_program.py 
b/Lib/test/test_unittest/test_program.py
index f6d52f93e4a25f..d8f5d3692a5088 100644
--- a/Lib/test/test_unittest/test_program.py
+++ b/Lib/test/test_unittest/test_program.py
@@ -167,6 +167,18 @@ def test_ExitAsDefault(self):
 'expected failures=1, unexpected successes=1)\n')
 self.assertTrue(out.endswith(expected))
 
+def test_ExitSkippedSuite(self):
+stream = BufferedWriter()
+with self.assertRaises(SystemExit) as cm:
+unittest.main(
+argv=["foobar", "-k", "testSkipped"],
+testRunner=unittest.TextTestRunner(stream=stream),
+testLoader=self.TestLoader(self.FooBar))
+self.assertEqual(cm.exception.code, 0)
+out = stream.getvalue()
+expected = '\n\nOK (skipped=1)\n'
+self.assertTrue(out.endswith(expected))
+
 def test_ExitEmptySuite(self):
 stream = BufferedWriter()
 with self.assertRaises(SystemExit) as cm:
diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py
index 51b81a6c3728bb..dd4dbf7535f06b 100644
--- a/Lib/unittest/main.py
+++ b/Lib/unittest/main.py
@@ -280,7 +280,7 @@ def runTests(self):
 testRunner = self.testRunner
 self.result = testRunner.run(self.test)
 if self.exit:
-if self.result.testsRun == 0:
+if self.result.testsRun == 0 and len(self.result.skipped) == 0:
 sys.exit(_NO_TESTS_EXITCODE)
 elif self.result.wasSuccessful():
 sys.exit(0)
diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py
index e3c020e0ace96d..2bcadf0c998bd9 100644
--- a/Lib/unittest/runner.py
+++ b/Lib/unittest/runner.py
@@ -274,7 +274,7 @@ def run(self, test):
 infos.append("failures=%d" % failed)
 if errored:
 infos.append("errors=%d" % errored)
-elif run == 0:
+elif run == 0 and not skipped:
 self.stream.write("NO TESTS RAN")
 else:
 self.stream.write("OK")
diff --git 
a/Misc/NEWS.d/next/Library/2024-01-09-08-59-43.gh-issue-113661.asvXSx.rst 
b/Misc/NEWS.d/next/Library/2024-01-09-08-59-43.gh-issue-113661.asvXSx.rst
new file mode 100644
index 00..f4a4f1a9841d1a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-01-09-08-59-43.gh-issue-113661.asvXSx.rst
@@ -0,0 +1,3 @@
+unittest runner: Don't exit 5 if tests were skipped. The intention of
+exiting 5 was to detect issues where the test suite wasn't discovered at
+all. If we skipped tests, it was correctly discovered.

___
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-113528: Deoptimise `pathlib._abc.PurePathBase.parts` (#113883)

2024-01-09 Thread barneygale
https://github.com/python/cpython/commit/5c7bd0e39839b27bc524e1790fe4936d987f384a
commit: 5c7bd0e39839b27bc524e1790fe4936d987f384a
branch: main
author: Barney Gale 
committer: barneygale 
date: 2024-01-09T22:46:50Z
summary:

GH-113528: Deoptimise `pathlib._abc.PurePathBase.parts` (#113883)

Implement `parts` using `_stack`, which itself calls `pathmod.split()`
repeatedly. This avoids use of `_tail`, which will be moved to `PurePath`
shortly.

files:
M Lib/pathlib/__init__.py
M Lib/pathlib/_abc.py

diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py
index 749c68d2999bdc..26e14b3f7b2005 100644
--- a/Lib/pathlib/__init__.py
+++ b/Lib/pathlib/__init__.py
@@ -195,6 +195,15 @@ def __ge__(self, other):
 return NotImplemented
 return self._parts_normcase >= other._parts_normcase
 
+@property
+def parts(self):
+"""An object providing sequence-like access to the
+components in the filesystem path."""
+if self.drive or self.root:
+return (self.drive + self.root,) + tuple(self._tail)
+else:
+return tuple(self._tail)
+
 @property
 def parent(self):
 """The logical parent of the path."""
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index caa84fc40559f7..c16beca71aa7c7 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -381,10 +381,10 @@ def is_relative_to(self, other):
 def parts(self):
 """An object providing sequence-like access to the
 components in the filesystem path."""
-if self.drive or self.root:
-return (self.drive + self.root,) + tuple(self._tail)
-else:
-return tuple(self._tail)
+anchor, parts = self._stack
+if anchor:
+parts.append(anchor)
+return tuple(reversed(parts))
 
 def joinpath(self, *pathsegments):
 """Combine this path with one or several arguments, and return a

___
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-113528: Deoptimise `pathlib._abc.PurePathBase.relative_to()` (again) (#113882)

2024-01-09 Thread barneygale
https://github.com/python/cpython/commit/cdca0ce0ad47604b7007229415817a7a152f7f9a
commit: cdca0ce0ad47604b7007229415817a7a152f7f9a
branch: main
author: Barney Gale 
committer: barneygale 
date: 2024-01-09T23:04:14Z
summary:

GH-113528: Deoptimise `pathlib._abc.PurePathBase.relative_to()` (again) 
(#113882)

Restore full battle-tested implementations of `PurePath.[is_]relative_to()`. 
These were recently split up in 3375dfe and a15a773.

In `PurePathBase`, add entirely new implementations based on `_stack`, which 
itself calls `pathmod.split()` repeatedly to disassemble a path. These new 
implementations preserve features like trailing slashes where possible, while 
still observing that a `..` segment cannot be added to traverse an empty or `.` 
segment in *walk_up* mode. They do not rely on `parents` nor `__eq__()`, nor do 
they spin up temporary path objects.

Unfortunately calling `pathmod.relpath()` isn't an option, as it calls 
`abspath()` and in turn `os.getcwd()`, which is impure.

files:
M Lib/pathlib/__init__.py
M Lib/pathlib/_abc.py

diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py
index 26e14b3f7b2005..ccdd9c3d547c8e 100644
--- a/Lib/pathlib/__init__.py
+++ b/Lib/pathlib/__init__.py
@@ -11,6 +11,7 @@
 import posixpath
 import sys
 import warnings
+from itertools import chain
 from _collections_abc import Sequence
 
 try:
@@ -254,10 +255,19 @@ def relative_to(self, other, /, *_deprecated, 
walk_up=False):
"scheduled for removal in Python 3.14")
 warnings.warn(msg, DeprecationWarning, stacklevel=2)
 other = self.with_segments(other, *_deprecated)
-path = _abc.PurePathBase.relative_to(self, other, walk_up=walk_up)
-path._drv = path._root = ''
-path._tail_cached = path._raw_paths.copy()
-return path
+elif not isinstance(other, PurePath):
+other = self.with_segments(other)
+for step, path in enumerate(chain([other], other.parents)):
+if path == self or path in self.parents:
+break
+elif not walk_up:
+raise ValueError(f"{str(self)!r} is not in the subpath of 
{str(other)!r}")
+elif path.name == '..':
+raise ValueError(f"'..' segment in {str(other)!r} cannot be 
walked")
+else:
+raise ValueError(f"{str(self)!r} and {str(other)!r} have different 
anchors")
+parts = ['..'] * step + self._tail[len(path._tail):]
+return self._from_parsed_parts('', '', parts)
 
 def is_relative_to(self, other, /, *_deprecated):
 """Return True if the path is relative to another path or False.
@@ -268,7 +278,9 @@ def is_relative_to(self, other, /, *_deprecated):
"scheduled for removal in Python 3.14")
 warnings.warn(msg, DeprecationWarning, stacklevel=2)
 other = self.with_segments(other, *_deprecated)
-return _abc.PurePathBase.is_relative_to(self, other)
+elif not isinstance(other, PurePath):
+other = self.with_segments(other)
+return other == self or other in self.parents
 
 def as_uri(self):
 """Return the path as a URI."""
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index c16beca71aa7c7..5caad3c0502399 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -3,7 +3,6 @@
 import posixpath
 import sys
 from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL
-from itertools import chain
 from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, 
S_ISFIFO
 
 #
@@ -358,24 +357,40 @@ def relative_to(self, other, *, walk_up=False):
 """
 if not isinstance(other, PurePathBase):
 other = self.with_segments(other)
-for step, path in enumerate(chain([other], other.parents)):
-if path == self or path in self.parents:
-break
+anchor0, parts0 = self._stack
+anchor1, parts1 = other._stack
+if anchor0 != anchor1:
+raise ValueError(f"{str(self)!r} and {str(other)!r} have different 
anchors")
+while parts0 and parts1 and parts0[-1] == parts1[-1]:
+parts0.pop()
+parts1.pop()
+for part in parts1:
+if not part or part == '.':
+pass
 elif not walk_up:
 raise ValueError(f"{str(self)!r} is not in the subpath of 
{str(other)!r}")
-elif path.name == '..':
+elif part == '..':
 raise ValueError(f"'..' segment in {str(other)!r} cannot be 
walked")
-else:
-raise ValueError(f"{str(self)!r} and {str(other)!r} have different 
anchors")
-parts = ['..'] * step + self._tail[len(path._tail):]
-return self.with_segments(*parts)
+else:
+parts0.append('..')
+return self.with_segments('', *reversed(parts0))
 
 def is_relative_to(self, other):
 """Return True if the path is relative 

[Python-checkins] gh-111968: Introduce _PyFreeListState and _PyFreeListState_GET API (gh-113584)

2024-01-09 Thread corona10
https://github.com/python/cpython/commit/57bdc6c30d2665c2760ff5a88487e57c8b3c397a
commit: 57bdc6c30d2665c2760ff5a88487e57c8b3c397a
branch: main
author: Donghee Na 
committer: corona10 
date: 2024-01-10T08:04:41+09:00
summary:

gh-111968: Introduce _PyFreeListState and _PyFreeListState_GET API (gh-113584)

files:
A Include/internal/pycore_freelist.h
A Python/gc_free_threading.c
A Python/gc_gil.c
M Include/internal/pycore_gc.h
M Include/internal/pycore_interp.h
M Include/internal/pycore_list.h
M Include/internal/pycore_pystate.h
M Include/internal/pycore_tstate.h
M Makefile.pre.in
M Objects/listobject.c
M PCbuild/_freeze_module.vcxproj
M PCbuild/_freeze_module.vcxproj.filters
M PCbuild/pythoncore.vcxproj
M PCbuild/pythoncore.vcxproj.filters
M Python/gc.c
M Python/pylifecycle.c
M Python/pystate.c

diff --git a/Include/internal/pycore_freelist.h 
b/Include/internal/pycore_freelist.h
new file mode 100644
index 00..b725986528d864
--- /dev/null
+++ b/Include/internal/pycore_freelist.h
@@ -0,0 +1,35 @@
+#ifndef Py_INTERNAL_FREELIST_H
+#define Py_INTERNAL_FREELIST_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+#  error "this header requires Py_BUILD_CORE define"
+#endif
+
+#ifndef WITH_FREELISTS
+// without freelists
+#  define PyList_MAXFREELIST 0
+#endif
+
+/* Empty list reuse scheme to save calls to malloc and free */
+#ifndef PyList_MAXFREELIST
+#  define PyList_MAXFREELIST 80
+#endif
+
+struct _Py_list_state {
+#if PyList_MAXFREELIST > 0
+PyListObject *free_list[PyList_MAXFREELIST];
+int numfree;
+#endif
+};
+
+typedef struct _Py_freelist_state {
+struct _Py_list_state list;
+} _PyFreeListState;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_INTERNAL_FREELIST_H */
diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h
index 2a79c403803ed1..5d90d3a7f865da 100644
--- a/Include/internal/pycore_gc.h
+++ b/Include/internal/pycore_gc.h
@@ -8,6 +8,8 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
+#include "pycore_freelist.h"   // _PyFreeListState
+
 /* GC information is stored BEFORE the object structure. */
 typedef struct {
 // Pointer to next object in the list.
@@ -238,9 +240,11 @@ extern PyObject *_PyGC_GetObjects(PyInterpreterState 
*interp, Py_ssize_t generat
 extern PyObject *_PyGC_GetReferrers(PyInterpreterState *interp, PyObject 
*objs);
 
 // Functions to clear types free lists
+extern void _PyGC_ClearAllFreeLists(PyInterpreterState *interp);
+extern void _Py_ClearFreeLists(_PyFreeListState *state, int is_finalization);
 extern void _PyTuple_ClearFreeList(PyInterpreterState *interp);
 extern void _PyFloat_ClearFreeList(PyInterpreterState *interp);
-extern void _PyList_ClearFreeList(PyInterpreterState *interp);
+extern void _PyList_ClearFreeList(_PyFreeListState *state, int 
is_finalization);
 extern void _PyDict_ClearFreeList(PyInterpreterState *interp);
 extern void _PyAsyncGen_ClearFreeLists(PyInterpreterState *interp);
 extern void _PyContext_ClearFreeList(PyInterpreterState *interp);
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index 4512b1edb4b9b3..4d49fa2a51b88c 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -179,6 +179,9 @@ struct _is {
 // One bit is set for each non-NULL entry in code_watchers
 uint8_t active_code_watchers;
 
+#if !defined(Py_GIL_DISABLED)
+struct _Py_freelist_state freelist_state;
+#endif
 struct _py_object_state object_state;
 struct _Py_unicode_state unicode;
 struct _Py_float_state float_state;
@@ -190,7 +193,6 @@ struct _is {
 PySliceObject *slice_cache;
 
 struct _Py_tuple_state tuple;
-struct _Py_list_state list;
 struct _Py_dict_state dict_state;
 struct _Py_async_gen_state async_gen;
 struct _Py_context_state context;
diff --git a/Include/internal/pycore_list.h b/Include/internal/pycore_list.h
index 55d67b32bc8a63..6c29d882335512 100644
--- a/Include/internal/pycore_list.h
+++ b/Include/internal/pycore_list.h
@@ -8,6 +8,7 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
+#include "pycore_freelist.h"  // _PyFreeListState
 
 extern PyObject* _PyList_Extend(PyListObject *, PyObject *);
 extern void _PyList_DebugMallocStats(FILE *out);
@@ -15,28 +16,9 @@ extern void _PyList_DebugMallocStats(FILE *out);
 
 /* runtime lifecycle */
 
-extern void _PyList_Fini(PyInterpreterState *);
+extern void _PyList_Fini(_PyFreeListState *);
 
 
-/* other API */
-
-#ifndef WITH_FREELISTS
-// without freelists
-#  define PyList_MAXFREELIST 0
-#endif
-
-/* Empty list reuse scheme to save calls to malloc and free */
-#ifndef PyList_MAXFREELIST
-#  define PyList_MAXFREELIST 80
-#endif
-
-struct _Py_list_state {
-#if PyList_MAXFREELIST > 0
-PyListObject *free_list[PyList_MAXFREELIST];
-int numfree;
-#endif
-};
-
 #define _PyList_ITEMS(op) _Py_RVALUE(_PyList_CAST(op)->ob_item)
 
 extern int
diff --git a/Include/internal/pycore_pystate.h

[Python-checkins] GH-113528: Deoptimise `pathlib._abc.PurePathBase` (#113559)

2024-01-09 Thread barneygale
https://github.com/python/cpython/commit/beb80d11ec0ddaf00a97f8a38ec9eae68e07c28e
commit: beb80d11ec0ddaf00a97f8a38ec9eae68e07c28e
branch: main
author: Barney Gale 
committer: barneygale 
date: 2024-01-09T23:52:15Z
summary:

GH-113528: Deoptimise `pathlib._abc.PurePathBase` (#113559)

Apply pathlib's normalization and performance tuning in `pathlib.PurePath`, but 
not `pathlib._abc.PurePathBase`.

With this change, the pathlib ABCs do not normalize away alternate path 
separators, empty segments, or dot segments. A single string given to the 
initialiser will round-trip by default, i.e. `str(PurePathBase(my_string)) == 
my_string`. Implementors can set their own path domain-specific normalization 
scheme by overriding `__str__()`

Eliminating path normalization makes maintaining and caching the path's parts 
and string representation both optional and not very useful, so this commit 
moves the `_drv`, `_root`, `_tail_cached` and `_str` slots from `PurePathBase` 
to `PurePath`. Only `_raw_paths` and `_resolving` slots remain in 
`PurePathBase`. This frees the ABCs from the burden of some of pathlib's 
hardest-to-understand code.

files:
M Lib/pathlib/__init__.py
M Lib/pathlib/_abc.py
M Lib/test/test_pathlib/test_pathlib.py
M Lib/test/test_pathlib/test_pathlib_abc.py

diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py
index ccdd9c3d547c8e..9d3fcd894164e5 100644
--- a/Lib/pathlib/__init__.py
+++ b/Lib/pathlib/__init__.py
@@ -76,6 +76,20 @@ class PurePath(_abc.PurePathBase):
 """
 
 __slots__ = (
+# The `_drv`, `_root` and `_tail_cached` slots store parsed and
+# normalized parts of the path. They are set when any of the `drive`,
+# `root` or `_tail` properties are accessed for the first time. The
+# three-part division corresponds to the result of
+# `os.path.splitroot()`, except that the tail is further split on path
+# separators (i.e. it is a list of strings), and that the root and
+# tail are normalized.
+'_drv', '_root', '_tail_cached',
+
+# The `_str` slot stores the string representation of the path,
+# computed from the drive, root and tail when `__str__()` is called
+# for the first time. It's used to implement `_str_normcase`
+'_str',
+
 # The `_str_normcase_cached` slot stores the string path with
 # normalized case. It is set when the `_str_normcase` property is
 # accessed for the first time. It's used to implement `__eq__()`
@@ -196,6 +210,94 @@ def __ge__(self, other):
 return NotImplemented
 return self._parts_normcase >= other._parts_normcase
 
+def __str__(self):
+"""Return the string representation of the path, suitable for
+passing to system calls."""
+try:
+return self._str
+except AttributeError:
+self._str = self._format_parsed_parts(self.drive, self.root,
+  self._tail) or '.'
+return self._str
+
+@classmethod
+def _format_parsed_parts(cls, drv, root, tail):
+if drv or root:
+return drv + root + cls.pathmod.sep.join(tail)
+elif tail and cls.pathmod.splitdrive(tail[0])[0]:
+tail = ['.'] + tail
+return cls.pathmod.sep.join(tail)
+
+def _from_parsed_parts(self, drv, root, tail):
+path_str = self._format_parsed_parts(drv, root, tail)
+path = self.with_segments(path_str)
+path._str = path_str or '.'
+path._drv = drv
+path._root = root
+path._tail_cached = tail
+return path
+
+@classmethod
+def _parse_path(cls, path):
+if not path:
+return '', '', []
+sep = cls.pathmod.sep
+altsep = cls.pathmod.altsep
+if altsep:
+path = path.replace(altsep, sep)
+drv, root, rel = cls.pathmod.splitroot(path)
+if not root and drv.startswith(sep) and not drv.endswith(sep):
+drv_parts = drv.split(sep)
+if len(drv_parts) == 4 and drv_parts[2] not in '?.':
+# e.g. //server/share
+root = sep
+elif len(drv_parts) == 6:
+# e.g. //?/unc/server/share
+root = sep
+parsed = [sys.intern(str(x)) for x in rel.split(sep) if x and x != '.']
+return drv, root, parsed
+
+def _load_parts(self):
+paths = self._raw_paths
+if len(paths) == 0:
+path = ''
+elif len(paths) == 1:
+path = paths[0]
+else:
+path = self.pathmod.join(*paths)
+self._drv, self._root, self._tail_cached = self._parse_path(path)
+
+@property
+def drive(self):
+"""The drive prefix (letter or UNC path), if any."""
+try:
+return self._drv
+except AttributeError:
+self._load_parts()
+return self._drv
+
+@property
+def root(self):
+

[Python-checkins] pathlib ABCs: Require one or more initialiser arguments (#113885)

2024-01-09 Thread barneygale
https://github.com/python/cpython/commit/5d8a3e74b51a59752f24cb869e7daa065b673f83
commit: 5d8a3e74b51a59752f24cb869e7daa065b673f83
branch: main
author: Barney Gale 
committer: barneygale 
date: 2024-01-10T01:12:58Z
summary:

pathlib ABCs: Require one or more initialiser arguments (#113885)

Refuse to guess what a user means when they initialise a pathlib ABC
without any positional arguments. In mainline pathlib it's normalised to
`.`, but in the ABCs this guess isn't appropriate; for example, the path
type may not represent the current directory as `.`, or may have no concept
of a "current directory" at all.

files:
M Lib/pathlib/_abc.py
M Lib/test/test_pathlib/test_pathlib_abc.py

diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index 6a1928495c9775..2fc087d13aee85 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -167,13 +167,7 @@ def with_segments(self, *pathsegments):
 def __str__(self):
 """Return the string representation of the path, suitable for
 passing to system calls."""
-paths = self._raw_paths
-if len(paths) == 1:
-return paths[0]
-elif paths:
-return self.pathmod.join(*paths)
-else:
-return ''
+return self.pathmod.join(*self._raw_paths)
 
 def as_posix(self):
 """Return the string representation of the path with forward (/)
@@ -838,7 +832,7 @@ def cwd(cls):
 # enable users to replace the implementation of 'absolute()' in a
 # subclass and benefit from the new behaviour here. This works because
 # os.path.abspath('.') == os.getcwd().
-return cls().absolute()
+return cls('').absolute()
 
 def expanduser(self):
 """ Return a new path with expanded ~ and ~user constructs
diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py 
b/Lib/test/test_pathlib/test_pathlib_abc.py
index 7c8a0f4687f00c..14df1e69db1f96 100644
--- a/Lib/test/test_pathlib/test_pathlib_abc.py
+++ b/Lib/test/test_pathlib/test_pathlib_abc.py
@@ -227,9 +227,9 @@ def test_match_common(self):
 self.assertFalse(P('c:/a/B.Py').match('C:/A/*.pY', 
case_sensitive=True))
 self.assertTrue(P('/a/b/c.py').match('/A/*/*.Py', 
case_sensitive=False))
 # Matching against empty path
-self.assertFalse(P().match('*'))
-self.assertTrue(P().match('**'))
-self.assertFalse(P().match('**/*'))
+self.assertFalse(P('').match('*'))
+self.assertTrue(P('').match('**'))
+self.assertFalse(P('').match('**/*'))
 
 def test_parts_common(self):
 # `parts` returns a tuple.
@@ -249,8 +249,8 @@ def test_parent_common(self):
 p = P('a/b/c')
 self.assertEqual(p.parent, P('a/b'))
 self.assertEqual(p.parent.parent, P('a'))
-self.assertEqual(p.parent.parent.parent, P())
-self.assertEqual(p.parent.parent.parent.parent, P())
+self.assertEqual(p.parent.parent.parent, P(''))
+self.assertEqual(p.parent.parent.parent.parent, P(''))
 # Anchored
 p = P('/a/b/c')
 self.assertEqual(p.parent, P('/a/b'))
@@ -478,20 +478,20 @@ def test_relative_to_common(self):
 p = P('a/b')
 self.assertRaises(TypeError, p.relative_to)
 self.assertRaises(TypeError, p.relative_to, b'a')
-self.assertEqual(p.relative_to(P()), P('a/b'))
+self.assertEqual(p.relative_to(P('')), P('a/b'))
 self.assertEqual(p.relative_to(''), P('a/b'))
 self.assertEqual(p.relative_to(P('a')), P('b'))
 self.assertEqual(p.relative_to('a'), P('b'))
 self.assertEqual(p.relative_to('a/'), P('b'))
-self.assertEqual(p.relative_to(P('a/b')), P())
-self.assertEqual(p.relative_to('a/b'), P())
-self.assertEqual(p.relative_to(P(), walk_up=True), P('a/b'))
+self.assertEqual(p.relative_to(P('a/b')), P(''))
+self.assertEqual(p.relative_to('a/b'), P(''))
+self.assertEqual(p.relative_to(P(''), walk_up=True), P('a/b'))
 self.assertEqual(p.relative_to('', walk_up=True), P('a/b'))
 self.assertEqual(p.relative_to(P('a'), walk_up=True), P('b'))
 self.assertEqual(p.relative_to('a', walk_up=True), P('b'))
 self.assertEqual(p.relative_to('a/', walk_up=True), P('b'))
-self.assertEqual(p.relative_to(P('a/b'), walk_up=True), P())
-self.assertEqual(p.relative_to('a/b', walk_up=True), P())
+self.assertEqual(p.relative_to(P('a/b'), walk_up=True), P(''))
+self.assertEqual(p.relative_to('a/b', walk_up=True), P(''))
 self.assertEqual(p.relative_to(P('a/c'), walk_up=True), P('../b'))
 self.assertEqual(p.relative_to('a/c', walk_up=True), P('../b'))
 self.assertEqual(p.relative_to(P('a/b/c'), walk_up=True), P('..'))
@@ -517,15 +517,15 @@ def test_relative_to_common(self):
 self.assertEqual(p.relative_to(P('/a')), P('b'))
 self.assertEqual(p.relative_to('/a'), P('b'))
 self.assertEqual(p.relative_to('/

[Python-checkins] gh-112182: Replace StopIteration with RuntimeError for future (#113220)

2024-01-09 Thread gvanrossum
https://github.com/python/cpython/commit/4826d52338396758b2d6790a498c2a06eec19a86
commit: 4826d52338396758b2d6790a498c2a06eec19a86
branch: main
author: Jamie Phan 
committer: gvanrossum 
date: 2024-01-09T21:21:00-08:00
summary:

gh-112182: Replace StopIteration with RuntimeError for future (#113220)

When an `StopIteration` raises into `asyncio.Future`, this will cause
a thread to hang. This commit address this by not raising an exception
and silently transforming the `StopIteration` with a `RuntimeError`,
which the caller can reconstruct from `fut.exception().__cause__`

files:
A Misc/NEWS.d/next/Library/2023-12-17-10-22-55.gh-issue-112182.jLWGlr.rst
M Lib/asyncio/futures.py
M Lib/test/test_asyncio/test_futures.py
M Modules/_asynciomodule.c

diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
index d19e5d8c9194fd..5d35321db7943b 100644
--- a/Lib/asyncio/futures.py
+++ b/Lib/asyncio/futures.py
@@ -269,9 +269,13 @@ def set_exception(self, exception):
 raise exceptions.InvalidStateError(f'{self._state}: {self!r}')
 if isinstance(exception, type):
 exception = exception()
-if type(exception) is StopIteration:
-raise TypeError("StopIteration interacts badly with generators "
-"and cannot be raised into a Future")
+if isinstance(exception, StopIteration):
+new_exc = RuntimeError("StopIteration interacts badly with "
+   "generators and cannot be raised into a "
+   "Future")
+new_exc.__cause__ = exception
+new_exc.__context__ = exception
+exception = new_exc
 self._exception = exception
 self._exception_tb = exception.__traceback__
 self._state = _FINISHED
diff --git a/Lib/test/test_asyncio/test_futures.py 
b/Lib/test/test_asyncio/test_futures.py
index 2184b2091f84ee..d3e8efec1c04c2 100644
--- a/Lib/test/test_asyncio/test_futures.py
+++ b/Lib/test/test_asyncio/test_futures.py
@@ -270,10 +270,6 @@ def test_exception(self):
 f = self._new_future(loop=self.loop)
 self.assertRaises(asyncio.InvalidStateError, f.exception)
 
-# StopIteration cannot be raised into a Future - CPython issue26221
-self.assertRaisesRegex(TypeError, "StopIteration .* cannot be raised",
-   f.set_exception, StopIteration)
-
 f.set_exception(exc)
 self.assertFalse(f.cancelled())
 self.assertTrue(f.done())
@@ -283,6 +279,25 @@ def test_exception(self):
 self.assertRaises(asyncio.InvalidStateError, f.set_exception, None)
 self.assertFalse(f.cancel())
 
+def test_stop_iteration_exception(self, 
stop_iteration_class=StopIteration):
+exc = stop_iteration_class()
+f = self._new_future(loop=self.loop)
+f.set_exception(exc)
+self.assertFalse(f.cancelled())
+self.assertTrue(f.done())
+self.assertRaises(RuntimeError, f.result)
+exc = f.exception()
+cause = exc.__cause__
+self.assertIsInstance(exc, RuntimeError)
+self.assertRegex(str(exc), 'StopIteration .* cannot be raised')
+self.assertIsInstance(cause, stop_iteration_class)
+
+def test_stop_iteration_subclass_exception(self):
+class MyStopIteration(StopIteration):
+pass
+
+self.test_stop_iteration_exception(MyStopIteration)
+
 def test_exception_class(self):
 f = self._new_future(loop=self.loop)
 f.set_exception(RuntimeError)
diff --git 
a/Misc/NEWS.d/next/Library/2023-12-17-10-22-55.gh-issue-112182.jLWGlr.rst 
b/Misc/NEWS.d/next/Library/2023-12-17-10-22-55.gh-issue-112182.jLWGlr.rst
new file mode 100644
index 00..dc5bb697aac414
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-12-17-10-22-55.gh-issue-112182.jLWGlr.rst
@@ -0,0 +1,3 @@
+:meth:`asyncio.futures.Future.set_exception()` now transforms 
:exc:`StopIteration`
+into :exc:`RuntimeError` instead of hanging or other misbehavior. Patch
+contributed by Jamie Phan.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index b929e6d9922b4e..c1aa849ecf1aad 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -597,12 +597,27 @@ future_set_exception(asyncio_state *state, FutureObj 
*fut, PyObject *exc)
 PyErr_SetString(PyExc_TypeError, "invalid exception object");
 return NULL;
 }
-if (Py_IS_TYPE(exc_val, (PyTypeObject *)PyExc_StopIteration)) {
+if (PyErr_GivenExceptionMatches(exc_val, PyExc_StopIteration)) {
+const char *msg = "StopIteration interacts badly with "
+  "generators and cannot be raised into a "
+  "Future";
+PyObject *message = PyUnicode_FromString(msg);
+if (message == NULL) {
+Py_DECREF(exc_val);
+return NULL;
+}
+PyObject *err = PyObject_CallOneArg(PyExc_RuntimeError, message);
+