Skip to content

Commit d01f243

Browse files
committed
gh-149532: Remove bool.__invert__ magic method
1 parent 49918f5 commit d01f243

5 files changed

Lines changed: 36 additions & 32 deletions

File tree

Doc/library/stdtypes.rst

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -881,11 +881,6 @@ return a bool equivalent to the logical operations "and", "or", "xor". However,
881881
the logical operators ``and``, ``or`` and ``!=`` should be preferred
882882
over ``&``, ``|`` and ``^``.
883883

884-
.. deprecated:: 3.12
885-
886-
The use of the bitwise inversion operator ``~`` is deprecated and will
887-
raise an error in Python 3.16.
888-
889884
:class:`bool` is a subclass of :class:`int` (see :ref:`typesnumeric`). In
890885
many numeric contexts, ``False`` and ``True`` behave like the integers 0 and 1, respectively.
891886
However, relying on this is discouraged; explicitly convert using :func:`int`

Doc/whatsnew/3.16.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ module_name
106106
Removed
107107
=======
108108

109+
builtins
110+
--------
111+
112+
* Bitwise inversion on boolean types, ``~True`` or ``~False``
113+
has been deprecated since Python 3.12,
114+
as it produces surprising and unintuitive results (``-2`` and ``-1``).
115+
Use ``not x`` instead for the logical negation of a Boolean.
116+
In the rare case that you need the bitwise inversion of
117+
the underlying integer, convert to ``int`` explicitly (``~int(x)``).
118+
109119
sysconfig
110120
---------
111121

Lib/test/test_bool.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from test.support import os_helper
55

66
import os
7+
import re
8+
79

810
class BoolTest(unittest.TestCase):
911

@@ -58,22 +60,6 @@ def test_math(self):
5860
self.assertEqual(-True, -1)
5961
self.assertEqual(abs(True), 1)
6062
self.assertIsNot(abs(True), True)
61-
with self.assertWarns(DeprecationWarning):
62-
# We need to put the bool in a variable, because the constant
63-
# ~False is evaluated at compile time due to constant folding;
64-
# consequently the DeprecationWarning would be issued during
65-
# module loading and not during test execution.
66-
false = False
67-
self.assertEqual(~false, -1)
68-
with self.assertWarns(DeprecationWarning):
69-
# also check that the warning is issued in case of constant
70-
# folding at compile time
71-
self.assertEqual(eval("~False"), -1)
72-
with self.assertWarns(DeprecationWarning):
73-
true = True
74-
self.assertEqual(~true, -2)
75-
with self.assertWarns(DeprecationWarning):
76-
self.assertEqual(eval("~True"), -2)
7763

7864
self.assertEqual(False+2, 2)
7965
self.assertEqual(True+2, 3)
@@ -169,6 +155,25 @@ def test_math(self):
169155
self.assertIs(not True, False)
170156
self.assertIs(not False, True)
171157

158+
def test_invert(self):
159+
# See gh-149532
160+
msg = re.escape("bad operand type for unary ~: 'bool'")
161+
162+
# Check constants in case of a folding:
163+
with self.assertRaisesRegex(TypeError, msg):
164+
~False
165+
with self.assertRaisesRegex(TypeError, msg):
166+
~True
167+
168+
# Check variable:
169+
for bool_val in [True, False]:
170+
with self.subTest(bool_val), self.assertRaisesRegex(TypeError, msg):
171+
~bool_val
172+
173+
# Check `eval`:
174+
with self.assertRaisesRegex(TypeError, msg):
175+
eval("~False")
176+
172177
def test_convert(self):
173178
self.assertRaises(TypeError, bool, 42, 42)
174179
self.assertIs(bool(10), True)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Remove :meth:`!bool.__invert__` magic method, which was deprecated since
2+
3.12.

Objects/boolobject.c

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,9 @@ bool_vectorcall(PyObject *type, PyObject * const*args,
7070
static PyObject *
7171
bool_invert(PyObject *v)
7272
{
73-
if (PyErr_WarnEx(PyExc_DeprecationWarning,
74-
"Bitwise inversion '~' on bool is deprecated and will be removed in "
75-
"Python 3.16. This returns the bitwise inversion of the underlying int "
76-
"object and is usually not what you expect from negating "
77-
"a bool. Use the 'not' operator for boolean negation or "
78-
"~int(x) if you really want the bitwise inversion of the "
79-
"underlying int.",
80-
1) < 0) {
81-
return NULL;
82-
}
83-
return PyLong_Type.tp_as_number->nb_invert(v);
73+
// This method is needed to shadow the `int` base method:
74+
PyErr_SetString(PyExc_TypeError, "bad operand type for unary ~: 'bool'");
75+
return NULL;
8476
}
8577

8678
static PyObject *

0 commit comments

Comments
 (0)