From 75348d648ffc81045aed9589d71f798f701b7600 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Tue, 13 Aug 2024 21:44:12 +0300 Subject: [PATCH 01/12] Prohibit bitwise inversion on bool --- Doc/library/stdtypes.rst | 6 +++--- Doc/whatsnew/3.14.rst | 6 ++++++ Lib/test/test_bool.py | 21 +++++-------------- .../2024-08-13-21-40-20.gh-issue-122982.rst | 3 +++ Objects/boolobject.c | 13 ++---------- 5 files changed, 19 insertions(+), 30 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 01121feb2b2311..96b4d3b6a271cc 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -865,10 +865,10 @@ return a bool equivalent to the logical operations "and", "or", "xor". However, the logical operators ``and``, ``or`` and ``!=`` should be preferred over ``&``, ``|`` and ``^``. -.. deprecated:: 3.12 +.. versionchanged:: 3.14 - The use of the bitwise inversion operator ``~`` is deprecated and will - raise an error in Python 3.14. + The use of the bitwise inversion operator ``~`` is prohibited and raises + a :exc:`TypeError` now. :class:`bool` is a subclass of :class:`int` (see :ref:`typesnumeric`). In many numeric contexts, ``False`` and ``True`` behave like the integers 0 and 1, respectively. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index b975f6a4f8a931..20b613b5edbd20 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -270,6 +270,12 @@ asyncio (Contributed by Kumar Aditya in :gh:`120804`.) +builtins +-------- + +* Prohibit bitwise inversion (``~``) on operand of type :func:`bool` which had + previously raised a :exc:`DeprecationWarning` since Python 3.12. + collections.abc --------------- diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 34ecb45f161dfe..a042e6e97a2820 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -58,22 +58,11 @@ def test_math(self): self.assertEqual(-True, -1) self.assertEqual(abs(True), 1) self.assertIsNot(abs(True), True) - with self.assertWarns(DeprecationWarning): - # We need to put the bool in a variable, because the constant - # ~False is evaluated at compile time due to constant folding; - # consequently the DeprecationWarning would be issued during - # module loading and not during test execution. - false = False - self.assertEqual(~false, -1) - with self.assertWarns(DeprecationWarning): - # also check that the warning is issued in case of constant - # folding at compile time - self.assertEqual(eval("~False"), -1) - with self.assertWarns(DeprecationWarning): - true = True - self.assertEqual(~true, -2) - with self.assertWarns(DeprecationWarning): - self.assertEqual(eval("~True"), -2) + + with self.assertRaises(TypeError): + ~True + with self.assertRaises(TypeError): + ~False self.assertEqual(False+2, 2) self.assertEqual(True+2, 3) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.rst b/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.rst new file mode 100644 index 00000000000000..38ecaf1ba86b1e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.rst @@ -0,0 +1,3 @@ +Bitwise inversion (``~``) is prohibited for operand of type :func:`bool`. +Since Python 3.12 it raises a :exc:`DeprecationWarning`, now it's raises a +:exc:`TypeError`. diff --git a/Objects/boolobject.c b/Objects/boolobject.c index fb48dcbeca7850..4c41d24a8ec59c 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -70,17 +70,8 @@ bool_vectorcall(PyObject *type, PyObject * const*args, static PyObject * bool_invert(PyObject *v) { - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "Bitwise inversion '~' on bool is deprecated. This " - "returns the bitwise inversion of the underlying int " - "object and is usually not what you expect from negating " - "a bool. Use the 'not' operator for boolean negation or " - "~int(x) if you really want the bitwise inversion of the " - "underlying int.", - 1) < 0) { - return NULL; - } - return PyLong_Type.tp_as_number->nb_invert(v); + PyErr_SetString(PyExc_TypeError, "bad operand for unary ~: 'bool'"); + return NULL; } static PyObject * From 8dc66a70c6bfe76aee12da443d41b46c6e705e8f Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Tue, 13 Aug 2024 21:47:34 +0300 Subject: [PATCH 02/12] Fix blurb --- ...-122982.rst => 2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/Core and Builtins/{2024-08-13-21-40-20.gh-issue-122982.rst => 2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst} (100%) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.rst b/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst similarity index 100% rename from Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.rst rename to Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst From 77b4080f35d8169125207d8f5dca8661f8b65fc7 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Wed, 14 Aug 2024 11:24:57 +0300 Subject: [PATCH 03/12] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- .../2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst | 2 +- Objects/boolobject.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst b/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst index 38ecaf1ba86b1e..bc254f0a07e74f 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst @@ -1,3 +1,3 @@ Bitwise inversion (``~``) is prohibited for operand of type :func:`bool`. -Since Python 3.12 it raises a :exc:`DeprecationWarning`, now it's raises a +Since Python 3.12 it raises a :exc:`DeprecationWarning`, now it raises a :exc:`TypeError`. diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 4c41d24a8ec59c..3088d45a2159dd 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -70,7 +70,7 @@ bool_vectorcall(PyObject *type, PyObject * const*args, static PyObject * bool_invert(PyObject *v) { - PyErr_SetString(PyExc_TypeError, "bad operand for unary ~: 'bool'"); + PyErr_SetString(PyExc_TypeError, "bad operand type for unary ~: 'bool'"); return NULL; } From a8fdf662373b42a4b78bec7b1519b333332295fc Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Wed, 14 Aug 2024 11:37:52 +0300 Subject: [PATCH 04/12] Add more test cases --- Lib/test/test_bool.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index a042e6e97a2820..0493c161fcdbd4 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -59,10 +59,21 @@ def test_math(self): self.assertEqual(abs(True), 1) self.assertIsNot(abs(True), True) + # Bitwise inversion is prohibited on bool type. with self.assertRaises(TypeError): ~True with self.assertRaises(TypeError): ~False + with self.assertRaises(TypeError): + true = True + ~true + with self.assertRaises(TypeError): + false = False + ~false + with self.assertRaises(TypeError): + eval("~True") + with self.assertRaises(TypeError): + eval("~False") self.assertEqual(False+2, 2) self.assertEqual(True+2, 3) From 7a3e73a58b66fd02f932d823684eb47c4bb7defd Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Wed, 14 Aug 2024 11:48:35 +0300 Subject: [PATCH 05/12] func -> class --- Doc/whatsnew/3.14.rst | 2 +- .../2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 20b613b5edbd20..87f8882ec9278e 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -273,7 +273,7 @@ asyncio builtins -------- -* Prohibit bitwise inversion (``~``) on operand of type :func:`bool` which had +* Prohibit bitwise inversion (``~``) on operand of type :class:`bool` which had previously raised a :exc:`DeprecationWarning` since Python 3.12. collections.abc diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst b/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst index bc254f0a07e74f..77199dd7de7512 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst @@ -1,3 +1,3 @@ -Bitwise inversion (``~``) is prohibited for operand of type :func:`bool`. +Bitwise inversion (``~``) is prohibited for operand of type :class:`bool`. Since Python 3.12 it raises a :exc:`DeprecationWarning`, now it raises a :exc:`TypeError`. From 9bf6fdfab079bdafe77b6c05a9b31630489f413d Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Wed, 14 Aug 2024 12:42:28 +0300 Subject: [PATCH 06/12] Reword news --- Doc/whatsnew/3.14.rst | 4 ++-- .../2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 87f8882ec9278e..56c810af5413ab 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -273,8 +273,8 @@ asyncio builtins -------- -* Prohibit bitwise inversion (``~``) on operand of type :class:`bool` which had - previously raised a :exc:`DeprecationWarning` since Python 3.12. +* Bitwise inversion (``~``) is prohibited for :data:`True` and :data:`False` + which had previously raised a :exc:`DeprecationWarning` since Python 3.12. collections.abc --------------- diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst b/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst index 77199dd7de7512..7972bcdc5023a8 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-08-13-21-40-20.gh-issue-122982.JSLp1k.rst @@ -1,3 +1 @@ -Bitwise inversion (``~``) is prohibited for operand of type :class:`bool`. -Since Python 3.12 it raises a :exc:`DeprecationWarning`, now it raises a -:exc:`TypeError`. +Bitwise inversion (``~``) is prohibited for :data:`True` and :data:`False`. From 33b13979cd1e0743041abd1d8b669a3b0352412f Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Wed, 14 Aug 2024 14:05:06 +0300 Subject: [PATCH 07/12] Address Nikita's comment --- Lib/test/test_bool.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 0493c161fcdbd4..cc4343f29066bc 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -64,11 +64,12 @@ def test_math(self): ~True with self.assertRaises(TypeError): ~False + + true = True + false = False with self.assertRaises(TypeError): - true = True ~true with self.assertRaises(TypeError): - false = False ~false with self.assertRaises(TypeError): eval("~True") From 4760de8ecbbe2fa79142b7552cd664336c8b97cc Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Wed, 14 Aug 2024 21:58:26 +0300 Subject: [PATCH 08/12] Address Petr's comment --- Doc/deprecations/pending-removal-in-future.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/Doc/deprecations/pending-removal-in-future.rst b/Doc/deprecations/pending-removal-in-future.rst index 6942b9d62cb8f2..ae33236a6cf604 100644 --- a/Doc/deprecations/pending-removal-in-future.rst +++ b/Doc/deprecations/pending-removal-in-future.rst @@ -11,7 +11,6 @@ although there is currently no date scheduled for their removal. * :mod:`builtins`: - * ``~bool``, bitwise inversion on bool. * ``bool(NotImplemented)``. * Generators: ``throw(type, exc, tb)`` and ``athrow(type, exc, tb)`` signature is deprecated: use ``throw(exc)`` and ``athrow(exc)`` instead, From a949b8aef6d569eefc62ddca4215c87f2be31edb Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Fri, 16 Aug 2024 09:38:21 +0300 Subject: [PATCH 09/12] Add a suggestion --- Objects/boolobject.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Objects/boolobject.c b/Objects/boolobject.c index 3088d45a2159dd..bc7938df3d895c 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -70,7 +70,10 @@ bool_vectorcall(PyObject *type, PyObject * const*args, static PyObject * bool_invert(PyObject *v) { - PyErr_SetString(PyExc_TypeError, "bad operand type for unary ~: 'bool'"); + PyErr_SetString( + PyExc_TypeError, + "bad operand type for unary ~: 'bool'. Maybe you meant 'not' instead of '~'?" + ); return NULL; } From 0e26ff84c34a7a6da18118f6f27edf2a8c5e0c32 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Fri, 16 Aug 2024 09:40:55 +0300 Subject: [PATCH 10/12] Add few test cases --- Lib/test/test_bool.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index cc4343f29066bc..1da0ed989f377f 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -75,6 +75,15 @@ def test_math(self): eval("~True") with self.assertRaises(TypeError): eval("~False") + with self.assertRaises(TypeError): + true.__invert__() + with self.assertRaises(TypeError): + false.__invert__() + + with self.assertRaisesRegex(TypeError, "Maybe you meant 'not' instead of '~'?"): + ~true + with self.assertRaisesRegex(TypeError, "Maybe you meant 'not' instead of '~'?"): + ~false self.assertEqual(False+2, 2) self.assertEqual(True+2, 3) From d0d8ffad8509fb8f55aaefce97be76fb9dbd4338 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Fri, 16 Aug 2024 09:52:33 +0300 Subject: [PATCH 11/12] Use raw strings --- Lib/test/test_bool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 1da0ed989f377f..5f5f52dfce8de2 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -80,9 +80,9 @@ def test_math(self): with self.assertRaises(TypeError): false.__invert__() - with self.assertRaisesRegex(TypeError, "Maybe you meant 'not' instead of '~'?"): + with self.assertRaisesRegex(TypeError, r"Maybe you meant 'not' instead of '~'\?"): ~true - with self.assertRaisesRegex(TypeError, "Maybe you meant 'not' instead of '~'?"): + with self.assertRaisesRegex(TypeError, r"Maybe you meant 'not' instead of '~'\?"): ~false self.assertEqual(False+2, 2) From d980ef250895edebea7acf22810e0dbba305c0c9 Mon Sep 17 00:00:00 2001 From: Kirill Podoprigora Date: Sat, 17 Aug 2024 14:44:40 +0300 Subject: [PATCH 12/12] Apply suggestions from code review Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Lib/test/test_bool.py | 4 ++-- Objects/boolobject.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 5f5f52dfce8de2..fbb975d3d6ba56 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -80,9 +80,9 @@ def test_math(self): with self.assertRaises(TypeError): false.__invert__() - with self.assertRaisesRegex(TypeError, r"Maybe you meant 'not' instead of '~'\?"): + with self.assertRaisesRegex(TypeError, r"Did you mean 'not' instead of '~'\?"): ~true - with self.assertRaisesRegex(TypeError, r"Maybe you meant 'not' instead of '~'\?"): + with self.assertRaisesRegex(TypeError, r"Did you mean 'not' instead of '~'\?"): ~false self.assertEqual(False+2, 2) diff --git a/Objects/boolobject.c b/Objects/boolobject.c index bc7938df3d895c..77a39a56adc3e8 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -72,7 +72,7 @@ bool_invert(PyObject *v) { PyErr_SetString( PyExc_TypeError, - "bad operand type for unary ~: 'bool'. Maybe you meant 'not' instead of '~'?" + "bad operand type for unary ~: 'bool'. Did you mean 'not' instead of '~'?" ); return NULL; }