Repository: deepdoctection/deepdoctection (language: python)

Issue:
Propagate logs from third party library loggers to root logger
**Enhancement** 🚀
Propagating logs from third part library loggers to root logger

**Motivation**  💪 
Loggers from third party libraries have their own format that make look logging inconsistent. 

The introduction of a uniform logging format would allow output in the terminal to be displayed more consistently. In addition, there would be the possibility of creating a logging file that always has the same format for pipeline processing, which would result in an analysis option with pandas, for example.

**Alternatives** ⚖️
Are there any alternatives you have considered

**Additional context** 🧬
Screenshots, etc. if relevant



Discussion / hints:

Fix patch:
diff --git a/deepdoctection/analyzer/dd.py b/deepdoctection/analyzer/dd.py
index 20641324..c560b0e1 100644
--- a/deepdoctection/analyzer/dd.py
+++ b/deepdoctection/analyzer/dd.py
@@ -70,16 +70,12 @@ def _auto_select_lib_and_device() -> Tuple[str, str]:
             return "tf", "cuda"
         if pytorch_available():
             return "pt", "cpu"
-        raise ImportError("No GPU is available. You must install Pytorch in order to run on a CPU")
+        raise ModuleNotFoundError("Install Pytorch and Torchvision to run with a CPU")
     if pytorch_available():
         if cuda.is_available():
             return "pt", "gpu"
         return "pt", "cpu"
-    raise ImportError(
-        "Neither Pytorch nor Tensorflow are installed. You must install at least one of them before "
-        "running the analyzer. Note, that if no GPU is available you can only use Detectron2 along "
-        "with Pytorch."
-    )
+    raise ModuleNotFoundError("Install Tensorflow or Pytorch before building analyzer")
 
 
 def _maybe_copy_config_to_cache(file_name: str) -> str:
@@ -267,26 +263,7 @@ def get_dd_analyzer(
     cfg.LANG = language
     cfg.freeze()
 
-    logger.info("Deep Doctection Analyzer Config: ------------------------------------------\n %s", str(cfg))
-
-    logger.info(
-        "Building the Analyzer pipeline. This includes layout analysis with detection of titles, text, lists "
-        "tables and figures."
-    )
-
-    if cfg.TAB:
-        logger.info(
-            "As tables have been chosen, a table recognition system will be invoked. This means, "
-            "that the interior of each detected table will be segmented into cells, rows and column and every "
-            "cell will be labeled with its row and column position as well as its spans."
-        )
-
-    if cfg.OCR:
-        logger.info(
-            " OCR will be performed and each words will be assigned to the detected layout "
-            "compartment, if possible. Finally, words will be stringed together according to its"
-            " reading order."
-        )
+    logger.info("Config: \n %s", str(cfg), cfg.to_dict())
 
     # will silent all TP loggings while building the tower
     if tensorpack_available():
diff --git a/deepdoctection/analyzer/tools.py b/deepdoctection/analyzer/tools.py
index da63f98f..17021d7e 100644
--- a/deepdoctection/analyzer/tools.py
+++ b/deepdoctection/analyzer/tools.py
@@ -27,7 +27,7 @@
 from ..dataflow.custom_serialize import SerializerJsonlines
 from ..datapoint.page import Page
 from ..utils.detection_types import Pathlike
-from ..utils.fs import is_file_extension
+from ..utils.utils import is_file_extension
 
 
 def load_page(path: Pathlike) -> Page:
diff --git a/deepdoctection/dataflow/base.py b/deepdoctection/dataflow/base.py
index 0bf157c1..aec570c1 100644
--- a/deepdoctection/dataflow/base.py
+++ b/deepdoctection/dataflow/base.py
@@ -20,12 +20,22 @@
 class DataFlowTerminated(BaseException):
     """
     An exception indicating that the DataFlow is unable to produce any more
-    data, i.e. something wrong happened so that calling :meth:`get_data`
+    data, i.e. something wrong happened so that calling :meth:`__iter__`
     cannot give a valid iterator anymore.
     In most DataFlow this will never be raised.
     """
 
 
+class DataFlowResetStateNotCalled(BaseException):
+    """
+    An exception indicating that :meth:`reset_state()` has not been called before starting
+    iteration.
+    """
+
+    def __init__(self) -> None:
+        super().__init__("Iterating a dataflow requires .reset_state() to be called first")
+
+
 class DataFlowReentrantGuard:
     """
     A tool to enforce non-reentrancy.
@@ -39,7 +49,7 @@ def __init__(self) -> None:
     def __enter__(self) -> None:
         self._succ = self._lock.acquire(False)
         if not self._succ:
-            raise threading.ThreadError("This DataFlow is not reentrant!")
+            raise threading.ThreadError("This dataflow is not reentrant!")
 
     @no_type_check
     def __exit__(self, exc_type, exc_val, exc_tb):
@@ -111,7 +121,7 @@ def reset_state(self) -> None:
         * A dataflow is not fork-safe after :meth:`reset_state` is called (because this will violate the guarantee).
           There are a few other dataflows that are not fork-safe anytime, which will be mentioned in the docs.
         * You should take the responsibility and follow the above guarantee if you're the caller of a dataflow yourself
-          (either when you're using dataflow outside of tensorpack, or if you're writing a wrapper dataflow).
+          (either when you're using dataflow outside tensorpack, or if you're writing a wrapper dataflow).
         * Tensorpack's built-in forking dataflows (:class:`MultiProcessRunner`, :class:`MultiProcessMapData`, etc)
           and other component that uses dataflows (:class:`InputSource`)
           already take care of the responsibility of calling this method.
diff --git a/deepdoctection/dataflow/common.py b/deepdoctection/dataflow/common.py
index 254119c5..8b6340f7 100644
--- a/deepdoctection/dataflow/common.py
+++ b/deepdoctection/dataflow/common.py
@@ -16,7 +16,6 @@
 
 import tqdm
 
-from ..utils.logger import logger
 from ..utils.tqdm import get_tqdm, get_tqdm_default_kwargs
 from .base import DataFlow, ProxyDataFlow
 
@@ -223,12 +222,14 @@ class JoinData(DataFlow):
 
     .. code-block:: python
 
-        df1 produces: [c1, c2]
-        df2 produces: [c3, c4]
-        joined: [c1, c2, c3, c4]
+        df1 produces: [[c1], [c2]]
+        df2 produces: [[c3], [c4]]
+        joined: [[c1, c3], [c2, c4]]
         df1 produces: {"a":c1, "b":c2}
         df2 produces: {"c":c3}
         joined: {"a":c1, "b":c2, "c":c3}
+
+    `JoinData` will stop once the first Dataflow throws a StopIteration
     """
 
     def __init__(self, df_lists: List[DataFlow]) -> None:
@@ -242,13 +243,6 @@ def __init__(self, df_lists: List[DataFlow]) -> None:
         """
         self.df_lists = df_lists
 
-        try:
-            self._size = len(self.df_lists[0])
-            for df in self.df_lists:
-                assert len(df) == self._size, f"All DataFlow must have the same size! {len(df)} != {self._size}"
-        except (NotImplementedError, AssertionError):
-            logger.info("[JoinData] Size check failed for the list of dataflow to be joined!")
-
     def reset_state(self) -> None:
         for df in set(self.df_lists):
             df.reset_state()
diff --git a/deepdoctection/dataflow/custom.py b/deepdoctection/dataflow/custom.py
index 85687b5f..a7dc3a4b 100644
--- a/deepdoctection/dataflow/custom.py
+++ b/deepdoctection/dataflow/custom.py
@@ -28,7 +28,7 @@
 from ..utils.logger import logger
 from ..utils.tqdm import get_tqdm
 from ..utils.utils import get_rng
-from .base import DataFlow, DataFlowReentrantGuard, ProxyDataFlow
+from .base import DataFlow, DataFlowReentrantGuard, DataFlowResetStateNotCalled, ProxyDataFlow
 from .serialize import DataFromIterable, DataFromList
 
 __all__ = ["CacheData", "CustomDataFromList", "CustomDataFromIterable"]
@@ -44,9 +44,8 @@ class CacheData(ProxyDataFlow):
 
     def __init__(self, df: DataFlow, shuffle: bool = False) -> None:
         """
-        Args:
-            df (DataFlow): input DataFlow.
-            shuffle (bool): whether to shuffle the cache before yielding from it.
+        :param df: input DataFlow.
+        :param shuffle: whether to shuffle the cache before yielding from it.
         """
         self.shuffle = shuffle
         self.buffer: List[Any] = []
@@ -61,10 +60,7 @@ def reset_state(self) -> None:
 
     def __iter__(self) -> Iterator[Any]:
         if self._guard is None:
-            raise RuntimeError(
-                "Iteration has been started before method reset_state has been called. Please "
-                "call reset_state() before"
-            )
+            raise DataFlowResetStateNotCalled()
 
         with self._guard:
             if self.buffer:
@@ -129,8 +125,6 @@ def __init__(
                                list and re-balance the sample. Only the output list of the re-balancing function will be
                                considered.
         """
-        if shuffle:
-            logger.info("Make sure to call .reset_state() for the dataflow otherwise an error will be raised")
         super().__init__(lst, shuffle)
         self.max_datapoints = max_datapoints
         self.rebalance_func = rebalance_func
@@ -142,10 +136,7 @@ def __len__(self) -> int:
 
     def __iter__(self) -> Iterator[Any]:
         if self.rng is None:
-            raise RuntimeError(
-                "Iteration has been started before method reset_state has been called. Please "
-                "call reset_state() before"
-            )
+            raise DataFlowResetStateNotCalled()
         if self.rebalance_func is not None:
             lst_tmp = self.rebalance_func(self.lst)
             logger.info("subset size after re-balancing: %s", len(lst_tmp))
diff --git a/deepdoctection/dataflow/custom_serialize.py b/deepdoctection/dataflow/custom_serialize.py
index b9d445fc..87705c38 100644
--- a/deepdoctection/dataflow/custom_serialize.py
+++ b/deepdoctection/dataflow/custom_serialize.py
@@ -29,10 +29,9 @@
 
 from ..utils.context import timed_operation
 from ..utils.detection_types import JsonDict, Pathlike
-from ..utils.fs import is_file_extension
-from ..utils.logger import logger
 from ..utils.pdf_utils import PDFStreamer
 from ..utils.tqdm import get_tqdm
+from ..utils.utils import FileExtensionError, is_file_extension
 from .base import DataFlow
 from .common import FlattenData, JoinData, MapData
 from .custom import CacheData, CustomDataFromIterable, CustomDataFromList
@@ -87,17 +86,14 @@ def save(df: DataFlow, path: Pathlike, file_name: str, max_datapoints: Optional[
         :param max_datapoints: maximum number of datapoint to consider writing to a file.
         """
 
-        assert os.path.isdir(path), f"not a dir {path}"
-        assert is_file_extension(file_name, ".jsonl")
+        if not os.path.isdir(path):
+            raise NotADirectoryError(path)
+        if not is_file_extension(file_name, ".jsonl"):
+            raise FileExtensionError(f"Expected .jsonl file got {path}")
 
+        df.reset_state()
         with open(os.path.join(path, file_name), "w") as file:  # pylint: disable=W1514
             writer = Writer(file)
-            length = _reset_df_and_get_length(df)
-            if length == 0:
-                logger.info("cannot estimate length of dataflow")
-            if max_datapoints is not None:
-                if max_datapoints < length:
-                    logger.info("dataflow larger than max_datapoints")
             for k, dp in enumerate(df):
                 if max_datapoints is None:
                     writer.write(dp)
@@ -147,16 +143,12 @@ def save(df: DataFlow, path: Pathlike, file_name: str, max_datapoints: Optional[
         :param max_datapoints: maximum number of datapoint to consider writing to a file.
         """
 
-        assert os.path.isdir(path), f"not a dir {path}"
-        assert is_file_extension(file_name, ".txt")
+        if not os.path.isdir(path):
+            raise NotADirectoryError(path)
+        if not is_file_extension(file_name, ".jsonl"):
+            raise FileExtensionError(f"Expected .txt file got {path}")
 
         with open(os.path.join(path, file_name), "w", encoding="UTF-8") as file:
-            length = _reset_df_and_get_length(df)
-            if length == 0:
-                logger.info("cannot estimate length of dataflow")
-            if max_datapoints is not None:
-                if max_datapoints < length:
-                    logger.info("dataflow larger than max_datapoints")
             for k, dp in enumerate(df):
                 if max_datapoints is None:
                     file.write(dp)
@@ -254,7 +246,8 @@ def __init__(self, annotation_file: Optional[Pathlike] = None) -> None:
             with timed_operation(message="Loading annotations to memory"):
                 with open(annotation_file, "r", encoding="UTF-8") as file:
                     dataset = json.load(file)
-                assert isinstance(dataset, dict), f"annotation file format {type(dataset)} not supported"
+                if not isinstance(dataset, dict):
+                    raise TypeError(f"Annotation file format {type(dataset)} for {annotation_file} not supported")
             self.dataset = dataset
             self._create_index()
 
@@ -485,9 +478,11 @@ def load(path: Pathlike, max_datapoints: Optional[int] = None) -> DataFlow:
         :param path: a path to a .json file.
         :return: dataflow to iterate from
         """
-        assert os.path.isfile(path), path
+        if not os.path.isfile(path):
+            raise FileNotFoundError(path)
         file = os.path.split(path)[1]
-        assert is_file_extension(file, ".json"), file
+        if not is_file_extension(file, ".json"):
+            raise FileExtensionError(f"Expected .json file got {path}")
 
         with timed_operation("Start loading .json file and serializing"):
             coco = CocoParser(path)
@@ -559,7 +554,8 @@ def split(path: Pathlike, path_target: Optional[Pathlike] = None, max_datapoint:
         """
         if path_target is None:
             path_target, _ = os.path.split(path)
-        assert os.path.isdir(path_target), f"not a dir {path_target}"
+        if not os.path.isdir(path_target):
+            raise NotADirectoryError(path)
         df = SerializerPdfDoc.load(path, max_datapoint)
         for dp in df:
             with open(os.path.join(path_target, dp["file_name"]), "wb") as page:
diff --git a/deepdoctection/dataflow/parallel_map.py b/deepdoctection/dataflow/parallel_map.py
index 578d7675..2d716b50 100644
--- a/deepdoctection/dataflow/parallel_map.py
+++ b/deepdoctection/dataflow/parallel_map.py
@@ -66,9 +66,11 @@ def _get_pipe_name(name):
         pipename = f"ipc://@{name}-pipe-{str(uuid.uuid1())[:8]}"
     else:
         pipedir = "."
-        assert os.path.isdir(pipedir), pipedir
+        if not os.path.isdir(pipedir):
+            raise NotADirectoryError(pipedir)
         filename = f"{pipedir.rstrip('/')}/{name}-pipe-{str(uuid.uuid1())[:6]}"
-        assert not os.path.exists(filename), f"Pipe {filename} exists! You may be unlucky."
+        if os.path.exists(filename):
+            raise FileExistsError(filename)
         pipename = f"ipc://{filename}"
     return pipename
 
@@ -76,7 +78,8 @@ def _get_pipe_name(name):
 class _ParallelMapData(ProxyDataFlow, ABC):
     def __init__(self, df: DataFlow, buffer_size: int, strict: bool = False) -> None:
         super().__init__(df)
-        assert buffer_size > 0, buffer_size
+        if not buffer_size:
+            raise ValueError("buffer_size must be a positive number")
         self._buffer_size = buffer_size
         self._buffer_occupancy = 0  # actual #elements in buffer, only useful in strict mode
         self._strict = strict
@@ -102,7 +105,7 @@ def _send(self, dp: Any):
     @no_type_check
     def _recv_filter_none(self):
         ret = self._recv()
-        assert ret is not None, f"[{type(self).__name__}] Map function cannot return None when strict mode is used."
+        assert ret is not None, f"[{type(self).__name__}] map function cannot return None when strict mode is used."
         return ret
 
     @no_type_check
@@ -115,7 +118,7 @@ def _fill_buffer(self, cnt=None):
                 self._send(dp)
         except StopIteration as exce:
             raise RuntimeError(
-                f"[{type(self).__name__}] buffer_size cannot be larger than the size of the DataFlow when strict=True! "
+                f"[{type(self).__name__}] buffer_size cannot be larger than the size of the dataflow when strict=True! "
                 "Please use a smaller buffer_size!"
             ) from exce
         self._buffer_occupancy += cnt
@@ -226,7 +229,8 @@ def __init__(
                 pass
 
         super().__init__(df, buffer_size, strict)
-        assert num_thread > 0, num_thread
+        if not num_thread:
+            raise ValueError("num_thread must be a positive number")
 
         self._strict = strict
         self.num_thread = num_thread
@@ -276,7 +280,8 @@ def __del__(self) -> Any:
 
 class _MultiProcessZMQDataFlow(DataFlow, ABC):
     def __init__(self) -> None:
-        assert os.name != "nt", "ZMQ IPC doesn't support windows!"
+        if os.name == "nt":
+            raise EnvironmentError("ZMQ IPC doesn't support windows")
         self._reset_done = False
         self._procs: List[Any] = []
         self.context = None
@@ -307,7 +312,7 @@ def __del__(self) -> None:
             for x in self._procs:
                 x.terminate()
                 x.join(5)
-            print(f"{type(self).__name__} successfully cleaned-up.")
+            logger.info("%s successfully cleaned-up.", type(self).__name__)
         except Exception:  # pylint: disable=W0703
             pass
 
@@ -318,9 +323,8 @@ def _bind_guard(sock, name):
         sock.bind(name)
     except zmq.ZMQError:
         logger.error(
-            "ZMQError in socket.bind('{name}'). Perhaps you're \
-using pipes on a non-local file system. See documentation of MultiProcessRunnerZMQ \
-for more information."
+            "ZMQError in socket.bind('{name}'). Perhaps you're using pipes on a non-local file system. "
+            "See documentation of MultiProcessRunnerZMQ for more information."
         )
         raise
 
@@ -391,7 +395,8 @@ def __init__(
 
         _ParallelMapData.__init__(self, df, buffer_size, strict)
         _MultiProcessZMQDataFlow.__init__(self)
-        assert num_proc > 0, num_proc
+        if not num_proc:
+            raise ValueError("num_proc must be a positive number")
         self.num_proc = num_proc
         self.map_func = map_func
         self._strict = strict
diff --git a/deepdoctection/dataflow/serialize.py b/deepdoctection/dataflow/serialize.py
index 97d1cd1e..7f232d0c 100644
--- a/deepdoctection/dataflow/serialize.py
+++ b/deepdoctection/dataflow/serialize.py
@@ -16,7 +16,7 @@
 
 import numpy as np
 
-from .base import DataFlow, RNGDataFlow
+from .base import DataFlow, DataFlowResetStateNotCalled, RNGDataFlow
 
 
 class DataFromList(RNGDataFlow):
@@ -44,10 +44,7 @@ def __iter__(self) -> Iterator[Any]:
                 for k in idxs:
                     yield self.lst[k]
             else:
-                raise RuntimeError(
-                    "Iteration has been started before method reset_state has been called. Please "
-                    "call reset_state() before"
-                )
+                raise DataFlowResetStateNotCalled()
 
 
 class DataFromIterable(DataFlow):
@@ -103,18 +100,15 @@ def __init__(
         self.random = random
         self.dtype = [dtype] * len(shapes) if isinstance(dtype, str) else dtype
         self.domain = [domain] * len(shapes) if isinstance(domain, tuple) else domain
-        assert len(self.dtype) == len(self.shapes)
-        assert len(self.domain) == len(self.domain)
+        if len(self.dtype) != len(self.shapes):
+            raise ValueError(f"self.dtype={self.dtype} and self.shapes={self.shapes} must have same length")
 
     def __len__(self) -> int:
         return self._size
 
     def __iter__(self) -> Iterator[Any]:
         if self.rng is None:
-            raise RuntimeError(
-                "Iteration has been started before method reset_state has been called. Please "
-                "call reset_state() before"
-            )
+            raise DataFlowResetStateNotCalled()
         if self.random:
             for _ in range(self._size):
                 val = []
diff --git a/deepdoctection/dataflow/stats.py b/deepdoctection/dataflow/stats.py
index c8d42066..d475df26 100644
--- a/deepdoctection/dataflow/stats.py
+++ b/deepdoctection/dataflow/stats.py
@@ -70,7 +70,8 @@ def __init__(
         if isinstance(axis, int):
             axis = (axis,)
         if axis is not None:
-            assert 0 in axis, "Requires 0-th axis for calculating mean"
+            if 0 not in axis:
+                raise ValueError("0-th axis required for calculating mean")
         self.axis = axis
         self.key = key
         self.max_datapoints = max_datapoints
@@ -98,7 +99,7 @@ def start(self) -> npt.NDArray[Any]:
             self.df.reset_state()
         itr = self.df.__iter__()
 
-        logger.info("____________________ CALCULATING MEAN ____________________")
+        logger.info("Calculating mean")
 
         len_df: Optional[int]
         try:
@@ -196,7 +197,8 @@ def __init__(
         if isinstance(axis, int):
             axis = (axis,)
         if axis is not None:
-            assert 0 in axis, "Requires 0-th axis for calculating mean"
+            if 0 not in axis:
+                raise ValueError("0-th axis required for calculating std")
         self.axis = axis
         self.key = key
         self.max_datapoints = max_datapoints
@@ -225,7 +227,7 @@ def start(self) -> npt.NDArray[Any]:
             self.df.reset_state()
         itr = self.df.__iter__()
 
-        logger.info("____________________ CALCULATING STANDARD DEVIATION ____________________")
+        logger.info("Calculating standard deviation")
         try:
             len_df = len(self.df)
         except NotImplementedError:
diff --git a/deepdoctection/datapoint/annotation.py b/deepdoctection/datapoint/annotation.py
index 5f7e82d8..32c6c841 100644
--- a/deepdoctection/datapoint/annotation.py
+++ b/deepdoctection/datapoint/annotation.py
@@ -25,6 +25,7 @@
 
 from ..utils.detection_types import JsonDict
 from ..utils.identifier import get_uuid, is_uuid_like
+from ..utils.logger import logger
 from ..utils.settings import DefaultType, ObjectTypes, SummaryType, TypeOrStr, get_type
 from .box import BoundingBox
 from .convert import as_dict
@@ -105,13 +106,13 @@ def annotation_id(self, input_id: str) -> None:
         annotation_id setter
         """
         if self._annotation_id is not None:
-            raise AssertionError("annotation_id already defined and cannot be reset")
+            raise AssertionError("Annotation_id already defined and cannot be reset")
         if is_uuid_like(input_id):
             self._annotation_id = input_id
         elif isinstance(input_id, property):
             pass
         else:
-            raise ValueError("annotation_id must be uuid3 string")
+            raise ValueError("Annotation_id must be uuid3 string")
 
     @abstractmethod
     def get_defining_attributes(self) -> List[str]:
@@ -126,9 +127,8 @@ def get_defining_attributes(self) -> List[str]:
 
     def _assert_defining_attributes_have_str(self) -> None:
         for attr in self.get_defining_attributes():
-            assert hasattr(
-                eval("self." + attr), "__str__"  # pylint: disable=W0123
-            ), f"attribute {attr} must have __str__ method"
+            if not hasattr(eval("self." + attr), "__str__"):  # pylint: disable=W0123
+                raise AttributeError(f"Attribute {attr} must have __str__ method")
 
     @staticmethod
     def set_annotation_id(annotation: "CategoryAnnotation", *container_id_context: Optional[str]) -> str:
@@ -238,9 +238,9 @@ def dump_sub_category(
         :param container_id_context: Tuple/list of context ids.
         """
 
-        assert sub_category_name not in self.sub_categories, (
-            f"{sub_category_name} as sub category already defined for " f"{self.annotation_id}"
-        )
+        if sub_category_name in self.sub_categories:
+            raise KeyError(f"{sub_category_name} as sub category already defined for " f"{self.annotation_id}")
+
         if self._annotation_id is not None:
             if annotation._annotation_id is None:  # pylint: disable=W0212
                 annotation.annotation_id = self.set_annotation_id(annotation, self.annotation_id, *container_id_context)
@@ -281,7 +281,9 @@ def dump_relationship(self, key: TypeOrStr, annotation_id: str) -> None:
         :param key: The key, where to place the annotation id.
         :param annotation_id: An annotation id
         """
-        assert is_uuid_like(annotation_id), "annotation_id must be uuid"
+        if not is_uuid_like(annotation_id):
+            raise ValueError("Annotation_id must be uuid")
+
         key_type = get_type(key)
         if key not in self.relationships:
             self.relationships[key_type] = []
@@ -316,7 +318,7 @@ def remove_relationship(self, key: ObjectTypes, annotation_ids: Optional[Union[L
                 try:
                     self.relationships[key].remove(ann_id)
                 except ValueError:
-                    pass
+                    logger.warning("Relationship %s cannot be removed because it does not exist", key)
         else:
             self.relationships[key].clear()
 
diff --git a/deepdoctection/datapoint/box.py b/deepdoctection/datapoint/box.py
index 6c63f519..7c70ed18 100644
--- a/deepdoctection/datapoint/box.py
+++ b/deepdoctection/datapoint/box.py
@@ -139,6 +139,10 @@ def iou(boxes1: npt.NDArray[float32], boxes2: npt.NDArray[float32]) -> npt.NDArr
     return np_iou(boxes1, boxes2)
 
 
+class BoundingBoxError(BaseException):
+    """Special exception only for :class:`BoundingBox`"""
+
+
 @dataclass
 class BoundingBox:
     """
@@ -174,30 +178,34 @@ class BoundingBox:
 
     def __post_init__(self) -> None:
         if self.width == 0.0:
-            assert self.lrx is not None, "Bounding box not fully initialized"
+            if self.lrx is None:
+                raise BoundingBoxError("Bounding box not fully initialized")
             self.width = self.lrx - self.ulx
         if self.height == 0.0:
-            assert self.lry is not None, "Bounding box not fully initialized"
+            if self.lry is None:
+                raise BoundingBoxError("Bounding box not fully initialized")
             self.height = self.lry - self.uly
 
         if self.lrx == 0.0:
-            assert self.width is not None, "Bounding box not fully initialized"
+            if self.width is None:
+                raise BoundingBoxError("Bounding box not fully initialized")
             self.lrx = self.ulx + self.width
         if self.lry == 0.0:
-            assert self.height is not None, "Bounding box not fully initialized"
+            if self.height is None:
+                raise BoundingBoxError("Bounding box not fully initialized")
             self.lry = self.uly + self.height
 
-        assert self.ulx >= 0.0 and self.uly >= 0.0, "bounding box ul must be >= (0.,0.)"
-        assert self.height > 0.0 and self.width > 0.0, (
-            f"bounding box must have height and width >0. Check coords "
-            f"ulx: {self.ulx}, uly: {self.uly}, lrx: {self.lrx}, "
-            f"lry: {self.lry}."
-        )
-
+        if not (self.ulx >= 0.0 and self.uly >= 0.0):
+            raise BoundingBoxError("Bounding box ul must be >= (0.,0.)")
+        if not (self.height > 0.0 and self.width > 0.0):
+            raise BoundingBoxError(
+                f"bounding box must have height and width >0. Check coords "
+                f"ulx: {self.ulx}, uly: {self.uly}, lrx: {self.lrx}, "
+                f"lry: {self.lry}."
+            )
         if not self.absolute_coords:
-            assert (
-                self.ulx <= 1.0 and self.uly <= 1.0 and self.lrx <= 1.0 and self.lry <= 1.0
-            ), "coordinates must be between 0 and 1"
+            if not (self.ulx <= 1.0 and self.uly <= 1.0 and self.lrx <= 1.0 and self.lry <= 1.0):
+                raise BoundingBoxError("coordinates must be between 0 and 1")
 
     @property
     def cx(self) -> float:
@@ -245,7 +253,7 @@ def to_np_array(self, mode: str, scale_x: float = 1.0, scale_y: float = 1.0) ->
         np_poly_scale = np.array(
             [scale_x, scale_y, scale_x, scale_y, scale_x, scale_y, scale_x, scale_y], dtype=np.float32
         )
-        assert mode in ("xyxy", "xywh", "poly"), "not a valid mode"
+        assert mode in ("xyxy", "xywh", "poly"), "Not a valid mode"
         if mode == "xyxy":
             return np.array([self.ulx, self.uly, self.lrx, self.lry], dtype=np.float32) * np_box_scale
         if mode == "xywh":
@@ -267,7 +275,7 @@ def to_list(self, mode: str, scale_x: float = 1.0, scale_y: float = 1.0) -> List
         :param scale_y: rescale the y coordinate. Defaults to 1
         :return: box coordinates
         """
-        assert mode in ("xyxy", "xywh", "poly"), "not a valid mode"
+        assert mode in ("xyxy", "xywh", "poly"), "Not a valid mode"
         if mode == "xyxy":
             return [
                 self.ulx * scale_x,
@@ -331,7 +339,7 @@ def transform(
         return self
 
     def __str__(self) -> str:
-        return f"Bounding Box ulx: {self.ulx} uly: {self.uly} lrx: {self.lrx} lry: {self.lry}"
+        return f"Bounding Box ulx: {self.ulx}, uly: {self.uly}, lrx: {self.lrx}, lry: {self.lry}"
 
     @staticmethod
     def remove_keys() -> List[str]:
@@ -430,7 +438,11 @@ def local_to_global_coords(local_box: BoundingBox, embedding_box: BoundingBox) -
     :return: bounding box with local box transformed to absolute coords
     """
 
-    assert local_box.absolute_coords and embedding_box.absolute_coords, "absolute coords required"
+    assert local_box.absolute_coords and embedding_box.absolute_coords, (
+        f"absolute coords "
+        f"(={local_box.absolute_coords} for local_box and embedding_box (={embedding_box.absolute_coords}) must be "
+        f"True"
+    )
     assert embedding_box.ulx is not None and embedding_box.uly is not None
     assert (
         local_box.ulx is not None
@@ -459,15 +471,12 @@ def global_to_local_coords(global_box: BoundingBox, embedding_box: BoundingBox)
     :return: Bounding box of the embedded box in local coordinates.
     """
 
-    assert global_box.absolute_coords and embedding_box.absolute_coords, "absolute coords required"
-    assert embedding_box.ulx is not None and embedding_box.uly is not None
-    assert embedding_box.width is not None and embedding_box.height is not None
-    assert (
-        global_box.ulx is not None
-        and global_box.uly is not None
-        and global_box.lrx is not None
-        and global_box.lry is not None
+    assert global_box.absolute_coords and embedding_box.absolute_coords, (
+        f"absolute coords "
+        f"(={global_box.absolute_coords} for local_box and embedding_box (={embedding_box.absolute_coords}) must be "
+        f"True"
     )
+
     return BoundingBox(
         absolute_coords=True,
         ulx=max(global_box.ulx - embedding_box.ulx, 0),
diff --git a/deepdoctection/datapoint/image.py b/deepdoctection/datapoint/image.py
index 7b659151..7c7c2523 100644
--- a/deepdoctection/datapoint/image.py
+++ b/deepdoctection/datapoint/image.py
@@ -111,7 +111,7 @@ def image_id(self, input_id: str) -> None:
         image_id setter
         """
         if self._image_id is not None:
-            raise AssertionError("image_id already defined and cannot be reset")
+            raise ValueError("image_id already defined and cannot be reset")
         if is_uuid_like(input_id):
             self._image_id = input_id
         elif isinstance(input_id, property):
@@ -147,17 +147,20 @@ def image(self, image: Optional[Union[str, ImageType, bytes]]) -> None:
             self.set_width_height(self._image.shape[1], self._image.shape[0])
             self._self_embedding()
         else:
-            assert isinstance(image, np.ndarray), f"cannot load image is of type: {type(image)}"
+            if not isinstance(image, np.ndarray):
+                raise TypeError(f"Cannot load image is of type: {type(image)}")
             self._image = image.astype(uint8)
             self.set_width_height(self._image.shape[1], self._image.shape[0])
             self._self_embedding()
 
     @property
     def summary(self) -> Optional[SummaryAnnotation]:
+        """summary"""
         return self._summary
 
     @summary.setter
     def summary(self, summary_annotation: SummaryAnnotation) -> None:
+        """summary setter"""
         if summary_annotation._annotation_id is None:  # pylint: disable=W0212
             summary_annotation.annotation_id = self.define_annotation_id(summary_annotation)
         self._summary = summary_annotation
@@ -269,7 +272,8 @@ def set_embedding(self, image_id: str, bounding_box: BoundingBox) -> None:
         :param image_id: A uuid of the embedding image.
         :param bounding_box: bounding box of this image in terms of the embedding image.
         """
-        assert isinstance(bounding_box, BoundingBox), "bounding box must be of type BoundingBox"
+        if not isinstance(bounding_box, BoundingBox):
+            raise TypeError(f"Bounding box must be of type BoundingBox, is of type {type(bounding_box)}")
         self.embeddings[image_id] = bounding_box
 
     def get_embedding(self, image_id: str) -> BoundingBox:
@@ -284,7 +288,6 @@ def get_embedding(self, image_id: str) -> BoundingBox:
 
     def _self_embedding(self) -> None:
         if self._bbox is not None:
-            assert isinstance(self.image_id, str)
             self.set_embedding(self.image_id, self._bbox)
 
     def dump(self, annotation: ImageAnnotation) -> None:
@@ -295,15 +298,15 @@ def dump(self, annotation: ImageAnnotation) -> None:
 
         :param annotation: image annotation to store
         """
-        assert isinstance(annotation, ImageAnnotation), (
-            f"Annotation must be of type ImageAnnotation: "
-            f"{annotation.annotation_id} but is of type {str(type(annotation))}"
-        )
+        if not isinstance(annotation, ImageAnnotation):
+            raise TypeError(
+                f"Annotation must be of type ImageAnnotation: "
+                f"{annotation.annotation_id} but is of type {str(type(annotation))}"
+            )
         if annotation._annotation_id is None:  # pylint: disable=W0212
             annotation.annotation_id = self.define_annotation_id(annotation)
-        assert annotation.annotation_id not in self._annotation_ids, (
-            f"Cannot dump annotation with already taken " f"id {annotation.annotation_id}"
-        )
+        if annotation.annotation_id in self._annotation_ids:
+            raise ValueError(f"Cannot dump annotation with already taken " f"id {annotation.annotation_id}")
         self._annotation_ids.append(annotation.annotation_id)
         self.annotations.append(annotation)
 
@@ -326,11 +329,7 @@ def get_annotation(
         :return: A (possibly empty) list of Annotations
         """
 
-        cat_names = (
-            [category_names]
-            if isinstance(category_names, (ObjectTypes,str))
-            else category_names
-        )
+        cat_names = [category_names] if isinstance(category_names, (ObjectTypes, str)) else category_names
         if cat_names is not None:
             cat_names = [get_type(cat_name) for cat_name in cat_names]
         ann_ids = [annotation_ids] if isinstance(annotation_ids, str) else annotation_ids
@@ -398,7 +397,6 @@ def define_annotation_id(self, annotation: Annotation) -> str:
 
         attributes = annotation.get_defining_attributes()
         attributes_values = [str(getattr(annotation, attribute)) for attribute in attributes]
-        assert self.image_id is not None
         return get_uuid(*attributes_values, str(self.image_id))
 
     def remove(self, annotation: ImageAnnotation) -> None:
@@ -425,13 +423,12 @@ def image_ann_to_image(self, annotation_id: str, crop_image: bool = False) -> No
         """
 
         ann = self.get_annotation(annotation_ids=annotation_id)[0]
-        assert isinstance(
-            ann, ImageAnnotation
-        ), f"Annotation must be of type ImageAnnotation: {annotation_id} but is of type {str(type(ann))}"
 
         new_image = Image(file_name=self.file_name, location=self.location, external_id=annotation_id)
 
-        assert self._bbox is not None and ann.bounding_box is not None
+        if self._bbox is None or ann.bounding_box is None:
+            raise ValueError(f"Bounding box for image and ImageAnnotation ({annotation_id}) must be set")
+
         new_bounding_box = intersection_box(self._bbox, ann.bounding_box, self.width, self.height)
         if new_bounding_box.absolute_coords:
             width = new_bounding_box.width
@@ -445,7 +442,7 @@ def image_ann_to_image(self, annotation_id: str, crop_image: bool = False) -> No
         if crop_image and self.image is not None:
             new_image.image = crop_box_from_image(self.image, ann.bounding_box, self.width, self.height)
         elif crop_image and self.image is None:
-            raise ValueError("crop_image = 'True' requires self.image to be not None")
+            raise ValueError("crop_image = True requires self.image to be not None")
 
         ann.image = new_image
 
@@ -462,7 +459,8 @@ def maybe_ann_to_sub_image(self, annotation_id: str, category_names: Union[str,
         """
 
         ann = self.get_annotation(annotation_ids=annotation_id)[0]
-        assert ann.image is not None, "adding sub images to annotations requires ann to have an image"
+        if ann.image is None:
+            raise ValueError("When adding sub images to ImageAnnotation then ImageAnnotation.image must not be None")
         assert ann.bounding_box is not None
         box = ann.bounding_box.to_list("xyxy")
         proposals = self.get_annotation(category_names)
@@ -476,7 +474,10 @@ def maybe_ann_to_sub_image(self, annotation_id: str, category_names: Union[str,
         selected_ids = ann_ids[indices]
         sub_images = self.get_annotation(annotation_ids=selected_ids.tolist())
         for sub_image in sub_images:
-            assert sub_image.image is not None
+            if sub_image.image is None:
+                raise ValueError(
+                    "When setting an embedding to ImageAnnotation then ImageAnnotation.image must not " "be None"
+                )
             sub_image.image.set_embedding(
                 annotation_id,
                 global_to_local_coords(
diff --git a/deepdoctection/datasets/base.py b/deepdoctection/datasets/base.py
index 58d2c00b..a17c254c 100644
--- a/deepdoctection/datasets/base.py
+++ b/deepdoctection/datasets/base.py
@@ -234,7 +234,10 @@ def explicit_dataflows(self, *dataflows: DataFlow) -> None:
         :param dataflows: An arbitrary number of dataflows
         """
         self.dataflows = dataflows
-        assert len(self.datasets) <= len(self.dataflows)
+        if len(self.datasets) > len(self.dataflows):
+            raise ValueError(
+                f"len(self.datasets) = {len(self.datasets)} must be" f" <= len(self.dataflows) = {len(self.dataflows)}"
+            )
         self._dataflow_builder = self._builder()
         self._dataflow_builder.categories = self._categories()
 
@@ -256,7 +259,7 @@ def split_datasets(self, ratio: float = 0.1, add_test: bool = True) -> None:
                       split.
         :param add_test: Add a test split
         """
-        assert self.datapoint_list is not None, "datasets need to be buffered before splitting"
+        assert self.datapoint_list is not None, "Datasets need to be buffered before splitting"
         number_datapoints = len(self.datapoint_list)
         indices = np.random.binomial(1, ratio, number_datapoints)
         train_dataset = [self.datapoint_list[i] for i in range(number_datapoints) if indices[i] == 0]
@@ -307,7 +310,8 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
                 """
 
                 split = kwargs.get("split", "train")
-                assert isinstance(split, str)
+                if not isinstance(split, str):
+                    raise ValueError("'split' must be a string")
                 max_datapoints = kwargs.get("max_datapoints")
                 if isinstance(max_datapoints, str):
                     max_datapoints = int(max_datapoints)
diff --git a/deepdoctection/datasets/dataflow_builder.py b/deepdoctection/datasets/dataflow_builder.py
index d2ba45ca..d7eb1dff 100644
--- a/deepdoctection/datasets/dataflow_builder.py
+++ b/deepdoctection/datasets/dataflow_builder.py
@@ -110,3 +110,10 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
         :return: dataflow
         """
         raise NotImplementedError
+
+    def get_annotation_file(self, split: str) -> str:
+        """Get single annotation file."""
+        split_file = self.annotation_files[split]
+        if isinstance(split_file, str):
+            return split_file
+        raise TypeError("Unsupported get method for sequence type splits")
diff --git a/deepdoctection/datasets/info.py b/deepdoctection/datasets/info.py
index 762c7cc4..a2a9b06d 100644
--- a/deepdoctection/datasets/info.py
+++ b/deepdoctection/datasets/info.py
@@ -232,7 +232,7 @@ def get_sub_categories(
         for cat in _categories:  # pylint: disable=R1702
             assert cat in self.get_categories(  # pylint: disable=E1135
                 as_dict=False, filtered=True
-            ), f"{cat} not in categories, maybe has been replaced with sub category"
+            ), f"{cat} not in categories. Maybe it has been replaced with sub category"
             sub_cat_dict = self.init_sub_categories.get(cat)
             if sub_cat_dict is None:
                 if self._cat_to_sub_cat:
@@ -299,7 +299,8 @@ def set_cat_to_sub_cat(self, cat_to_sub_cat: Dict[TypeOrStr, TypeOrStr]) -> None
         """
 
         _cat_to_sub_cat = {get_type(key): get_type(value) for key, value in cat_to_sub_cat.items()}
-        assert self._allow_update, "Replacing categories with sub categories is not allowed"
+        if not self._allow_update:
+            raise PermissionError("Replacing categories with sub categories is not allowed")
         self._categories_update = self.init_categories
         categories = self.get_categories(name_as_key=True)
         cats_or_sub_cats = [
@@ -324,7 +325,8 @@ def filter_categories(self, categories: Union[TypeOrStr, List[TypeOrStr]]) -> No
         :param categories: A single category name or a list of category names.
         """
 
-        assert self._allow_update, "Filtering categories is not allowed"
+        if not self._allow_update:
+            raise PermissionError("Filtering categories is not allowed")
         if isinstance(categories, (ObjectTypes, str)):
             categories = [get_type(categories)]
         else:
diff --git a/deepdoctection/datasets/instances/doclaynet.py b/deepdoctection/datasets/instances/doclaynet.py
index 8fd2e861..29776b48 100644
--- a/deepdoctection/datasets/instances/doclaynet.py
+++ b/deepdoctection/datasets/instances/doclaynet.py
@@ -139,8 +139,7 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
 
         # Load
         df: DataFlow
-        dataset_split = self.annotation_files[split]
-        path = self.get_workdir() / dataset_split  # type: ignore
+        path = self.get_workdir() / self.get_annotation_file(split)
         df = SerializerCoco.load(path, max_datapoints=max_datapoints)
 
         # Map
diff --git a/deepdoctection/datasets/instances/fintabnet.py b/deepdoctection/datasets/instances/fintabnet.py
index 47787c0b..db577e34 100644
--- a/deepdoctection/datasets/instances/fintabnet.py
+++ b/deepdoctection/datasets/instances/fintabnet.py
@@ -181,13 +181,11 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
         if max_datapoints is not None:
             max_datapoints = int(max_datapoints)
         if kwargs.get("build_mode", "") != "table":
-            logger.info("Logic will currently display only ONE table per page, even if there are more !!")
+            logger.info("Logic will display only only table per page, even if there are more!!")
 
         # Load
         df: DataFlow
-        dataset_split = self.annotation_files[split]
-        assert isinstance(dataset_split, str)
-        path = self.get_workdir() / dataset_split
+        path = self.get_workdir() / self.get_annotation_file(split)
         df = SerializerJsonlines.load(path, max_datapoints=max_datapoints)
 
         # Map
diff --git a/deepdoctection/datasets/instances/funsd.py b/deepdoctection/datasets/instances/funsd.py
index ec60d1ce..d31ec60d 100644
--- a/deepdoctection/datasets/instances/funsd.py
+++ b/deepdoctection/datasets/instances/funsd.py
@@ -146,9 +146,7 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
             max_datapoints = int(max_datapoints)
 
         # Load
-        annotation_split = self.annotation_files[split]
-        assert isinstance(annotation_split, str)
-        path_ann_files = self.get_workdir() / self.splits[split] / annotation_split
+        path_ann_files = self.get_workdir() / self.splits[split] / self.get_annotation_file(split)
 
         df = SerializerFiles.load(path_ann_files, ".json", max_datapoints)
 
@@ -168,9 +166,7 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
             values_as_dict=True,
             name_as_key=True,
         )[LayoutType.word][WordType.token_tag]
-        df = MapData(
-            df, xfund_to_image(load_image, False, category_names_mapping, ner_token_to_id_mapping)
-        )
+        df = MapData(df, xfund_to_image(load_image, False, category_names_mapping, ner_token_to_id_mapping))
         df = MapData(df, xfund_to_image(load_image, False, category_names_mapping, ner_token_to_id_mapping))
         if self.categories.is_cat_to_sub_cat():
             df = MapData(
diff --git a/deepdoctection/datasets/instances/iiitar13k.py b/deepdoctection/datasets/instances/iiitar13k.py
index aaee45ea..6916a94b 100644
--- a/deepdoctection/datasets/instances/iiitar13k.py
+++ b/deepdoctection/datasets/instances/iiitar13k.py
@@ -130,9 +130,7 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
             max_datapoints = int(max_datapoints)
 
         # Load
-        annotation_split = self.annotation_files[split]
-        assert isinstance(annotation_split, str)
-        path_ann_files = self.get_workdir() / annotation_split
+        path_ann_files = self.get_workdir() / self.get_annotation_file(split)
 
         df = SerializerFiles.load(path_ann_files, ".xml", max_datapoints)
         utf8_parser = etree.XMLParser(encoding="utf-8")
diff --git a/deepdoctection/datasets/instances/publaynet.py b/deepdoctection/datasets/instances/publaynet.py
index 7dae566d..1aca5301 100644
--- a/deepdoctection/datasets/instances/publaynet.py
+++ b/deepdoctection/datasets/instances/publaynet.py
@@ -113,9 +113,7 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
         fake_score = kwargs.get("fake_score", False)
 
         # Load
-        dataset_split = self.annotation_files[split]
-        assert isinstance(dataset_split, str)
-        path = self.get_workdir() / dataset_split
+        path = self.get_workdir() / self.get_annotation_file(split)
         df = SerializerCoco.load(path, max_datapoints=max_datapoints)
 
         # Map
@@ -128,7 +126,6 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
         )
         df = MapData(df, coco_mapper)
 
-        assert self.categories is not None  # avoid many typing issues
         if self.categories.is_filtered():
             df = MapData(
                 df,
diff --git a/deepdoctection/datasets/instances/pubtables1m.py b/deepdoctection/datasets/instances/pubtables1m.py
index f2be35c0..2f5edbb7 100644
--- a/deepdoctection/datasets/instances/pubtables1m.py
+++ b/deepdoctection/datasets/instances/pubtables1m.py
@@ -128,9 +128,7 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
             max_datapoints = int(max_datapoints)
 
         # Load
-        dataset_split = self.annotation_files[split]
-        assert isinstance(dataset_split, str)
-        path_ann_files = self.get_workdir() / dataset_split
+        path_ann_files = self.get_workdir() / self.get_annotation_file(split)
 
         df = SerializerFiles.load(path_ann_files, ".xml", max_datapoints)
         utf8_parser = etree.XMLParser(encoding="utf-8")
diff --git a/deepdoctection/datasets/instances/pubtabnet.py b/deepdoctection/datasets/instances/pubtabnet.py
index e6758725..81e34197 100644
--- a/deepdoctection/datasets/instances/pubtabnet.py
+++ b/deepdoctection/datasets/instances/pubtabnet.py
@@ -36,7 +36,7 @@
 from ...mapper.cats import cat_to_sub_cat, filter_cat
 from ...mapper.pubstruct import pub_to_image
 from ...utils.detection_types import JsonDict
-from ...utils.logger import log_once, logger
+from ...utils.logger import logger
 from ...utils.settings import CellType, DatasetType, LayoutType, ObjectTypes, TableType, WordType
 from ..base import _BuiltInDataset
 from ..dataflow_builder import DataFlowBaseBuilder
@@ -143,7 +143,7 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
         """
         split = str(kwargs.get("split", "val"))
         if split == "val":
-            log_once("Loading annotations for 'val' split from Pubtabnet will take some time...")
+            logger.info("Loading annotations for 'val' split from Pubtabnet will take some time.")
         max_datapoints = kwargs.get("max_datapoints")
         if max_datapoints is not None:
             max_datapoints = int(max_datapoints)
@@ -153,14 +153,12 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
         fake_score = kwargs.get("fake_score", False)
         dd_pipe_like = kwargs.get("dd_pipe_like", False)
         if dd_pipe_like:
-            logger.info("When 'dd_pipe_like' is set to True will reset 'load_image' to True")
+            logger.info("When 'dd_pipe_like'=True will set 'load_image'=True")
             load_image = True
 
         # Load
         df: DataFlow
-        dataset_split = self.annotation_files["all"]
-        assert isinstance(dataset_split, str)
-        path = self.get_workdir() / dataset_split
+        path = self.get_workdir() / self.get_annotation_file("all")
         df = SerializerJsonlines.load(path, max_datapoints=max_datapoints)
 
         # Map
diff --git a/deepdoctection/datasets/instances/rvlcdip.py b/deepdoctection/datasets/instances/rvlcdip.py
index 3102b1e6..309fc7a7 100644
--- a/deepdoctection/datasets/instances/rvlcdip.py
+++ b/deepdoctection/datasets/instances/rvlcdip.py
@@ -131,9 +131,7 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
 
         # Load
         df: DataFlow
-        dataset_split = self.annotation_files[split]
-        assert isinstance(dataset_split, str)
-        path = self.get_workdir() / dataset_split
+        path = self.get_workdir() / self.get_annotation_file(split)
         df = SerializerTabsepFiles.load(path, max_datapoints)
 
         @curry
@@ -155,7 +153,6 @@ def _map_str_to_image(dp: str, load_img: bool) -> Image:
 
         df = MapData(df, _map_str_to_image(load_image))  # pylint: disable=E1120
 
-        assert self.categories is not None
         if self.categories.is_filtered():
             df = MapData(
                 df,
diff --git a/deepdoctection/datasets/instances/xfund.py b/deepdoctection/datasets/instances/xfund.py
index 4d65a9d3..7ddba0fe 100644
--- a/deepdoctection/datasets/instances/xfund.py
+++ b/deepdoctection/datasets/instances/xfund.py
@@ -180,9 +180,7 @@ def replace_filename(dp: JsonDict) -> JsonDict:
             values_as_dict=True,
             name_as_key=True,
         )[LayoutType.word][WordType.token_tag]
-        df = MapData(
-            df, xfund_to_image(load_image, False, category_names_mapping, ner_token_to_id_mapping)
-        )
+        df = MapData(df, xfund_to_image(load_image, False, category_names_mapping, ner_token_to_id_mapping))
 
         if self.categories.is_cat_to_sub_cat():
             df = MapData(
diff --git a/deepdoctection/datasets/save.py b/deepdoctection/datasets/save.py
index 64dbd2d9..4bcdb626 100644
--- a/deepdoctection/datasets/save.py
+++ b/deepdoctection/datasets/save.py
@@ -80,5 +80,6 @@ def dataflow_to_json(
                 json.dump(dp, file)
 
     else:
-        assert file_name, "if single_files is set to False must pass a valid file name for .jsonl file"
+        if not file_name:
+            raise ValueError("If single_files is set to False must pass a valid file name for .jsonl file")
         SerializerJsonlines.save(df, path, file_name, max_datapoints)
diff --git a/deepdoctection/eval/accmetric.py b/deepdoctection/eval/accmetric.py
index b4594f3d..60b1552e 100644
--- a/deepdoctection/eval/accmetric.py
+++ b/deepdoctection/eval/accmetric.py
@@ -54,9 +54,8 @@
 def _mask_some_gt_and_pr_labels(
     np_label_gt: NDArray[int32], np_label_pr: NDArray[int32], masks: Sequence[int]
 ) -> Tuple[NDArray[int32], NDArray[int32]]:
-    assert len(np_label_gt) == len(
-        masks
-    ), f"length of label_gt ({len(np_label_gt)}) and masks ({len(masks)}) must be equal"
+    if len(np_label_gt) != len(masks):
+        raise ValueError(f"length of label_gt ({len(np_label_gt)}) and masks ({len(masks)}) must be equal")
     np_masks = np.asarray(masks)
     np_masks.astype(bool)
     np_label_gt = np_label_gt[np_masks]
@@ -85,10 +84,10 @@ def accuracy(label_gt: Sequence[int], label_predictions: Sequence[int], masks: O
     """
 
     np_label_gt, np_label_pr = np.asarray(label_gt), np.asarray(label_predictions)
-    assert len(np_label_gt) == len(
-        np_label_pr
-    ), f"length of label_gt ({len(np_label_gt)}) and label_predictions ({len(np_label_pr)}) must be equal"
-
+    if len(np_label_gt) != len(np_label_pr):
+        raise ValueError(
+            f"length of label_gt ({len(np_label_gt)}) and label_predictions" f" ({len(np_label_pr)}) must be equal"
+        )
     if masks is not None:
         np_label_gt, np_label_pr = _mask_some_gt_and_pr_labels(np_label_gt, np_label_pr, masks)
 
@@ -261,8 +260,13 @@ def get_distance(
         results = []
         for key in labels_gt:  # pylint: disable=C0206
             res = cls.metric(labels_gt[key], labels_pr[key])
-            results.append({"key": key.value if isinstance(key, ObjectTypes) else key,
-                            "val": res, "num_samples": len(labels_gt[key])})
+            results.append(
+                {
+                    "key": key.value if isinstance(key, ObjectTypes) else key,
+                    "val": res,
+                    "num_samples": len(labels_gt[key]),
+                }
+            )
 
         cls._results = results
         return results
@@ -327,11 +331,14 @@ def _category_sanity_checks(cls, categories: DatasetCategories) -> None:
 
         if cls._cats:
             for cat in cls._cats:
+                if cat not in cats:
+                    raise ValueError(f"{cat} must be in {cats}")
                 assert cat in cats
 
         if cls._sub_cats:
             for key, val in cls._sub_cats.items():
-                assert set(val) <= set(sub_cats[key])
+                if set(val) > set(sub_cats[key]):
+                    raise ValueError(f"set(val) = {set(val)} must be a sub set of sub_cats[{key}]={sub_cats[key]}")
 
         if cls._cats is None and cls._sub_cats is None and cls._summary_sub_cats is None:
             logger.warning(
@@ -447,10 +454,12 @@ def get_distance(
             number_labels: TypeCounter[int] = Counter(labels_gt[key])
             for label_id, val in enumerate(score, 1):
                 results.append(
-                    {"key": key.value if isinstance(key, ObjectTypes) else key,
-                     "category_id": label_id,
-                     "val": float(val),
-                     "num_samples": number_labels[label_id]}
+                    {
+                        "key": key.value if isinstance(key, ObjectTypes) else key,
+                        "category_id": label_id,
+                        "val": float(val),
+                        "num_samples": number_labels[label_id],
+                    }
                 )
         cls._results = results
         return results
@@ -495,9 +504,13 @@ def get_distance(
         results = []
         for key in labels_gt:  # pylint: disable=C0206
             score = cls.metric(labels_gt[key], labels_pr[key], micro=True)
-            results.append({"key": key.value if isinstance(key, ObjectTypes) else key,
-                            "val": float(score),
-                            "num_samples": len(labels_gt[key])})
+            results.append(
+                {
+                    "key": key.value if isinstance(key, ObjectTypes) else key,
+                    "val": float(score),
+                    "num_samples": len(labels_gt[key]),
+                }
+            )
         cls._results = results
         return results
 
diff --git a/deepdoctection/eval/cocometric.py b/deepdoctection/eval/cocometric.py
index adfdecf2..4b532c68 100644
--- a/deepdoctection/eval/cocometric.py
+++ b/deepdoctection/eval/cocometric.py
@@ -228,10 +228,10 @@ def set_params(
         :param f1_iou: Use with f1_score True and reset the f1 iou threshold
         """
         if max_detections is not None:
-            assert len(max_detections) == 3
+            assert len(max_detections) == 3, max_detections
             cls._params["maxDets"] = max_detections
         if area_range is not None:
-            assert len(area_range) == 4
+            assert len(area_range) == 4, area_range
             cls._params["areaRng"] = area_range
 
         cls._f1_score = f1_score
diff --git a/deepdoctection/eval/eval.py b/deepdoctection/eval/eval.py
index 6468121f..4cf17149 100644
--- a/deepdoctection/eval/eval.py
+++ b/deepdoctection/eval/eval.py
@@ -146,7 +146,6 @@ def run(
         :return: dict with metric results.
         """
 
-        assert self.dataset.dataflow.categories is not None, "dataset requires dataflow.categories to be not None"
         df_gt = self.dataset.dataflow.build(**dataflow_build_kwargs)
         df_pr = self.dataset.dataflow.build(**dataflow_build_kwargs)
 
@@ -173,10 +172,9 @@ def _run_pipe_or_component(self, df_pr: DataFlow) -> DataFlow:
             df_pr_list = self.pipe_component.start()
             return DataFromList(df_pr_list)
         df_pr = MapData(df_pr, maybe_load_image)
-        assert self.pipe
-        df_pr = self.pipe.analyze(dataset_dataflow=df_pr, output="image")
+        df_pr = self.pipe.analyze(dataset_dataflow=df_pr, output="image")  # type: ignore
         # deactivate timer for components
-        for comp in self.pipe.pipe_component_list:
+        for comp in self.pipe.pipe_component_list:  # type: ignore
             comp.timer_on = False
         df_pr = MapData(df_pr, maybe_remove_image)
         df_list = CacheData(df_pr).get_cache()
@@ -185,8 +183,7 @@ def _run_pipe_or_component(self, df_pr: DataFlow) -> DataFlow:
     def _clean_up_predict_dataflow_annotations(self, df_pr: DataFlow) -> DataFlow:
         # will use the first pipe component of MultiThreadPipelineComponent to get meta annotation
         pipe_or_component = self.pipe_component.pipe_components[0] if self.pipe_component is not None else self.pipe
-        assert pipe_or_component
-        meta_anns = pipe_or_component.get_meta_annotation()
+        meta_anns = pipe_or_component.get_meta_annotation()  # type: ignore
         possible_cats_in_datapoint = self.dataset.dataflow.categories.get_categories(as_dict=False, filtered=True)
 
         # clean-up procedure depends on the dataset type
diff --git a/deepdoctection/eval/tedsmetric.py b/deepdoctection/eval/tedsmetric.py
index a7905e45..30cf9192 100644
--- a/deepdoctection/eval/tedsmetric.py
+++ b/deepdoctection/eval/tedsmetric.py
@@ -156,9 +156,13 @@ def evaluate(self, inputs: Tuple[str, str]) -> float:
         parser = etree.XMLParser()
         try:
             ground_truth_tr = etree.XML(ground_truth, parser)
+        except etree.XMLSyntaxError:
+            logger.info("SyntaxError while xml parsing ground truth. Sample will be removed", {"xml_gt": ground_truth})
+            return -1.0
+        try:
             pred_tr = etree.XML(pred, parser)
         except etree.XMLSyntaxError:
-            logger.info("Error while xml parsing. Sample will be removed")
+            logger.info("SyntaxError while xml parsing prediction. Sample will be removed", {"xml_pr": pred})
             return -1.0
 
         etree.strip_tags(pred_tr)
diff --git a/deepdoctection/eval/tp_eval_callback.py b/deepdoctection/eval/tp_eval_callback.py
index 2a744413..8b77bb3f 100644
--- a/deepdoctection/eval/tp_eval_callback.py
+++ b/deepdoctection/eval/tp_eval_callback.py
@@ -62,7 +62,7 @@ def __init__(
         pipeline_component: PredictorPipelineComponent,
         in_names: str,
         out_names: str,
-        **build_eval_kwargs: str
+        **build_eval_kwargs: str,
     ) -> None:
         """
         :param dataset: dataset
@@ -83,7 +83,10 @@ def __init__(
         self.num_gpu = get_num_gpu()
         self.category_names = category_names
         self.sub_categories = sub_categories
-        assert isinstance(pipeline_component.predictor, TPFrcnnDetector)
+        assert isinstance(pipeline_component.predictor, TPFrcnnDetector), (
+            f"pipeline_component.predictor must be of "
+            f"type TPFrcnnDetector but is type {type(pipeline_component.predictor)}"
+        )
         self.cfg = pipeline_component.predictor.model.cfg
         if _use_replicated(self.cfg):
             self.evaluator = Evaluator(dataset, pipeline_component, metric, num_threads=self.num_gpu * 2)
@@ -92,10 +95,15 @@ def __init__(
 
     def _setup_graph(self) -> None:
         if _use_replicated(self.cfg):
-            assert self.evaluator.pipe_component
+            if self.evaluator.pipe_component is None:
+                raise TypeError("self.evaluator.pipe_component cannot be None")
             for idx, comp in enumerate(self.evaluator.pipe_component.pipe_components):
-                assert isinstance(comp, PredictorPipelineComponent)
-                assert isinstance(comp.predictor, TPFrcnnDetector)
+                if not isinstance(comp, PredictorPipelineComponent):
+                    raise TypeError(f"comp must be of type PredictorPipelineComponent but is type {type(comp)}")
+                if not isinstance(comp.predictor, TPFrcnnDetector):
+                    raise TypeError(
+                        f"comp.predictor mus be of type TPFrcnnDetector but is of type {type(comp.predictor)}"
+                    )
                 comp.predictor.tp_predictor = self._build_predictor(idx % self.num_gpu)
 
     def _build_predictor(self, idx: int) -> OnlinePredictor:
@@ -113,7 +121,6 @@ def _before_train(self) -> None:
 
     def _eval(self) -> None:
         scores = self.evaluator.run(True, **self.build_eval_kwargs)
-        assert isinstance(scores, dict)
         for k, val in scores.items():
             self.trainer.monitors.put_scalar(self.dataset_name + "-" + k, val)
 
diff --git a/deepdoctection/extern/base.py b/deepdoctection/extern/base.py
index 590ae667..487d0d53 100644
--- a/deepdoctection/extern/base.py
+++ b/deepdoctection/extern/base.py
@@ -117,10 +117,12 @@ class ObjectDetector(PredictorBase):
 
     @property
     def categories(self) -> Mapping[str, ObjectTypes]:
+        """categories"""
         return self._categories
 
     @categories.setter
     def categories(self, categories: Mapping[str, TypeOrStr]) -> None:
+        """categories setter"""
         self._categories = {key: get_type(value) for key, value in categories.items()}
 
     @abstractmethod
@@ -157,6 +159,7 @@ class PdfMiner(PredictorBase):
 
     @property
     def categories(self) -> Mapping[str, ObjectTypes]:
+        """categories"""
         return self._categories
 
     @categories.setter
@@ -273,10 +276,12 @@ class LMTokenClassifier(PredictorBase):
 
     @property
     def categories(self) -> Mapping[str, ObjectTypes]:
+        """categories"""
         return self._categories
 
     @categories.setter
     def categories(self, categories: Mapping[str, TypeOrStr]) -> None:
+        """categories setter"""
         self._categories = {key: get_type(value) for key, value in categories.items()}
 
     @abstractmethod
@@ -310,10 +315,12 @@ class LMSequenceClassifier(PredictorBase):
 
     @property
     def categories(self) -> Mapping[str, ObjectTypes]:
+        """categories"""
         return self._categories
 
     @categories.setter
     def categories(self, categories: Mapping[str, TypeOrStr]) -> None:
+        """categories setter"""
         self._categories = {key: get_type(value) for key, value in categories.items()}
 
     @abstractmethod
@@ -347,10 +354,12 @@ class LanguageDetector(PredictorBase):
 
     @property
     def categories(self) -> Mapping[str, ObjectTypes]:
+        """categories"""
         return self._categories
 
     @categories.setter
     def categories(self, categories: Mapping[str, TypeOrStr]) -> None:
+        """categories setter"""
         self._categories = {key: get_type(value) for key, value in categories.items()}
 
     @abstractmethod
diff --git a/deepdoctection/extern/d2detect.py b/deepdoctection/extern/d2detect.py
index d4533ac2..867239b4 100644
--- a/deepdoctection/extern/d2detect.py
+++ b/deepdoctection/extern/d2detect.py
@@ -29,7 +29,7 @@
     get_pytorch_requirement,
     pytorch_available,
 )
-from ..utils.settings import ObjectTypes, get_type, TypeOrStr
+from ..utils.settings import ObjectTypes, TypeOrStr, get_type
 from ..utils.transform import InferenceResize
 from .base import DetectionResult, ObjectDetector, PredictorBase
 from .pt.ptutils import set_torch_auto_device
@@ -224,8 +224,8 @@ def _map_category_names(self, detection_results: List[DetectionResult]) -> List[
         """
         for result in detection_results:
             result.class_name = self._categories_d2[str(result.class_id)]
-            assert isinstance(result.class_id, int)
-            result.class_id += 1
+            if isinstance(result.class_id, int):
+                result.class_id += 1
         return detection_results
 
     @classmethod
diff --git a/deepdoctection/extern/hflayoutlm.py b/deepdoctection/extern/hflayoutlm.py
index 40dcfff1..320b94c5 100644
--- a/deepdoctection/extern/hflayoutlm.py
+++ b/deepdoctection/extern/hflayoutlm.py
@@ -33,10 +33,10 @@
     BioTag,
     ObjectTypes,
     TokenClasses,
+    TypeOrStr,
+    get_type,
     token_class_tag_to_token_class_with_tag,
     token_class_with_tag_to_token_class_and_tag,
-    TypeOrStr,
-    get_type
 )
 from .base import LMSequenceClassifier, LMTokenClassifier, SequenceClassResult, TokenClassResult
 from .pt.ptutils import set_torch_auto_device
@@ -164,17 +164,22 @@ def __init__(
         """
 
         if categories is None:
-            assert categories_semantics is not None
-            assert categories_bio is not None
+            if categories_semantics is None:
+                raise ValueError("If categories is None then categories_semantics cannot be None")
+            if categories_bio is None:
+                raise ValueError("If categories is None then categories_bio cannot be None")
 
         self.path_config = path_config_json
         self.path_weights = path_weights
-        self.categories_semantics = [get_type(cat_sem) for cat_sem in categories_semantics] if categories_semantics is not None else []
+        self.categories_semantics = (
+            [get_type(cat_sem) for cat_sem in categories_semantics] if categories_semantics is not None else []
+        )
         self.categories_bio = [get_type(cat_bio) for cat_bio in categories_bio] if categories_bio is not None else []
         if categories:
             self.categories = copy(categories)  # type: ignore
         else:
-            self.categories = self._categories_orig_to_categories(self.categories_semantics, self.categories_bio)  # type: ignore
+            self.categories = self._categories_orig_to_categories(self.categories_semantics,
+                                                                  self.categories_bio)  # type: ignore
 
         config = PretrainedConfig.from_pretrained(pretrained_model_name_or_path=self.path_config)
         self.model = LayoutLMForTokenClassification.from_pretrained(
@@ -210,14 +215,14 @@ def predict(self, **encodings: Union[List[List[str]], "torch.Tensor"]) -> List[T
         boxes = encodings.get("bbox")
         tokens = encodings.get("tokens")
 
-        assert isinstance(ann_ids, list)
+        assert isinstance(ann_ids, list), type(ann_ids)
         if len(ann_ids) > 1:
             raise ValueError("HFLayoutLmTokenClassifier accepts for inference only batch size of 1")
-        assert isinstance(input_ids, torch.Tensor)
-        assert isinstance(attention_mask, torch.Tensor)
-        assert isinstance(token_type_ids, torch.Tensor)
-        assert isinstance(boxes, torch.Tensor)
-        assert isinstance(tokens, list)
+        assert isinstance(input_ids, torch.Tensor), type(input_ids)
+        assert isinstance(attention_mask, torch.Tensor), type(attention_mask)
+        assert isinstance(token_type_ids, torch.Tensor), type(token_type_ids)
+        assert isinstance(boxes, torch.Tensor), type(boxes)
+        assert isinstance(tokens, list), type(tokens)
 
         input_ids = input_ids.to(self.device)
         attention_mask = attention_mask.to(self.device)
@@ -322,10 +327,10 @@ def predict(self, **encodings: Union[List[List[str]], "torch.Tensor"]) -> Sequen
         token_type_ids = encodings.get("token_type_ids")
         boxes = encodings.get("bbox")
 
-        assert isinstance(input_ids, torch.Tensor)
-        assert isinstance(attention_mask, torch.Tensor)
-        assert isinstance(token_type_ids, torch.Tensor)
-        assert isinstance(boxes, torch.Tensor)
+        assert isinstance(input_ids, torch.Tensor), type(input_ids)
+        assert isinstance(attention_mask, torch.Tensor), type(attention_mask)
+        assert isinstance(token_type_ids, torch.Tensor), type(token_type_ids)
+        assert isinstance(boxes, torch.Tensor), type(boxes)
 
         input_ids = input_ids.to(self.device)
         attention_mask = attention_mask.to(self.device)
diff --git a/deepdoctection/extern/model.py b/deepdoctection/extern/model.py
index b4a73475..5a020847 100644
--- a/deepdoctection/extern/model.py
+++ b/deepdoctection/extern/model.py
@@ -519,7 +519,7 @@ def get_full_path_weights(name: str) -> str:
         if profile.name is not None:
             return os.path.join(get_weights_dir_path(), profile.name)
         logger.info(
-            "Model is not registered. Please make sure the weights are available in the weights cache " "directory"
+            "Model is not registered. Please make sure the weights are available in the weights cache directory"
         )
         return os.path.join(get_weights_dir_path(), name)
 
@@ -658,7 +658,8 @@ def maybe_download_weights_and_configs(name: str) -> str:
                 file_names = get_tp_weight_names(name)
             else:
                 hf_model_name = profile.hf_model_name
-                assert isinstance(hf_model_name, str)
+                if hf_model_name is None:
+                    raise ValueError("hf_model_name cannot be None")
                 file_names.append(hf_model_name)
             if profile.hf_repo_id:
                 ModelDownloadManager.load_model_from_hf_hub(profile, absolute_path_weights, file_names)
@@ -684,18 +685,23 @@ def load_model_from_hf_hub(profile: ModelProfile, absolute_path: str, file_names
                            models
         """
         repo_id = profile.hf_repo_id
-        assert repo_id
+        if repo_id is None:
+            raise ValueError("hf_repo_id cannot be None")
         directory, _ = os.path.split(absolute_path)
 
         for expect_size, file_name in zip(profile.size, file_names):
             size = ModelDownloadManager._load_from_hf_hub(repo_id, file_name, directory)
             if expect_size is not None and size != expect_size:
-                logger.error("File downloaded from %s does not match the expected size!", repo_id)
-                logger.error("You may have downloaded a broken file, or the upstream may have modified the file.")
+                logger.error(
+                    "File downloaded from %s does not match the expected size! You may have downloaded"
+                    " a broken file, or the upstream may have modified the file.",
+                    repo_id,
+                )
 
     @staticmethod
     def _load_from_gd(profile: ModelProfile, absolute_path: str, file_names: List[str]) -> None:
-        assert profile.urls is not None
+        if profile.urls is None:
+            raise ValueError("urls cannot be None")
         for size, url, file_name in zip(profile.size, profile.urls, file_names):
             directory, _ = os.path.split(absolute_path)
             download(str(url), directory, file_name, int(size))
@@ -711,10 +717,11 @@ def load_configs_from_hf_hub(profile: ModelProfile, absolute_path: str) -> None:
         """
 
         repo_id = profile.hf_repo_id
-        assert repo_id
+        if repo_id is None:
+            raise ValueError("hf_repo_id cannot be None")
         directory, _ = os.path.split(absolute_path)
-        assert isinstance(profile.hf_config_file, list)
-
+        if not profile.hf_config_file:
+            raise ValueError("hf_config_file cannot be None")
         for file_name in profile.hf_config_file:
             ModelDownloadManager._load_from_hf_hub(repo_id, file_name, directory)
 
diff --git a/deepdoctection/extern/tp/tpfrcnn/config/config.py b/deepdoctection/extern/tp/tpfrcnn/config/config.py
index 64a0a255..e5e5ad79 100644
--- a/deepdoctection/extern/tp/tpfrcnn/config/config.py
+++ b/deepdoctection/extern/tp/tpfrcnn/config/config.py
@@ -347,9 +347,7 @@ def train_frcnn_config(config: AttrDict) -> Tuple[List[Tuple[int, int]], List[Tu
     for idx, steps in enumerate(config.TRAIN.LR_SCHEDULE[:-1]):
         mult = 0.1 ** (idx + 1)
         lr_schedule.append((steps * float(factor) // step_num, config.TRAIN.BASE_LR * float(mult)))
-    logger.info("Warm Up Schedule (steps, value): %s", str(warmup_schedule))
-    logger.info("LR Schedule (epochs, value): %s", str(lr_schedule))
 
     config.freeze()
-    logger.info("Config: ------------------------------------------\n %s", str(config))
+    logger.info("Config: \n %s", str(config), config.to_dict())
     return warmup_schedule, lr_schedule, step_num
diff --git a/deepdoctection/mapper/cocostruct.py b/deepdoctection/mapper/cocostruct.py
index f0afc832..df5c2ba9 100644
--- a/deepdoctection/mapper/cocostruct.py
+++ b/deepdoctection/mapper/cocostruct.py
@@ -119,7 +119,9 @@ def image_to_coco(dp: Image) -> Tuple[JsonDict, List[JsonDict]]:
     :return: A tuple of dicts, the first corresponding to the coco-image object, the second to their coco-annotations
     """
 
-    assert isinstance(dp, Image), f"datapoints must be of type Image, is of type {type(dp)}"
+    if not isinstance(dp, Image):
+        raise TypeError(f"datapoints must be of type Image, is of type {type(dp)}")
+
     img: JsonDict = {}
     anns: List[JsonDict] = []
 
diff --git a/deepdoctection/mapper/d2struct.py b/deepdoctection/mapper/d2struct.py
index dc77448a..e53d821d 100644
--- a/deepdoctection/mapper/d2struct.py
+++ b/deepdoctection/mapper/d2struct.py
@@ -25,7 +25,6 @@
 
 from detectron2.structures import BoxMode
 
-from ..datapoint.box import BoundingBox
 from ..datapoint.image import Image
 from ..mapper.maputils import curry
 from ..utils.detection_types import JsonDict
@@ -64,7 +63,8 @@ def image_to_d2_frcnn_training(dp: Image, add_mask: bool = False) -> Optional[Js
     annotations = []
 
     for ann in anns:
-        assert isinstance(ann.bounding_box, BoundingBox)
+        if ann.bounding_box is None:
+            raise ValueError("BoundingBox cannot be None")
         mapped_ann: Dict[str, Union[str, int, List[float]]] = {
             "bbox_mode": BoxMode.XYXY_ABS,
             "bbox": ann.bounding_box.to_list(mode="xyxy"),
diff --git a/deepdoctection/mapper/laylmstruct.py b/deepdoctection/mapper/laylmstruct.py
index 222603c3..6673248e 100644
--- a/deepdoctection/mapper/laylmstruct.py
+++ b/deepdoctection/mapper/laylmstruct.py
@@ -110,9 +110,11 @@ def image_to_layoutlm(
     all_input_ids = []
     for ann in anns:
         char_cat = ann.get_sub_category(WordType.characters)
-        assert isinstance(char_cat, ContainerAnnotation)
+        if not isinstance(char_cat, ContainerAnnotation):
+            raise TypeError(f"char_cat must be of type ContainerAnnotation but is of type {type(char_cat)}")
         word = char_cat.value
-        assert isinstance(word, str)
+        if not isinstance(word, str):
+            raise ValueError(f"word must be of type str but is of type {type(word)}")
         words.append(word)
         word_tokens = tokenizer.tokenize(word)
         all_input_ids.extend(tokenizer.convert_tokens_to_ids(word_tokens))
@@ -122,7 +124,7 @@ def image_to_layoutlm(
             box = ann.image.get_embedding(dp.image_id)
         else:
             box = ann.bounding_box
-        assert box is not None
+        assert box is not None, box
         if not box.absolute_coords:
             box = box.transform(dp.width, dp.height, absolute_coords=True)
         box = box.to_list(mode="xyxy")
@@ -229,16 +231,18 @@ class is a stand-alone class with no NER_TAG.
     for ann in anns:
         all_ann_ids.append(ann.annotation_id)
         char_cat = ann.get_sub_category(WordType.characters)
-        assert isinstance(char_cat, ContainerAnnotation)
+        if not isinstance(char_cat, ContainerAnnotation):
+            raise TypeError(f"char_cat must be of type ContainerAnnotation but is of type {type(char_cat)}")
         word = char_cat.value
-        assert isinstance(word, str)
+        if not isinstance(word, str):
+            raise ValueError(f"word must be of type str but is of type {type(word)}")
         all_words.append(word)
 
         if ann.image is not None:
             box = ann.image.get_embedding(dp.image_id)
         else:
             box = ann.bounding_box
-        assert box is not None
+        assert box is not None, box
         if not box.absolute_coords:
             box = box.transform(dp.width, dp.height, absolute_coords=True)
         all_boxes.append(box.to_list(mode="xyxy"))
@@ -492,10 +496,10 @@ class LayoutLMDataCollator:
     def __post_init__(self) -> None:
         assert isinstance(self.tokenizer, PreTrainedTokenizerFast), "Tokenizer must be a fast tokenizer"
         if self.return_tensors:
-            assert self.padding not in ("do_not_pad",)
-            assert self.truncation
+            assert self.padding not in ("do_not_pad",), self.padding
+            assert self.truncation, self.truncation
         if self.return_overflowing_tokens:
-            assert self.truncation
+            assert self.truncation, self.truncation
 
     def __call__(self, raw_features: Union[RawLayoutLMFeatures, List[RawLayoutLMFeatures]]) -> LayoutLMFeatures:
         """
diff --git a/deepdoctection/mapper/maputils.py b/deepdoctection/mapper/maputils.py
index 0689582b..72cbb01d 100644
--- a/deepdoctection/mapper/maputils.py
+++ b/deepdoctection/mapper/maputils.py
@@ -20,6 +20,7 @@
 """
 import functools
 import itertools
+import traceback
 from types import TracebackType
 from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Union
 
@@ -27,8 +28,9 @@
 from tabulate import tabulate
 from termcolor import colored
 
+from ..datapoint.box import BoundingBoxError
 from ..utils.detection_types import DP, BaseExceptionType, S, T
-from ..utils.logger import log_once, logger
+from ..utils.logger import logger
 from ..utils.settings import ObjectTypes
 
 __all__ = ["MappingContextManager", "DefaultMapper", "maybe_get_fake_score", "LabelSummarizer", "curry"]
@@ -40,12 +42,17 @@ class MappingContextManager:
     context if an exception has been thrown.
     """
 
-    def __init__(self, dp_name: Optional[str] = None) -> None:
+    def __init__(self, dp_name: Optional[str] = None, filter_level: str = "image",
+                 **kwargs: Dict[str, Optional[str]]) -> None:
         """
         :param dp_name: A name for the datapoint to be mapped
+        :param filter_level: Indicates if the :class:`MappingContextManager` is use on datapoint level,
+                             annotation level etc. Filter level will only be used for logging
         """
         self.dp_name = dp_name if dp_name is not None else ""
+        self.filter_level = filter_level
         self.context_error = True
+        self.kwargs = kwargs
 
     def __enter__(self) -> "MappingContextManager":
         """
@@ -62,11 +69,23 @@ def __exit__(
         """
         context exit
         """
-        if exc_type in (KeyError, ValueError, IndexError, AssertionError, TypeError) and exc_tb is not None:
-            log_once(
-                f"""dp: {self.dp_name}, err: {type(exc_val).__name__},
-            msg: {str(exc_val)} in: {str(exc_tb.tb_frame)} will be filtered"""
-            )
+        if (
+            exc_type in (KeyError, ValueError, IndexError, AssertionError, TypeError, BoundingBoxError)
+            and exc_tb is not None
+        ):
+            frame_summary = traceback.extract_tb(exc_tb)[0]
+            log_dict = {
+                "file_name": self.dp_name,
+                "error_type": type(exc_val).__name__,
+                "error_msg": str(exc_val),
+                "orig_module": frame_summary.filename,
+                "line": frame_summary.lineno,
+            }
+            for key, value in self.kwargs.items():
+                if isinstance(value, dict):
+                    log_dict["type"] = key
+                    log_dict.update(value)
+            logger.warning("MappingContextManager error. Will filter %s", self.filter_level, log_dict)
             return True
         if exc_type is None:
             self.context_error = False
diff --git a/deepdoctection/mapper/misc.py b/deepdoctection/mapper/misc.py
index 3727d640..3491927e 100644
--- a/deepdoctection/mapper/misc.py
+++ b/deepdoctection/mapper/misc.py
@@ -27,7 +27,8 @@
 from ..datapoint.image import Image
 from ..utils.detection_types import JsonDict
 from ..utils.file_utils import lxml_available
-from ..utils.fs import get_load_image_func, is_file_extension, load_image_from_file
+from ..utils.fs import get_load_image_func, load_image_from_file
+from ..utils.utils import is_file_extension
 from .maputils import MappingContextManager, curry
 
 if lxml_available():
diff --git a/deepdoctection/mapper/prodigystruct.py b/deepdoctection/mapper/prodigystruct.py
index a551a2fe..8f41852e 100644
--- a/deepdoctection/mapper/prodigystruct.py
+++ b/deepdoctection/mapper/prodigystruct.py
@@ -126,7 +126,8 @@ def prodigy_to_image(
                     label = span["label"]
             else:
                 label = span["label"]
-            assert isinstance(label, str)
+            if not isinstance(label, str):
+                raise ValueError("label could not assigned to be a string")
 
             annotation = ImageAnnotation(
                 category_name=label,
@@ -161,7 +162,8 @@ def image_to_prodigy(dp: Image) -> JsonDict:
 
     spans = []
     for ann in dp.get_annotation_iter():
-        assert isinstance(ann.bounding_box, BoundingBox)
+        if ann.bounding_box is None:
+            raise ValueError("ann.bounding_box cannot be None")
         box: JsonDict = {"label": ann.category_name, "annotation_id": ann.annotation_id}
         if ann.score is not None:
             box["score"] = float(round(ann.score, 3))
diff --git a/deepdoctection/mapper/pubstruct.py b/deepdoctection/mapper/pubstruct.py
index 72a62af8..de62dfcf 100644
--- a/deepdoctection/mapper/pubstruct.py
+++ b/deepdoctection/mapper/pubstruct.py
@@ -29,8 +29,9 @@
 from ..datapoint.convert import convert_pdf_bytes_to_np_array_v2
 from ..datapoint.image import Image
 from ..utils.detection_types import JsonDict
-from ..utils.fs import is_file_extension, load_bytes_from_pdf_file, load_image_from_file
+from ..utils.fs import load_bytes_from_pdf_file, load_image_from_file
 from ..utils.settings import CellType, LayoutType, Relationships, TableType, WordType
+from ..utils.utils import is_file_extension
 from .maputils import MappingContextManager, curry, maybe_get_fake_score
 
 __all__ = ["pub_to_image"]
diff --git a/deepdoctection/mapper/tpstruct.py b/deepdoctection/mapper/tpstruct.py
index bf5a1608..2ec8706e 100644
--- a/deepdoctection/mapper/tpstruct.py
+++ b/deepdoctection/mapper/tpstruct.py
@@ -50,7 +50,8 @@ def image_to_tp_frcnn_training(dp: Image, add_mask: bool = False) -> Optional[Js
         return None
 
     for ann in anns:
-        assert ann.bounding_box is not None
+        if ann.bounding_box is None:
+            raise ValueError("ann.bounding_box cannot be None")
         all_boxes.append(ann.bounding_box.to_list(mode="xyxy"))
         all_categories.append(ann.category_id)
 
diff --git a/deepdoctection/mapper/xfundstruct.py b/deepdoctection/mapper/xfundstruct.py
index 4ff4f373..e1f0c6f0 100644
--- a/deepdoctection/mapper/xfundstruct.py
+++ b/deepdoctection/mapper/xfundstruct.py
@@ -86,11 +86,11 @@ def xfund_to_image(
         entity_id_to_entity_link_id = defaultdict(list)
         ann_id_to_entity_id = {}
 
-        entities = dp.get("document")
+        entities = dp.get("document", [])
 
         if entities is None:
-            entities = dp.get("form")
-        assert isinstance(entities, list)
+            entities = dp.get("form", [])
+
         for entity in entities:
             words = entity.get("words")
 
diff --git a/deepdoctection/pipe/anngen.py b/deepdoctection/pipe/anngen.py
index f16fafe1..90e447d1 100644
--- a/deepdoctection/pipe/anngen.py
+++ b/deepdoctection/pipe/anngen.py
@@ -18,6 +18,7 @@
 """
 Module for datapoint populating helpers
 """
+from dataclasses import asdict
 from typing import Dict, List, Mapping, Optional, Union
 
 import numpy as np
@@ -57,7 +58,7 @@ def datapoint(self) -> Image:
         """
         if self._datapoint is not None:
             return self._datapoint
-        raise AssertionError("no datapoint passed")
+        raise ValueError("no datapoint passed")
 
     @datapoint.setter
     def datapoint(self, dp: Image) -> None:
@@ -116,10 +117,15 @@ def set_image_annotation(
         :return: the annotation_id of the generated image annotation
         """
         self.assert_datapoint_passed()
-        assert detect_result.class_id
-        assert isinstance(detect_result.box, (list, np.ndarray))
+        assert detect_result.class_id, detect_result.class_id
+        if not isinstance(detect_result.box, (list, np.ndarray)):
+            raise TypeError(
+                f"detect_result.box must be of type list or np.ndarray, " f"but is of type {(type(detect_result.box))}"
+            )
         detect_result.class_id = self.maybe_map_category_id(detect_result.class_id)
-        with MappingContextManager(dp_name=str(detect_result)) as annotation_context:
+        with MappingContextManager(
+            dp_name=self.datapoint.file_name, filter_level="annotation", detect_result=asdict(detect_result)
+        ) as annotation_context:
             box = BoundingBox(
                 ulx=detect_result.box[0],
                 uly=detect_result.box[1],
@@ -143,13 +149,13 @@ def set_image_annotation(
             )
             if to_annotation_id is not None:
                 parent_ann = self._cache_anns[to_annotation_id]
-                assert parent_ann.image
+                assert parent_ann.image, parent_ann.image
                 parent_ann.image.dump(ann)
                 parent_ann.image.image_ann_to_image(ann.annotation_id)
                 ann_global_box = local_to_global_coords(
                     ann.bounding_box, parent_ann.image.get_embedding(self.datapoint.image_id)  # type: ignore
                 )
-                assert ann.image
+                assert ann.image, ann.image
                 ann.image.set_embedding(parent_ann.annotation_id, ann.bounding_box)
                 ann.image.set_embedding(self.datapoint.image_id, ann_global_box)
                 parent_ann.dump_relationship(Relationships.child, ann.annotation_id)
@@ -171,7 +177,7 @@ def set_category_annotation(
         sub_cat_key: ObjectTypes,
         annotation_id: str,
         score: Optional[float] = None,
-    ) -> str:
+    ) -> Optional[str]:
         """
         Create a category annotation and dump it as sub category to an already created annotation.
 
@@ -183,8 +189,19 @@ def set_category_annotation(
         :return: the annotation_id of the generated category annotation
         """
         self.assert_datapoint_passed()
-        cat_ann = CategoryAnnotation(category_name=category_name, category_id=str(category_id), score=score)
-        self._cache_anns[annotation_id].dump_sub_category(sub_cat_key, cat_ann)
+        with MappingContextManager(
+            dp_name=self.datapoint.file_name,
+            filter_level="annotation",
+            category_annotation={
+                "category_name": category_name.value,
+                "sub_cat_key": sub_cat_key.value,
+                "annotation_id": annotation_id,
+            },
+        ) as annotation_context:
+            cat_ann = CategoryAnnotation(category_name=category_name, category_id=str(category_id), score=score)
+            self._cache_anns[annotation_id].dump_sub_category(sub_cat_key, cat_ann)
+        if annotation_context.context_error:
+            return None
         return cat_ann.annotation_id
 
     def set_container_annotation(
@@ -195,7 +212,7 @@ def set_container_annotation(
         annotation_id: str,
         value: Union[str, List[str]],
         score: Optional[float] = None,
-    ) -> str:
+    ) -> Optional[str]:
         """
         Create a container annotation and dump it as sub category to an already created annotation.
 
@@ -208,10 +225,22 @@ def set_container_annotation(
         :return: annotation_id of the generated container annotation
         """
         self.assert_datapoint_passed()
-        cont_ann = ContainerAnnotation(
-            category_name=category_name, category_id=str(category_id), value=value, score=score
-        )
-        self._cache_anns[annotation_id].dump_sub_category(sub_cat_key, cont_ann)
+        with MappingContextManager(
+            dp_name=self.datapoint.file_name,
+            filter_level="annotation",
+            container_annotation={
+                "category_name": category_name.value,
+                "sub_cat_key": sub_cat_key.value,
+                "annotation_id": annotation_id,
+                "value": str(value),
+            },
+        ) as annotation_context:
+            cont_ann = ContainerAnnotation(
+                category_name=category_name, category_id=str(category_id), value=value, score=score
+            )
+            self._cache_anns[annotation_id].dump_sub_category(sub_cat_key, cont_ann)
+        if annotation_context.context_error:
+            return None
         return cont_ann.annotation_id
 
     def set_summary_annotation(
@@ -222,7 +251,7 @@ def set_summary_annotation(
         summary_value: Optional[str] = None,
         summary_score: Optional[float] = None,
         annotation_id: Optional[str] = None,
-    ) -> str:
+    ) -> Optional[str]:
         """
         Creates a sub category of a summary annotation. If a summary of the given annotation_id does not exist, it will
         create a new one.
@@ -240,16 +269,34 @@ def set_summary_annotation(
             image = self._cache_anns[annotation_id].image
         else:
             image = self.datapoint
-        assert image is not None
+        assert image is not None, image
         if image.summary is None:
             image.summary = SummaryAnnotation()
 
         ann: Union[CategoryAnnotation, ContainerAnnotation]
-        if summary_value:
-            ann = ContainerAnnotation(
-                category_name=summary_name, category_id=str(summary_number), value=summary_value, score=summary_score
-            )
-        else:
-            ann = CategoryAnnotation(category_name=summary_name, category_id=str(summary_number), score=summary_score)
-        image.summary.dump_sub_category(summary_key, ann, image.image_id)
+        with MappingContextManager(
+            dp_name=annotation_id,
+            filter_level="annotation",
+            summary_annotation={
+                "summary_key": summary_key.value,
+                "summary_name": summary_name.value,
+                "summary_value": summary_value,
+                "annotation_id": annotation_id,
+            },
+        ) as annotation_context:
+            if summary_value:
+                ann = ContainerAnnotation(
+                    category_name=summary_name,
+                    category_id=str(summary_number),
+                    value=summary_value,
+                    score=summary_score,
+                )
+            else:
+                ann = CategoryAnnotation(
+                    category_name=summary_name, category_id=str(summary_number), score=summary_score
+                )
+            image.summary.dump_sub_category(summary_key, ann, image.image_id)
+
+        if annotation_context.context_error:
+            return None
         return ann.annotation_id
diff --git a/deepdoctection/pipe/base.py b/deepdoctection/pipe/base.py
index 8ec92db5..0c1c8046 100644
--- a/deepdoctection/pipe/base.py
+++ b/deepdoctection/pipe/base.py
@@ -152,7 +152,8 @@ def __init__(
 
     def clone(self) -> "PredictorPipelineComponent":
         predictor = self.predictor.clone()
-        assert isinstance(predictor, (ObjectDetector, PdfMiner))
+        if not isinstance(predictor, (ObjectDetector, PdfMiner)):
+            raise ValueError(f"predictor must be of type ObjectDetector or PdfMiner, but is of type {type(predictor)}")
         return self.__class__(predictor, copy(self.dp_manager.category_id_mapping))
 
 
diff --git a/deepdoctection/pipe/cell.py b/deepdoctection/pipe/cell.py
index e9d1f2d7..b0e4bff2 100644
--- a/deepdoctection/pipe/cell.py
+++ b/deepdoctection/pipe/cell.py
@@ -143,9 +143,8 @@ def __init__(
         self.sub_image_name = sub_image_names
         self.dummy_generator_cls = None
         if add_dummy_detection:
-            assert category_id_mapping is not None, (
-                "Using DetectResult dummy generator requires passing a " "category_id_mapping"
-            )
+            if category_id_mapping is None:
+                raise ValueError("Using DetectResult dummy generator requires passing a category_id_mapping")
             self.dummy_generator_cls = DetectResultGenerator
             group_categories_dict = defaultdict(list)
             for group in category_id_mapping.items():
@@ -163,7 +162,8 @@ def serve(self, dp: Image) -> None:
         """
         sub_image_anns = dp.get_annotation_iter(category_names=self.sub_image_name)
         for sub_image_ann in sub_image_anns:
-            assert sub_image_ann.image is not None
+            if sub_image_ann.image is None:
+                raise ValueError("sub_image_ann.image is None, but must be an image")
             detect_result_list = self.predictor.predict(sub_image_ann.image.image)
             if self.has_dummy_generator():
                 if hasattr(self.predictor, "categories"):
diff --git a/deepdoctection/pipe/common.py b/deepdoctection/pipe/common.py
index 30f48293..40cdb486 100644
--- a/deepdoctection/pipe/common.py
+++ b/deepdoctection/pipe/common.py
@@ -19,7 +19,7 @@
 Module for common pipeline components
 """
 
-from typing import List, Optional, Union, Sequence
+from typing import List, Optional, Sequence, Union
 
 import numpy as np
 
@@ -29,7 +29,7 @@
 from ..mapper.maputils import MappingContextManager
 from ..mapper.match import match_anns_by_intersection
 from ..utils.detection_types import JsonDict
-from ..utils.settings import LayoutType, Relationships, TypeOrStr, get_type, ObjectTypes
+from ..utils.settings import LayoutType, ObjectTypes, Relationships, TypeOrStr, get_type
 from .base import PipelineComponent
 from .registry import pipeline_component_registry
 
@@ -209,9 +209,10 @@ def predict_dataflow(self, df: DataFlow) -> DataFlow:
         return MapData(df, self.pass_datapoint)
 
     def _init_sanity_checks(self) -> None:
-        assert self._text_container in [LayoutType.word, LayoutType.line], (
-            f"text_container must be either {LayoutType.word} or " f"{LayoutType.line}"
-        )
+        assert self._text_container in [
+            LayoutType.word,
+            LayoutType.line,
+        ], f"text_container must be either {LayoutType.word} or {LayoutType.line}"
         assert set(self._floating_text_block_names) <= set(
             self._text_block_names
         ), "floating_text_block_names must be a subset of text_block_names"
diff --git a/deepdoctection/pipe/doctectionpipe.py b/deepdoctection/pipe/doctectionpipe.py
index 0852e505..de18abf4 100644
--- a/deepdoctection/pipe/doctectionpipe.py
+++ b/deepdoctection/pipe/doctectionpipe.py
@@ -65,16 +65,18 @@ def __init__(self, pipeline_component_list: List[PipelineComponent]):
 
     def _entry(self, **kwargs: Union[str, DataFlow, bool, int, Pathlike, Union[str, List[str]]]) -> DataFlow:
         dataset_dataflow = kwargs.get("dataset_dataflow")
-
         path = kwargs.get("path")
-        assert path is not None or dataset_dataflow is not None, "pass either path or dataset_dataflow as argument"
+        if path is None and dataset_dataflow is None:
+            raise ValueError("Pass either path or dataset_dataflow as argument")
 
         shuffle = kwargs.get("shuffle", False)
-        assert isinstance(shuffle, bool)
+        if not isinstance(shuffle, bool):
+            raise TypeError(f"shuffle must be of type bool but is of type {type(shuffle)}")
 
         doc_path = None
         if path:
-            assert isinstance(path, (str, Path))
+            if not isinstance(path, (str, Path)):
+                raise TypeError("path must be of type PathOrStr")
             path_type = maybe_path_or_pdf(path)
             if path_type == 2:
                 doc_path = path
@@ -85,11 +87,13 @@ def _entry(self, **kwargs: Union[str, DataFlow, bool, int, Pathlike, Union[str,
         file_type = kwargs.get("file_type", [".jpg", ".png", ".tif"])
 
         max_datapoints = kwargs.get("max_datapoints")
+        if not isinstance(max_datapoints, (int, type(None))):
+            raise TypeError(f"max_datapoints must be of type int, but is of type {type(max_datapoints)}")
         df: DataFlow
-        assert isinstance(max_datapoints, (int, type(None)))
 
         if isinstance(path, (str, Path)):
-            assert isinstance(file_type, (str, list))
+            if not isinstance(file_type, (str, list)):
+                raise TypeError(f"file_type must be of type string or list, but is of type {type(file_type)}")
             df = DoctectionPipe.path_to_dataflow(path, file_type, shuffle=shuffle)
         if isinstance(doc_path, (str, Path)):
             df = DoctectionPipe.doc_to_dataflow(
@@ -99,7 +103,11 @@ def _entry(self, **kwargs: Union[str, DataFlow, bool, int, Pathlike, Union[str,
             df = dataset_dataflow
 
         def _proto_process(dp: Image) -> Image:
-            logger.info("processing %s", dp.file_name)
+            if path is None:
+                path_tmp = doc_path
+            else:
+                path_tmp = path  # type: ignore
+            logger.info("Processing %s", dp.file_name, {"path": path_tmp, "df": path_tmp, "file_name": dp.file_name})
             return dp
 
         df = MapData(df, _proto_process)
@@ -122,7 +130,8 @@ def path_to_dataflow(
         :param shuffle: Shuffle file names in order to stream them randomly
         :return: dataflow
         """
-        assert os.path.isdir(path), f"{path} not a directory"
+        if not os.path.isdir(path):
+            raise NotADirectoryError(f"{path} not a directory")
         df = SerializerFiles.load(path, file_type, max_datapoints, shuffle)
 
         def _to_image(dp: str) -> Optional[Image]:
@@ -142,7 +151,9 @@ def doc_to_dataflow(path: Pathlike, max_datapoints: Optional[int] = None) -> Dat
         :param max_datapoints: max number of datapoints to consider
         :return: dataflow
         """
-        assert os.path.isfile(path), f"{path} not a file"
+        if not os.path.isfile(path):
+            raise FileExistsError(f"{path} not a file")
+
         df = SerializerPdfDoc.load(path, max_datapoints=max_datapoints)
 
         @curry
@@ -174,7 +185,7 @@ def analyze(self, **kwargs: Union[str, DataFlow, bool, int, Pathlike, Union[str,
         """
 
         output = kwargs.get("output", "page")
-        assert output in ["page", "image", "dict"], "output must be either page image or dict"
+        assert output in ("page", "image", "dict"), "output must be either page image or dict"
         df = self._entry(**kwargs)
         df = self._build_pipe(df)
         if output == "page":
diff --git a/deepdoctection/pipe/language.py b/deepdoctection/pipe/language.py
index d71df5bd..8ef6285d 100644
--- a/deepdoctection/pipe/language.py
+++ b/deepdoctection/pipe/language.py
@@ -89,7 +89,8 @@ def serve(self, dp: Image) -> None:
             page = Page.from_image(dp, self._text_container, self._floating_text_block_names, self._text_block_names)
             text = page.get_text()
         else:
-            assert dp.image is not None
+            if dp.image is None:
+                raise ValueError("dp.image cannot be None")
             detect_result_list = self.text_detector.predict(dp.image)
             # this is a concatenation of all detection result. No reading order
             text = " ".join([result.text for result in detect_result_list if result.text is not None])
diff --git a/deepdoctection/pipe/refine.py b/deepdoctection/pipe/refine.py
index 8fc45018..5461a176 100644
--- a/deepdoctection/pipe/refine.py
+++ b/deepdoctection/pipe/refine.py
@@ -64,7 +64,7 @@ def tiles_to_cells(dp: Image, table: ImageAnnotation) -> List[Tuple[Tuple[int, i
         cs = int(cell.get_sub_category(CellType.column_span).category_id)
         for k in range(rs):
             for l in range(cs):
-                assert cell.annotation_id is not None
+                assert cell.annotation_id is not None, cell.annotation_id
                 tile_to_cells.append(((row_number + k, col_number + l), cell.annotation_id))
 
     return tile_to_cells
@@ -300,7 +300,8 @@ def generate_html_string(table: ImageAnnotation) -> List[str]:
     :param table: An annotation that has a not None image and fully segmented cell annotation.
     :return: HTML representation of the table
     """
-    assert table.image is not None
+    if table.image is None:
+        raise ValueError("table.image cannot be None")
     table_image = table.image
     cells = table_image.get_annotation(category_names=[LayoutType.cell, CellType.header, CellType.body])
     number_of_rows = int(table_image.summary.get_sub_category(TableType.number_of_rows).category_id)
@@ -395,7 +396,8 @@ def __init__(self) -> None:
     def serve(self, dp: Image) -> None:
         tables = dp.get_annotation(category_names=self._table_name)
         for table in tables:
-            assert table.image is not None
+            if table.image is None:
+                raise ValueError("table.image cannot be None")
             tiles_to_cells_list = tiles_to_cells(dp, table)
             connected_components, tile_to_cell_dict = connected_component_tiles(tiles_to_cells_list)
             rectangle_tiling = generate_rectangle_tiling(connected_components)
diff --git a/deepdoctection/pipe/segment.py b/deepdoctection/pipe/segment.py
index 9773f0fe..13a6baff 100644
--- a/deepdoctection/pipe/segment.py
+++ b/deepdoctection/pipe/segment.py
@@ -136,11 +136,13 @@ def stretch_item_per_table(
     item_ann_ids = table.get_relationship(Relationships.child)
 
     rows = dp.get_annotation(category_names=row_name, annotation_ids=item_ann_ids)
-    assert isinstance(table.image, Image)
+    if table.image is None:
+        raise ValueError("table.image cannot be None")
     table_embedding_box = table.image.get_embedding(dp.image_id)
 
     for row in rows:
-        assert isinstance(row, ImageAnnotation) and isinstance(row.image, Image)
+        if row.image is None:
+            raise ValueError("row.image cannot be None")
         row_embedding_box = row.image.get_embedding(dp.image_id)
         row_embedding_box.ulx = table_embedding_box.ulx + 1.0
         row_embedding_box.lrx = table_embedding_box.lrx - 1.0
@@ -150,7 +152,8 @@ def stretch_item_per_table(
     cols = dp.get_annotation(category_names=col_name, annotation_ids=item_ann_ids)
 
     for col in cols:
-        assert isinstance(col, ImageAnnotation) and isinstance(col.image, Image)
+        if col.image is None:
+            raise ValueError("row.image cannot be None")
         col_embedding_box = col.image.get_embedding(dp.image_id)
         col_embedding_box.uly = table_embedding_box.uly + 1.0
         col_embedding_box.lry = table_embedding_box.lry - 1.0
@@ -177,13 +180,15 @@ def tile_tables_with_items_per_table(dp: Image, table: ImageAnnotation, item_nam
     items = dp.get_annotation(category_names=item_name, annotation_ids=item_ann_ids)
     items.sort(key=lambda x: x.bounding_box.cx if item_name == LayoutType.column else x.bounding_box.cy)  # type: ignore
 
-    assert isinstance(table.image, Image)
+    if table.image is None:
+        raise ValueError("table.image cannot be None")
     table_embedding_box = table.image.get_embedding(dp.image_id)
 
     tmp_item_xy = table_embedding_box.uly + 1.0 if item_name == LayoutType.row else table_embedding_box.ulx + 1.0
     for item in items:
         with MappingContextManager(dp_name=dp.file_name):
-            assert isinstance(item, ImageAnnotation) and isinstance(item.image, Image)
+            if item.image is None:
+                raise ValueError("item.image cannot be None")
             item_embedding_box = item.image.get_embedding(dp.image_id)
             new_embedding_box = BoundingBox(
                 ulx=item_embedding_box.ulx if item_name == LayoutType.row else tmp_item_xy,
@@ -387,7 +392,7 @@ def __init__(
         :param remove_iou_threshold_rows: iou threshold for removing overlapping rows
         :param remove_iou_threshold_cols: iou threshold for removing overlapping columns
         """
-        assert segment_rule in ["iou", "ioa"], "segment rule must be either iou or ioa"
+        assert segment_rule in ("iou", "ioa"), "segment_rule must be either iou or ioa"
 
         self.segment_rule = segment_rule
         self.threshold_rows = threshold_rows
diff --git a/deepdoctection/pipe/text.py b/deepdoctection/pipe/text.py
index abac3a2a..3dfa46bd 100644
--- a/deepdoctection/pipe/text.py
+++ b/deepdoctection/pipe/text.py
@@ -27,7 +27,7 @@
 from ..extern.base import ObjectDetector, PdfMiner, TextRecognizer
 from ..utils.detection_types import ImageType, JsonDict
 from ..utils.logger import logger
-from ..utils.settings import LayoutType, Relationships, WordType, get_type, TypeOrStr
+from ..utils.settings import LayoutType, Relationships, TypeOrStr, WordType, get_type
 from .base import PipelineComponent, PredictorPipelineComponent
 from .registry import pipeline_component_registry
 
@@ -80,13 +80,15 @@ def __init__(
 
         if extract_from_roi is None:
             extract_from_roi = []
-        self.extract_from_category = [get_type(extract_from_roi)] if isinstance(extract_from_roi, str) else \
-            [get_type(roi_category) for roi_category in extract_from_roi]
+        self.extract_from_category = (
+            [get_type(extract_from_roi)]
+            if isinstance(extract_from_roi, str)
+            else [get_type(roi_category) for roi_category in extract_from_roi]
+        )
         super().__init__(text_extract_detector, category_id_mapping)
         if self.extract_from_category:
-            assert isinstance(
-                self.predictor, (ObjectDetector, TextRecognizer)
-            ), "Predicting from a cropped image requires to pass an ObjectDetector or TextRecognizer."
+            if not isinstance(self.predictor, (ObjectDetector, TextRecognizer)):
+                raise TypeError("Predicting from a cropped image requires to pass an ObjectDetector or TextRecognizer.")
 
     def serve(self, dp: Image) -> None:
         maybe_batched_text_rois = self.get_text_rois(dp)
@@ -95,7 +97,8 @@ def serve(self, dp: Image) -> None:
             if isinstance(text_roi, ImageAnnotation):
                 ann_id = text_roi.annotation_id
             predictor_input = self.get_predictor_input(text_roi)
-            assert predictor_input is not None
+            if predictor_input is None:
+                raise ValueError("predictor_input cannot be None")
             width, height = None, None
             detect_result_list = self.predictor.predict(predictor_input)  # type: ignore
             if isinstance(self.predictor, PdfMiner):
@@ -151,11 +154,14 @@ def get_predictor_input(
         """
 
         if isinstance(text_roi, ImageAnnotation):
-            assert text_roi.image is not None
-            assert text_roi.image.image is not None
+            if text_roi.image is None:
+                raise ValueError("text_roi.image cannot be None")
+            if text_roi.image.image is None:
+                raise ValueError("text_roi.image.image cannot be None")
             return text_roi.image.image
         if isinstance(self.predictor, ObjectDetector):
-            assert isinstance(text_roi, Image)
+            if not isinstance(text_roi, Image):
+                raise ValueError("text_roi must be an image")
             return text_roi.image
         if isinstance(text_roi, list):
             assert all(roi.image is not None for roi in text_roi)
@@ -167,7 +173,11 @@ def get_meta_annotation(self) -> JsonDict:
         if self.extract_from_category:
             sub_cat_dict = {category: {WordType.characters} for category in self.extract_from_category}
         else:
-            assert isinstance(self.predictor, (ObjectDetector, PdfMiner))
+            if not isinstance(self.predictor, (ObjectDetector, PdfMiner)):
+                raise TypeError(
+                    f"self.predictor must be of type ObjectDetector or PdfMiner but is of type "
+                    f"{type(self.predictor)}"
+                )
             sub_cat_dict = {category: {WordType.characters} for category in self.predictor.possible_categories()}
         return dict(
             [
diff --git a/deepdoctection/train/d2_frcnn_train.py b/deepdoctection/train/d2_frcnn_train.py
index d0450579..03c96c24 100644
--- a/deepdoctection/train/d2_frcnn_train.py
+++ b/deepdoctection/train/d2_frcnn_train.py
@@ -206,7 +206,7 @@ def train_d2_faster_rcnn(
     augment_list = [ResizeShortestEdge(cfg.INPUT.MIN_SIZE_TRAIN, cfg.INPUT.MAX_SIZE_TRAIN), RandomFlip()]
     mapper = DatasetMapper(is_train=True, augmentations=augment_list, image_format="BGR")
 
-    logger.info("Config: ------------------------------------------\n %s", str(cfg))
+    logger.info("Config: \n %s", str(cfg), cfg)
     trainer = D2Trainer(cfg, dataset, mapper)
     trainer.resume_or_load()
 
diff --git a/deepdoctection/train/hf_layoutlm_train.py b/deepdoctection/train/hf_layoutlm_train.py
index 98df70b8..63152b56 100644
--- a/deepdoctection/train/hf_layoutlm_train.py
+++ b/deepdoctection/train/hf_layoutlm_train.py
@@ -298,10 +298,7 @@ def train_hf_layoutlm(
         )
 
     arguments = TrainingArguments(**conf_dict)
-    logger.info(
-        "Config: ------------------------------------------\n %s",
-        pprint.pformat(arguments.to_dict(), width=100, compact=True),
-    )
+    logger.info("Config: \n %s", str(arguments.to_dict()), arguments.to_dict())
 
     model_cls, tokenizer_fast = _get_model_class_and_tokenizer(path_config_json, dataset_type)
 
diff --git a/deepdoctection/utils/__init__.py b/deepdoctection/utils/__init__.py
index be21e364..2fcda498 100644
--- a/deepdoctection/utils/__init__.py
+++ b/deepdoctection/utils/__init__.py
@@ -49,13 +49,9 @@ def _global_import(
 _global_import("file_utils", suffix=("_available", "_requirement"))
 _global_import("metacfg", prefix=("set_config_by_yaml", "save_config_to_yaml", "config_to_cli_str"))
 _global_import("utils", prefix=("delete_keys_from_dict", "split_string", "string_to_dict"))
-_global_import("settings", suffix=("Type",
-                                   "TokenClasses",
-                                   "BioTag",
-                                   "TokenClassWithTag",
-                                   "Relationships",
-                                   "Languages",
-                                   "get_type"))
+_global_import(
+    "settings", suffix=("Type", "TokenClasses", "BioTag", "TokenClassWithTag", "Relationships", "Languages", "get_type")
+)
 
 # pylint: disable=undefined-variable
 __all__.extend(context.__all__)  # type: ignore
diff --git a/deepdoctection/utils/file_utils.py b/deepdoctection/utils/file_utils.py
index 79c11a69..5af1ab8d 100644
--- a/deepdoctection/utils/file_utils.py
+++ b/deepdoctection/utils/file_utils.py
@@ -306,7 +306,7 @@ def get_tesseract_requirement() -> Requirement:
 # Poppler utils or resp. pdftoppm and pdftocairo for Linux platforms
 _PDF_TO_PPM_AVAILABLE = which("pdftoppm") is not None
 _PDF_TO_CAIRO_AVAILABLE = which("pdftocairo") is not None
-_POPPLER_ERR_MSG = "Poppler is not found. Please check that Poppler is installed and it is added to your path"
+_POPPLER_ERR_MSG = "Poppler cannot be found. Please check that Poppler is installed and it is added to your path"
 
 
 def pdf_to_ppm_available() -> bool:
diff --git a/deepdoctection/utils/fs.py b/deepdoctection/utils/fs.py
index 498d7737..ce77ce5b 100644
--- a/deepdoctection/utils/fs.py
+++ b/deepdoctection/utils/fs.py
@@ -25,15 +25,16 @@
 from base64 import b64encode
 from io import BytesIO
 from pathlib import Path
-from typing import Callable, Literal, Optional, Protocol, Sequence, Union, overload
+from typing import Callable, Literal, Optional, Protocol, Union, overload
 from urllib.request import urlretrieve
 
 from cv2 import IMREAD_COLOR, imread
 
-from ..utils.pdf_utils import get_pdf_file_reader, get_pdf_file_writer
 from .detection_types import ImageType, JsonDict, Pathlike
 from .logger import logger
+from .pdf_utils import get_pdf_file_reader, get_pdf_file_writer
 from .tqdm import get_tqdm
+from .utils import FileExtensionError, is_file_extension
 
 __all__ = [
     "load_image_from_file",
@@ -44,6 +45,7 @@
     "mkdir_p",
     "is_file_extension",
     "load_json",
+    "FileExtensionError",
 ]
 
 
@@ -124,19 +126,6 @@ def inner(byte, b_size, t_size=None):  # type: ignore
     return f_path
 
 
-def is_file_extension(file_name: Pathlike, extension: Union[str, Sequence[str]]) -> bool:
-    """
-    Check if a given file name has a given extension
-
-    :param file_name: the file name, either full along with path or as stand alone
-    :param extension: the extension of the file. Must add a dot (.)
-    :return: True/False
-    """
-    if isinstance(extension, str):
-        return os.path.splitext(file_name)[-1].lower() == extension
-    return os.path.splitext(file_name)[-1].lower() in extension
-
-
 @overload
 def load_image_from_file(path: Pathlike, type_id: Literal["np"] = "np") -> Optional[ImageType]:
     ...
diff --git a/deepdoctection/utils/logger.py b/deepdoctection/utils/logger.py
index 40d16813..a5e559ca 100644
--- a/deepdoctection/utils/logger.py
+++ b/deepdoctection/utils/logger.py
@@ -24,13 +24,15 @@
 
 import errno
 import functools
+import json
 import logging
+import logging.config
 import os
 import shutil
 import sys
 from datetime import datetime
 from pathlib import Path
-from typing import Optional
+from typing import Any, Dict, Optional, no_type_check
 
 from termcolor import colored
 
@@ -39,33 +41,83 @@
 __all__ = ["logger", "set_logger_dir", "auto_set_dir", "get_logger_dir"]
 
 
-class _Formatter(logging.Formatter):
+class CustomFilter(logging.Filter):
+    """A custom filter"""
+
+    def filter(self, record: logging.LogRecord) -> bool:
+        log_dict = {}
+        args = record.args
+        str_args = []
+        if args:
+            for arg in args:
+                if isinstance(arg, dict):
+                    log_dict.update(arg)
+                else:
+                    str_args.append(arg)
+        record.args = tuple(str_args)
+        if not hasattr(record, "log_dict"):
+            setattr(record, "log_dict", log_dict)
+        return True
+
+
+class StreamFormatter(logging.Formatter):
+    """A custom formatter to produce unified LogRecords"""
+
+    @no_type_check
     def format(self, record: logging.LogRecord) -> str:
         date = colored("[%(asctime)s @%(filename)s:%(lineno)d]", "green")
-        msg = "%(message)s"
+        msg = colored("%(message)s", "white")
+
         if record.levelno == logging.WARNING:
-            fmt = date + " " + colored("WRN", "magenta", attrs=["blink"]) + " " + msg
+            fmt = f"{date}  {colored('WRN', 'magenta', attrs=['blink'])}  {msg}"
         elif record.levelno == logging.ERROR or record.levelno == logging.CRITICAL:  # pylint: disable=R1714
-            fmt = date + " " + colored("ERR", "red", attrs=["blink", "underline"]) + " " + msg
+            fmt = f"{date}  {colored('ERR', 'red', attrs=['blink', 'underline'])}  {msg}"
         elif record.levelno == logging.DEBUG:
-            fmt = date + " " + colored("DBG", "green", attrs=["blink"]) + " " + msg
+            fmt = f"{date}  {colored('DBG', 'green', attrs=['blink'])}  {msg}"
         elif record.levelno == logging.INFO:
-            fmt = date + " " + colored("INF", "green") + " " + msg
+            fmt = f"{date}  {colored('INF', 'green')}  {msg}"
         else:
-            fmt = date + " " + msg
+            fmt = f"{date} {msg}"
         self._style._fmt = fmt  # pylint: disable=W0212
         self._fmt = fmt
         return super().format(record)
 
 
+class FileFormatter(logging.Formatter):
+    """A custom formatter to produce a loggings in json format"""
+
+    @no_type_check
+    def format(self, record: logging.LogRecord) -> str:
+        message = super().format(record)
+        log_dict = {
+            "level_no": record.levelno,
+            "level_name": record.levelname,
+            "module_name": record.filename,
+            "line_number": record.lineno,
+            "time": datetime.now().strftime("%m%d-%H%M%S"),
+            "message": message,
+        }
+        log_dict.update(record.log_dict)
+        return json.dumps(log_dict)
+
+
+_LOG_DIR = None
+_CONFIG_DICT: Dict[str, Any] = {
+    "version": 1,
+    "filters": {"customfilter": {"()": lambda: CustomFilter()}}, # pylint: disable=W0108
+    "formatters": {
+        "streamformatter": {"()": lambda: StreamFormatter(datefmt="%m%d %H:%M.%S")},
+    },
+    "handlers": {
+        "streamhandler": {"filters": ["customfilter"], "formatter": "streamformatter", "class": "logging.StreamHandler"}
+    },
+    "root": {"handlers": ["streamhandler"], "level": "INFO", "propagate": False},
+}
+
+
 def _get_logger() -> logging.Logger:
-    package_name = __name__
-    _logger = logging.getLogger(package_name)
-    _logger.propagate = False
-    _logger.setLevel(logging.DEBUG)
-    handler = logging.StreamHandler(sys.stdout)
-    handler.setFormatter(_Formatter(datefmt="%m%d %H:%M.%S"))
-    _logger.addHandler(handler)
+    logging.config.dictConfig(_CONFIG_DICT)
+    _logger = logging.getLogger(__name__)
     return _logger
 
 
@@ -76,7 +128,7 @@ def _get_logger() -> logging.Logger:
     locals()[func] = getattr(logger, func)
     __all__.append(func)
 
-LOG_DIR = None
+
 _FILE_HANDLER = None
 
 
@@ -93,7 +145,8 @@ def _set_file(path: Pathlike) -> None:
         shutil.move(path, backup_name)
         logger.info("Existing log file %s backuped to %s", path, backup_name)
     hdl = logging.FileHandler(filename=path, encoding="utf-8", mode="w")
-    hdl.setFormatter(_Formatter(datefmt="%m%d %H:%M:%S"))
+    hdl.setFormatter(FileFormatter(datefmt="%m%d %H:%M:%S"))
+    hdl.addFilter(CustomFilter())
 
     _FILE_HANDLER = hdl
     logger.addHandler(hdl)
@@ -119,8 +172,9 @@ def set_logger_dir(dir_name: Pathlike, action: Optional[str] = None) -> None:
     if isinstance(dir_name, Path):
         dir_name = dir_name.as_posix()
     dir_name = os.path.normpath(dir_name)
-    global LOG_DIR, _FILE_HANDLER  # pylint: disable=W0603
+    global _LOG_DIR, _FILE_HANDLER  # pylint: disable=W0603
     if _FILE_HANDLER:
+        # unload and close the old file handler, so that we may safely delete the logger directory
         logger.removeHandler(_FILE_HANDLER)
         del _FILE_HANDLER
 
@@ -151,15 +205,14 @@ def dir_nonempty(directory: str) -> int:
             pass
         else:
             raise OSError(f"Directory {dir_name} exits!")
-    LOG_DIR = dir_name
-
+    _LOG_DIR = os.path.join(dir_name, "log.jsonl")
     try:
         os.makedirs(dir_name)
     except OSError as err:
         if err.errno != errno.EEXIST:
             raise err
 
-    _set_file(os.path.join(dir_name, "log.log"))
+    _set_file(_LOG_DIR)
 
 
 def auto_set_dir(action: Optional[str] = None, name: Optional[str] = None) -> None:
@@ -184,7 +237,7 @@ def get_logger_dir() -> Optional[str]:
     The logger directory, or None if not set.
     The directory is used for general logging, tensorboard events, checkpoints, etc.
     """
-    return LOG_DIR
+    return _LOG_DIR
 
 
 @functools.lru_cache(maxsize=None)
@@ -197,5 +250,4 @@ def log_once(message: str, function: str = "info") -> None:
     :param function: the name of the logger method. e.g. "info", "warn", "error".
     :return:
     """
-
     getattr(logger, function)(message)
diff --git a/deepdoctection/utils/pdf_utils.py b/deepdoctection/utils/pdf_utils.py
index 8cd1802b..dae26d09 100644
--- a/deepdoctection/utils/pdf_utils.py
+++ b/deepdoctection/utils/pdf_utils.py
@@ -35,6 +35,7 @@
 from .detection_types import ImageType, Pathlike
 from .file_utils import PopplerNotFound, pdf_to_cairo_available, pdf_to_ppm_available, qpdf_available
 from .logger import logger
+from .utils import FileExtensionError, is_file_extension
 
 __all__ = ["decrypt_pdf_document", "get_pdf_file_reader", "get_pdf_file_writer", "PDFStreamer", "pdf_to_np_array"]
 
@@ -78,9 +79,11 @@ def get_pdf_file_reader(path: Pathlike) -> PdfFileReader:
     :return: A file reader object from which you can iterate through the document.
     """
 
-    assert os.path.isfile(path), path
+    if not os.path.isfile(path):
+        raise FileNotFoundError(str(path))
     file_name = os.path.split(path)[1]
-    assert os.path.splitext(file_name)[-1].lower() == ".pdf", f"must be a pdf file: {file_name}"
+    if not is_file_extension(file_name, ".pdf"):
+        raise FileExtensionError(f"must be a pdf file: {file_name}")
 
     with open(path, "rb") as file:
         input_pdf_as_bytes = PdfFileReader(file)
@@ -88,7 +91,7 @@ def get_pdf_file_reader(path: Pathlike) -> PdfFileReader:
         if input_pdf_as_bytes.isEncrypted:
             is_decrypted = decrypt_pdf_document(path)
             if not is_decrypted:
-                logger.error("pdf document cannot be decrypted and therefore cannot be processed further.")
+                logger.error("pdf document %s cannot be decrypted and therefore cannot be processed further.", path)
                 sys.exit()
 
     file_reader = PdfFileReader(open(path, "rb"))  # pylint: disable=R1732
diff --git a/deepdoctection/utils/settings.py b/deepdoctection/utils/settings.py
index 8e577adf..fc753db9 100644
--- a/deepdoctection/utils/settings.py
+++ b/deepdoctection/utils/settings.py
@@ -61,7 +61,8 @@ class DefaultType(ObjectTypes):
 
 @object_types_registry.register("PageType")
 class PageType(ObjectTypes):
-    """Type for document page properties """
+    """Type for document page properties"""
+
     document_type = "DOC_CLASS"
     language = "LANGUAGE"
 
@@ -69,12 +70,14 @@ class PageType(ObjectTypes):
 @object_types_registry.register("SummaryType")
 class SummaryType(ObjectTypes):
     """Summary type member"""
+
     summary = "SUMMARY"
 
 
 @object_types_registry.register("DocumentType")
 class DocumentType(ObjectTypes):
     """Document types"""
+
     letter = "LETTER"
     form = "FORM"
     email = "EMAIL"
@@ -101,6 +104,7 @@ class DocumentType(ObjectTypes):
 @object_types_registry.register("LayoutType")
 class LayoutType(ObjectTypes):
     """Layout types"""
+
     table = "TABLE"
     figure = "FIGURE"
     list = "LIST"
@@ -125,6 +129,7 @@ class LayoutType(ObjectTypes):
 @object_types_registry.register("TableType")
 class TableType(ObjectTypes):
     """Types for table properties"""
+
     item = "ITEM"
     number_of_rows = "NUMBER_OF_ROWS"
     number_of_columns = "NUMBER_OF_COLUMNS"
@@ -136,6 +141,7 @@ class TableType(ObjectTypes):
 @object_types_registry.register("CellType")
 class CellType(ObjectTypes):
     """Types for cell properties"""
+
     header = "HEADER"
     body = "BODY"
     row_number = "ROW_NUMBER"
@@ -147,6 +153,7 @@ class CellType(ObjectTypes):
 @object_types_registry.register("WordType")
 class WordType(ObjectTypes):
     """Types for word properties"""
+
     characters = "CHARS"
     block = "BLOCK"
     token_class = "TOKEN_CLASS"
@@ -158,6 +165,7 @@ class WordType(ObjectTypes):
 @object_types_registry.register("TokenClasses")
 class TokenClasses(ObjectTypes):
     """Types for token classes"""
+
     header = "HEADER"
     question = "QUESTION"
     answer = "ANSWER"
@@ -167,6 +175,7 @@ class TokenClasses(ObjectTypes):
 @object_types_registry.register("BioTag")
 class BioTag(ObjectTypes):
     """Types for tags"""
+
     begin = "B"
     inside = "I"
     outside = "O"
@@ -175,6 +184,7 @@ class BioTag(ObjectTypes):
 @object_types_registry.register("TokenClassWithTag")
 class TokenClassWithTag(ObjectTypes):
     """Types for token classes with tags, e.g. B-ANSWER"""
+
     b_answer = "B-ANSWER"
     b_header = "B-HEADER"
     b_question = "B-QUESTION"
@@ -192,6 +202,7 @@ class TokenClassWithTag(ObjectTypes):
 @object_types_registry.register("Relationships")
 class Relationships(ObjectTypes):
     """Types for describing relationships between types"""
+
     child = "CHILD"
     reading_order = "READING_ORDER"
     semantic_entity_link = "SEMANTIC_ENTITY_LINK"
@@ -200,6 +211,7 @@ class Relationships(ObjectTypes):
 @object_types_registry.register("Languages")
 class Languages(ObjectTypes):
     """Language types"""
+
     english = "eng"
     russian = "rus"
     german = "deu"
@@ -264,6 +276,7 @@ class Languages(ObjectTypes):
 @object_types_registry.register("DatasetType")
 class DatasetType(ObjectTypes):
     """Dataset types"""
+
     object_detection = "OBJECT_DETECTION"
     sequence_classification = "SEQUENCE_CLASSIFICATION"
     token_classification = "TOKEN_CLASSIFICATION"
diff --git a/deepdoctection/utils/utils.py b/deepdoctection/utils/utils.py
index dd89dc78..9aff81ad 100644
--- a/deepdoctection/utils/utils.py
+++ b/deepdoctection/utils/utils.py
@@ -23,10 +23,12 @@
 import os
 from collections.abc import MutableMapping
 from datetime import datetime
-from typing import Any, Callable, Dict, List, Set, Union
+from typing import Any, Callable, Dict, List, Sequence, Set, Union
 
 import numpy as np
 
+from .detection_types import Pathlike
+
 
 def delete_keys_from_dict(
     dictionary: Union[Dict[Any, Any], MutableMapping], keys: Union[str, List[str], Set[str]]  # type: ignore
@@ -140,3 +142,22 @@ def get_rng(obj: Any = None) -> np.random.RandomState:
     """
     seed = (id(obj) + os.getpid() + int(datetime.now().strftime("%Y%m%d%H%M%S%f"))) % 4294967295
     return np.random.RandomState(seed)
+
+
+class FileExtensionError(BaseException):
+    """
+    An exception indicating that a file does not seem to have an expected type
+    """
+
+
+def is_file_extension(file_name: Pathlike, extension: Union[str, Sequence[str]]) -> bool:
+    """
+    Check if a given file name has a given extension
+
+    :param file_name: the file name, either full along with path or as stand alone
+    :param extension: the extension of the file. Must add a dot (.)
+    :return: True/False
+    """
+    if isinstance(extension, str):
+        return os.path.splitext(file_name)[-1].lower() == extension
+    return os.path.splitext(file_name)[-1].lower() in extension
diff --git a/setup.cfg b/setup.cfg
index b8d9c085..fe525477 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -15,9 +15,10 @@ disable=
     R1710, # inconsistent-return-statements
     W0401, # wildcard-import
     W0614, # unused-wildcard-import
-    R0401,  # cyclic import
-    W0511,  # fixme
-    E1120 # no-value-for-parameter (because it does not work proberly with the ubiquitous curry decorator
+    R0401, # cyclic import
+    W0511, # fixme
+    E1120, # no-value-for-parameter (because it does not work properly with the ubiquitous curry decorator
+    E1205, # logging-too-many-args
 
 extension-pkg-whitelist=cv2
 load-plugins=pylint_strict_informational
@@ -39,7 +40,7 @@ max-attributes=15
 max-locals=36
 max-returns=8
 max-statements=100
-max-public-methods=22
+max-public-methods=30
 min-public-methods=1
 
 [pylint.MISCELLANEOUS]


Test patch:
diff --git a/deepdoctection/datasets/instances/layouttest.py b/deepdoctection/datasets/instances/layouttest.py
index 1fd9640f..1258e751 100644
--- a/deepdoctection/datasets/instances/layouttest.py
+++ b/deepdoctection/datasets/instances/layouttest.py
@@ -31,8 +31,6 @@
 from ...dataflow.custom_serialize import SerializerJsonlines
 from ...datasets.info import DatasetInfo
 from ...mapper.prodigystruct import prodigy_to_image
-
-# from ...utils.settings import names
 from ...utils.settings import DatasetType, LayoutType
 from ..base import _BuiltInDataset
 from ..dataflow_builder import DataFlowBaseBuilder
@@ -114,9 +112,7 @@ def build(self, **kwargs: Union[str, int]) -> DataFlow:
 
         # Load
         df: DataFlow
-        dataset_split = self.annotation_files[split]
-        assert isinstance(dataset_split, str)
-        path = self.get_workdir() / self.splits[split] / dataset_split
+        path = self.get_workdir() / self.splits[split] / self.get_annotation_file(split)
 
         df = SerializerJsonlines.load(path, max_datapoints=max_datapoints)
 
diff --git a/tests/data.py b/tests/data.py
index 0b1af492..e3ad14fc 100644
--- a/tests/data.py
+++ b/tests/data.py
@@ -427,10 +427,10 @@ class Annotations:
     ]  # global coordinates calculated depending on table annotation from fixture image_annotations
 
     def __post_init__(self) -> None:
-        self.layout_anns[0]._annotation_id = "53b76155-60ac-3305-a8fe-3507c4aae308"  # pylint: disable=W0212
-        self.layout_anns[1]._annotation_id = "cf3161ca-0e26-385d-9891-dc5cf5c8202e"  # pylint: disable=W0212
-        self.layout_anns[2]._annotation_id = "6c59831c-79e3-339f-82ef-153d64aa348b"  # pylint: disable=W0212
-        self.layout_anns[3]._annotation_id = "f58a8974-f891-3926-85e5-2ddebcbccf47"  # pylint: disable=W0212
+        self.layout_anns[0]._annotation_id = "38c755da-ace2-3712-98ac-b2c800e0725e"  # pylint: disable=W0212
+        self.layout_anns[1]._annotation_id = "0c53dc7c-b0ed-3082-9bf0-7522739fb42c"  # pylint: disable=W0212
+        self.layout_anns[2]._annotation_id = "e2341ea6-d7e5-3d69-96a5-d6ee1d0e3a39"  # pylint: disable=W0212
+        self.layout_anns[3]._annotation_id = "5a576938-6452-33db-aa28-8d4728e7b337"  # pylint: disable=W0212
 
     def get_layout_detect_results(self) -> List[DetectionResult]:
         """
@@ -1437,10 +1437,10 @@ def get_textract_response() -> JsonDict:
     "image": np.ones((1000, 1000, 3)),
     "ids": [
         "CLS",
-        "429e2ed0-7f89-31bf-bba5-0f0f65c0eb2e",
-        "2b46086c-a480-357d-8e07-29b177d150b8",
-        "8c6c765c-3e99-3154-ae2e-6d8b661e9bcb",
-        "16860148-9a2b-3530-b33e-9aaba857f5ce",
+        "7e79459d-0cf9-3954-802c-6d8c4d017792",
+        "08e645c4-f28d-30e6-befe-ab22dc53da92",
+        "224d479a-77e1-3c7a-9cd1-08430c61f020",
+        "841c74d7-45b2-3e0d-a8d5-7be3391685d6",
         "SEP",
     ],
     "boxes": [
@@ -1464,10 +1464,10 @@ def get_textract_response() -> JsonDict:
     "ann_ids": [
         [
             "[CLS]",
-            "429e2ed0-7f89-31bf-bba5-0f0f65c0eb2e",
-            "2b46086c-a480-357d-8e07-29b177d150b8",
-            "8c6c765c-3e99-3154-ae2e-6d8b661e9bcb",
-            "16860148-9a2b-3530-b33e-9aaba857f5ce",
+            "7e79459d-0cf9-3954-802c-6d8c4d017792",
+            "08e645c4-f28d-30e6-befe-ab22dc53da92",
+            "224d479a-77e1-3c7a-9cd1-08430c61f020",
+            "841c74d7-45b2-3e0d-a8d5-7be3391685d6",
             "[SEP]",
         ]
     ],
@@ -1553,6 +1553,7 @@ def get_sequence_class_result() -> SequenceClassResult:
 # pylint: disable=invalid-name, disallowed-name
 class TestType(ObjectTypes):
     """Object type members for testing purposes"""
+
     foo = "foo"
     FOO = "FOO"
     bak = "bak"
@@ -1582,4 +1583,6 @@ class TestType(ObjectTypes):
     three = "3"
     four = "4"
     five = "5"
+
+
 # pylint: enable=invalid-name, disallowed-name
diff --git a/tests/datapoint/test_box.py b/tests/datapoint/test_box.py
index a492bf04..3129e9f1 100644
--- a/tests/datapoint/test_box.py
+++ b/tests/datapoint/test_box.py
@@ -27,6 +27,7 @@
 
 from deepdoctection.datapoint import (
     BoundingBox,
+    BoundingBoxError,
     crop_box_from_image,
     global_to_local_coords,
     intersection_box,
@@ -53,10 +54,10 @@ def test_incomplete_data_for_bounding_box(box: Box) -> None:
         """
 
         # Act and Assert
-        with raises(AssertionError):
+        with raises(BoundingBoxError):
             BoundingBox(ulx=box.ulx, uly=box.uly, lrx=box.lrx, absolute_coords=box.absolute_coords)
 
-        with raises(AssertionError):
+        with raises(BoundingBoxError):
             BoundingBox(ulx=box.ulx, uly=box.uly, height=box.h, width=0.0, absolute_coords=box.absolute_coords)
 
     @staticmethod
diff --git a/tests/datapoint/test_image.py b/tests/datapoint/test_image.py
index 212e57e7..97f24733 100644
--- a/tests/datapoint/test_image.py
+++ b/tests/datapoint/test_image.py
@@ -80,7 +80,7 @@ def test_image_id_cannot_be_reassigned(image: WhiteImage) -> None:
         test_image = Image(file_name=image.file_name, location=image.loc, external_id=image.external_id)
 
         # Act and assert
-        with raises(AssertionError):
+        with raises(ValueError):
             test_image.image_id = "ec2aac06-c261-3669-b8bd-4486a54ce740"
 
     @staticmethod
@@ -171,9 +171,9 @@ def test_dump_cat_and_check_ann_id(image: WhiteImage) -> None:
         # Assert
         assert test_image.image_id == get_uuid(image.loc + image.file_name)
         assert (
-            cat.annotation_id == "948709a4-8be9-3745-8c57-de53ea0e2edd"
+            cat.annotation_id == "531191bc-3b48-3592-b4c3-70a0a5ac20aa"
         )  # get_uuid("FOOBounding Box ulx: 1.0 uly: 1.0 lrx: 2.0 lry: 3.090c05f37-a017-39cc-a178-b84f9d14ff48")
-        assert sub_cat_1.annotation_id == "963e0b93-c520-325e-bd8c-03889cb98754"
+        assert sub_cat_1.annotation_id == "1377b99d-127d-366f-a7fc-ba25296fe4e5"
 
     @staticmethod
     def test_dump_same_annotation_not_possible(image: WhiteImage) -> None:
@@ -192,7 +192,7 @@ def test_dump_same_annotation_not_possible(image: WhiteImage) -> None:
         # Act and Assert
         test_image.dump(cat)
 
-        with raises(AssertionError):
+        with raises(ValueError):
             test_image.dump(cat)
 
     @staticmethod
diff --git a/tests/datasets/test_info.py b/tests/datasets/test_info.py
index cd3bc02d..93e78aee 100644
--- a/tests/datasets/test_info.py
+++ b/tests/datasets/test_info.py
@@ -259,6 +259,6 @@ def test_merge_categories_cannot_update_or_filter() -> None:
         merge = get_merged_categories(cat_1, cat_2)
 
         # Act & Assert
-        with pytest.raises(AssertionError):
+        with pytest.raises(PermissionError):
             merge.filter_categories(categories="BAZ")
             merge.set_cat_to_sub_cat({"FOO": "FOO_1"})
diff --git a/tests/extern/test_hflayoutlm.py b/tests/extern/test_hflayoutlm.py
index 1d4b4732..de46c736 100644
--- a/tests/extern/test_hflayoutlm.py
+++ b/tests/extern/test_hflayoutlm.py
@@ -85,7 +85,7 @@ def test_categories_are_constructed_properly() -> None:
         """
 
         # Arrange, Act & Assert
-        with raises(AssertionError):
+        with raises(ValueError):
             HFLayoutLmTokenClassifier("path/to/json", "path/to/model", ["foo"], None)
 
         # Arrange
diff --git a/tests/mapper/data.py b/tests/mapper/data.py
index f71b2866..772974bf 100644
--- a/tests/mapper/data.py
+++ b/tests/mapper/data.py
@@ -726,7 +726,7 @@ def get_first_ann_category(self, as_index: bool = True) -> str:
 
 
 @dataclass
-class DatapointPubtabnet:  # pylint: disable=R0904
+class DatapointPubtabnet:
     """
     Class for datapoint in pubtabnet annotation format
     """
@@ -962,14 +962,14 @@ def __init__(self) -> None:
         self.len_spans: int = 2
         self.first_span: JsonDict = {
             "label": TestType.FOO,
-            "annotation_id": "aa62b83f-0c73-3e00-a289-f602ef512db5",
+            "annotation_id": "ec7e5bca-b4f4-3272-8952-35a500663131",
             "score": 0.53,
             "type": "rect",
             "points": [[2.6, 3.7], [2.6, 5.7], [4.6, 5.7], [4.6, 3.7]],
         }
         self.second_span: JsonDict = {
             "label": TestType.BAK,
-            "annotation_id": "108246c0-4eee-32cc-b3c7-5b6705d4bee6",
+            "annotation_id": "b8cb337a-de1b-3bfd-bb36-603d852104f2",
             "score": 0.99,
             "type": "rect",
             "points": [[16.6, 26.6], [16.6, 30.6], [30.6, 30.6], [30.6, 26.6]],
@@ -1917,9 +1917,9 @@ class DatapointXfund:
         "width": 1000,
         "height": 1000,
         "ann_ids": [
-            "9189ca58-d603-348e-96f6-a41c322e4a55",
-            "6cd1cb41-fdf4-3508-8f41-d457ae6e26d1",
-            "499ea7ac-ddff-3efd-8f48-bb5e580e613b",
+            "440c677a-05ca-33a5-8e61-dd2a9724367d",
+            "75d7e019-4951-342b-b073-27aba447e7e8",
+            "0b07ba23-6f2c-3c73-ab62-cb1bd891ae7f",
         ],
         "words": ["Akademisches", "Auslandsamt", "Bewerbungsformular"],
         "bbox": [[325.0, 184.0, 578.0, 230.0], [586.0, 186.0, 834.0, 232.0], [1058.0, 413.0, 1701.0, 482.0]],
diff --git a/tests/pipe/test_lm.py b/tests/pipe/test_lm.py
index 5ca71961..e55a6895 100644
--- a/tests/pipe/test_lm.py
+++ b/tests/pipe/test_lm.py
@@ -81,19 +81,19 @@ def test_pass_datapoint(
         dp = lm_service.pass_datapoint(dp)
 
         # Assert
-        words = dp.get_annotation(annotation_ids="429e2ed0-7f89-31bf-bba5-0f0f65c0eb2e")
+        words = dp.get_annotation(annotation_ids="7e79459d-0cf9-3954-802c-6d8c4d017792")
         assert words[0].get_sub_category(WordType.token_class).category_name == TokenClasses.header
         assert words[0].get_sub_category(WordType.tag).category_name == BioTag.begin
 
-        words = dp.get_annotation(annotation_ids="2b46086c-a480-357d-8e07-29b177d150b8")
+        words = dp.get_annotation(annotation_ids="08e645c4-f28d-30e6-befe-ab22dc53da92")
         assert words[0].get_sub_category(WordType.token_class).category_name == TokenClasses.header
         assert words[0].get_sub_category(WordType.tag).category_name == BioTag.begin
 
-        words = dp.get_annotation(annotation_ids="8c6c765c-3e99-3154-ae2e-6d8b661e9bcb")
+        words = dp.get_annotation(annotation_ids="224d479a-77e1-3c7a-9cd1-08430c61f020")
         assert words[0].get_sub_category(WordType.token_class).category_name == TokenClasses.header
         assert words[0].get_sub_category(WordType.tag).category_name == BioTag.inside
 
-        words = dp.get_annotation(annotation_ids="16860148-9a2b-3530-b33e-9aaba857f5ce")
+        words = dp.get_annotation(annotation_ids="841c74d7-45b2-3e0d-a8d5-7be3391685d6")
         assert words[0].get_sub_category(WordType.token_class).category_name == TokenClasses.header
         assert words[0].get_sub_category(WordType.tag).category_name == BioTag.inside
 
@@ -120,19 +120,19 @@ def test_pass_datapoint_2(
         dp = lm_service.pass_datapoint(dp)
 
         # Assert
-        words = dp.get_annotation(annotation_ids="429e2ed0-7f89-31bf-bba5-0f0f65c0eb2e")
+        words = dp.get_annotation(annotation_ids="7e79459d-0cf9-3954-802c-6d8c4d017792")
         assert words[0].get_sub_category(WordType.token_class).category_name == TokenClasses.header
         assert words[0].get_sub_category(WordType.tag).category_name == BioTag.begin
 
-        words = dp.get_annotation(annotation_ids="2b46086c-a480-357d-8e07-29b177d150b8")
+        words = dp.get_annotation(annotation_ids="08e645c4-f28d-30e6-befe-ab22dc53da92")
         assert words[0].get_sub_category(WordType.token_class).category_name == TokenClasses.header
         assert words[0].get_sub_category(WordType.tag).category_name == BioTag.begin
 
-        words = dp.get_annotation(annotation_ids="8c6c765c-3e99-3154-ae2e-6d8b661e9bcb")
+        words = dp.get_annotation(annotation_ids="224d479a-77e1-3c7a-9cd1-08430c61f020")
         assert words[0].get_sub_category(WordType.token_class).category_name == TokenClasses.header
         assert words[0].get_sub_category(WordType.tag).category_name == BioTag.inside
 
-        words = dp.get_annotation(annotation_ids="16860148-9a2b-3530-b33e-9aaba857f5ce")
+        words = dp.get_annotation(annotation_ids="841c74d7-45b2-3e0d-a8d5-7be3391685d6")
         assert words[0].get_sub_category(WordType.token_class).category_name == TokenClasses.header
         assert words[0].get_sub_category(WordType.tag).category_name == BioTag.inside
 
diff --git a/tests/pipe/test_refine.py b/tests/pipe/test_refine.py
index 21a05769..1f2c4406 100644
--- a/tests/pipe/test_refine.py
+++ b/tests/pipe/test_refine.py
@@ -304,18 +304,18 @@ def test_integration_pipeline_component(self, dp_image_fully_segmented_fully_til
             "<table>",
             "<tr>",
             "<td>",
-            "46513100-30d3-3470-bee6-eada48721f08",
+            "2c1458bf-6b21-327a-9632-21373da468bb",
             "</td>",
             "<td>",
-            "a9b5063e-0517-3020-8a31-76b026acfbc4",
+            "609243a2-0bd8-3002-939a-c8bcf7b2e3c8",
             "</td>",
             "</tr>",
             "<tr>",
             "<td>",
-            "77be6027-0295-33ee-b04f-db8dd0184a9b",
+            "9b2b0bd1-3885-3aa6-8d45-ebebdd1abddd",
             "</td>",
             "<td>",
-            "b363e62f-ff29-3ae8-948c-be92ae12b0b1",
+            "6e70cf3d-e260-3f73-81bf-77616b6d54b2",
             "</td>",
             "</tr>",
             "</table>",
diff --git a/tests/pipe/test_text.py b/tests/pipe/test_text.py
index 4910bf6e..526c629c 100644
--- a/tests/pipe/test_text.py
+++ b/tests/pipe/test_text.py
@@ -109,7 +109,7 @@ def test_text_extraction_service_raises_error_with_inconsistent_attributes() ->
     text_extract_detector = MagicMock(spec=PdfMiner)
 
     # Act and Assert
-    with raises(AssertionError):
+    with raises(TypeError):
         TextExtractionService(text_extract_detector, extract_from_roi=LayoutType.table)