From ccca98fc69e72d782ec25e0d4d8f5138f08fb138 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Sat, 27 Dec 2025 01:19:32 +0900 Subject: [PATCH] bool(NotImplemented) --- Lib/test/test_builtin.py | 21 +++++++++------------ crates/vm/src/builtins/singletons.rs | 23 +++++++++++++++++------ extra_tests/snippets/builtin_bool.py | 4 +++- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 183caa898e..63cb180088 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -2012,21 +2012,18 @@ def test_construct_singletons(self): self.assertRaises(TypeError, tp, 1, 2) self.assertRaises(TypeError, tp, a=1, b=2) - # TODO: RUSTPYTHON - @unittest.expectedFailure - def test_warning_notimplemented(self): - # Issue #35712: NotImplemented is a sentinel value that should never + def test_bool_notimplemented(self): + # GH-79893: NotImplemented is a sentinel value that should never # be evaluated in a boolean context (virtually all such use cases # are a result of accidental misuse implementing rich comparison # operations in terms of one another). - # For the time being, it will continue to evaluate as a true value, but - # issue a deprecation warning (with the eventual intent to make it - # a TypeError). - self.assertWarns(DeprecationWarning, bool, NotImplemented) - with self.assertWarns(DeprecationWarning): - self.assertTrue(NotImplemented) - with self.assertWarns(DeprecationWarning): - self.assertFalse(not NotImplemented) + msg = "NotImplemented should not be used in a boolean context" + self.assertRaisesRegex(TypeError, msg, bool, NotImplemented) + with self.assertRaisesRegex(TypeError, msg): + if NotImplemented: + pass + with self.assertRaisesRegex(TypeError, msg): + not NotImplemented class TestBreakpoint(unittest.TestCase): diff --git a/crates/vm/src/builtins/singletons.rs b/crates/vm/src/builtins/singletons.rs index bdc032cc86..30c2b863eb 100644 --- a/crates/vm/src/builtins/singletons.rs +++ b/crates/vm/src/builtins/singletons.rs @@ -103,14 +103,11 @@ impl Constructor for PyNotImplemented { } } -#[pyclass(with(Constructor))] +#[pyclass(with(Constructor, AsNumber))] impl PyNotImplemented { - // TODO: As per https://bugs.python.org/issue35712, using NotImplemented - // in boolean contexts will need to raise a DeprecationWarning in 3.9 - // and, eventually, a TypeError. #[pymethod] - const fn __bool__(&self) -> bool { - true + fn __bool__(&self, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error("NotImplemented should not be used in a boolean context".to_owned())) } #[pymethod] @@ -119,6 +116,20 @@ impl PyNotImplemented { } } +impl AsNumber for PyNotImplemented { + fn as_number() -> &'static PyNumberMethods { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + boolean: Some(|_number, vm| { + Err(vm.new_type_error( + "NotImplemented should not be used in a boolean context".to_owned(), + )) + }), + ..PyNumberMethods::NOT_IMPLEMENTED + }; + &AS_NUMBER + } +} + impl Representable for PyNotImplemented { #[inline] fn repr(_zelf: &Py, vm: &VirtualMachine) -> PyResult { diff --git a/extra_tests/snippets/builtin_bool.py b/extra_tests/snippets/builtin_bool.py index 6b6b4e0e08..902ed0cced 100644 --- a/extra_tests/snippets/builtin_bool.py +++ b/extra_tests/snippets/builtin_bool.py @@ -18,7 +18,9 @@ assert bool(1) is True assert bool({}) is False -assert bool(NotImplemented) is True +# NotImplemented cannot be used in a boolean context (Python 3.14+) +with assert_raises(TypeError): + bool(NotImplemented) assert bool(...) is True if not 1: