[Python-checkins] [3.12] gh-113692: skip a test if multiprocessing isn't available. (GH-113704) (GH-113844)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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);
+
