Repository: deepset-ai/haystack

Issue:
Normalise the use of `importlib`
Recently we introduced a generalised approach to load a class from it's qualified named string:

- https://github.com/deepset-ai/haystack/blob/main/haystack/core/serialization.py#L195

There are a few places in the code where this approach should be used:

- [x] document_writer.py:82
- [x] cache_checker.py:81
- [x] file_retriever.py:85


Discussion:

Resolving patch:
diff --git a/haystack/components/caching/cache_checker.py b/haystack/components/caching/cache_checker.py
--- a/haystack/components/caching/cache_checker.py
+++ b/haystack/components/caching/cache_checker.py
@@ -2,10 +2,10 @@
 #
 # SPDX-License-Identifier: Apache-2.0
 
-import importlib
 from typing import Any, Dict, List
 
 from haystack import DeserializationError, Document, component, default_from_dict, default_to_dict, logging
+from haystack.core.serialization import import_class_by_name
 from haystack.document_stores.types import DocumentStore
 
 logger = logging.getLogger(__name__)
@@ -77,19 +77,13 @@ def from_dict(cls, data: Dict[str, Any]) -> "CacheChecker":
         if "type" not in init_params["document_store"]:
             raise DeserializationError("Missing 'type' in document store's serialization data")
 
+        doc_store_data = data["init_parameters"]["document_store"]
         try:
-            module_name, type_ = init_params["document_store"]["type"].rsplit(".", 1)
-            logger.debug("Trying to import module '{module_name}'", module_name=module_name)
-            module = importlib.import_module(module_name)
-        except (ImportError, DeserializationError) as e:
-            raise DeserializationError(
-                f"DocumentStore of type '{init_params['document_store']['type']}' not correctly imported"
-            ) from e
-
-        docstore_class = getattr(module, type_)
-        docstore = docstore_class.from_dict(init_params["document_store"])
-
-        data["init_parameters"]["document_store"] = docstore
+            doc_store_class = import_class_by_name(doc_store_data["type"])
+        except ImportError as e:
+            raise DeserializationError(f"Class '{doc_store_data['type']}' not correctly imported") from e
+        data["init_parameters"]["document_store"] = default_from_dict(doc_store_class, doc_store_data)
+
         return default_from_dict(cls, data)
 
     @component.output_types(hits=List[Document], misses=List)
diff --git a/haystack/components/retrievers/filter_retriever.py b/haystack/components/retrievers/filter_retriever.py
--- a/haystack/components/retrievers/filter_retriever.py
+++ b/haystack/components/retrievers/filter_retriever.py
@@ -2,10 +2,10 @@
 #
 # SPDX-License-Identifier: Apache-2.0
 
-import importlib
 from typing import Any, Dict, List, Optional
 
 from haystack import DeserializationError, Document, component, default_from_dict, default_to_dict, logging
+from haystack.core.serialization import import_class_by_name
 from haystack.document_stores.types import DocumentStore
 
 logger = logging.getLogger(__name__)
@@ -81,17 +81,14 @@ def from_dict(cls, data: Dict[str, Any]) -> "FilterRetriever":
             raise DeserializationError("Missing 'document_store' in serialization data")
         if "type" not in init_params["document_store"]:
             raise DeserializationError("Missing 'type' in document store's serialization data")
+
+        doc_store_data = data["init_parameters"]["document_store"]
         try:
-            module_name, type_ = init_params["document_store"]["type"].rsplit(".", 1)
-            logger.debug("Trying to import module '{module_name}'", module_name=module_name)
-            module = importlib.import_module(module_name)
-        except (ImportError, DeserializationError) as e:
-            raise DeserializationError(
-                f"DocumentStore of type '{init_params['document_store']['type']}' not correctly imported"
-            ) from e
-
-        docstore_class = getattr(module, type_)
-        data["init_parameters"]["document_store"] = docstore_class.from_dict(data["init_parameters"]["document_store"])
+            doc_store_class = import_class_by_name(doc_store_data["type"])
+        except ImportError as e:
+            raise DeserializationError(f"Class '{doc_store_data['type']}' not correctly imported") from e
+        data["init_parameters"]["document_store"] = default_from_dict(doc_store_class, doc_store_data)
+
         return default_from_dict(cls, data)
 
     @component.output_types(documents=List[Document])
diff --git a/haystack/components/writers/document_writer.py b/haystack/components/writers/document_writer.py
--- a/haystack/components/writers/document_writer.py
+++ b/haystack/components/writers/document_writer.py
@@ -2,10 +2,10 @@
 #
 # SPDX-License-Identifier: Apache-2.0
 
-import importlib
 from typing import Any, Dict, List, Optional
 
 from haystack import DeserializationError, Document, component, default_from_dict, default_to_dict, logging
+from haystack.core.serialization import import_class_by_name
 from haystack.document_stores.types import DocumentStore, DuplicatePolicy
 
 logger = logging.getLogger(__name__)
@@ -79,20 +79,14 @@ def from_dict(cls, data: Dict[str, Any]) -> "DocumentWriter":
         if "type" not in init_params["document_store"]:
             raise DeserializationError("Missing 'type' in document store's serialization data")
 
+        doc_store_data = data["init_parameters"]["document_store"]
         try:
-            module_name, type_ = init_params["document_store"]["type"].rsplit(".", 1)
-            logger.debug("Trying to import module '{module_name}'", module_name=module_name)
-            module = importlib.import_module(module_name)
-        except (ImportError, DeserializationError) as e:
-            raise DeserializationError(
-                f"DocumentStore of type '{init_params['document_store']['type']}' not correctly imported"
-            ) from e
-
-        docstore_class = getattr(module, type_)
-        docstore = docstore_class.from_dict(init_params["document_store"])
-
-        data["init_parameters"]["document_store"] = docstore
+            doc_store_class = import_class_by_name(doc_store_data["type"])
+        except ImportError as e:
+            raise DeserializationError(f"Class '{doc_store_data['type']}' not correctly imported") from e
+        data["init_parameters"]["document_store"] = default_from_dict(doc_store_class, doc_store_data)
         data["init_parameters"]["policy"] = DuplicatePolicy[data["init_parameters"]["policy"]]
+
         return default_from_dict(cls, data)
 
     @component.output_types(documents_written=int)


Test patch: