From 3e61311772133fd4f3645dab01de836e04a97bf8 Mon Sep 17 00:00:00 2001
From: Frederik Hennig <frederik.hennig@fau.de>
Date: Fri, 5 Apr 2024 12:58:32 +0200
Subject: [PATCH] Weakened type uniquing: Pickle may now create its own
 instances

---
 src/pystencils/types/meta.py       | 33 +++++++++++++-----------------
 tests/nbackend/types/test_types.py |  1 -
 2 files changed, 14 insertions(+), 20 deletions(-)

diff --git a/src/pystencils/types/meta.py b/src/pystencils/types/meta.py
index 198abf0ec..02c2f5a3b 100644
--- a/src/pystencils/types/meta.py
+++ b/src/pystencils/types/meta.py
@@ -27,7 +27,7 @@ For this to work, all instantiable subclasses of `PsType` must implement the fol
   arguments in their canonical order. This method is used by `PsTypeMeta` to identify instances,
   and to catch the various different possibilities Python offers for passing function arguments.
 - The ``__args__`` method, when called on an instance of the type, must return a tuple containing the constructor
-  arguments required to create that exact instance. This is used for pickling and unpickling of type objects,
+  arguments required to create that exact instance. This is used for comparing type objects
   as well as const-conversion.
 
 As a rule, ``MyType.__canonical_args__(< arguments >)`` and ``MyType(< arguments >).__args__()`` must always return
@@ -81,27 +81,11 @@ class PsType(metaclass=PsTypeMeta):
     #   Internals: Object creation, pickling and unpickling
     #   -------------------------------------------------------------------------------------------
 
-    def __new__(cls, *args, _pickle=False, **kwargs):
-        if _pickle:
-            #   force unpickler to use metaclass uniquing mechanism
-            return cls(*args, **kwargs)
-        else:
-            return super().__new__(cls)
-
-    def __getnewargs_ex__(self):
-        args = self.__args__()
-        kwargs = {"const": self._const, "_pickle": True}
-        return args, kwargs
-
-    def __getstate__(self):
-        #   To make sure pickle does not unnecessarily override the instance dictionary
-        return None
-
     @abstractmethod
     def __args__(self) -> tuple[Any, ...]:
         """Return the arguments used to create this instance, in canonical order, excluding the const-qualifier.
 
-        The tuple returned by this method is used to serialize, deserialize, and const-convert types.
+        The tuple returned by this method is used to identify, check equality, and const-convert types.
         For each instantiable subclass ``MyType`` of ``PsType``, the following must hold::
 
             t = MyType(< arguments >)
@@ -115,7 +99,18 @@ class PsType(metaclass=PsTypeMeta):
         """Return a tuple containing the positional and keyword arguments of ``__init__``
         in their canonical order."""
 
-    #   __eq__ and __hash__ unimplemented because due to uniquing, types are equal iff their instances are equal
+    def __eq__(self, other: object) -> bool:
+        if self is other:
+            return True
+        
+        if type(self) is not type(other):
+            return False
+        
+        other = cast(PsType, other)
+        return self.const == other.const and self.__args__() == other.__args__()
+    
+    def __hash__(self) -> int:
+        return hash((type(self), self.const, self.__args__()))
 
     #   -------------------------------------------------------------------------------------------
     #   Constructor and properties
diff --git a/tests/nbackend/types/test_types.py b/tests/nbackend/types/test_types.py
index 65cbf9d08..39f89e6fe 100644
--- a/tests/nbackend/types/test_types.py
+++ b/tests/nbackend/types/test_types.py
@@ -163,4 +163,3 @@ def test_pickle():
 
     for t1, t2 in zip(types, restored):
         assert t1 == t2
-        assert t1 is t2
-- 
GitLab