Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -881,11 +881,6 @@ 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

The use of the bitwise inversion operator ``~`` is deprecated and will
raise an error in Python 3.16.

: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.
However, relying on this is discouraged; explicitly convert using :func:`int`
Expand Down
10 changes: 10 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ module_name
Removed
=======

builtins
--------

* Bitwise inversion on boolean types, ``~True`` or ``~False``
has been deprecated since Python 3.12,
as it produces surprising and unintuitive results (``-2`` and ``-1``).
Use ``not x`` instead for the logical negation of a Boolean.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Use ``not x`` instead for the logical negation of a Boolean.
Use ``not x`` instead for the logical negation of a boolean.

In the rare case that you need the bitwise inversion of
the underlying integer, convert to ``int`` explicitly (``~int(x)``).

sysconfig
---------

Expand Down
37 changes: 21 additions & 16 deletions Lib/test/test_bool.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from test.support import os_helper

import os
import re


class BoolTest(unittest.TestCase):

Expand Down Expand Up @@ -58,22 +60,6 @@ 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)

self.assertEqual(False+2, 2)
self.assertEqual(True+2, 3)
Expand Down Expand Up @@ -169,6 +155,25 @@ def test_math(self):
self.assertIs(not True, False)
self.assertIs(not False, True)

def test_invert(self):
# See gh-149532
msg = re.escape("bad operand type for unary ~: 'bool'")

# Check constants in case of a folding:
with self.assertRaisesRegex(TypeError, msg):
~False
with self.assertRaisesRegex(TypeError, msg):
~True

# Check variable:
for bool_val in [True, False]:
with self.subTest(bool_val), self.assertRaisesRegex(TypeError, msg):
~bool_val

# Check `eval`:
with self.assertRaisesRegex(TypeError, msg):
eval("~False")
Comment on lines +160 to +175
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious if those shouldn't be subtests


def test_convert(self):
self.assertRaises(TypeError, bool, 42, 42)
self.assertIs(bool(10), True)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Remove :meth:`!bool.__invert__` magic method, which was deprecated since
3.12.
14 changes: 3 additions & 11 deletions Objects/boolobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,9 @@ bool_vectorcall(PyObject *type, PyObject * const*args,
static PyObject *
bool_invert(PyObject *v)
{
if (PyErr_WarnEx(PyExc_DeprecationWarning,
"Bitwise inversion '~' on bool is deprecated and will be removed in "
"Python 3.16. 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);
// This method is needed to shadow the `int` base method:
PyErr_SetString(PyExc_TypeError, "bad operand type for unary ~: 'bool'");
return NULL;
}

static PyObject *
Expand Down
Loading