Skip to content

Commit edd07f2

Browse files
gh-82012: Deprecate bitwise inversion (~) of bool
The bitwise inversion operator on bool returns the bitwise inversion of the underlying int value; i.e. `~True == -2` such that `bool(~True) == True`. It's a common pitfall that users mistake `~` as negation operator and actually want `not`. Supporting `~` is an artifact of bool inheriting from int. Since there is no real use-case for the current behavior, let's deprecate `~` on bool and later raise an error. This removes a potential source errors for users. Full reasoning: #82012 (comment) 📜🤖 Added by blurb_it. Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 00e2c59 commit edd07f2

File tree

4 files changed

+46
-3
lines changed

4 files changed

+46
-3
lines changed

Doc/whatsnew/3.12.rst

+8
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,14 @@ Deprecated
705705
replaced by :data:`calendar.Month.JANUARY` and :data:`calendar.Month.FEBRUARY`.
706706
(Contributed by Prince Roshan in :gh:`103636`.)
707707

708+
* The bitwise inversion operator (~) on bool is deprecated. It will throw an
709+
710+
* The bitwise inversion operator (``~``) on bool is deprecated. It will throw an
711+
error in Python 3.14. Use ``not`` for logical negation of bools instead.
712+
In the rare case that you really need the bitwise inversion of the underlying
713+
``int``, convert to int explicitly with ``~int(x)``. (Contributed by Tim Hoffmann
714+
in :gh:`103487`.)
715+
708716
Pending Removal in Python 3.13
709717
------------------------------
710718

Lib/test/test_bool.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,22 @@ def test_math(self):
5858
self.assertEqual(-True, -1)
5959
self.assertEqual(abs(True), 1)
6060
self.assertIsNot(abs(True), True)
61-
self.assertEqual(~False, -1)
62-
self.assertEqual(~True, -2)
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)
6377

6478
self.assertEqual(False+2, 2)
6579
self.assertEqual(True+2, 3)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
The bitwise inversion operator (~) on bool is deprecated.
2+
It returns the bitwise inversion of the underlying ``int`` representation such that
3+
``bool(~True) == True``, which can be confusing. Use ``not`` for logical negation
4+
of bools. In the rare case that you really need the bitwise inversion of the underlying ``int``,
5+
convert to int explicitly ``~int(x)``.

Objects/boolobject.c

+17-1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,22 @@ bool_vectorcall(PyObject *type, PyObject * const*args,
7373

7474
/* Arithmetic operations redefined to return bool if both args are bool. */
7575

76+
static PyObject *
77+
bool_invert(PyObject *v)
78+
{
79+
if (PyErr_WarnEx(PyExc_DeprecationWarning,
80+
"Bitwise inversion '~' on bool is deprecated. This "
81+
"returns the bitwise inversion of the underlying int "
82+
"object and is usually not what you expect from negating "
83+
"a bool. Use the 'not' operator for boolean negation or "
84+
"~int(x) if you really want the bitwise inversion of the "
85+
"underlying int.",
86+
1) < 0) {
87+
return NULL;
88+
}
89+
return PyLong_Type.tp_as_number->nb_invert(v);
90+
}
91+
7692
static PyObject *
7793
bool_and(PyObject *a, PyObject *b)
7894
{
@@ -119,7 +135,7 @@ static PyNumberMethods bool_as_number = {
119135
0, /* nb_positive */
120136
0, /* nb_absolute */
121137
0, /* nb_bool */
122-
0, /* nb_invert */
138+
(unaryfunc)bool_invert, /* nb_invert */
123139
0, /* nb_lshift */
124140
0, /* nb_rshift */
125141
bool_and, /* nb_and */

0 commit comments

Comments
 (0)