Repository: deepset-ai/haystack

Issue:
Pre 1.0 redirect import paths break `unittest.mock.patch`
**Describe the bug**
- Currently it's impossible to mock any class or method of Haystack with `unittest.mock.patch`.
- Commenting out everything below line 28 of `haystack/__init__.py` fixes the issue.

**To Reproduce**
```python
from unittest import mock

def test_test():
    with mock.patch("haystack.document_stores.memory") as p:
        pass
```
Run with `pytest test_file.py`

**Error message**
```
======================================= test session starts =======================================
platform linux -- Python 3.10.4, pytest-7.1.3, pluggy-1.0.0
rootdir: /home/sara/work/haystack, configfile: pyproject.toml
plugins: typeguard-2.13.3, anyio-3.6.1, custom-exit-code-0.3.0
collected 1 item                                                                                  

examples/test_file.py::test_test FAILED                                                       [100%]

============================================ FAILURES =============================================
____________________________________________ test_test ____________________________________________

    def test_test():
>       with mock.patch("haystack.document_stores.memory") as p:

examples/test_file.py:4: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.10/unittest/mock.py:1437: in __enter__
    original, local = self.get_original()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <unittest.mock._patch object at 0x7f917debfd00>

    def get_original(self):
        target = self.getter()
        name = self.attribute
    
        original = DEFAULT
        local = False
    
        try:
            original = target.__dict__[name]
        except (AttributeError, KeyError):
            original = getattr(target, name, DEFAULT)
        else:
            local = True
    
        if name in _builtins and isinstance(target, ModuleType):
            self.create = True
    
        if not self.create and original is DEFAULT:
>           raise AttributeError(
                "%s does not have the attribute %r" % (target, name)
            )
E           AttributeError: None does not have the attribute 'memory'

/usr/lib/python3.10/unittest/mock.py:1410: AttributeError
======================================== warnings summary =========================================
examples/test_file.py::test_test
  /home/sara/work/haystack/venv/lib/python3.10/site-packages/transformers/image_utils.py:222: DeprecationWarning: BILINEAR is deprecated and will be removed in Pillow 10 (2023-07-01). Use Resampling.BILINEAR instead.
    def resize(self, image, size, resample=PIL.Image.BILINEAR, default_to_square=True, max_size=None):

examples/example.py::test_test
  /home/sara/work/haystack/venv/lib/python3.10/site-packages/transformers/models/vit/feature_extraction_vit.py:68: DeprecationWarning: BILINEAR is deprecated and will be removed in Pillow 10 (2023-07-01). Use Resampling.BILINEAR instead.
    resample=Image.BILINEAR,

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
===================================== short test summary info =====================================
FAILED examples/test_file.py::test_test - AttributeError: None does not have the attribute 'memory'
================================== 1 failed, 2 warnings in 7.45s ==================================
```

**Expected behavior**
```
======================================= test session starts =======================================
platform linux -- Python 3.10.4, pytest-7.1.3, pluggy-1.0.0
rootdir: /home/sara/work/haystack, configfile: pyproject.toml
plugins: typeguard-2.13.3, anyio-3.6.1, custom-exit-code-0.3.0
collected 1 item                                                                                  

examples/test_file.py::test_test PASSED                                                       [100%]

======================================== warnings summary =========================================
venv/lib/python3.10/site-packages/transformers/image_utils.py:222
  /home/sara/work/haystack/venv/lib/python3.10/site-packages/transformers/image_utils.py:222: DeprecationWarning: BILINEAR is deprecated and will be removed in Pillow 10 (2023-07-01). Use Resampling.BILINEAR instead.
    def resize(self, image, size, resample=PIL.Image.BILINEAR, default_to_square=True, max_size=None):

venv/lib/python3.10/site-packages/transformers/models/vit/feature_extraction_vit.py:68
  /home/sara/work/haystack/venv/lib/python3.10/site-packages/transformers/models/vit/feature_extraction_vit.py:68: DeprecationWarning: BILINEAR is deprecated and will be removed in Pillow 10 (2023-07-01). Use Resampling.BILINEAR instead.
    resample=Image.BILINEAR,

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================== 1 passed, 2 warnings in 8.22s ==================================
```

**Additional context**
- The commented out code supports obsolete import paths that were removed before Haystack 1.0. Support for such paths should be safe to remove.
- reported by @ArzelaAscoIi 



Discussion:

Resolving patch:
diff --git a/haystack/__init__.py b/haystack/__init__.py
--- a/haystack/__init__.py
+++ b/haystack/__init__.py
@@ -23,182 +23,3 @@
 
 
 pd.options.display.max_colwidth = 80
-
-
-# ###########################################
-# Enable old style imports (temporary)
-import sys
-
-logger = logging.getLogger(__name__)
-
-# Wrapper emitting a warning on import
-def DeprecatedModule(mod, deprecated_attributes=None, is_module_deprecated=True):
-    """
-    Return a wrapped object that warns about deprecated accesses at import
-    """
-
-    class DeprecationWrapper:
-        warned = []
-
-        def __getattr__(self, attr):
-            is_a_deprecated_attr = deprecated_attributes and attr in deprecated_attributes
-            is_a_deprecated_module = is_module_deprecated and attr not in ["__path__", "__spec__", "__name__"]
-            warning_already_emitted = attr in self.warned
-            attribute_exists = getattr(mod, attr) is not None
-
-            if (is_a_deprecated_attr or is_a_deprecated_module) and not warning_already_emitted and attribute_exists:
-                logger.warn(
-                    f"Object '{attr}' is imported through a deprecated path. Please check out the docs for the new import path."
-                )
-                self.warned.append(attr)
-            return getattr(mod, attr)
-
-    return DeprecationWrapper()
-
-
-# All modules to be aliased need to be imported here
-
-# This self-import is used to monkey-patch, keep for now
-import haystack  # pylint: disable=import-self
-from haystack import pipelines
-from haystack.nodes import (
-    connector,
-    document_classifier,
-    extractor,
-    file_converter,
-    answer_generator as generator,
-    preprocessor,
-    question_generator,
-    ranker,
-    reader,
-    retriever,
-    summarizer,
-    translator,
-    label_generator,
-)
-
-# Note that we ignore the ImportError here because if the user did not install
-# the correct dependency group for a document store, we don't need to setup
-# import warnings for that, so the import here is useless and should fail silently.
-
-document_stores: Union[ModuleType, None] = None
-try:
-    from haystack import document_stores
-except ImportError:
-    pass
-
-graph_retriever: Union[ModuleType, None] = None
-try:
-    from haystack.nodes.retriever import text2sparql as graph_retriever
-except ImportError:
-    pass
-
-knowledge_graph: Union[ModuleType, None] = None
-try:
-    from haystack.document_stores import graphdb as knowledge_graph
-except ImportError:
-    pass
-
-from haystack.modeling.evaluation import eval
-from haystack.nodes.other import JoinDocuments, Docs2Answers, JoinAnswers, RouteDocuments
-from haystack.nodes.query_classifier import SklearnQueryClassifier, TransformersQueryClassifier
-from haystack.nodes.file_classifier import FileTypeClassifier
-from haystack.utils import preprocessing
-import haystack.modeling.utils as modeling_utils
-from haystack.utils import cleaning
-
-# For the alias to work as an importable module (like `from haystack import reader`),
-# modules need to be set as attributes of their parent model.
-# To make chain imports work (`from haystack.reader import FARMReader`) the module
-# needs to be also present in sys.modules with its complete import path.
-if knowledge_graph:
-    setattr(knowledge_graph, "graphdb", DeprecatedModule(knowledge_graph))
-    sys.modules["haystack.knowledge_graph.graphdb"] = DeprecatedModule(knowledge_graph)
-
-setattr(preprocessor, "utils", DeprecatedModule(preprocessing))
-setattr(preprocessor, "cleaning", DeprecatedModule(cleaning))
-sys.modules["haystack.preprocessor.utils"] = DeprecatedModule(preprocessing)
-sys.modules["haystack.preprocessor.cleaning"] = DeprecatedModule(cleaning)
-
-setattr(haystack, "document_store", DeprecatedModule(document_stores))
-setattr(haystack, "connector", DeprecatedModule(connector))
-setattr(haystack, "generator", DeprecatedModule(generator))
-setattr(haystack, "document_classifier", DeprecatedModule(document_classifier))
-setattr(haystack, "extractor", DeprecatedModule(extractor))
-setattr(haystack, "eval", DeprecatedModule(eval))
-setattr(haystack, "file_converter", DeprecatedModule(file_converter, deprecated_attributes=["FileTypeClassifier"]))
-setattr(haystack, "knowledge_graph", DeprecatedModule(knowledge_graph, deprecated_attributes=["graphdb"]))
-setattr(
-    haystack,
-    "pipeline",
-    DeprecatedModule(
-        pipelines,
-        deprecated_attributes=[
-            "JoinDocuments",
-            "Docs2Answers",
-            "SklearnQueryClassifier",
-            "TransformersQueryClassifier",
-        ],
-    ),
-)
-setattr(haystack, "preprocessor", DeprecatedModule(preprocessor, deprecated_attributes=["utils", "cleaning"]))
-setattr(haystack, "question_generator", DeprecatedModule(question_generator))
-setattr(haystack, "ranker", DeprecatedModule(ranker))
-setattr(haystack, "reader", DeprecatedModule(reader))
-setattr(haystack, "retriever", DeprecatedModule(retriever))
-setattr(haystack, "summarizer", DeprecatedModule(summarizer))
-setattr(haystack, "translator", DeprecatedModule(translator))
-sys.modules["haystack.document_store"] = DeprecatedModule(document_stores)
-sys.modules["haystack.connector"] = DeprecatedModule(connector)
-sys.modules["haystack.generator"] = DeprecatedModule(generator)
-sys.modules["haystack.document_classifier"] = DeprecatedModule(document_classifier)
-sys.modules["haystack.extractor"] = DeprecatedModule(extractor)
-sys.modules["haystack.eval"] = DeprecatedModule(eval)
-sys.modules["haystack.file_converter"] = DeprecatedModule(file_converter)
-sys.modules["haystack.knowledge_graph"] = DeprecatedModule(knowledge_graph)
-sys.modules["haystack.pipeline"] = DeprecatedModule(pipelines)
-sys.modules["haystack.preprocessor"] = DeprecatedModule(preprocessor, deprecated_attributes=["utils", "cleaning"])
-sys.modules["haystack.question_generator"] = DeprecatedModule(question_generator)
-sys.modules["haystack.ranker"] = DeprecatedModule(ranker)
-sys.modules["haystack.reader"] = DeprecatedModule(reader)
-sys.modules["haystack.retriever"] = DeprecatedModule(retriever)
-sys.modules["haystack.summarizer"] = DeprecatedModule(summarizer)
-sys.modules["haystack.translator"] = DeprecatedModule(translator)
-if graph_retriever:
-    setattr(haystack, "graph_retriever", DeprecatedModule(graph_retriever))
-    sys.modules["haystack.graph_retriever"] = DeprecatedModule(graph_retriever)
-
-# To be imported from modules, classes need only to be set as attributes,
-# they don't need to be present in sys.modules too.
-# Adding them to sys.modules would enable `import haystack.pipelines.JoinDocuments`,
-# which I believe it's a very rare import style.
-setattr(file_converter, "FileTypeClassifier", FileTypeClassifier)
-setattr(pipelines, "JoinDocuments", JoinDocuments)
-setattr(pipelines, "Docs2Answers", Docs2Answers)
-setattr(pipelines, "SklearnQueryClassifier", SklearnQueryClassifier)
-setattr(pipelines, "TransformersQueryClassifier", TransformersQueryClassifier)
-
-# This last line is used to throw the deprecation error for imports like `from haystack import connector`
-deprecated_attributes = [
-    "document_store",
-    "connector",
-    "generator",
-    "document_classifier",
-    "extractor",
-    "eval",
-    "file_converter",
-    "knowledge_graph",
-    "pipeline",
-    "preprocessor",
-    "question_generator",
-    "ranker",
-    "reader",
-    "retriever",
-    "summarizer",
-    "translator",
-]
-if graph_retriever:
-    deprecated_attributes.append("graph_retriever")
-sys.modules["haystack"] = DeprecatedModule(
-    haystack, is_module_deprecated=False, deprecated_attributes=deprecated_attributes
-)


Test patch: