From 3db69a1ff681f58484b66308428bc50d50b37ca4 Mon Sep 17 00:00:00 2001
From: cmoineau <cyril.moineau@cea.fr>
Date: Fri, 23 Aug 2024 09:01:42 +0000
Subject: [PATCH] [ExportLib] Make _export_node_registry a classproperty,
 allowing to separate registry between instance.

---
 aidge_core/export_utils/export_registry.py | 63 +++++++++++++++-------
 1 file changed, 45 insertions(+), 18 deletions(-)

diff --git a/aidge_core/export_utils/export_registry.py b/aidge_core/export_utils/export_registry.py
index 50699b484..ce8a8ae7e 100644
--- a/aidge_core/export_utils/export_registry.py
+++ b/aidge_core/export_utils/export_registry.py
@@ -1,35 +1,57 @@
-from typing import Dict, List, Set
+from typing import Dict, List, Set, Type
 import aidge_core
 from aidge_core.export_utils import ExportNode
-
+from abc import ABC
 from enum import Enum
 
 # Language
 LANGUAGE = Enum('LANGUAGE', ['Cpp/C'])
 
+
+class classproperty:
+    """Helper class to define class properties,
+    Is equiavlent to applying the decorator ``@property`` and ``@classmethod``.
+    But these two decorator are exclusive with python > 12.
+    See discussion https://discuss.python.org/t/add-a-supported-read-only-classproperty-decorator-in-the-stdlib/18090/12
+    """
+
+    def __init__(self, fget):
+        self.fget = fget
+
+    def __get__(self, instance, owner):
+        return self.fget(owner)
+
+
 # TODO: very naive implementation !
 # error handling should be added !
-class ExportLib(): # Should be abstract ?
+class ExportLib(ABC):
     """Aidge export lib, define a registry
     """
     # PUBLIC
     # Lib name usefull ?
     # Help define namespace
-    name:str = None
+    name: str = None
     # key: Path where static file is
     # Value: Path where to copy the file relative to the export root
-    static_files:Dict[str, str] = {}
+    static_files: Dict[str, str] = {}
     # PRIVATE
-    # Registry of exportNode
-    _export_node_registry:Dict[str, List[ExportNode]] = {}
+    # Registry of exportNode, class level dictionnary, shared accross all ExportLib
+    _cls_export_node_registry: Dict[Type, Dict[str, List['ExportNode']]] = {}
+
+    @classproperty
+    def _export_node_registry(cls) -> Dict[str, List['ExportNode']]:
+        """Define as a class property to access the registry at class level while keeping it at instance level.
+
+        :return: The export node registry specific to the class
+        :rtype: Dict[str, List[ExportNode]]
+        """
+        return cls._cls_export_node_registry.setdefault(cls, {})
     # The language type usefull ?
     _language: LANGUAGE = None
-    _compilo:str = None
+    _compilo: str = None
 
-    def __init__(self) -> None:
-         raise RuntimeError("ExportLib should not be instanciated")
     @classmethod
-    def exportable(cls, node:aidge_core.Node)->bool:
+    def exportable(cls, node: aidge_core.Node) -> bool:
         """
         :param node: aidge_node to try to export
         :type node: aidge_core.Node
@@ -44,15 +66,17 @@ class ExportLib(): # Should be abstract ?
                 if i.exportable(node):
                     return True
         return False
+
     @classmethod
-    def supported_operators(cls)->Set[str]:
+    def supported_operators(cls) -> Set[str]:
         """
         :return: Set of operator supported by this ExportLib
         :rtype: Set[str]
         """
         return cls._export_node_registry.keys()
+
     @classmethod
-    def get_export_node(cls, node:aidge_core.Node)->ExportNode:
+    def get_export_node(cls, node: aidge_core.Node) -> ExportNode:
         """
         :param node: Node to transform
         :type node: :py:class:`aidge_core.Node`
@@ -60,19 +84,23 @@ class ExportLib(): # Should be abstract ?
         :rtype: ExportNode
         """
         if not cls.exportable(node):
-            raise ValueError(f"Node {node.type()} is not exportable by ExportLib {cls.name} !")
+            raise ValueError(
+                f"Node {node.type()} is not exportable by ExportLib {cls.name} !")
         if len(cls._export_node_registry[node.type()]) != 1:
-            raise RuntimeError("ExportLib registry doesn't support when multiple export node are available yet ...")
+            raise RuntimeError(
+                "ExportLib registry doesn't support when multiple export node are available yet ...")
         else:
             return cls._export_node_registry[node.type()][0]
+
     @classmethod
-    def add_export_node(cls, key:str, eNode:ExportNode)->None:
+    def add_export_node(cls, key: str, eNode: ExportNode) -> None:
         if key not in cls._export_node_registry:
             cls._export_node_registry[key] = [eNode]
         else:
             cls._export_node_registry[key].append(eNode)
 
-def operator_register(lib: ExportLib, key:str, *args):
+
+def operator_register(lib: ExportLib, key: str, *args):
     """Helper decorator to register an :py:class:`ExportNode` to an :py:class:`ExportLib`
     """
     def decorator(operator):
@@ -81,4 +109,3 @@ def operator_register(lib: ExportLib, key:str, *args):
         lib.add_export_node(key, operator)
         return wrapper
     return decorator
-
-- 
GitLab