Repository: RustPython/RustPython (language: rust)

Issue:
None.__ne__(None) behavior is changed in 3.12
Related test:  https://github.com/youknowone/RustPython/blob/6e699d93fe9ed1f16921ef3ab4a83fc0a72c5473/extra_tests/snippets/builtin_none.py#L25


Discussion / hints:
Feel free to assign this one to me
Just want to check - is there  a way to run this test outside of installing Python 3.12 on my system and running `python3 builtin_none.py`? Doesn't look like it's in a test class, so PyTest can't pick it up.
> installing Python 3.12 on my system

You can use `pyenv` or `conda` to install a newer version of Python than the one that's on your system.

That `builtin_none.py` should be fine as one of the `extra_tests`.
Even though the test is working under pytest, you sill need to install Python 3.12 to test with it.

Steps:
1. Change test to the new behavior (you need Python 3.12 here)
2. Fix RustPython to work like the new one. (See vm/src/builtins/singleton.rs)
So I've been looking at this for a while now - I think that it ends up requiring a change [here](https://github.com/RustPython/RustPython/blob/8fc2c39d62d230f53ed701395612c3168a4464be/vm/src/builtins/object.rs#L100), but I'm not sure how to get the `richcompare` slot to resolve to `PyNotImplemented` for `None`. Any tips?

cc @youknowone 
PyNone is here: https://github.com/RustPython/RustPython/blob/8fc2c39d62d230f53ed701395612c3168a4464be/vm/src/builtins/singletons.rs#L12

You can implement `Comparable` for `PyNone` like other types. e.g. https://github.com/RustPython/RustPython/blob/8fc2c39d62d230f53ed701395612c3168a4464be/vm/src/builtins/range.rs#L441-L468
Ah got it, thanks for the pointer. Hadn't seen the `Comparable` trait, I'll keep that in mind for future issues.

Fix patch:
diff --git a/vm/src/builtins/singletons.rs b/vm/src/builtins/singletons.rs
index 65b171a262..d4487b586d 100644
--- a/vm/src/builtins/singletons.rs
+++ b/vm/src/builtins/singletons.rs
@@ -2,9 +2,10 @@ use super::{PyStrRef, PyType, PyTypeRef};
 use crate::{
     class::PyClassImpl,
     convert::ToPyObject,
+    function::{PyArithmeticValue, PyComparisonValue},
     protocol::PyNumberMethods,
-    types::{AsNumber, Constructor, Representable},
-    Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
+    types::{AsNumber, Comparable, Constructor, PyComparisonOp, Representable},
+    Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
 };
 
 #[pyclass(module = false, name = "NoneType")]
@@ -42,7 +43,7 @@ impl Constructor for PyNone {
     }
 }
 
-#[pyclass(with(Constructor, AsNumber, Representable))]
+#[pyclass(with(Constructor, Comparable, AsNumber, Representable))]
 impl PyNone {
     #[pymethod(magic)]
     fn bool(&self) -> bool {
@@ -72,6 +73,28 @@ impl AsNumber for PyNone {
     }
 }
 
+impl Comparable for PyNone {
+    fn cmp(
+        _zelf: &Py<Self>,
+        other: &PyObject,
+        op: PyComparisonOp,
+        vm: &VirtualMachine,
+    ) -> PyResult<PyComparisonValue> {
+        let value = match op {
+            PyComparisonOp::Eq => {
+                if vm.is_none(other) {
+                    PyArithmeticValue::Implemented(true)
+                } else {
+                    PyArithmeticValue::NotImplemented
+                }
+            }
+            _ => PyComparisonValue::NotImplemented,
+        };
+
+        Ok(value)
+    }
+}
+
 #[pyclass(module = false, name = "NotImplementedType")]
 #[derive(Debug)]
 pub struct PyNotImplemented;


Test patch:
diff --git a/extra_tests/snippets/builtin_none.py b/extra_tests/snippets/builtin_none.py
index c75f04ea73..c8a07f518d 100644
--- a/extra_tests/snippets/builtin_none.py
+++ b/extra_tests/snippets/builtin_none.py
@@ -22,4 +22,4 @@ def none2():
 assert None.__eq__(3) is NotImplemented
 assert None.__ne__(3) is NotImplemented
 assert None.__eq__(None) is True
-# assert None.__ne__(None) is False  # changed in 3.12
+assert None.__ne__(None) is NotImplemented