Repository: automl/DeepCAVE (language: python)

Issue:
Attempted Append to None Value
[This parameter](https://github.com/automl/DeepCAVE/blob/1851126c064a3120299250fb39b2c00d00abbc2e/deepcave/evaluators/footprint.py#L50) is optional, therefore could be None. [Here](https://github.com/automl/DeepCAVE/blob/1851126c064a3120299250fb39b2c00d00abbc2e/deepcave/evaluators/footprint.py#L446) it is tried, to append to a possible None value, which is not allowed.


Discussion / hints:

Fix patch:
diff --git a/.flake8 b/.flake8
index b4f03155..69880fa1 100644
--- a/.flake8
+++ b/.flake8
@@ -6,5 +6,7 @@ extend-exclude =
     .venv
     build
 extend-ignore =
-    E203  # No whitespace before ':' in [x : y]
-    E731  # No lambdas — too strict
+    # No whitespace before ':' in [x : y]
+    E203
+    # No lambdas — too strict
+    E731
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b3921a30..1f5396e5 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
         files: tests
 
   - repo: https://github.com/ambv/black
-    rev: 23.1.0
+    rev: 23.3.0
     hooks:
       - id: black
         name: black formatter deepcave
@@ -33,14 +33,38 @@ repos:
     hooks:
       - id: pydocstyle
         files: deepcave
-        additional_dependencies: ["toml"] # Needed to parse pyproject.toml
+        additional_dependencies: ["tomli"] # Needed to parse pyproject.toml
 
   - repo: https://github.com/pre-commit/mirrors-mypy
-    rev: v0.930
+    rev: v1.5.1
     hooks:
       - id: mypy
         name: mypy deepcave
         files: deepcave
+        args: [--install-types, --non-interactive]
+        additional_dependencies: [
+            'wheel>=0.41.2',
+            'setuptools==68.2.2',
+            'absl-py>=1.0.0',
+            'jsonlines>=3.0.0',
+            'pandas>=1.3.4',
+            'numpy>=1.22.2',
+            'matplotlib>=3.5.1',
+            'pyyaml>=6.0.1',
+            'kaleido>=0.2.1',
+            'gplearn>=0.4.2',
+            'sympy>=1.12',
+            'ConfigSpace==0.6.1',
+            'pyrfr>=0.9.0',
+            'hpbandster==0.7.4',
+            'dash==2.0.0',
+            'dash-extensions==0.0.71',
+            'dash-bootstrap-components==1.0.3',
+            'redis>=4.1.4',
+            'rq>=1.10.1',
+            'werkzeug==2.0.3',
+            'pyPDPPartitioner>=0.1.8'
+        ] # Needed for mypy, so that it knows the types to check
 
   - repo: https://github.com/PyCQA/flake8
     rev: 6.0.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 29a43c06..aef9dca8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,24 @@
 - Fix errors due to changing inputs before runselection (#64).
 - For fANOVA, remove constant hyperparameters from configspace (#9).
 
+## Version-Updates
+- Black version from 23.1.0 to 23.3.0
+- Mypy from 0.930 to 1.5.1
+
+## Mypy
+- Updated args so there are no missing imports
+- Updated additional dependencies, so mypy can check all types
+- Note: If the installs in requirements change, it has to be adapted in additional dependencies
+- Added many type annotations
+- In some cases internal refactoring for variables, due to typing
+
+## Pydocstyle and Linter
+- Major overhaul of docstrings in various files
+- Removed unused imports and variables
+
+## Additional Changes
+- Added a "make install examples" in Makefile
+
 # Version 1.1.3
 
 ## Bug-Fixes
diff --git a/Makefile b/Makefile
index 84b857e9..f3dfd255 100644
--- a/Makefile
+++ b/Makefile
@@ -48,7 +48,10 @@ install:
 install-dev:
 	$(PIP) install -e ".[dev]"
 	pre-commit install
-
+	
+install-examples:
+	$(PIP) install -e ".[examples]"
+	
 check-black:
 	$(BLACK) ${SOURCE_DIR} --check || :
 	$(BLACK) ${EXAMPLES_DIR} --check || :
@@ -62,7 +65,7 @@ check-pydocstyle:
 	$(PYDOCSTYLE) ${SOURCE_DIR} || :
 
 check-mypy:
-	$(MYPY) ${SOURCE_DIR} || :
+	$(MYPY) --check-untyped-defs --install-types --non-interactive --ignore-missing-imports ${SOURCE_DIR} || :
 
 check-flake8:
 	$(FLAKE8) ${SOURCE_DIR} || :
diff --git a/README.md b/README.md
index 01a79e19..9df307cb 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,11 @@ conda install -c anaconda swig
 make install-dev
 ```
 
+If you want to use the given examples, run this after installing:
+```bash
+make install-examples
+```
+
 Please visit the [documentation](https://automl.github.io/DeepCAVE/main/installation.html) to get
 further help (e.g. if you can not install redis server or you are on a mac).
 
diff --git a/deepcave/__init__.py b/deepcave/__init__.py
index 84c9914c..bc3e4cfe 100644
--- a/deepcave/__init__.py
+++ b/deepcave/__init__.py
@@ -1,3 +1,12 @@
+#  noqa: D400
+"""
+# DeepCAVE
+
+This module is used to initialize and set up the configuration for the DeepCAVE framework.
+
+The Dash application gets created.
+"""
+
 from typing import Any, Callable, TypeVar, cast
 
 import datetime
@@ -25,7 +34,20 @@
 ROOT_DIR = Path(__file__).parent
 
 
-def get_app(title: str):
+def get_app(title: str) -> Any:
+    """
+    Get the Dash Proxy.
+
+    Parameters
+    ----------
+    title : str
+        The title of the application.
+
+    Returns
+    -------
+    DashProxy
+        The dash proxy.
+    """
     import dash_bootstrap_components as dbc
     from dash_extensions.enrich import (
         DashProxy,
@@ -61,8 +83,8 @@ def get_app(title: str):
 if any(file in _exec_file for file in _exec_files):
     from deepcave.custom_queue import Queue
     from deepcave.runs.handler import RunHandler
-    from deepcave.runs.objective import Objective  # noqa
-    from deepcave.runs.recorder import Recorder  # noqa
+    from deepcave.runs.objective import Objective
+    from deepcave.runs.recorder import Recorder
     from deepcave.utils.cache import Cache
     from deepcave.utils.configs import parse_config
     from deepcave.utils.notification import Notification
@@ -129,8 +151,37 @@ def get_app(title: str):
 
 
 def interactive(func: F) -> F:
+    """
+    Define the interactive decorator.
+
+    Parameters
+    ----------
+    func : F
+        The function to be decorated.
+
+    Returns
+    -------
+    F
+        The decorated function.
+    """
+
     @wraps(func)
     def inner(*args: Any, **kwargs: Any) -> Any:
+        """
+        Inner function of the decorator.
+
+        Parameters
+        ----------
+        *args : Any
+            Arguments to be passed to the wrap function.
+        **kwargs : Any
+            Keyword arguments to be passed to the wrap function.
+
+        Returns
+        -------
+        Any
+            The result of the function.
+        """
         if _api_mode:
             return
 
diff --git a/deepcave/cli.py b/deepcave/cli.py
index c5c0e347..36f5986f 100644
--- a/deepcave/cli.py
+++ b/deepcave/cli.py
@@ -1,3 +1,14 @@
+#  noqa: D400
+"""
+# CLI
+
+This module defines command-line options using flags.
+
+This includes the entry point for the programs execution.
+"""
+
+from typing import Any
+
 import multiprocessing
 import subprocess
 from pathlib import Path
@@ -21,7 +32,8 @@
 )
 
 
-def execute(_) -> None:
+def execute(_: Any) -> None:
+    """Entry point for the programs execution."""
     if (config_key := FLAGS.get_config_value) is not None:
         config = FLAGS.config
         if config is not None:
@@ -47,6 +59,7 @@ def execute(_) -> None:
 
 
 def main() -> None:
+    """Call the execute function."""
     try:
         app.run(execute)
     except KeyboardInterrupt:
diff --git a/deepcave/config.py b/deepcave/config.py
index 0cc24f68..64482e66 100644
--- a/deepcave/config.py
+++ b/deepcave/config.py
@@ -1,9 +1,50 @@
+#  noqa: D400
+"""
+# Config
+
+This module defines the config object and its constants.
+Also defines multiple constants for directories, the server name, available plugins and converters.
+
+## Classes
+    - Config: Describe the config object.
+"""
+
 from typing import Any, Dict, List, Type
 
 from pathlib import Path
 
+from deepcave.runs.run import Run
+
 
 class Config:
+    """
+    Describe the config object.
+
+    Also define the constants of the config object.
+    Includes multiple constants for directories, the server name, available plugins and converters.
+
+    Constants
+    ---------
+    TITLE : str
+    DEBUG: bool
+    REFRESH_RATE: int
+    SAVE_IMAGES: bool
+    FIGURE_MARGIN: Dict
+    FIGURE_HEIGHT: str
+    REDIS_PORT: int
+    REDIS_ADDRESS: str
+    DASH_PORT: int
+    DASH_ADDRESS: str
+    META_DEFAULT: Dict
+
+    Properties
+    ----------
+    DASH_ADRESS : str
+        The address of the server name.
+    DASH_PORT : int
+        The port of the server name.
+    """
+
     # General config
     TITLE: str = "DeepCAVE"
     DEBUG: bool = False
@@ -34,18 +75,22 @@ class Config:
 
     @property
     def DEFAULT_WORKING_DIRECTORY(self) -> Path:
+        """Specifies the default working directory."""
         return Path.cwd() / "logs"
 
     @property
     def CACHE_DIR(self) -> Path:
+        """Specifies the default cache directory."""
         return Path(__file__).parent / "cache"
 
     @property
     def SERVER_NAME(self) -> str:
+        """Specifies the server name, consisting of address and port."""
         return f"http://{self.DASH_ADDRESS}:{self.DASH_PORT}"
 
     @property
-    def PLUGINS(self) -> Dict[str, List["Plugin"]]:
+    def PLUGINS(self) -> Dict[str, List[Any]]:
+        """A list of available plugins per category."""
         from deepcave.plugins.budget.budget_correlation import BudgetCorrelation
         from deepcave.plugins.hyperparameter.importances import Importances
         from deepcave.plugins.hyperparameter.pdp import PartialDependencies
@@ -60,6 +105,7 @@ def PLUGINS(self) -> Dict[str, List["Plugin"]]:
         from deepcave.plugins.summary.footprint import FootPrint
         from deepcave.plugins.summary.overview import Overview
 
+        plugins: Dict[str, List[Any]] = {}
         plugins = {
             "Summary": [
                 Overview(),
@@ -85,6 +131,7 @@ def PLUGINS(self) -> Dict[str, List["Plugin"]]:
 
     @property
     def CONVERTERS(self) -> List[Type["Run"]]:
+        """Get a list of available run converters."""
         from deepcave.runs.converters.bohb import BOHBRun
         from deepcave.runs.converters.deepcave import DeepCAVERun
         from deepcave.runs.converters.smac3v1 import SMAC3v1Run
diff --git a/deepcave/constants.py b/deepcave/constants.py
index 9fe9aa7e..54f27eb2 100644
--- a/deepcave/constants.py
+++ b/deepcave/constants.py
@@ -1,3 +1,20 @@
+#  noqa: D400
+"""
+# Constants
+
+This module defines the constants for the DeepCAVE framework.
+
+## Constants
+    NAN_VALUE: float
+    NAN_LABEL: str
+    VALUE_RANGE: List
+    CONSTANT_VALUE: float
+    BORDER_CONFIG_ID: int
+    RANDOM_CONFIG_ID: innt
+    COMBINED_COST_NAME: str
+    COMBINED_BUDGET: int
+"""
+
 NAN_VALUE = -0.2
 NAN_LABEL = "NaN"
 VALUE_RANGE = [NAN_VALUE, 1]
diff --git a/deepcave/custom_queue.py b/deepcave/custom_queue.py
index 42d48cb2..dcc08b13 100644
--- a/deepcave/custom_queue.py
+++ b/deepcave/custom_queue.py
@@ -1,4 +1,16 @@
-from typing import Any, Callable, Dict, List
+#  noqa: D400
+"""
+# Custom Queue
+
+This module provides a class defining a queue for job handling.
+Utilities include getting jobs and their status, getting workers, enqueuing jobs,
+as well as deleting and adding jobs from/to the queue.
+
+## Classes
+    - Queue: This class defines all components for a job queue.
+"""
+
+from typing import Any, Callable, Dict, List, Optional
 
 import redis
 from rq import Queue as _Queue
@@ -13,11 +25,28 @@
 
 
 class Queue:
+    """
+    Define all components for a job queue.
+
+    Add and remove jobs from/to the queue.
+    """
+
     def __init__(self, address: str, port: int) -> None:
         self._connection = redis.from_url(address + ":" + str(port))
         self._queue = _Queue("high", connection=self._connection, default_timeout=-1)
 
     def ready(self) -> bool:
+        """
+        Check if the queue object is ready.
+
+        Check if there are already workers in use.
+
+        Returns
+        -------
+        bool
+            True, if the queue is ready.
+            Otherwise False.
+        """
         # Check if at least one worker is in use:
         workers = self.get_workers()
 
@@ -26,23 +55,79 @@ def ready(self) -> bool:
 
         return False
 
-    def get_worker(self, worker_name) -> Worker:
+    def get_worker(self, worker_name: str) -> Worker:
+        """
+        Retrieve a Worker from a name.
+
+        Parameters
+        ----------
+        worker_name : str
+            The name of the worker.
+
+        Returns
+        -------
+        Worker
+            The worker object corresponding to the worker name.
+
+        Raises
+        ------
+        ValueError
+            If the Worker object associated with the given name cannot be found.
+        """
         for worker in self.get_workers():
             if worker.name == worker_name:
                 return worker
 
         raise ValueError("Worker not found.")
 
-    def get_workers(self):
+    def get_workers(self) -> List[Worker]:
+        """
+        Get the workers in the queue.
+
+        Returns
+        -------
+        List[Worker]
+            A list of the queued workers.
+        """
         return Worker.all(queue=self._queue)
 
     def is_processed(self, job_id: str) -> bool:
+        """
+        Check if the job is or was processed.
+
+        This means it is either running, pending or finished.
+
+        Parameters
+        ----------
+        job_id : str
+            The id of the job being checked.
+
+        Returns
+        -------
+        bool
+            True, if the job is or was processed.
+            Otherwise False.
+        """
         if self.is_running(job_id) or self.is_pending(job_id) or self.is_finished(job_id):
             return True
 
         return False
 
     def is_running(self, job_id: str) -> bool:
+        """
+        Check if the job is running.
+
+        Parameters
+        ----------
+        job_id : str
+            The id of the job being checked.
+
+        Returns
+        -------
+        bool
+            True, if the job is running.
+            Otherwise False.
+        """
         for id in self._queue.started_job_registry.get_job_ids():
             if job_id == id:
                 return True
@@ -50,6 +135,20 @@ def is_running(self, job_id: str) -> bool:
         return False
 
     def is_pending(self, job_id: str) -> bool:
+        """
+        Check if the job is pending in a queue.
+
+        Parameters
+        ----------
+        job_id : str
+            The id of the job being checked.
+
+        Returns
+        -------
+        bool
+            True, if the job is pending.
+            Otherwise False.
+        """
         for id in self._queue.get_job_ids():
             if job_id == id:
                 return True
@@ -57,6 +156,20 @@ def is_pending(self, job_id: str) -> bool:
         return False
 
     def is_finished(self, job_id: str) -> bool:
+        """
+        Check if the job is finished.
+
+        Parameters
+        ----------
+        job_id : str
+            The id of the job being checked.
+
+        Returns
+        -------
+        bool
+            True, if the job is finished.
+            Otherwise False.
+        """
         for id in self._queue.finished_job_registry.get_job_ids():
             if job_id == id:
                 return True
@@ -64,6 +177,20 @@ def is_finished(self, job_id: str) -> bool:
         return False
 
     def has_failed(self, job_id: str) -> bool:
+        """
+        Check if a job has failed in a registry.
+
+        Parameters
+        ----------
+        job_id : str
+            The id of the job being checked.
+
+        Returns
+        -------
+        bool
+            True, if the job has failed.
+            Otherwise False.
+        """
         for id in self._queue.failed_job_registry.get_job_ids():
             if job_id == id:
                 return True
@@ -71,9 +198,41 @@ def has_failed(self, job_id: str) -> bool:
         return False
 
     def get_job(self, job_id: str) -> Job:
+        """
+        Get the job fitting the given job id.
+
+        Parameters
+        ----------
+        job_id : str
+            The id of the job.
+
+        Returns
+        -------
+        Job
+            The job Object to the job ID.
+        """
         return Job.fetch(job_id, connection=self._connection)
 
     def get_jobs(self, registry: str = "running") -> List[Job]:
+        """
+        Retrieve list of jobs from the registry.
+
+        Parameters
+        ----------
+        registry : str, optional
+            The registry to retrieve jobs from.
+            Default is "running".
+
+        Returns
+        -------
+        List[Job]
+            A list of the jobs from the registry.
+
+        Raises
+        ------
+        NotImplementedError
+            If the registry value is not recognized.
+        """
         if registry == "running":
             r = self._queue.started_job_registry
         elif registry == "pending":
@@ -93,17 +252,41 @@ def get_jobs(self, registry: str = "running") -> List[Job]:
         return results
 
     def get_running_jobs(self) -> List[Job]:
+        """
+        Get the running jobs in the registry.
+
+        Returns
+        -------
+        List[Job]
+            The list with the corresponding jobs.
+        """
         return self.get_jobs(registry="running")
 
     def get_pending_jobs(self) -> List[Job]:
+        """
+        Get the pending jobs in the registry.
+
+        Returns
+        -------
+        List[Job]
+            The list with the corresponding jobs.
+        """
         return self.get_jobs(registry="pending")
 
     def get_finished_jobs(self) -> List[Job]:
+        """
+        Get the finished jobs in the registry.
+
+        Returns
+        -------
+        List[Job]
+            The list with the corresponding jobs.
+        """
         return self.get_jobs(registry="finished")
 
-    def delete_job(self, job_id: str = None) -> None:
+    def delete_job(self, job_id: Optional[str] = None) -> None:
         """
-        Delete a job from the queue. If no job_id is given, delete all jobs.
+        Delete a job from the queue. If no job id is given, delete all jobs.
 
         Parameters
         ----------
@@ -111,26 +294,42 @@ def delete_job(self, job_id: str = None) -> None:
             Id of the job, which should be removed. By default None.
         """
 
-        def remove_jobs(registry: BaseRegistry, job_id: str = None) -> None:
+        def remove_jobs(registry: BaseRegistry, job_id: Optional[str] = None) -> None:
+            """
+            Remove a job from the registry. If no job id is given, remove all.
+
+            Parameters
+            ----------
+            registry : BaseRegistry
+                The registry from where to remove the job.
+            job_id: Optional[str], optional
+                The identificator of the job to be removed.
+                Default is None.
+
+            Raises
+            ------
+            Exception
+                If the job could not be removed.
+            """
             if job_id is not None:
                 try:
-                    registry.remove(job_id, delete_job=True)
+                    registry.remove(self.get_job(job_id), delete_job=True)
                 except Exception:
                     pass
             else:
                 # Remove all
                 for job_id in registry.get_job_ids():
                     try:
-                        registry.remove(job_id, delete_job=True)
+                        registry.remove(self.get_job(job_id), delete_job=True)
                     except Exception:
-                        registry.remove(job_id)
+                        registry.remove(self.get_job(job_id))
 
-        remove_jobs(self._queue, job_id)
+        remove_jobs(self._queue, job_id)  # type: ignore
         remove_jobs(self._queue.finished_job_registry, job_id)
         remove_jobs(self._queue.canceled_job_registry, job_id)
 
         # Started jobs perform differently
-        # We have to "kill" the worker
+        # The worker has to be "killed"
         for worker in self.get_workers():
             job_id_ = str(worker._job_id)  # b'cbece'
             job_id_ = job_id_.replace("b'", "").replace("'", "")
@@ -152,20 +351,37 @@ def remove_jobs(registry: BaseRegistry, job_id: str = None) -> None:
                 if job_id != job_id_:
                     continue
 
-            # Wait till the job is actually cancled
+            # Wait till the job is actually canceled
             while worker.state == "busy":
-                # We need to "update" the worker every time
+                # The worker needs to be "updated" every time
                 worker = self.get_worker(worker.name)
 
-        # Now we also have to remove the failed jobs caused by the kill horse command
+        # Now the failed jobs also have to be removed caused by the kill horse command
         remove_jobs(self._queue.failed_job_registry, job_id)
 
     def delete_jobs(self) -> None:
+        """Delete the jobs in the queue."""
         self.delete_job()
 
     def enqueue(
-        self, func: Callable[[Any], Any], args: Any, job_id: str, meta: Dict[str, str]
+        self, func: Callable[..., Any], args: Any, job_id: str, meta: Dict[str, str]
     ) -> None:
+        """
+        Add a job for processing to the queue.
+
+        Logs a debug message if the job id is already in use.
+
+        Parameters
+        ----------
+        func : Callable[[Any], Any]
+            A function to be executed, once the job is done.
+        args : Any
+            Arguments to be passed to func.
+        job_id : str
+            The identifier for the job.
+        meta : Dict[str, str]
+            A dictionary containing the jobs metadata.
+        """
         # First check if job_id is already in use
         if self.is_processed(job_id):
             logger.debug("Job was not added because it was processed already.")
@@ -179,11 +395,8 @@ def enqueue(
             result_ttl=-1,  # Make sure it's not automatically deleted.
         )
 
-    def __getattr__(self, name):
-        """
-        If function is not found, make sure we access self._queue directly.
-        """
-
+    def __getattr__(self, name: str) -> Any:
+        """If function is not found, make sure self._queue is accessed directly."""
         try:
             return self.__getattribute__(name)
         except Exception:
diff --git a/deepcave/evaluators/__init__.py b/deepcave/evaluators/__init__.py
index e69de29b..4f996a95 100644
--- a/deepcave/evaluators/__init__.py
+++ b/deepcave/evaluators/__init__.py
@@ -0,0 +1,13 @@
+#  noqa: D400
+"""
+# evaluators
+
+## Modules
+----------
+fanova
+    This module provides a tool for assessing the importance of an algorithms Hyperparameters.
+footprint
+    This module provides utilities for creating a footprint of a run.
+lpi
+    This module provides utilities to calculate the local parameter importance (LPI).
+"""
diff --git a/deepcave/evaluators/epm/__init__.py b/deepcave/evaluators/epm/__init__.py
index e69de29b..a6a3c2e0 100644
--- a/deepcave/evaluators/epm/__init__.py
+++ b/deepcave/evaluators/epm/__init__.py
@@ -0,0 +1,16 @@
+# noqa: D400
+"""
+# epm
+
+## Modules
+----------
+fanova_forest
+    The module provides utilities for creating a fANOVA forest.
+random_forest
+    This module can be used for training and using a Random Forest Regression model.
+random_forest_surrogate
+    This module provides a RandomForest Surrogate model.
+utils
+    This module provides a utility to get the types
+    as well as the bounds of the Hyperparameters.
+"""
diff --git a/deepcave/evaluators/epm/fanova_forest.py b/deepcave/evaluators/epm/fanova_forest.py
index 2443a622..4766e3f6 100644
--- a/deepcave/evaluators/epm/fanova_forest.py
+++ b/deepcave/evaluators/epm/fanova_forest.py
@@ -1,4 +1,17 @@
-from typing import List, Optional, Tuple
+# noqa: D400
+"""
+# FanovaForest
+
+The module provides utilities for creating a fANOVA forest.
+
+It includes a FanovaForest wrapper for pyrfr.
+fANOVA can be used for analyzing the importances of Hyperparameters.
+
+## Classes
+    - FanovaForest: A fANOVA forest wrapper for pyrfr.
+"""
+
+from typing import Any, Dict, List, Optional, Tuple, Union
 
 import itertools as it
 
@@ -13,7 +26,32 @@
 
 class FanovaForest(RandomForest):
     """
-    A fanova forest wrapper for pyrfr.
+    A fANOVA forest wrapper for pyrfr.
+
+    Properties
+    ----------
+    cutoffs : Tuple[float, float]
+        The cutoffs of the model.
+    percentiles : NDArray[floating]
+        The percentiles of the data points Y.
+    all_midpoints : List
+        All midpoints tree wise for the whole forest.
+    all_sizes : List
+        All interval sizes tree wise for the whole forest.
+    bounds : List[Tuple[float, float]
+        Stores feature bounds.
+    trees_total_variances : List
+        The total variances of the trees.
+    trees_total_variance : Any
+        The total variance of a tree.
+    trees_variance_fractions : Dict
+        The variance fractions of the trees.
+    V_U_total : Dict[Tuple[int, ...], List[Any]]
+        Store variance-related information across all trees.
+    V_U_individual : Dict[Tuple[int, ...], List[Any]]
+        Store variance-related information for individual subsets.
+    n_params : int
+        The number of Hyperparameters to sample.
     """
 
     def __init__(
@@ -52,7 +90,7 @@ def __init__(
 
     def _get_model(self) -> regression.base_tree:
         """
-        Returns the internal model.
+        Return the internal model.
 
         Returns
         -------
@@ -63,7 +101,7 @@ def _get_model(self) -> regression.base_tree:
 
     def _train(self, X: np.ndarray, Y: np.ndarray) -> None:
         """
-        Trains the random forest on X and Y.
+        Train the Random Forest on X and Y.
 
         Parameters
         ----------
@@ -75,7 +113,7 @@ def _train(self, X: np.ndarray, Y: np.ndarray) -> None:
         super()._train(X, Y)
         self.percentiles = np.percentile(Y, range(0, 100))
 
-        # all midpoints and interval sizes treewise for the whole forest
+        # all midpoints and interval sizes tree wise for the whole forest
         self.all_midpoints = []
         self.all_sizes = []
 
@@ -84,8 +122,8 @@ def _train(self, X: np.ndarray, Y: np.ndarray) -> None:
 
         # compute midpoints and interval sizes for variables in each tree
         for tree_split_values in forest_split_values:
-            sizes = []
-            midpoints = []
+            sizes: List = []
+            midpoints: List = []
             for i, split_vals in enumerate(tree_split_values):
                 if np.isnan(self.bounds[i][1]):  # categorical parameter
                     # check if the tree actually splits on this parameter
@@ -108,14 +146,14 @@ def _train(self, X: np.ndarray, Y: np.ndarray) -> None:
             self.all_sizes.append(sizes)
 
         # capital V in the paper
-        self.trees_total_variances = []
+        self.trees_total_variances: list = []
 
         # dict of lists where the keys are tuples of the dimensions
         # and the value list contains \hat{f}_U for the individual trees
         # reset all the variance fractions computed
-        self.trees_variance_fractions = {}
-        self.V_U_total = {}
-        self.V_U_individual = {}
+        self.trees_variance_fractions: dict = {}
+        self.V_U_total: Dict[Tuple[int, ...], List[Any]] = {}
+        self.V_U_individual: Dict[Tuple[int, ...], List[Any]] = {}
 
         # Set cut-off
         self._model.set_cutoffs(self.cutoffs[0], self.cutoffs[1])
@@ -123,16 +161,29 @@ def _train(self, X: np.ndarray, Y: np.ndarray) -> None:
         # recompute the trees' total variance
         self.trees_total_variance = self._model.get_trees_total_variances()
 
-    def compute_marginals(self, hp_ids: List[int], depth=1):
+    def compute_marginals(
+        self, hp_ids: Union[List[int], Tuple[int, ...]], depth: int = 1
+    ) -> Tuple[Dict[Tuple[int, ...], List[Any]], Dict[Tuple[int, ...], List[Any]],]:
         """
-        Returns the marginal of selected parameters.
+        Return the marginal of selected Hyperparameters.
 
         Parameters
         ----------
-        hp_ids: List[int]
-            Contains the indices of the configspace for the selected parameters (starts with 0).
+        hp_ids: Union[List[int], Tuple[int, ...]]
+            Contains the indices of the configspace for the selected Hyperparameters
+            (starts with 0).
+        depth: int
+            The depth of the marginalization.
+            Default value is 1.
+
+        Returns
+        -------
+        Tuple[Dict[Tuple[int, ...], List[Any]],
+        Dict[Tuple[int, ...], List[Any]],
+            The marginal of selected Hyperparameters.
         """
-        hp_ids = tuple(hp_ids)
+        if not isinstance(hp_ids, tuple):
+            hp_ids = tuple(hp_ids)
 
         # check if values has been previously computed
         if hp_ids in self.V_U_individual:
@@ -164,7 +215,7 @@ def compute_marginals(self, hp_ids: List[int], depth=1):
             prod_midpoints = it.product(*midpoints)
             prod_sizes = it.product(*sizes)
 
-            sample = np.full(self.n_params, np.nan, dtype=float)
+            sample: np.ndarray = np.full(self.n_params, np.nan, dtype=float)
 
             # make prediction for all midpoints and weigh them by the corresponding size
             for i, (m, s) in enumerate(zip(prod_midpoints, prod_sizes)):
@@ -178,7 +229,7 @@ def compute_marginals(self, hp_ids: List[int], depth=1):
             # line 10 in algorithm 2
             # note that V_U^2 can be computed by var(\hat a)^2 - \sum_{subU} var(f_subU)^2
             # which is why, \hat{f} is never computed in the code, but
-            # appears in the pseudocode
+            # appears in the pseudo code
             V_U_total = np.nan
             V_U_individual = np.nan
 
diff --git a/deepcave/evaluators/epm/random_forest.py b/deepcave/evaluators/epm/random_forest.py
index fc4d1e61..06bc9172 100644
--- a/deepcave/evaluators/epm/random_forest.py
+++ b/deepcave/evaluators/epm/random_forest.py
@@ -1,8 +1,23 @@
-from typing import Dict, Optional, Tuple
+# noqa: D400
+"""
+# RandomForest
+
+This module can be used for training and using a Random Forest Regression model.
+
+A pyrfr wrapper is used for simplification.
+
+## Classes
+    - RandomForest: A random forest wrapper for pyrfr.
+
+## Constants
+    VERY_SMALL_NUMBER : float
+    PYRFR_MAPPING : Dict[str, str]
+"""
+
+from typing import Any, Dict, List, Optional, Tuple, Union
 
 import functools
 import warnings
-from random import random
 
 import numpy as np
 import pyrfr.regression as regression
@@ -34,12 +49,39 @@
 
 class RandomForest:
     """
-    A random forest wrapper for pyrfr. This is handy because we only need to pass the configspace
+    A random forest wrapper for pyrfr.
+
+    This is handy because only the configuration space needs to be passed.
     and have a working version without specifying e.g. types and bounds.
 
     Note
     ----
     This wrapper also supports instances.
+
+    Properties
+    ----------
+    cs : ConfigurationSpace
+        The configuration space.
+    log_y : bool
+        Whether y should be treated as a logarithmic transformation.
+    seed : int
+        The seed. If not provided, it is random.
+    types : List[int]
+        The types of the Hyperparameters.
+    bounds : List[Tuple[float, float]]
+        The bounds of the Hyperparameters.
+    n_params : int
+        The number of Hyperparameters in the configuration space.
+    n_features : int
+        The number of features.
+    pca_components : int
+        The number of components to keep for the principal component analysis (PCA).
+    pca : PCA
+        The principal component analysis (PCA) object.
+    scaler : MinMaxScaler
+        A MinMaxScaler to scale the features.
+    instance_features : ndarray
+        The instance features.
     """
 
     def __init__(
@@ -56,14 +98,15 @@ def __init__(
         instance_features: Optional[np.ndarray] = None,
         pca_components: Optional[int] = 2,
         log_y: bool = False,
-        seed: int = 0,
+        seed: Optional[int] = 0,
     ):
         self.cs = configspace
         self.log_y = log_y
         self.seed = seed
 
         # Set types and bounds automatically
-        self.types, self.bounds = get_types(configspace, instance_features)
+        types, self.bounds = get_types(configspace, instance_features)
+        self.types = np.array(types)
 
         # Prepare everything for PCA
         self.n_params = len(configspace.get_hyperparameters())
@@ -95,7 +138,7 @@ def __init__(
 
     def _get_model(self) -> regression.base_tree:
         """
-        Returns the internal model.
+        Return the internal model.
 
         Returns
         -------
@@ -104,26 +147,34 @@ def _get_model(self) -> regression.base_tree:
         """
         return regression.binary_rss_forest()
 
-    def _get_model_options(self, **kwargs) -> regression.forest_opts:
+    def _get_model_options(self, **kwargs: Union[int, float, bool]) -> regression.forest_opts:
         """
-        Get model options from kwargs. The mapping `PYRFR_MAPPING` is used in combination with
+        Get model options from kwargs.
+
+        The mapping `PYRFR_MAPPING` is used in combination with
         a recursive attribute setter to set the options for the pyrfr model.
 
+        Parameters
+        ----------
+        **kwargs : Dict[str, Any]
+            The key word arguments for the model options.
+
         Returns
         -------
         options : regression.forest_opts
             Random forest options.
         """
-        # Now we set the options
+        # Now the options are set
         options = regression.forest_opts()
 
-        def rgetattr(obj, attr, *args):
-            def _getattr(obj, attr):
+        def rgetattr(obj: object, attr: str, *args: Any) -> Any:
+            def _getattr(obj: object, attr: object) -> Any:
+                attr = str(attr)
                 return getattr(obj, attr, *args)
 
             return functools.reduce(_getattr, [obj] + attr.split("."))
 
-        def rsetattr(obj, attr, val):
+        def rsetattr(obj: object, attr: str, val: Any) -> None:
             pre, _, post = attr.rpartition(".")
             return setattr(rgetattr(obj, pre) if pre else obj, post, val)
 
@@ -135,7 +186,7 @@ def rsetattr(obj, attr, val):
 
     def _impute_inactive(self, X: np.ndarray) -> np.ndarray:
         """
-        Imputs inactive values in X.
+        Impute inactive values in X.
 
         Parameters
         ----------
@@ -150,10 +201,10 @@ def _impute_inactive(self, X: np.ndarray) -> np.ndarray:
         Raises
         ------
         ValueError
-            If hyperparameter is not supported.
+            If Hyperparameter is not supported.
         """
-        conditional = {}  # type: Dict[int, bool]
-        impute_values = {}  # type: Dict[int, float]
+        conditional: Dict[int, bool] = {}
+        impute_values: Dict[int, float] = {}
 
         X = X.copy()
         for idx, hp in enumerate(self.cs.get_hyperparameters()):
@@ -180,7 +231,7 @@ def _impute_inactive(self, X: np.ndarray) -> np.ndarray:
 
     def _check_dimensions(self, X: np.ndarray, Y: Optional[np.ndarray] = None) -> None:
         """
-        Checks if the dimensions of X and Y are correct with respect to features.
+        Check if the dimensions of X and Y are correct with respect to features.
 
         Parameters
         ----------
@@ -210,8 +261,9 @@ def _get_data_container(
         self, X: np.ndarray, y: np.ndarray
     ) -> regression.default_data_container:
         """
-        Fills a pyrfr default data container s.t. the forest knows
-        categoricals and bounds for continuous data.
+        Fill a pyrfr default data container.
+
+        The goal here is, that the forest knows categoricals and bounds for continuous data.
 
         Parameters
         ----------
@@ -223,9 +275,8 @@ def _get_data_container(
         Returns
         -------
         data : regression.default_data_container
-            The filled data container that pyrfr can interpret
+            The filled data container that pyrfr can interpret.
         """
-
         # retrieve the types and the bounds from the ConfigSpace
         data = regression.default_data_container(X.shape[1])
 
@@ -242,7 +293,9 @@ def _get_data_container(
 
     def train(self, X: np.ndarray, Y: np.ndarray) -> None:
         """
-        Trains the random forest on X and Y. Transforms X if PCA is applied.
+        Train the random forest on X and Y.
+
+        Transform X if principal component analysis (PCA) is applied.
         Afterwards, `_train` is called.
 
         Parameters
@@ -283,7 +336,7 @@ def train(self, X: np.ndarray, Y: np.ndarray) -> None:
 
     def _train(self, X: np.ndarray, Y: np.ndarray) -> None:
         """
-        Trains the random forest on X and Y.
+        Train the random forest on X and Y.
 
         Parameters
         ----------
@@ -302,7 +355,7 @@ def _train(self, X: np.ndarray, Y: np.ndarray) -> None:
         self._model.options.num_data_points_per_tree = X.shape[0]
         self._model.fit(data, rng=rng)
 
-    def predict(self, X: np.ndarray) -> Tuple[np.ndarray, Optional[np.ndarray]]:
+    def predict(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
         """
         Predict means and variances for a given X.
 
@@ -315,7 +368,7 @@ def predict(self, X: np.ndarray) -> Tuple[np.ndarray, Optional[np.ndarray]]:
         -------
         means : np.ndarray [n_samples, n_objectives]
             Predictive mean.
-        vars : Optional[np.ndarray] [n_samples, n_objectives] or [n_samples, n_samples]
+        vars : np.ndarray [n_samples, n_objectives] or [n_samples, n_samples]
             Predictive variance or standard deviation.
         """
         self._check_dimensions(X)
@@ -343,13 +396,14 @@ def predict(self, X: np.ndarray) -> Tuple[np.ndarray, Optional[np.ndarray]]:
 
         return mean, var
 
-    def _predict(self, X: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
+    def _predict(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
         """
         Predict means and variances for a given X.
 
         Parameters
         ----------
-        X : np.ndarray [n_samples, n_features (config + instance features)]
+        X : np.ndarray
+            [n_samples, n_features (config + instance features)]
 
         Returns
         -------
@@ -358,7 +412,6 @@ def _predict(self, X: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
         vars : np.ndarray [n_samples, 1]
             Predictive variance.
         """
-
         self._check_dimensions(X)
         X = self._impute_inactive(X)
 
@@ -366,7 +419,8 @@ def _predict(self, X: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
             all_preds = []
             third_dimension = 0
 
-            # Gather data in a list of 2d arrays and get statistics about the required size of the 3d array
+            # Gather data in a list of 2d arrays and get statistics about the required size of the
+            # 3d array
             for row_X in X:
                 preds_per_tree = self._model.all_leaf_values(row_X)
                 all_preds.append(preds_per_tree)
@@ -374,9 +428,9 @@ def _predict(self, X: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
                 third_dimension = max(max_num_leaf_data, third_dimension)
 
             # Transform list of 2d arrays into a 3d array
-            preds_as_array = (
-                np.zeros((X.shape[0], self._model_options.num_trees, third_dimension)) * np.NaN
-            )
+            num_trees = self._model.options.num_trees
+            shape = (X.shape[0], num_trees, third_dimension)
+            preds_as_array = np.zeros(shape) * np.NaN
             for i, preds_per_tree in enumerate(all_preds):
                 for j, pred in enumerate(preds_per_tree):
                     preds_as_array[i, j, : len(pred)] = pred
@@ -402,7 +456,8 @@ def _predict(self, X: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
     def predict_marginalized(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
         """
         Predict mean and variance marginalized over all instances.
-        Returns the predictive mean and variance marginalised over all
+
+        Return the predictive mean and variance marginalized over all
         instances for a set of configurations.
 
         Parameters
@@ -430,13 +485,11 @@ def predict_marginalized(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
         X = self._impute_inactive(X)
 
         # marginalized predictions for each tree
-        dat_ = np.zeros((X.shape[0], self._model_options.num_trees))
+        dat_ = np.zeros((X.shape[0], self._model.options.num_trees))
         for i, x in enumerate(X):
-
             # marginalize over instances
             # 1. get all leaf values for each tree
-            # type: list[list[float]]
-            preds_trees = [[] for i in range(self._model_options.num_trees)]
+            preds_trees: List[List[float]] = [[] for i in range(self._model.options.num_trees)]
 
             for feat in self.instance_features:
                 x_ = np.concatenate([x, feat])
@@ -446,10 +499,10 @@ def predict_marginalized(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
 
             # 2. average in each tree
             if self.log_y:
-                for tree_id in range(self._model_options.num_trees):
+                for tree_id in range(self._model.options.num_trees):
                     dat_[i, tree_id] = np.log(np.exp(np.array(preds_trees[tree_id])).mean())
             else:
-                for tree_id in range(self._model_options.num_trees):
+                for tree_id in range(self._model.options.num_trees):
                     dat_[i, tree_id] = np.array(preds_trees[tree_id]).mean()
 
         # 3. compute statistics across trees
@@ -465,5 +518,18 @@ def predict_marginalized(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
 
         return mean_, var
 
-    def get_leaf_values(self, x: np.ndarray):
+    def get_leaf_values(self, x: np.ndarray) -> regression.binary_rss_forest:
+        """
+        Get the leaf values of the model.
+
+        Parameters
+        ----------
+        x : np.ndarray
+            Input data array.
+
+        Returns
+        -------
+        regression.binary_rss_forest
+            The leaf values of the model.
+        """
         return self._model.all_leaf_values(x)
diff --git a/deepcave/evaluators/epm/random_forest_surrogate.py b/deepcave/evaluators/epm/random_forest_surrogate.py
index 09e78f50..29bfcf86 100644
--- a/deepcave/evaluators/epm/random_forest_surrogate.py
+++ b/deepcave/evaluators/epm/random_forest_surrogate.py
@@ -1,4 +1,16 @@
-from typing import Tuple
+# noqa: D400
+"""
+# RandomForest Surrogate
+
+This module provides a RandomForest Surrogate model.
+
+Mean and standard deviation values can be predicted for a given input with this module.
+
+## Classes
+    - RandomForestSurrogate: Random forest surrogate for the pyPDP package.
+"""
+
+from typing import Optional, Tuple
 
 import ConfigSpace as CS
 import numpy as np
@@ -10,19 +22,44 @@
 class RandomForestSurrogate(SurrogateModel):
     """
     Random forest surrogate for the pyPDP package.
+
+    Predict deviations and fit the model.
     """
 
     def __init__(
         self,
         configspace: CS.ConfigurationSpace,
-        seed: int = None,
+        seed: Optional[int] = None,
     ):
         super().__init__(configspace, seed=seed)
         self._model = RandomForest(configspace=configspace, seed=seed)
 
     def predict(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
+        """
+        Predict the deviations.
+
+        Parameters
+        ----------
+        X : np.ndarray
+            The data points on which to predict.
+
+        Returns
+        -------
+        Tuple[np.ndarray, np.ndarray]
+            The means and standard deviation.
+        """
         means, stds = self._model.predict(X)
         return means[:, 0], stds[:, 0]
 
-    def _fit(self, X: np.ndarray, y: np.ndarray):
+    def _fit(self, X: np.ndarray, y: np.ndarray) -> None:
+        """
+        Train the surrogate model.
+
+        Parameters
+        ----------
+        X : np.ndarray
+            Input data points.
+        y : np.ndarray
+            Corresponding target values.
+        """
         self._model.train(X, y)
diff --git a/deepcave/evaluators/epm/utils.py b/deepcave/evaluators/epm/utils.py
index 6505fe96..c466f641 100644
--- a/deepcave/evaluators/epm/utils.py
+++ b/deepcave/evaluators/epm/utils.py
@@ -1,3 +1,11 @@
+#  noqa: D400
+"""
+# Utils
+
+This module provides a utility to get the types
+as well as the bounds of the Hyperparameters.
+"""
+
 import typing
 
 import numpy as np
@@ -19,8 +27,30 @@ def get_types(
     config_space: ConfigurationSpace,
     instance_features: typing.Optional[np.ndarray] = None,
 ) -> typing.Tuple[typing.List[int], typing.List[typing.Tuple[float, float]]]:
-    """Return the types of the hyperparameters and the bounds of the
-    hyperparameters and instance features.
+    """
+    Return the types of the Hyperparameters.
+
+    Also return the bounds of the Hyperparameters and instance features.
+
+    Parameters
+    ----------
+    config_space : ConfigurationSpace
+        The configuration space.
+    instance_features : Optional[np.ndarray], optional
+        The instance features.
+        Default is None.
+
+    Returns
+    -------
+    Tuple[typing.List[int], List[Tuple[float, float]]]
+        The types of the Hyperparameters, as well as the bounds and instance features.
+
+    Raises
+    ------
+    ValueError
+        Inactive parameters not supported for Beta and Normal Hyperparameters.
+    TypeError
+        If the Hyperparameter Type is unknown.
     """
     # Extract types vector for rf from config space and the bounds
     types = [0] * len(config_space.get_hyperparameters())
@@ -47,7 +77,7 @@ def get_types(
             else:
                 bounds[i] = (0, int(n_cats) - 1)
         elif isinstance(param, Constant):
-            # for constants we simply set types to 0 which makes it a numerical
+            # for constants types are simply set to 0 which makes it a numerical
             # parameter
             if can_be_inactive:
                 bounds[i] = (2, np.nan)
@@ -55,7 +85,7 @@ def get_types(
             else:
                 bounds[i] = (0, np.nan)
                 types[i] = 0
-            # and we leave the bounds to be 0 for now
+            # and the bounds are left to be 0 for now
         elif isinstance(param, UniformFloatHyperparameter):
             # Are sampled on the unit hypercube thus the bounds
             # are always 0.0, 1.0
diff --git a/deepcave/evaluators/fanova.py b/deepcave/evaluators/fanova.py
index f6873610..0feb2b4f 100644
--- a/deepcave/evaluators/fanova.py
+++ b/deepcave/evaluators/fanova.py
@@ -1,4 +1,16 @@
-from typing import Dict, List, Optional, Tuple, Union
+#  noqa: D400
+"""
+# fANOVA
+
+This module provides a tool for assessing the importance of an algorithms Hyperparameters.
+
+Utilities provide calculation of the data wrt the budget and train the forest on the encoded data.
+
+## Classes
+    - fANOVA: Calculate and provide midpoints and sizes.
+"""
+
+from typing import Any, Dict, List, Optional, Tuple, Union
 
 import itertools as it
 
@@ -12,8 +24,22 @@
 
 class fANOVA:
     """
-    Calculate and provide midpoints and sizes from the forest's
-    split values in order to get the marginals.
+    Calculate and provide midpoints and sizes.
+
+    They are generated from the forest's split values in order to get the marginals.
+
+    Properties
+    ----------
+    run : AbstractRun
+        The Abstract Run used for the calculation.
+    cs : ConfigurationSpace
+        The configuration space of the run.
+    hps : List[Hyperparameters]
+        The Hyperparameters of the configuration space.
+    hp_names : List[str]
+        The corresponding names of the Hyperparameters.
+    n_trees : int
+        The number of trees.
     """
 
     def __init__(self, run: AbstractRun):
@@ -33,7 +59,7 @@ def calculate(
         seed: int = 0,
     ) -> None:
         """
-        Get the data with respect to budget and trains the forest on the encoded data.
+        Get the data with respect to budget and train the forest on the encoded data.
 
         Note
         ----
@@ -42,7 +68,7 @@ def calculate(
         Parameters
         ----------
         objectives : Optional[Union[Objective, List[Objective]]], optional
-            Considerd objectives. By default None. If None, all objectives are considered.
+            Considered objectives. By default None. If None, all objectives are considered.
         budget : Optional[Union[int, float]], optional
             Considered budget. By default None. If None, the highest budget is chosen.
         n_trees : int, optional
@@ -54,7 +80,7 @@ def calculate(
             objectives = self.run.get_objectives()
 
         if budget is None:
-            budget = self.get_highest_budget()
+            budget = self.run.get_highest_budget()
 
         self.n_trees = n_trees
 
@@ -74,7 +100,7 @@ def get_importances(
         self, hp_names: Optional[List[str]] = None, depth: int = 1, sort: bool = True
     ) -> Dict[Union[str, Tuple[str, ...]], Tuple[float, float, float, float]]:
         """
-        Returns the importance scores from the passed hyperparameter names.
+        Return the importance scores from the passed Hyperparameter names.
 
         Warning
         -------
@@ -83,17 +109,17 @@ def get_importances(
         Parameters
         ----------
         hp_names : Optional[List[str]]
-            Selected hyperparameter names to get the importance scores from. If None, all
-            hyperparameters of the configspace are used.
+            Selected Hyperparameter names to get the importance scores from. If None, all
+            Hyperparameters of the configuration space are used.
         depth : int, optional
             How often dimensions should be combined. By default 1.
         sort : bool, optional
-            Whether the hyperparameters should be sorted by importance. By default True.
+            Whether the Hyperparameters should be sorted by importance. By default True.
 
         Returns
         -------
         Dict[Union[str, Tuple[str, ...]], Tuple[float, float, float, float]]
-            Dictionary with hyperparameter names and the corresbonding importance scores.
+            Dictionary with Hyperparameter names and the corresponding importance scores.
             The values are tuples of the form (mean individual, var individual, mean total,
             var total). Note that individual and total are the same if depth is 1.
 
@@ -112,14 +138,13 @@ def get_importances(
         # Calculate the marginals
         vu_individual, vu_total = self._model.compute_marginals(hp_ids, depth)
 
-        importances = {}
+        importances: Dict[Tuple[Any, ...], Tuple[float, float, float, float]] = {}
         for k in range(1, len(hp_ids) + 1):
             if k > depth:
                 break
 
             for sub_hp_ids in it.combinations(hp_ids, k):
                 sub_hp_ids = tuple(sub_hp_ids)
-                importances[sub_hp_ids] = {}
 
                 # clean here to catch zero variance in a trees
                 non_zero_idx = np.nonzero(
@@ -155,23 +180,24 @@ def get_importances(
                 k: v for k, v in sorted(importances.items(), key=lambda item: item[1][2])
             }
 
-        # We want to replace the ids with hyperparameter names again
+        # The ids get replaced with hyperparameter names again
         all_hp_names = self.cs.get_hyperparameter_names()
-        importances_ = {}
-        for hp_ids, values in importances.items():
-            hp_names = [all_hp_names[hp_id] for hp_id in hp_ids]
+        importances_: Dict[Union[str, Tuple[str, ...]], Tuple[float, float, float, float]] = {}
+        for hp_ids_importances, values in importances.items():
+            hp_names = [all_hp_names[hp_id] for hp_id in hp_ids_importances]
+            hp_names_key: Union[Tuple[str, ...], str]
             if len(hp_names) == 1:
-                hp_names = hp_names[0]
+                hp_names_key = hp_names[0]
             else:
-                hp_names = tuple(hp_names)
-            importances_[hp_names] = values
+                hp_names_key = tuple(hp_names)
+            importances_[hp_names_key] = values
 
         return importances_
 
     '''
     def marginal_mean_variance_for_values(self, dimlist, values_to_predict):
         """
-        Returns the marginal of selected parameters for specific values
+        Return the marginal of selected parameters for specific values
 
         Parameters
         ----------
@@ -194,7 +220,7 @@ def marginal_mean_variance_for_values(self, dimlist, values_to_predict):
 
     def get_most_important_pairwise_marginals(self, params=None, n=10):
         """
-        Returns the n most important pairwise marginals from the whole ConfigSpace.
+        Return the n most important pairwise marginals from the whole ConfigSpace.
 
         Parameters
         ----------
@@ -241,7 +267,7 @@ def get_most_important_pairwise_marginals(self, params=None, n=10):
 
     def get_triple_marginals(self, params=None):
         """
-        Returns the n most important pairwise marginals from the whole ConfigSpace
+        Return the n most important pairwise marginals from the whole ConfigSpace
 
         Parameters
         ----------
diff --git a/deepcave/evaluators/footprint.py b/deepcave/evaluators/footprint.py
index 09d98904..f7356b48 100644
--- a/deepcave/evaluators/footprint.py
+++ b/deepcave/evaluators/footprint.py
@@ -1,3 +1,16 @@
+#  noqa: D400
+"""
+# Footprint
+
+This module provides utilities for creating a footprint of a run.
+It uses multidimensional scaling (MDS).
+It also provides utilities to get the surface and the points of the plot.
+
+
+## Classes
+    - Footprint: Can train and create a footprint of a run.
+"""
+
 from typing import List, Optional, Tuple, Union
 
 import numpy as np
@@ -20,6 +33,20 @@
 
 
 class Footprint:
+    """
+    Can train and create a footprint of a run.
+
+    It uses multidimensional scaling (MDS).
+    Provides utilities to get the surface and the points of the plot.
+
+    Properties
+    ----------
+    run : AbstractRun
+        The AbstractRun used for the calculation of the footprint.
+    cs : ConfigurationSpace
+        The configuration space of the run.
+    """
+
     def __init__(self, run: AbstractRun):
         if run.configspace is None:
             raise RuntimeError("The run needs to be initialized.")
@@ -36,15 +63,16 @@ def __init__(self, run: AbstractRun):
                 is_categorical.append(False)
             depth.append(self._get_depth(hp))
 
-        self._is_categorical = np.array(is_categorical)  # type: ignore
-        self._depth = np.array(depth)  # type: ignore
+        self._is_categorical = np.array(is_categorical)
+        self._depth = np.array(depth)
 
         # Global variables
-        self._distances = None
+        self._distances: Optional[np.ndarray] = None
         self._trained = False
         self._reset()
 
     def _reset(self) -> None:
+        """Reset the footprint."""
         self._objective_model: Optional[RandomForestRegressor] = None
         self._area_model: Optional[RandomForestRegressor] = None
         self._config_ids: Optional[List[int]] = None
@@ -65,25 +93,30 @@ def calculate(
         exclude_configs: bool = False,
     ) -> None:
         """
-        Calculates the distances and trains the model.
+        Calculate the distances and train the model.
 
         Parameters
         ----------
         objective : Objective
-            Objective and colour to show.
+            Objective and color to show.
         budget : Union[int, float]
             All configurations with this budget are considered.
         support_discretization : Optional[int], optional
-            Discretization steps for integer and float hyperparameter values.
+            Discretization steps for integer and float hyperparameter (HP) values.
+            Default is set to 10.
         rejection_rate : float, optional
             Rejection rate whether a configuration should be rejected or not. Internally,
             the max distance is calculated and if a configuration has a distance smaller than
             max distance * rejection_rate, the configuration is rejected.
+            Default is set to 0.01.
         retries : int, optional
             How many times to retry adding a new configuration.
+            Default is set to 3.
         exclude_configs : bool, optional
-            Whether the configurations from the run should be excluded in the MDS scaling.
-            This is particullary interseting if only the search space should be plotted.
+            Whether the configurations from the run should be excluded
+            in the multidimensional scaling (MDS).
+            This is particularly interesting if only the search space should be plotted.
+            Default is set to False.
         """
         # Reset everything
         self._reset()
@@ -103,7 +136,7 @@ def calculate(
         # Make numpy arrays
         X = data[hp_names].to_numpy()
         Y = data[objective.name].to_numpy()
-        config_ids = data["config_id"].values.tolist()  # type: ignore
+        config_ids = data["config_id"].values.tolist()
 
         # Get the incumbent
         incumbent_config, _ = self.run.get_incumbent(objective, budget)
@@ -114,11 +147,12 @@ def calculate(
 
         # Init distances
         self._init_distances(X, config_ids, exclude_configs=exclude_configs)
+        assert self._distances is not None
 
         border_generator = sample_border_config(self.cs)
         random_generator = sample_random_config(self.cs, d=support_discretization)
 
-        # Now we add the border and random configs
+        # Now the border and random configs are added
         count_border = 0
         count_random = 0
         tries = 0
@@ -145,8 +179,8 @@ def calculate(
                     continue
 
                 # Encode config
-                config = np.array(self.run.encode_config(config))
-                rejected = self._update_distances(config, config_id, rejection_threshold)
+                config_array = np.array(self.run.encode_config(config))
+                rejected = self._update_distances(config_array, config_id, rejection_threshold)
                 if not rejected:
                     # Count
                     if config_id == BORDER_CONFIG_ID:
@@ -158,7 +192,7 @@ def calculate(
                     counter += 1
 
             # Abort criteria
-            # If we don't get new configs
+            # If there are no new configs
             if counter == 0:
                 tries += 1
             else:
@@ -167,16 +201,13 @@ def calculate(
             if tries >= retries:
                 break
 
-            # Or if we reach more than 4000 (otherwise it takes too long)
-            assert self._distances is not None
-
+            # Or if reach more than 4000 are reached (otherwise it takes too long)
             if self._distances.shape[0] % 100 == 0:
                 logger.info(f"Found {self._distances.shape[0]} configurations...")
 
             if self._distances.shape[0] > 4000:
                 break
 
-        assert self._distances is not None
         logger.info(f"Added {count_border} border configs and {count_random} random configs.")
         logger.info(f"Total configurations: {self._distances.shape[0]}.")
         logger.info("Getting MDS data...")
@@ -186,8 +217,8 @@ def calculate(
         self._MDS_X = MDS_X
 
         # But here's the catch: Get rid of border and random configs because
-        # we don't have the y values for them.
-        # However, it makes no sense to train the RF if we exclude the configs
+        # the y values for them are not known.
+        # However, it makes no sense to train the RF if the configs are excluded
         # which were evaluated.
         if not exclude_configs:
             self._train_on_objective(MDS_X[: len(X)], Y.ravel())
@@ -202,7 +233,7 @@ def get_surface(
         self, details: float = 0.5, performance: bool = True
     ) -> Tuple[List, List, List]:
         """
-        Get surface of the MDS plot.
+        Get surface of the multidimensional scaling (MDS) plot.
 
         Parameters
         ----------
@@ -210,6 +241,7 @@ def get_surface(
             Steps to create the meshgrid. By default 0.5.
         performance : bool, optional
             Whether to get the surface from the performance or the valid areas.
+            Default is set to True.
 
         Returns
         -------
@@ -220,6 +252,8 @@ def get_surface(
         ------
         RuntimeError
             If `calculate` was not called before.
+        RuntimeError
+            If evaluated configs weren't included.
         """
         X = self._MDS_X
         if X is None:
@@ -255,7 +289,7 @@ def get_surface(
 
     def get_points(self, category: str = "configs") -> Tuple[List[float], List[float], List[int]]:
         """
-        Returns the points of the MDS plot.
+        Return the points of the multidimensional scaling (MDS) plot.
 
         Parameters
         ----------
@@ -272,6 +306,8 @@ def get_points(self, category: str = "configs") -> Tuple[List[float], List[float
         ------
         RuntimeError
             If category is not supported.
+        RuntimeError
+            If calculated wasn't called before.
         """
         if category not in ["configs", "borders", "supports", "incumbents"]:
             raise RuntimeError("Unknown category.")
@@ -289,7 +325,7 @@ def get_points(self, category: str = "configs") -> Tuple[List[float], List[float
                 or (category == "incumbents" and config_id == self._incumbent_id)
                 or (category == "supports" and config_id == RANDOM_CONFIG_ID)
             ):
-                x = x.tolist()  # type: ignore
+                x = x.tolist()
                 X += [x[0]]
                 Y += [x[1]]
                 config_ids += [config_id]
@@ -299,15 +335,16 @@ def get_points(self, category: str = "configs") -> Tuple[List[float], List[float
     def _get_max_distance(self) -> float:
         """
         Calculate the maximum distance between all configs.
-        Basically, we just count the number of hps.
+
+        Basically, the number of Hyperparameters are just counted.
 
         Returns
         -------
         float
             Maximal distance between two configurations.
         """
-        # We just count the number of hps
-        # Since X is normalized, we can just sum 1
+        # The number of hps is just counted
+        # Since X is normalized, 1 can just be added
         max_distance = 0
         for hp in self.cs.get_hyperparameters():
             if isinstance(hp, CategoricalHyperparameter) or isinstance(hp, Constant):
@@ -319,7 +356,7 @@ def _get_max_distance(self) -> float:
 
     def _get_distance(self, x: np.ndarray, y: np.ndarray) -> float:
         """
-        Calculates distance between x and y. Both arrays must have the same length.
+        Calculate distance between x and y. Both arrays must have the same length.
 
         Parameters
         ----------
@@ -332,6 +369,11 @@ def _get_distance(self, x: np.ndarray, y: np.ndarray) -> float:
         -------
         float
             Distance from configuration 1 and configuration 2.
+
+        Raises
+        ------
+        RuntimeError
+            If calculate wasn't called first.
         """
         if self._depth is None or self._is_categorical is None:
             raise RuntimeError("You need to call `calculate` first.")
@@ -344,9 +386,22 @@ def _get_distance(self, x: np.ndarray, y: np.ndarray) -> float:
         return d
 
     def _get_distances(self, X: np.ndarray) -> np.ndarray:
+        """
+        Get the distances between the configurations.
+
+        Parameters
+        ----------
+        X : np.ndarray
+            The configurations.
+
+        Returns
+        -------
+        np.ndarray
+            The calculated distances.
+        """
         n_configs = X.shape[0]
 
-        # We initiate the distances
+        # The distances are initiated
         distances = np.zeros((n_configs, n_configs))
 
         for i in tqdm(range(n_configs)):
@@ -361,14 +416,14 @@ def _init_distances(
         self, X: np.ndarray, config_ids: List[int], exclude_configs: bool = False
     ) -> None:
         """
-        Initializes the distances.
+        Initialize the distances.
 
         Parameters
         ----------
         X : np.ndarray
             Encoded data.
         config_ids : List[int]
-            Corresponding config_ids.
+            Corresponding configuration ids.
         exclude_configs : bool, optional
             Whether the passed X should be used or not. By default False.
         """
@@ -389,24 +444,26 @@ def _update_distances(
         rejection_threshold: Optional[float] = 0.0,
     ) -> bool:
         """
-        Updates the internal distance if the passed config is not rejectded.
+        Update the internal distance if the passed configuration is not rejected.
 
         Parameters
         ----------
         config : np.ndarray
-            Config, which is tried to be added.
+            Configuration, which is tried to be added.
         config_id : int
-            Corresponding config id. This is important for later identification as the config might
-            be a border or random configuration.
+            Corresponding configuration id. This is important for later identification
+            as the configuration might be a border or random configuration.
         rejection_threshold : Optional[float], optional
-            Threshold for rejecting the config. By default 0.0.
+            Threshold for rejecting the configuration. By default 0.0.
 
         Returns
         -------
         rejected : bool
-            Whether the config was rejected or not.
+            Whether the configuration was rejected or not.
         """
         X = self._X
+
+        assert self._distances is not None
         distances = self._distances
 
         if X is None:
@@ -443,23 +500,26 @@ def _update_distances(
                 X = np.concatenate((X, np.array([config])), axis=0)
 
             self._X = X
-            self._config_ids += [config_id]
+            # There is no += to a None, an Issue has already been created
+            if self._config_ids is not None:
+                self._config_ids += [config_id]
             self._distances = new_distances
 
         return rejected
 
     def _get_depth(self, hp: Hyperparameter) -> int:
         """
-        Get depth (generations above) in configuration space of a given hyperparameter.
+        Get depth (generations above) in configuration space of a given hyperparameter (HP).
 
         Parameters
         ----------
-        param: str
-            name of parameter to inspect
+        hp: Hyperparameter
+            name of Hyperparameter to inspect
 
         Returns
+        -------
         int
-            Depth of the hyperparameter.
+            Depth of the Hyperparameter.
         """
         parents = self.cs.get_parents_of(hp)
         if not parents:
@@ -482,17 +542,17 @@ def _get_depth(self, hp: Hyperparameter) -> int:
 
     def _get_mds(self) -> np.ndarray:
         """
-        Perform MDS on the internal distances.
-
-        Parameters
-        ----------
-        distances : np.ndarray
-            Numpy array with distances between configurations.
+        Perform multidimensional scaling (MDS) on the internal distances.
 
         Returns
         -------
         np.ndarray
-            Numpy array with MDS coordinates in 2D.
+            Numpy array with multidimensional scaling (MDS) coordinates in 2D.
+
+        Raises
+        ------
+        RuntimeError
+            When calculated wasn't called first.
         """
         if self._distances is None:
             raise RuntimeError("You need to call `calculate` first.")
@@ -502,12 +562,12 @@ def _get_mds(self) -> np.ndarray:
 
     def _train_on_objective(self, X: np.ndarray, Y: np.ndarray) -> None:
         """
-        Trains the random forest on the performance.
+        Train the random forest on the performance.
 
         Parameters
         ----------
         X : np.ndarray
-            Numpy array with MDS coordinates in 2D.
+            Numpy array with multidimensional scaling (MDS) coordinates in 2D.
         Y : np.ndarray
             Numpy array with costs.
         """
@@ -517,7 +577,12 @@ def _train_on_objective(self, X: np.ndarray, Y: np.ndarray) -> None:
 
     def _train_on_areas(self) -> None:
         """
-        Trains the random forest on the "valid" areas.
+        Train the random forest on the "valid" areas.
+
+        Raises
+        ------
+        RuntimeError
+            If calculated wasn't called first.
         """
         if self._MDS_X is None:
             raise RuntimeError("You need to call `calculate` first.")
@@ -525,7 +590,7 @@ def _train_on_areas(self) -> None:
         logger.info("Training on area...")
         MDS_X = self._MDS_X
 
-        # We basically have to create a grid here
+        # Basically, a grid has to be created here
         x_min, x_max = MDS_X[:, 0].min() - 1, MDS_X[:, 0].max() + 1
         y_min, y_max = MDS_X[:, 1].min() - 1, MDS_X[:, 1].max() + 1
 
@@ -548,9 +613,9 @@ def _train_on_areas(self) -> None:
                 X.append(center)
                 Y.append(value)
 
-        X = np.array(X)  # type: ignore
-        Y = np.array(Y)  # type: ignore
+        X_array = np.array(X)
+        Y_array = np.array(Y)
 
         # Train the model
         self._area_model = RandomForestRegressor(random_state=0)
-        self._area_model.fit(X, Y)
+        self._area_model.fit(X_array, Y_array)
diff --git a/deepcave/evaluators/lpi.py b/deepcave/evaluators/lpi.py
index 4abaecc6..832b42da 100644
--- a/deepcave/evaluators/lpi.py
+++ b/deepcave/evaluators/lpi.py
@@ -1,6 +1,14 @@
-from typing import Dict, List, Optional, Tuple, Union
+#  noqa: D400
+"""
+# LPI
 
-from random import random
+This module provides utilities to calculate the local parameter importance (LPI).
+
+## Classes
+    - LPI: This class calculates the local parameter importance (LPI).
+"""
+
+from typing import Any, Dict, List, Optional, Tuple, Union
 
 import numpy as np
 from ConfigSpace import Configuration
@@ -17,12 +25,41 @@
 
 # https://github.com/automl/ParameterImportance/blob/f4950593ee627093fc30c0847acc5d8bf63ef84b/pimp/evaluator/local_parameter_importance.py#L27
 class LPI:
+    """
+    Calculate the local parameter importance (LPI).
+
+    Properties
+    ----------
+    run : AbstractRun
+        The AbstractRun to get the importance from.
+    cs : ConfigurationSpace
+        The configuration space of the run.
+    hp_names : List[str]
+        The names of the Hyperparameters.
+    variances : Dict[Any, list]
+        The overall variances per tree.
+    importances : dict
+        The importances of the Hyperparameters.
+    continuous_neighbors : int
+        The number of neighbors chosen for continuous Hyperparameters.
+    incumbent : Configuration
+        The incumbent of the run.
+    default : Configuration
+        A configuration containing Hyperparameters with default values.
+    incumbent_array : numpy.ndarray
+        The internal vector representation of the incumbent.
+    seed : int
+        The seed. If not provided it will be random.
+    rs : RandomState
+        A random state with a given seed value.
+    """
+
     def __init__(self, run: AbstractRun):
         self.run = run
         self.cs = run.configspace
         self.hp_names = self.cs.get_hyperparameter_names()
-        self.variances = None
-        self.importances = None
+        self.variances: Optional[Dict[Any, List[Any]]] = None
+        self.importances: Optional[Dict[Any, Any]] = None
 
     def calculate(
         self,
@@ -33,22 +70,28 @@ def calculate(
         seed: int = 0,
     ) -> None:
         """
-        Prepares the data and trains a RandomForest model.
+        Prepare the data and train a RandomForest model.
 
         Parameters
         ----------
         objectives : Optional[Union[Objective, List[Objective]]], optional
-            Considerd objectives. By default None. If None, all objectives are considered.
+            Considered objectives. By default, None. If None, all objectives are considered.
         budget : Optional[Union[int, float]], optional
-            Considered budget. By default None. If None, the highest budget is chosen.
-        continous_neighbors : int, optional
-            How many neighbors should be chosen for continous hyperparameters. By default 500.
+            Considered budget. By default, None. If None, the highest budget is chosen.
+        continuous_neighbors : int, optional
+            How many neighbors should be chosen for continuous hyperparameters (HPs).
+            By default, 500.
+        n_trees : int, optional
+            The number of trees for the fanova forest.
+            Default is 10.
+        seed : Optional[int], optional
+            The seed. By default None. If None, a random seed is chosen.
         """
         if objectives is None:
             objectives = self.run.get_objectives()
 
         if budget is None:
-            budget = self.get_highest_budget()
+            budget = self.run.get_highest_budget()
 
         # Set variables
         self.continous_neighbors = continous_neighbors
@@ -74,21 +117,21 @@ def calculate(
         # Get neighborhood sampled on an unit-hypercube.
         neighborhood = self._get_neighborhood()
 
-        # We need the delta performance from the default configuration and the incumbent
+        # The delta performance is needed from the default configuration and the incumbent
         def_perf, def_var = self._predict_mean_var(self.default)
         inc_perf, inc_var = self._predict_mean_var(self.incumbent)
         delta = def_perf - inc_perf
 
         # These are used for plotting and hold the predictions for each neighbor of each parameter.
         # That means performances holds the mean, variances the variance of the forest.
-        performances = {}
-        variances = {}
-        # This are used for importance and hold the corresponding importance/variance over
+        performances: Dict[str, List[np.ndarray]] = {}
+        variances: Dict[str, List[np.ndarray]] = {}
+        # These are used for importance and hold the corresponding importance/variance over
         # neighbors. Only import if NOT quantifying importance via performance-variance across
-        # neighbours.
+        # neighbors.
         importances = {}
         # Nested list of values per tree in random forest.
-        predictions = {}
+        predictions: Dict[str, List[List[np.ndarray]]] = {}
 
         # Iterate over parameters
         for hp_idx, hp_name in enumerate(self.incumbent.keys()):
@@ -180,29 +223,32 @@ def calculate(
             p: [t / sum_var_per_tree[idx] for idx, t in enumerate(trees)]
             for p, trees in overall_var_per_tree.items()
         }
-
         self.variances = overall_var_per_tree
         self.importances = importances
 
     def get_importances(self, hp_names: List[str]) -> Dict[str, Tuple[float, float]]:
         """
-        Returns the importances.
+        Return the importances.
 
         Parameters
         ----------
         hp_names : List[str]
-            Selected hyperparameter names to get the importance scores from.
+            Selected Hyperparameter names to get the importance scores from.
 
         Returns
         -------
         importances : Dict[str, Tuple[float, float]]
             Hyperparameter name and mean+var importance.
-        """
 
+        Raises
+        ------
+        RuntimeError
+            If the important scores are not calculated.
+        """
         if self.importances is None or self.variances is None:
             raise RuntimeError("Importance scores must be calculated first.")
 
-        importances = {}
+        importances: Dict[str, Tuple[float, float]] = {}
         for hp_name in hp_names:
             mean = 0
             std = 0
@@ -224,15 +270,21 @@ def get_importances(self, hp_names: List[str]) -> Dict[str, Tuple[float, float]]
 
         return importances
 
-    def _get_neighborhood(self):
+    def _get_neighborhood(self) -> Dict[str, List[Union[np.ndarray, List[np.ndarray]]]]:
         """
-        Slight modification of ConfigSpace's get_one_exchange neighborhood. This orders the
-        parameter values and samples more neighbors in one go. Further we need to rigorously
-        check each and every neighbor if it is forbidden or not.
+        Slight modification of ConfigSpace's get_one_exchange neighborhood.
+
+        This orders the parameter values and samples more neighbors in one go.
+        Further each and every neighbor needs to be rigorously checked if it is forbidden or not.
+
+        Returns
+        -------
+        neighborhood : Dict[str, List[Union[np.ndarray, List[np.ndarray]]]]
+            The neighborhood.
         """
         hp_names = self.cs.get_hyperparameter_names()
 
-        neighborhood = {}
+        neighborhood: Dict[str, List[Union[np.ndarray, List[np.ndarray]]]] = {}
         for hp_idx, hp_name in enumerate(hp_names):
             # Check if hyperparameter is active
             if not np.isfinite(self.incumbent_array[hp_idx]):
@@ -251,7 +303,7 @@ def _get_neighborhood(self):
                     base = np.e
                     log_lower = np.log(hp.lower) / np.log(base)
                     log_upper = np.log(hp.upper) / np.log(base)
-                    neighbors = np.logspace(
+                    neighbors_range = np.logspace(
                         start=log_lower,
                         stop=log_upper,
                         num=self.continous_neighbors,
@@ -259,8 +311,8 @@ def _get_neighborhood(self):
                         base=base,
                     )
                 else:
-                    neighbors = np.linspace(hp.lower, hp.upper, self.continous_neighbors)
-                neighbors = list(map(lambda x: hp._inverse_transform(x), neighbors))
+                    neighbors_range = np.linspace(hp.lower, hp.upper, self.continous_neighbors)
+                neighbors = list(map(lambda x: hp._inverse_transform(x), neighbors_range))
             else:
                 neighbors = hp.get_neighbors(self.incumbent_array[hp_idx], self.rs)
 
@@ -286,40 +338,42 @@ def _get_neighborhood(self):
                 map(lambda x: x[0], sorted(enumerate(checked_neighbors), key=lambda y: y[1]))
             )
             if isinstance(self.cs.get_hyperparameter(hp_name), CategoricalHyperparameter):
-                checked_neighbors_non_unit_cube = list(
+                checked_neighbors_non_unit_cube_categorical = list(
                     np.array(checked_neighbors_non_unit_cube)[sort_idx]
                 )
+                neighborhood[hp_name] = [
+                    np.array(checked_neighbors)[sort_idx],
+                    checked_neighbors_non_unit_cube_categorical,
+                ]
             else:
-                checked_neighbors_non_unit_cube = np.array(checked_neighbors_non_unit_cube)[
-                    sort_idx
+                checked_neighbors_non_unit_cube_non_categorical = np.array(
+                    checked_neighbors_non_unit_cube
+                )[sort_idx]
+                neighborhood[hp_name] = [
+                    np.array(checked_neighbors)[sort_idx],
+                    checked_neighbors_non_unit_cube_non_categorical,
                 ]
 
-            neighborhood[hp_name] = [
-                np.array(checked_neighbors)[sort_idx],
-                checked_neighbors_non_unit_cube,
-            ]
-
         return neighborhood
 
-    def _predict_mean_var(self, config):
+    def _predict_mean_var(self, config: Configuration) -> Tuple[np.ndarray, np.ndarray]:
         """
         Small wrapper to predict marginalized over instances.
 
         Parameter
         ---------
         config:Configuration
-            The self.incumbent of wich the performance across the whole instance set is to be
+            The self.incumbent of which the performance across the whole instance set is to be
             estimated.
 
         Returns
         -------
-        mean
+        mean: np.ndarray
             The mean performance over the instance set.
-        var
+        var: np.ndarray
             The variance over the instance set. If logged values are used, the variance might not
             be able to be used.
         """
-
         config = impute_inactive_values(config)
         array = np.array([config.get_array()])
         mean, var = self._model.predict_marginalized(array)
diff --git a/deepcave/layouts/__init__.py b/deepcave/layouts/__init__.py
index 97bf67e8..926b760b 100644
--- a/deepcave/layouts/__init__.py
+++ b/deepcave/layouts/__init__.py
@@ -1,3 +1,10 @@
+#  noqa: D400
+"""
+# Layout
+
+This module provides a foundation to create layouts.
+"""
+
 from abc import ABC, abstractmethod
 from typing import List, Union
 
@@ -8,14 +15,23 @@
 
 
 class Layout(ABC):
+    """
+    A foundation for creating layouts.
+
+    Properties
+    ----------
+    logger : Logger
+        A logger for the class.
+    """
+
     def __init__(self) -> None:
         self.register_callbacks()
         self.logger = get_logger(self.__class__.__name__)
 
     @interactive
-    def register_callbacks(self) -> None:
+    def register_callbacks(self) -> None:  # noqa: D102
         pass
 
     @abstractmethod
-    def __call__(self) -> Union[List[Component], Component]:
+    def __call__(self) -> Union[List[Component], Component]:  # noqa: D102
         pass
diff --git a/deepcave/layouts/general.py b/deepcave/layouts/general.py
index 2965399d..2f96f4af 100644
--- a/deepcave/layouts/general.py
+++ b/deepcave/layouts/general.py
@@ -1,3 +1,15 @@
+#  noqa: D400
+"""
+# GeneralLayout
+
+This module provides the General Layout.
+
+It handles different callbacks of the layout.
+
+## Classes
+    - GeneralLayout: Provide different utilities to handle callbacks for the layout.
+"""
+
 from typing import Dict, List, Optional, Type
 
 from pathlib import Path
@@ -17,7 +29,17 @@
 
 
 class GeneralLayout(Layout):
+    """
+    Provide different utilities to handle callbacks for the general layout.
+
+    Properties
+    ----------
+    logger : Logger
+        A logger used for debug information of the groups.
+    """
+
     def register_callbacks(self) -> None:
+        """Register if there were any callbacks."""
         self._callback_working_directory_changed()
         self._callback_display_available_runs()
         self._callback_display_selected_runs()
@@ -27,6 +49,7 @@ def register_callbacks(self) -> None:
         self._callback_clear_cache()
 
     def _callback_working_directory_changed(self) -> None:
+        """Check for change in working directory."""
         inputs = [
             Input("on-page-load", "href"),
             Input("general-working-directory-input", "value"),
@@ -47,12 +70,24 @@ def _callback_working_directory_changed(self) -> None:
 
         # Register updates from inputs
         @app.callback(outputs, inputs)  # type: ignore
-        def callback(  # type: ignore
+        def callback(
             _,
             working_dir: str,
             dynamic_n_clicks: List[Optional[int]],
             dynamic_working_dirs: List[str],
         ):
+            """
+            Register updates from inputs.
+
+            Parameters
+            ----------
+            working_dir : str
+                The working directory.
+            dynamic_n_clicks : List[Optional[int]]
+                The number of clicks.
+            dynamic_working_dirs : List[str]
+                The working directories.
+            """
             # `working_dir` is only none on page load
             if working_dir is None:
                 # Handler working directory
@@ -74,11 +109,20 @@ def callback(  # type: ignore
             )
 
     def _callback_display_available_runs(self) -> None:
+        """Display available runs."""
         output = Output("general-available-runs-container", "children")
         input = Input("general-available-runs", "data")
 
         @app.callback(output, input)  # type: ignore
-        def callback(run_paths: List[str]):  # type: ignore
+        def callback(run_paths: List[str]):
+            """
+            Display the available runs.
+
+            Parameters
+            ----------
+            run_paths : List[str]
+                The run paths.
+            """
             children = []
 
             # Add text to go to parent directory
@@ -126,11 +170,20 @@ def callback(run_paths: List[str]):  # type: ignore
             return children
 
     def _callback_display_selected_runs(self) -> None:
+        """Display the selected runs."""
         output = Output("general-selected-runs-container", "children")
         input = Input("general-selected-runs", "data")
 
         @app.callback(output, input)  # type: ignore
-        def callback(run_paths: List[str]):  # type: ignore
+        def callback(run_paths: List[str]):
+            """
+            Display the selected runs.
+
+            Parameters
+            ----------
+            run_paths : List[str]
+                The run paths.
+            """
             children = []
 
             for i, run_path in enumerate(run_paths):
@@ -171,6 +224,7 @@ def callback(run_paths: List[str]):  # type: ignore
             return children
 
     def _callback_manage_run(self) -> None:
+        """Manage runs, including adding and removing them."""
         outputs = [
             Output({"type": "general-dynamic-add-run", "index": ALL}, "n_clicks"),
             Output({"type": "general-dynamic-remove-run", "index": ALL}, "n_clicks"),
@@ -188,9 +242,8 @@ def _callback_manage_run(self) -> None:
         ]
 
         @app.callback(outputs, inputs, states)  # type: ignore
-        def callback(  # type: ignore
-            add_n_clicks, remove_n_clicks, available_run_paths, selected_run_paths
-        ):
+        def callback(add_n_clicks, remove_n_clicks, available_run_paths, selected_run_paths):
+            """Add and remove runs."""
             # Add run path
             for n_click, run_path in zip(add_n_clicks, available_run_paths):
                 if n_click is not None:
@@ -213,6 +266,7 @@ def callback(  # type: ignore
             return add_n_clicks, remove_n_clicks, run_handler.get_selected_run_paths()
 
     def _callback_display_groups(self) -> None:
+        """Responsible of displaying the groups."""
         outputs = [
             Output("general-group-container", "children"),
             Output("general-add-group", "n_clicks"),
@@ -226,13 +280,34 @@ def _callback_display_groups(self) -> None:
 
         # Let's take care of the groups here
         @app.callback(outputs, inputs)  # type: ignore
-        def callback(n_clicks: int, _trigger1, _trigger2, children):  # type: ignore
+        def callback(n_clicks: int, _trigger1, _trigger2, children):
+            """Display groups."""
+
             def get_layout(
                 index: int,
                 options: Dict[str, str],
                 input_value: str = "",
                 dropdown_value: Optional[List[str]] = None,
             ) -> Component:
+                """
+                Get the layout for the groups.
+
+                Parameters
+                ----------
+                index : int
+                    The index.
+                options : Dict[str, str]
+                    The options.
+                input_value : str, optional
+                    The input value, by default "".
+                dropdown_value : Optional[List[str]], optional
+                    The dropdown value, by default None.
+
+                Returns
+                -------
+                Component
+                    The layout for the groups.
+                """
                 if dropdown_value is None:
                     dropdown_value = []
 
@@ -276,6 +351,7 @@ def get_layout(
             return children, None
 
     def _callback_manage_groups(self) -> None:
+        """Manage the groups."""
         outputs = Output("general-group-trigger", "data")
         inputs = [
             Input({"type": "group-name", "index": ALL}, "value"),
@@ -284,8 +360,10 @@ def _callback_manage_groups(self) -> None:
         ]
 
         @app.callback(outputs, inputs)  # type: ignore
-        def callback(group_names, all_run_paths, i):  # type: ignore
+        def callback(group_names: List[str], all_run_paths, i):
+            """Manage the groups."""
             # Abort on page load
+            self._refresh_groups: bool
             if self._refresh_groups:
                 self._refresh_groups = False
                 raise PreventUpdate()
@@ -322,21 +400,23 @@ def callback(group_names, all_run_paths, i):  # type: ignore
             raise PreventUpdate()
 
     def _callback_clear_cache(self) -> None:
+        """Responsible for clearing the cache."""
         output = Output("general-clear-cache-button", "n_clicks")
         input = Input("general-clear-cache-button", "n_clicks")
 
         @app.callback(output, input)  # type: ignore
-        def callback(n_clicks):  # type: ignore
+        def callback(n_clicks):
+            """Clear the cache, stop all running jobs, create new run caches."""
             if n_clicks is not None:
                 rc.clear()
 
                 # Also remove last inputs
                 c.set("last_inputs", value={})
 
-                # Also: We have to stop all running jobs
+                # Also: All the running jobs have to be stopped
                 queue.delete_jobs()
 
-                # We have to call `update_runs` now to create the run caches again
+                # `update_runs` has to be called now to create the run caches again
                 run_handler.update_runs()
                 notification.update("Plugin caches successfully cleared.", "success")
 
@@ -344,6 +424,19 @@ def callback(n_clicks):  # type: ignore
 
     @staticmethod
     def get_converter_text(converters: List[Type[Run]]) -> html.Div:
+        """
+        Get the text for the available run converters.
+
+        Parameters
+        ----------
+        converters : List[Type[Run]]
+            A list of the avaialble run converters.
+
+        Returns
+        -------
+        html.Div
+            The text with all the available converters.
+        """
         converter_text = []
         for converter in converters:
             converter_text += [converter.prefix]
@@ -353,7 +446,7 @@ def get_converter_text(converters: List[Type[Run]]) -> html.Div:
             className="mt-2",
         )
 
-    def __call__(self) -> List[Component]:
+    def __call__(self) -> List[Component]:  # noqa: D102
         self._refresh_groups = True
 
         return [
diff --git a/deepcave/layouts/header.py b/deepcave/layouts/header.py
index 41ac88b7..d8517d31 100644
--- a/deepcave/layouts/header.py
+++ b/deepcave/layouts/header.py
@@ -1,3 +1,18 @@
+#  noqa: D400
+"""
+# Header
+
+This module defines the layout for visualizing the header.
+
+It handles different callbacks of the layout.
+
+## Classes
+    - HeaderLayout: This class provides the header and its layout.
+"""
+
+
+from typing import List, Literal, Optional, Tuple, Union
+
 import os
 import time
 
@@ -10,7 +25,14 @@
 
 
 class HeaderLayout(Layout):
+    """
+    Provide the header and its layout.
+
+    Register and handle callbacks.
+    """
+
     def register_callbacks(self) -> None:
+        """Register and handle the callbacks."""
         super().register_callbacks()
         self._callback_update_matplotlib_mode()
         self._callback_delete_jobs()
@@ -27,8 +49,29 @@ def _callback_update_matplotlib_mode(self) -> None:
             Input("matplotlib-mode-refresh", "pathname"),
         ]
 
-        @app.callback(outputs, inputs)
-        def callback(n_clicks, pathname):
+        @app.callback(outputs, inputs)  # type: ignore
+        def callback(
+            n_clicks: int, pathname: str
+        ) -> Union[
+            Tuple[Literal["primary"], Literal["on"], str],
+            Tuple[Literal["secondary"], Literal["off"], str],
+        ]:
+            """
+            Update the matplotlib mode.
+
+            Parameters
+            ----------
+            n_clicks : int
+                Number of clicks.
+            pathname : str
+                Pathname.
+
+            Returns
+            -------
+            Tuple[Literal["primary"], Literal["on"], str],
+            Tuple[Literal["secondary"], Literal["off"], str]
+                Tuple of either "primary", "on", pathname or "secondary", "off", pathname.
+            """
             update = None
             mode = c.get("matplotlib-mode")
             if mode is None:
@@ -52,9 +95,10 @@ def _callback_delete_jobs(self) -> None:
             Output("exit-deepcave", "disabled"),
         ]
 
-        @app.callback(inputs, outputs)
-        def callback(n_clicks):
-            # When clicking the Exit button, we first want to delete existing jobs and update the button
+        @app.callback(inputs, outputs)  # type: ignore
+        def callback(n_clicks: Optional[int]) -> Tuple[str, str, bool]:
+            # When clicking the Exit button, first existing jobs are deleted and then the button
+            # is updated
             if n_clicks is not None:
                 queue.delete_jobs()
                 return "danger", "Terminated DeepCAVE", True
@@ -63,16 +107,16 @@ def callback(n_clicks):
 
     def _callback_terminate_deepcave(self) -> None:
         inputs = [Input("exit-deepcave", "n_clicks")]
-        outputs = []
+        outputs: List[Output] = []
 
-        @app.callback(inputs, outputs)
-        def callback(n_clicks):
+        @app.callback(inputs, outputs)  # type: ignore
+        def callback(n_clicks: Optional[int]) -> None:
             # Then we want to terminate DeepCAVE
             if n_clicks is not None:
                 time.sleep(1)
                 os._exit(130)
 
-    def __call__(self) -> html.Header:
+    def __call__(self) -> html.Header:  # noqa: D102
         return html.Header(
             className="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow",
             children=[
diff --git a/deepcave/layouts/main.py b/deepcave/layouts/main.py
index e00190de..892d87fe 100644
--- a/deepcave/layouts/main.py
+++ b/deepcave/layouts/main.py
@@ -1,4 +1,16 @@
-from typing import Dict, List
+#  noqa: D400
+"""
+# MainLayout
+
+This module provides and defines the visualization of the main layout.
+
+Also registers and handles the callbacks.
+
+## Classes
+    - MainLayout: This class defines and provides the main layout.
+"""
+
+from typing import Any, Dict, List
 
 from urllib.parse import urlparse
 
@@ -18,6 +30,27 @@
 
 
 class MainLayout(Layout):
+    """
+    Define and provide the main layout.
+
+    Handle different callbacks of the layout.
+
+    Properties
+    ----------
+    plugins : Dict[str, Plugin]
+        A dictionary containing the different plugins.
+    sidebar_layout : SidebarLayout
+        A sidebar layout with the categorized plugins.
+    head_layout : HeaderLayout
+        The header layout.
+    general_layout : GeneralLayout
+        The general layout.
+    notification_layout : NotificationLayout
+        The notification layout.
+    not_found_layout : NotFoundLayout
+        The Not Found layout.
+    """
+
     def __init__(self, categorized_plugins: Dict[str, List[Plugin]]):
         super().__init__()
         self.plugins = {}
@@ -31,11 +64,25 @@ def __init__(self, categorized_plugins: Dict[str, List[Plugin]]):
                 self.plugins[plugin.id] = plugin
 
     def register_callbacks(self) -> None:
+        """Register and handle the callbacks."""
         output = Output("content", "children")
         input = Input("on-page-load", "pathname")
 
         @app.callback(output, input)  # type: ignore
-        def display_page(pathname: str):  # type: ignore
+        def display_page(pathname: str) -> Any:
+            """
+            Display the page with the given path url, check for plugins.
+
+            Parameters
+            ----------
+            pathname : str
+                Pathname.
+
+            Returns
+            -------
+            Any
+                Either a General Layout, Not Found Layout or an Alert.
+            """
             pathname = urlparse(pathname).path
             paths = pathname.split("/")[1:]
 
@@ -54,7 +101,7 @@ def display_page(pathname: str):  # type: ignore
 
             return self.not_found_layout(pathname)()
 
-    def __call__(self) -> Component:
+    def __call__(self) -> Component:  # noqa: D102
         return html.Div(
             children=[
                 dcc.Interval(id="global-update", interval=config.REFRESH_RATE),
diff --git a/deepcave/layouts/not_found.py b/deepcave/layouts/not_found.py
index e55fd177..969ea9f0 100644
--- a/deepcave/layouts/not_found.py
+++ b/deepcave/layouts/not_found.py
@@ -1,3 +1,12 @@
+#  noqa: D400
+"""
+# Not_Found
+
+This module defines a custom layout for displaying a "NotFound" page.
+
+## Classes
+    - NotFoundLayout: Define a custom "NotFound" layout.
+"""
 from typing import List
 
 from dash import html
@@ -7,11 +16,20 @@
 
 
 class NotFoundLayout(Layout):
-    def __init__(self, url) -> None:
+    """
+    Define a custom "NotFound" layout.
+
+    Properties
+    ----------
+    url : str
+        The url that could not be reached.
+    """
+
+    def __init__(self, url: str) -> None:
         super().__init__()
         self.url = url
 
-    def __call__(self) -> List[Component]:
+    def __call__(self) -> List[Component]:  # noqa: D102
         return [
             html.H2("This page does not exists."),
             html.Div(f"Tried to reach {self.url}."),
diff --git a/deepcave/layouts/notification.py b/deepcave/layouts/notification.py
index 8560cf13..d75f9d79 100644
--- a/deepcave/layouts/notification.py
+++ b/deepcave/layouts/notification.py
@@ -1,3 +1,17 @@
+# noqa: D400
+"""
+# Notification
+
+This module provides a notification layout.
+
+It utilizes Dash and provides utilities for displaying notifications.
+With a notification from the Notification module an alert component can be updated.
+Callbacks are registered and handled.
+
+## Classes
+    - NotificationLayout: Layout class for displaying notifications.
+"""
+
 from typing import List, Tuple, Union
 
 import dash_bootstrap_components as dbc
@@ -11,24 +25,40 @@
 
 
 class NotificationLayout(Layout):
+    """
+    Layout class for displaying notifications.
+
+    Provide callback registering methods.
+    """
+
     def __init__(self) -> None:
         super().__init__()
 
     def register_callbacks(self) -> None:
+        """Register callbacks for updating notification alert."""
+
         @app.callback(
             Output("alert", "children"),
             Output("alert", "color"),
             Output("alert", "is_open"),
             Trigger("global-update", "n_intervals"),
-        )
+        )  # type: ignore
         def update_alert() -> Tuple[str, str, bool]:
+            """
+            Update the notification alert.
+
+            Returns
+            -------
+            Tuple[str, str, bool]
+                The message, color and True.
+            """
             if (result := notification.get_latest()) is not None:
                 (message, color) = result
                 return message, color, True
             else:
                 raise PreventUpdate()
 
-    def __call__(self) -> Union[List[Component], Component]:
+    def __call__(self) -> Union[List[Component], Component]:  # noqa: D102
         return dbc.Alert(
             id="alert",
             is_open=False,
diff --git a/deepcave/layouts/sidebar.py b/deepcave/layouts/sidebar.py
index 0810e5f8..4830d2b5 100644
--- a/deepcave/layouts/sidebar.py
+++ b/deepcave/layouts/sidebar.py
@@ -1,3 +1,15 @@
+#  noqa: D400
+"""
+# SidebarLayout
+
+This module defines layout for the sidebar.
+
+Callbacks are registered and handled.
+
+## Classes
+    - SidebarLayout: Customize the Sidebar Layout.
+"""
+
 from typing import Dict, List, Tuple, Union
 
 import dash_bootstrap_components as dbc
@@ -12,6 +24,19 @@
 
 
 class SidebarLayout(Layout):
+    """
+    Customize the Sidebar Layout.
+
+    Callbacks are registered and handled.
+
+    Properties
+    ----------
+    plugins : Dict[str, List[Plugin]]
+        A dictionary of all categorized plugins.
+    nav_points : Dict[str, List[Tuple[str, str, str]]]
+        A dictionary with plugins attributes corresponding to their category.
+    """
+
     def __init__(self, categorized_plugins: Dict[str, List[Plugin]]) -> None:
         super().__init__()
         self.plugins = categorized_plugins
@@ -26,12 +51,26 @@ def __init__(self, categorized_plugins: Dict[str, List[Plugin]]) -> None:
         self.nav_points = nav_points
 
     def register_callbacks(self) -> None:
+        """Register the callbacks for the sidebar layout."""
         # Update navigation items
         output = Output("navigation-items", "children")
         input = Input("on-page-load", "pathname")
 
         @app.callback(output, input)  # type: ignore
-        def update_navigation_items(pathname):  # type: ignore
+        def update_navigation_items(pathname: str) -> List[Component]:
+            """
+            Update the navigation items.
+
+            Parameters
+            ----------
+            pathname : str
+                The pathname.
+
+            Returns
+            -------
+            List[Component]
+                The navigation items.
+            """
             layouts = []
             for category, points in self.nav_points.items():
                 layouts += [
@@ -43,7 +82,7 @@ def update_navigation_items(pathname):  # type: ignore
                 ]
 
                 point_layouts = []
-                for (id, name, icon) in points:
+                for id, name, icon in points:
                     href = f"/plugins/{id}"
                     point_layouts += [
                         html.Li(
@@ -60,8 +99,6 @@ def update_navigation_items(pathname):  # type: ignore
 
                 layouts += [html.Ul(className="nav flex-column", children=point_layouts)]
 
-            icon = {"data-feather": "file-text"}
-
             return html.Div(
                 className="position-sticky pt-3",
                 children=[
@@ -86,6 +123,7 @@ def update_navigation_items(pathname):  # type: ignore
             State({"type": "cancel-job", "index": ALL}, "name"),
         )
         def delete_job(n_clicks, job_ids):  # type: ignore
+            """Delete the job from the queue."""
             for n_click, job_id in zip(n_clicks, job_ids):
                 if n_click is not None:
                     queue.delete_job(job_id)
@@ -95,6 +133,7 @@ def delete_job(n_clicks, job_ids):  # type: ignore
 
         @app.callback(output, Trigger("global-update", "n_intervals"))  # type: ignore
         def update_queue_info() -> List[Component]:
+            """Update the information of the queue."""
             try:
                 all_jobs = [
                     queue.get_finished_jobs(),
@@ -106,7 +145,6 @@ def update_queue_info() -> List[Component]:
 
                 collect = []
                 for jobs, status in zip(all_jobs, job_stati):
-
                     for job in jobs:
                         name = job.meta["display_name"]
                         job_id = job.id
@@ -169,7 +207,7 @@ def update_queue_info() -> List[Component]:
             except Exception:
                 return []
 
-    def __call__(self) -> Union[List[Component], Component]:
+    def __call__(self) -> Union[List[Component], Component]:  # noqa: D102
         return html.Nav(
             className="col-md-3 col-lg-2 d-md-block sidebar collapse",
             id="sidebarMenu",
diff --git a/deepcave/open.py b/deepcave/open.py
index c50c9b60..8c43bb08 100644
--- a/deepcave/open.py
+++ b/deepcave/open.py
@@ -1,3 +1,13 @@
+#  noqa: D400
+"""
+# Open
+
+This module opens the browser after a given delay.
+
+## Constants
+    DELAY: int
+"""
+
 import sys
 import webbrowser
 from threading import Timer
@@ -12,8 +22,8 @@
     if "--port" in sys.argv:
         port = sys.argv[sys.argv.index("--port") + 1]
 
-    # Open the link in browser
     def open_browser() -> None:
+        """Open the link in the browser."""
         webbrowser.open_new(f"http://{address}:{port}")
 
     Timer(DELAY, open_browser).start()
diff --git a/deepcave/plugins/__init__.py b/deepcave/plugins/__init__.py
index a2ac0edc..79ed399c 100644
--- a/deepcave/plugins/__init__.py
+++ b/deepcave/plugins/__init__.py
@@ -1,3 +1,14 @@
+#  noqa: D400
+"""
+# Plugins
+
+This module provides a base class for all the available plugins.
+It provides different utilities to handle the plugins and check for compatibility in the runs.
+
+## Classes
+    - Plugin: Base class for all plugins.
+"""
+
 from abc import ABC
 from typing import Any, Callable, Dict, List, Optional, Tuple, Union
 
@@ -31,24 +42,35 @@ class Plugin(Layout, ABC):
     """
     Base class for all plugins.
 
-    Attributes
+    Provides different utilities to handle the plugins and check for compatibility in the runs.
+
+    Properties
     ----------
-    id : int
-        Unique identifier for the plugin.
-    name : str
-        Name of the plugin. It is shown in the navigation and in the title.
-    description : str, optional
-        Description of the plugin. Displayed below the title.
-    icon : str, optional
-        FontAwesome icon. Shown in the navigation.
-    help : str, optional
-        Path to the help file.
-    button_caption : str, optional
-        Caption of the button. Shown only, if `StaticPlugin` is used.
-    activate_run_selection : bool, optional
+    inputs : List[Tuple[str, str, bool, Any]]
+        The registered inputs.
+    outputs : List[Tuple[str, str, bool]]
+        The registered outputs.
+    previous_inputs : Dict[str, Dict[str, str]]
+        The previous inputs.
+    raw_outputs : Optional[Dict[str, Any]]
+        The raw outputs.
+    activate_run_selection : bool
         Shows a dropdown to select a run in the inputs layout.
         This feature is useful if only one run could be viewed at a time.
         Moreover, it prevents the plugin to calculate results across all runs.
+    id : str
+        The unique identifier for the plugin.
+    runs : List[AbstractRun]
+        A list of the abstract runs.
+    groups : List[Group]
+        A list of the groups.
+    help : str
+        The path to the documentation.
+    name : str
+        The name of the plugin.
+        It is shown in the navigation and title.
+    button_caption : str
+        Caption of the button. Shown only, if `StaticPlugin` is used.
     """
 
     id: str
@@ -68,7 +90,7 @@ def __init__(self) -> None:
         self.previous_inputs: Dict[str, Dict[str, str]] = {}
         self.raw_outputs: Optional[Dict[str, Any]] = None
 
-        # We have to call the output layout one time to register
+        # The output layout has to be called one time to register
         # the values
         # Problem: Inputs/Outputs can't be changed afterwards anymore.
 
@@ -86,7 +108,7 @@ def __init__(self) -> None:
     @interactive
     def get_base_url(cls) -> str:
         """
-        Generates the url for the plugin.
+        Generate the url for the plugin.
 
         Returns
         -------
@@ -100,8 +122,9 @@ def get_base_url(cls) -> str:
     @staticmethod
     def check_run_compatibility(run: AbstractRun) -> bool:
         """
-        Checks if a run is compatible with this plugin. If a plugin is not compatible,
-        you can not select the run.
+        Check if a run is compatible with this plugin.
+
+        If a plugin is not compatible, you can not select the run.
 
         Note
         ----
@@ -121,7 +144,8 @@ def check_run_compatibility(run: AbstractRun) -> bool:
 
     def check_runs_compatibility(self, runs: List[AbstractRun]) -> None:
         """
-        This function is needed if all selected runs need something in common
+        Needed if all selected runs need something in common.
+
         (e.g. budget or objective). Since this function is called before the layout is created,
         it can be also used to set common values for the plugin.
 
@@ -145,8 +169,10 @@ def register_input(
         type: Any = None,
     ) -> str:
         """
-        Registers an input variable for the plugin. It is important to register the inputs
-        because callbacks have to be defined before the server is started.
+        Register an input variable for the plugin.
+
+        It is important to register the inputs.
+        This is, because callbacks have to be defined before the server is started.
         After registering all inputs, an internal mapping is created.
 
         Parameters
@@ -154,7 +180,7 @@ def register_input(
         id : str
             Specifies the id of the input.
         attributes : Union[str, List[str]], optional
-            Attributes which should be passed to the (dash) component, by default ("value",)
+            Attributes which should be passed to the (dash) component, by default ("value",).
         filter : bool, optional
             Specifies if the input is a filter. By default False.
         type : Any, optional
@@ -178,9 +204,9 @@ def register_input(
             if key not in self.inputs:
                 self.inputs.append(key)
 
-        # We have to rearrange the inputs because `State`
-        # must follow all `Input`. Since all filters are `Input`, we have to
-        # shift them to the front.
+        # The inputs have to be rearranged, because `State`
+        # must follow all `Input`. Since all filters are `Input`, they have to
+        # be shifted to the front.
         self.inputs.sort(key=lambda x: x[2], reverse=True)
 
         return self.get_internal_input_id(id)
@@ -189,7 +215,7 @@ def register_output(
         self, id: str, attributes: Union[str, List[str]] = "value", mpl: bool = False
     ) -> str:
         """
-        Registers an output variable for the plugin.
+        Register an output variable for the plugin.
 
         Parameters
         ----------
@@ -219,18 +245,23 @@ def register_output(
         return self.get_internal_output_id(id)
 
     def get_internal_id(self, id: str) -> str:
+        """Get the internal id."""
         return f"{self.id}-{id}"
 
     def get_internal_input_id(self, id: str) -> str:
+        """Get the internal input id."""
         return f"{self.id}-{id}-input"
 
     def get_internal_output_id(self, id: str) -> str:
+        """Get the internal output id."""
         return f"{self.id}-{id}-output"
 
     @interactive
     def register_callbacks(self) -> None:
         """
-        Registers basic callbacks for the plugin. Following callbacks are registered:
+        Register basic callbacks for the plugin.
+
+        Following callbacks are registered:
         - If inputs changes, the changes are pasted back. This is in particular
         interest if input dependencies are used.
         - Raw data dialog to display raw data.
@@ -239,7 +270,7 @@ def register_callbacks(self) -> None:
         Raises
         ------
         RuntimeError
-            _description_
+            If no run id is found.
         """
         from deepcave import app, c, run_handler
 
@@ -257,7 +288,8 @@ def register_callbacks(self) -> None:
         if len(outputs) > 0:
 
             @app.callback(outputs, inputs)  # type: ignore
-            def plugin_input_update(pathname: str, *inputs_list: str) -> List[str]:
+            def plugin_input_update(pathname: str, *inputs_list: str) -> List[Optional[str]]:
+                """Update the input of the plugin."""
                 # Simple check if page was loaded for the first time
                 init = all(input is None for input in inputs_list)
 
@@ -273,10 +305,10 @@ def plugin_input_update(pathname: str, *inputs_list: str) -> List[str]:
                         # Overwrite/set the passed inputs
                         update_dict(inputs, passed_inputs)
 
-                        # Then we have to take care of the run_selection
+                        # Then the run_selection has to be taken care of
                         selected_run: Optional[AbstractRun] = None
                         if self.activate_run_selection:
-                            # If run_selection is active and we don't have an id, then
+                            # If run_selection is active and the id is not known, then
                             # the passed inputs have no use.
                             try:
                                 run_id = passed_inputs["run"]["value"]
@@ -314,14 +346,14 @@ def plugin_input_update(pathname: str, *inputs_list: str) -> List[str]:
                             update_dict(inputs, new_inputs)
 
                         # Set not used inputs
-                        for (id, attribute, _, _) in self.inputs:
+                        for id, attribute, _, _ in self.inputs:
                             if id not in inputs:
                                 inputs[id] = {}
 
                             if attribute not in inputs[id]:
                                 inputs[id][attribute] = None
                     elif inputs is not None:
-                        # We have to update the options of the run selection here.
+                        # The options of the run selection have to be updated here.
                         # This is important if the user have added/removed runs.
                         if self.activate_run_selection:
                             run_value = inputs["run"]["value"]
@@ -336,13 +368,16 @@ def plugin_input_update(pathname: str, *inputs_list: str) -> List[str]:
                             inputs["run"]["value"] = run_value
                 else:
                     # Map the list `inputs` to a dict.
-                    inputs = self._list_to_dict(inputs_list)  # type: ignore
+                    # inputs_list_as_list is necessary as new variable,
+                    # because inputs_list is a tuple and cant be passed to _list_to_dict.
+                    inputs_list_as_list = list(inputs_list)
+                    inputs = self._list_to_dict(inputs_list_as_list)
 
                     if len(self.previous_inputs) == 0:
                         self.previous_inputs = inputs.copy()
 
                     # Only work on copies.
-                    # We don't want the inputs dict to be changed by the user.
+                    # The inputs dict should not be changed by the user.
                     _previous_inputs = self.previous_inputs.copy()
                     _inputs = inputs.copy()
 
@@ -356,9 +391,9 @@ def plugin_input_update(pathname: str, *inputs_list: str) -> List[str]:
 
                         # Reset everything if run name changed.
                         if _previous_run_id is not None and _previous_run_id != _run_id:
-                            # We can't use load_inputs here only
+                            # load_inputs cannot be used here, only
                             # because `run` would be removed.
-                            # Also: We want to keep the current run name.
+                            # Also: The current run name does not need to be kept.
                             update_dict(_inputs, self.load_inputs())
                             # Reset inputs
                             if "objective_id" in _inputs.keys():
@@ -380,17 +415,17 @@ def plugin_input_update(pathname: str, *inputs_list: str) -> List[str]:
                         )
 
                         # Update dict
-                        # dict.update() remove keys, so we use our own method to do so
+                        # dict.update() removes keys, so our own method is used to do so
                         update_dict(inputs, user_dependencies_inputs)  # inplace operation
 
                 # Let's cast the inputs
                 inputs = self._cast_inputs(inputs)
 
                 # From dict to list
-                inputs_list = self._dict_to_list(inputs, input=True)  # type: ignore
+                inputs_list_from_dict = self._dict_to_list(inputs, input=True)
                 self.previous_inputs = inputs
 
-                return list(inputs_list)
+                return inputs_list_from_dict
 
         # Register modal for raw data here
         @app.callback(  # type: ignore
@@ -402,6 +437,7 @@ def plugin_input_update(pathname: str, *inputs_list: str) -> List[str]:
             State(self.get_internal_id("raw_data"), "is_open"),
         )
         def toggle_raw_data_modal(n: Optional[int], is_open: bool) -> Tuple[bool, str]:
+            """Toggle the raw data modal."""
             code = ""
             if n:
                 if (out := self.raw_outputs) is not None:
@@ -420,21 +456,22 @@ def toggle_raw_data_modal(n: Optional[int], is_open: bool) -> Tuple[bool, str]:
             Input(self.get_internal_id("show_help"), "n_clicks"),
             State(self.get_internal_id("help"), "is_open"),
         )
-        def toggle_help_modal(n: Optional[int], is_open: bool) -> Tuple[bool, str]:
+        def toggle_help_modal(n: Optional[int], is_open: bool) -> bool:
+            """Toggle the help modal."""
             if n:
                 return not is_open
-
             return is_open
 
         # Register callback to click on configurations
-        for (id, *_) in self.outputs:
+        for id, *_ in self.outputs:
             internal_id = self.get_internal_output_id(id)
 
             @app.callback(
                 Output(internal_id, "clickData"),
                 Input(internal_id, "clickData"),
-            )
-            def go_to_configuration(click_data):  # type: ignore
+            )  # type: ignore
+            def go_to_configuration(click_data: Any):
+                """Open link from hovertext."""
                 if click_data is not None:
                     # Get hovertext
                     try:
@@ -455,7 +492,7 @@ def _inputs_changed(
         self, inputs: Dict[str, Dict[str, str]], last_inputs: Dict[str, Dict[str, str]]
     ) -> Tuple[bool, bool]:
         """
-        Checks if the inputs have changed.
+        Check if the inputs have changed.
 
         Parameters
         ----------
@@ -473,11 +510,10 @@ def _inputs_changed(
         inputs_changed = False
         filters_changed = False
 
-        # If only filters changed, then we don't need to
+        # If only filters changed, there is no need to
         # calculate the results again.
         if last_inputs is not None:
-            for (id, attribute, filter, _) in self.inputs:
-
+            for id, attribute, filter, _ in self.inputs:
                 if self.activate_run_selection:
                     if id == "run":
                         continue
@@ -493,26 +529,44 @@ def _inputs_changed(
     @interactive
     def _process_raw_outputs(
         self, inputs: Dict[str, Dict[str, str]], raw_outputs: Dict[str, Any]
-    ) -> Union[Any, List[Any]]:
+    ) -> Any:
+        """
+        Process the raw outputs and update the layout.
+
+        Parameters
+        ----------
+        inputs : Dict[str, Dict[str, str]]
+            The inputs for the passed runs.
+        raw_outputs : Dict[str, Any]
+            The raw outputs to process.
+
+        Returns
+        -------
+        Any
+            The processed outputs.
+        """
         from deepcave import c, run_handler
 
         # Use raw outputs to update our layout
         mpl_active = c.get("matplotlib-mode")
+        passed_runs: Union[List[AbstractRun], AbstractRun]
 
         if self.activate_run_selection:
             passed_runs = run_handler.get_run(inputs["run"]["value"])
             passed_outputs = raw_outputs[passed_runs.id]
         else:
-            passed_runs = self.all_runs  # type: ignore
+            passed_runs = self.all_runs
             passed_outputs = raw_outputs
 
         # Clean inputs
         cleaned_inputs = self._clean_inputs(inputs)
 
+        # passed runs could be a list, but load mpl outputs and load outputs do not
+        # accept lists, but expect single runs
         if mpl_active:
-            outputs = self.__class__.load_mpl_outputs(passed_runs, cleaned_inputs, passed_outputs)
+            outputs = self.__class__.load_mpl_outputs(passed_runs, cleaned_inputs, passed_outputs)  # type: ignore # noqa: E501
         else:
-            outputs = self.__class__.load_outputs(passed_runs, cleaned_inputs, passed_outputs)
+            outputs = self.__class__.load_outputs(passed_runs, cleaned_inputs, passed_outputs)  # type: ignore # noqa: E501
 
         logger.debug("Raw outputs processed successfully.")
 
@@ -527,10 +581,10 @@ def _process_raw_outputs(
             if not isinstance(outputs, list):
                 outputs = [outputs]
 
-        # We have to add no_updates here for the mode we don't want
+        # no_updates has to be added here for the mode that is not wanted
         count_outputs = 0
         count_mpl_outputs = 0
-        for (_, _, mpl_mode) in self.outputs:
+        for _, _, mpl_mode in self.outputs:
             if mpl_mode:
                 count_mpl_outputs += 1
             else:
@@ -549,8 +603,9 @@ def _process_raw_outputs(
     @interactive
     def _list_to_dict(self, values: List[str], input: bool = True) -> Dict[str, Dict[str, str]]:
         """
-        Maps the given values to a dict, regarding the sorting from
-        either self.inputs or self.outputs.
+        Map the given values to a dict.
+
+        Regarding the sorting from either self.inputs or self.outputs.
 
         Parameters
         ----------
@@ -562,12 +617,15 @@ def _list_to_dict(self, values: List[str], input: bool = True) -> Dict[str, Dict
         Returns
         -------
         Dict[str, Dict[str, str]]
-            _description_
+            Dictionary containing the mapping information.
         """
+        # This is necessary, because of the conditional type of order
+        order: Union[List[Tuple[str, str, bool]], List[Tuple[str, str, bool, Any]]]
+
         if input:
             order = self.inputs
         else:
-            order = self.outputs  # type: ignore
+            order = self.outputs
 
         mapping: Dict[str, Any] = {}
         for value, (id, attribute, *_) in zip(values, order):
@@ -583,8 +641,9 @@ def _dict_to_list(
         self, d: Dict[str, Dict[str, str]], input: bool = False
     ) -> List[Optional[str]]:
         """
-        Maps the given dict to a list, respecting the sorting from either
-        self.inputs or self.outputs.
+        Map the given dict to a list.
+
+        Respecting the sorting from either self.inputs or self.outputs.
 
         Parameters
         ----------
@@ -600,13 +659,16 @@ def _dict_to_list(
         """
         from deepcave import c
 
+        # This is necessary, because of the conditional type of order
+        order: Union[List[Tuple[str, str, bool]], List[Tuple[str, str, bool, Any]]]
+
         if input:
             order = self.inputs
         else:
-            order = self.outputs  # type: ignore
+            order = self.outputs
 
         result: List[Optional[str]] = []
-        for (id, attribute, instance, *_) in order:
+        for id, attribute, instance, *_ in order:
             if not input:
                 # Instance is mlp_mode in case of outputs
                 # Simply ignore other outputs.
@@ -622,28 +684,33 @@ def _dict_to_list(
         return result
 
     @interactive
-    def _dict_as_key(self, d: Dict[str, Any], remove_filters: bool = False) -> Optional[str]:
+    def _dict_as_key(self, d: Dict[str, Any], remove_filters: bool = False) -> str:
         """
-        Converts a dictionary to a key. Only ids from self.inputs are considered.
+        Convert a dictionary to a key. Only ids from self.inputs are considered.
 
         Parameters
         ----------
         d : Dict[str, Any]
             Dictionary to get the key from.
         remove_filters : bool, optional
-            Option wheather the filters should be included or not. By default False.
+            Option whether the filters should be included or not. By default False.
 
         Returns
         -------
         Optional[str]
             Key as string from the given dictionary. Returns none if `d` is not a dictionary.
+
+        Raises
+        ------
+        TypeError
+            If `d` is not a dictionary.
         """
         if not isinstance(d, dict):
-            return None
+            raise TypeError("d must be a dictionary.")
 
         new_d = copy.deepcopy(d)
         if remove_filters:
-            for (id, _, filter, _) in self.inputs:
+            for id, _, filter, _ in self.inputs:
                 if filter:
                     if id in new_d:
                         del new_d[id]
@@ -652,8 +719,10 @@ def _dict_as_key(self, d: Dict[str, Any], remove_filters: bool = False) -> Optio
 
     def _cast_inputs(self, inputs: Dict[str, Dict[str, str]]) -> Dict[str, Dict[str, str]]:
         """
-        Casts the inputs based on `self.inputs`. Background is that dash always casts integers/
-        booleans to strings. This method ensured that the correct types are returned.
+        Cast the inputs based on `self.inputs`.
+
+        Background is that dash always casts integers/booleans to strings.
+        This method ensures that the correct types are returned.
 
         Parameters
         ----------
@@ -668,10 +737,9 @@ def _cast_inputs(self, inputs: Dict[str, Dict[str, str]]) -> Dict[str, Dict[str,
         casted_inputs: Dict[str, Dict[str, str]] = defaultdict(dict)
         for id, attributes in inputs.items():
             for attribute in attributes:
-
                 # Find corresponding input
                 type = None
-                for (id_, attribute_, _, type_) in self.inputs:
+                for id_, attribute_, _, type_ in self.inputs:
                     if id == id_ and attribute == attribute_:
                         type = type_
                         break
@@ -686,8 +754,9 @@ def _cast_inputs(self, inputs: Dict[str, Dict[str, str]]) -> Dict[str, Dict[str,
 
     def _clean_inputs(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
         """
-        Cleans the given inputs s.t. only the first value is used.
-        Also, boolean values are casted to booleans.
+        Clean the given inputs s.t. only the first value is used.
+
+        Also, boolean values are cast to booleans.
 
         Example
         -------
@@ -701,16 +770,19 @@ def _clean_inputs(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
 
         Parameters
         ----------
-        inputs (dict): Inputs to clean.
+        inputs : Dict[str, Any]
+            Inputs to clean.
 
-        Returns:
-            dict: Cleaned inputs.
+        Returns
+        -------
+        Dict[str, Any]
+            Cleaned inputs.
         """
         used_ids = []
         cleaned_inputs = {}
-        for (id, attribute, *_) in self.inputs:
-            # Since self.inputs is ordered, we use the first occuring attribute and add
-            # the id so it is not used again.
+        for id, attribute, *_ in self.inputs:
+            # Since self.inputs is ordered, the first occurring attribute is used and
+            # the id is added so it is not used again.
             if id not in used_ids:
                 i = inputs[id][attribute]
 
@@ -725,23 +797,47 @@ def _clean_inputs(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
 
         return cleaned_inputs
 
-    @property  # type: ignore
+    @property
     @interactive
     def runs(self) -> List[AbstractRun]:
+        """
+        Get the runs as a list.
+
+        Returns
+        -------
+        List[AbstractRun]
+            The list with the runs.
+        """
         from deepcave import run_handler
 
         return run_handler.get_runs()
 
-    @property  # type: ignore
+    @property
     @interactive
     def groups(self) -> List[Group]:
+        """
+        Get the groups as a list.
+
+        Returns
+        -------
+        List[Group]
+            The list with the groups.
+        """
         from deepcave import run_handler
 
         return run_handler.get_groups()
 
-    @property  # type: ignore
+    @property
     @interactive
     def all_runs(self) -> List[AbstractRun]:
+        """
+        Get all runs and include the groups as a list.
+
+        Returns
+        -------
+        List[AbstractRun]
+            The list with all runs and included groups.
+        """
         from deepcave import run_handler
 
         return run_handler.get_runs(include_groups=True)
@@ -749,13 +845,26 @@ def all_runs(self) -> List[AbstractRun]:
     @interactive
     def __call__(self, render_button: bool = False) -> List[Component]:
         """
-        Returns the components for the plugin. Basically, all blocks and elements of the plugin
-        are stacked-up here
+        Return the components for the plugin.
+
+        Basically, all blocks and elements of the plugin are stacked-up here.
+
+        Parameters
+        ----------
+        render_button : bool, optional
+            Whether to render the button or not. By default False.
 
         Returns
         -------
         List[Component]
             Layout as list of components.
+
+        Raises
+        ------
+        NotMergeableError
+            If runs are not compatible.
+        FileNotFoundError
+            If the help file can not be found.
         """
         from deepcave import c, notification
 
@@ -837,7 +946,7 @@ def __call__(self, render_button: bool = False) -> List[Component]:
             ],
         )
 
-        # We always have to render it because of the button.
+        # It always has to be rendered, because of the button.
         # Button tells us if the page was just loaded.
         components += [
             html.Div(
@@ -853,7 +962,27 @@ def __call__(self, render_button: bool = False) -> List[Component]:
             )
         ]
 
-        def register_in(a, b):  # type: ignore
+        def register_in(a: str, b: Union[List[str], str]) -> str:
+            """
+            Register the given input.
+
+            Note
+            ----
+            For more information, see 'register_input'.
+
+            Parameters
+            ----------
+            a : str
+                Specifies the id of the input.
+            b : Union[List[str], str]
+                Attributes which should be passed to the (dash) component, by default ("value",).
+
+            Returns
+            -------
+            str
+                Unique id for the input and plugin.
+                This is necessary because ids are defined globally.
+            """
             return self.register_input(a, b, filter=True)
 
         filter_layout = self.__class__.get_filter_layout(register_in)
@@ -877,7 +1006,27 @@ def register_in(a, b):  # type: ignore
                 )
             ]
 
-        def register_out(a, b):  # type: ignore
+        def register_out(a: str, b: Union[List[str], str]) -> str:
+            """
+            Register the output.
+
+            Note
+            ----
+            For more information, see 'register_output'
+
+            Parameters
+            ----------
+            a : str
+                Specifies the id of the output.
+            b : Union[List[str], str]
+                Attribute.
+
+            Returns
+            -------
+            str
+                Unique id for the output and plugin.
+                This is necessary because ids are defined globally.
+            """
             return self.register_output(a, b, mpl=True)
 
         output_layout = self.__class__.get_mpl_output_layout(register_out)
@@ -938,15 +1087,17 @@ def register_out(a, b):  # type: ignore
 
     @staticmethod
     @interactive
-    def get_run_input_layout(register: Callable[[str, Union[str, List[str]]], str]) -> Component:
+    def get_run_input_layout(register: Callable) -> Component:
         """
-        Generates the run selection input.
+        Generate the run selection input.
+
         This is only the case if `activate_run_selection` is True.
 
         Parameters
         ----------
-        register : Callable[[str, Union[str, List[str]]], str]
+        register : Callable
             The register method to register (user) variables.
+            For more information, see 'register_input'.
 
         Returns
         -------
@@ -970,14 +1121,15 @@ def load_run_inputs(
         check_run_compatibility: Callable[[AbstractRun], bool],
     ) -> Dict[str, Any]:
         """
-        Loads the options for `get_run_input_layout`.
+        Load the options for `get_run_input_layout`.
+
         Both runs and groups are displayed.
 
         Parameters
         ----------
-        runs : Dict[str, Run]
+        runs : List[AbstractRun]
             The runs to display.
-        groups : Dict[str, Group]
+        groups : List[Group]
             The groups to display.
         check_run_compatibility : Callable[[AbstractRun], bool]
             If a single run is compatible. If not, the run is not shown.
@@ -987,7 +1139,6 @@ def load_run_inputs(
         Dict[str, Any]
             Both runs and groups, separated by a separator.
         """
-
         labels = []
         values = []
         disabled = []
@@ -1024,7 +1175,8 @@ def load_run_inputs(
     @interactive
     def get_selected_runs(self, inputs: Dict[str, Any]) -> List[AbstractRun]:
         """
-        Parses selected runs from inputs.
+        Parse selected runs from inputs.
+
         If self.activate_run_selection is set, return only selected run. Otherwise, return all
         possible runs.
 
@@ -1056,7 +1208,7 @@ def get_selected_runs(self, inputs: Dict[str, Any]) -> List[AbstractRun]:
 
             # Also:
             # Remove `run` from inputs_key because
-            # we don't want the run names included.
+            # The runs name does not need to be included
             _inputs = inputs.copy()
             del _inputs["run"]
 
@@ -1067,6 +1219,7 @@ def get_selected_runs(self, inputs: Dict[str, Any]) -> List[AbstractRun]:
     def load_inputs(self) -> Dict[str, Any]:
         """
         Load the content for the defined inputs in `get_input_layout` and `get_filter_layout`.
+
         This method is necessary to pre-load contents for the inputs. So, if the plugin is
         called for the first time or there are no results in the cache, the plugin gets its
         content from this method.
@@ -1085,7 +1238,9 @@ def load_dependency_inputs(
         inputs: Dict[str, Any],
     ) -> Dict[str, Any]:
         """
-        Same as `load_inputs` but called after inputs have changed. Provides a lot of flexibility.
+        Load the content as in 'load_inputs' but called after inputs have changed.
+
+        Provides a lot of flexibility.
 
         Note
         ----
@@ -1094,7 +1249,7 @@ def load_dependency_inputs(
 
         Parameters
         ----------
-        selected_run : Optional[Union[AbstractRun, List[AbstractRun]]], optional
+        run : Optional[Union[AbstractRun, List[AbstractRun]]], optional
             The selected run from the user. In case of `activate_run_selection`, only one run
             is passed. Defaults to None.
         previous_inputs : Dict[str, Any]
@@ -1110,14 +1265,15 @@ def load_dependency_inputs(
         return inputs
 
     @staticmethod
-    def get_input_layout(register: Callable[[str, Union[str, List[str]]], str]) -> List[Component]:
+    def get_input_layout(register: Callable) -> List[Component]:
         """
         Layout for the input block.
 
         Parameters
         ----------
-        register : Callable[[str, Union[str, List[str]]], str]
+        register : Callable
             The register method to register (user) variables.
+            For more information, see 'register_input'.
 
         Returns
         -------
@@ -1127,14 +1283,15 @@ def get_input_layout(register: Callable[[str, Union[str, List[str]]], str]) -> L
         return []
 
     @staticmethod
-    def get_filter_layout(register: Callable[[str, Union[str, List[str]]], str]) -> List[Component]:
+    def get_filter_layout(register: Callable) -> List[Component]:
         """
         Layout for the filter block.
 
         Parameters
         ----------
-        register : Callable[[str, Union[str, List[str]]], str]
+        register : Callable
             The register method to register (user) variables.
+            For more information, see 'register_input'.
 
         Returns
         -------
@@ -1144,16 +1301,15 @@ def get_filter_layout(register: Callable[[str, Union[str, List[str]]], str]) ->
         return []
 
     @staticmethod
-    def get_output_layout(
-        register: Callable[[str, Union[str, List[str]]], str]
-    ) -> Optional[Union[Component, List[Component]]]:
+    def get_output_layout(register: Callable) -> Optional[Union[Component, List[Component]]]:
         """
         Layout for the output block.
 
         Parameters
         ----------
-        register : Callable[[str, Union[str, List[str]]], str]
+        register : Callable
             The register method to register outputs.
+            For more information, see 'register_input'.
 
         Returns
         -------
@@ -1163,20 +1319,19 @@ def get_output_layout(
         return None
 
     @staticmethod
-    def get_mpl_output_layout(
-        register: Callable[[str, Union[str, List[str]]], str]
-    ) -> Optional[Union[Component, List[Component]]]:
+    def get_mpl_output_layout(register: Callable) -> Optional[Union[Component, List[Component]]]:
         """
         Layout for the matplotlib output block.
 
         Parameters
         ----------
-        register : Callable[[str, Union[str, List[str]]], str]
+        register : Callable
             The register method to register outputs.
+            For more information, see 'register_input'.
 
         Returns
         -------
-        Union[Component, List[Component]]
+        Optional[Union[Component, List[Component]]]
             Layout for the matplotlib output block.
         """
         return None
@@ -1188,12 +1343,14 @@ def load_outputs(
         outputs: Dict[str, Union[str, Dict[str, str]]],
     ) -> Union[Component, List[Component]]:
         """
-        Reads in the raw data and prepares them for the layout.
+        Read in the raw data and prepare them for the layout.
 
         Note
         ----
         The passed `inputs` are cleaned and therefore differs compared to `load_inputs` or
-        `load_dependency_inputs`. Please see `_clean_inputs` for more information.
+        `load_dependency_inputs`.
+        Inputs are cleaned s.t. only the first value is used.
+        Also, boolean values are casted to booleans.
 
         Parameters
         ----------
@@ -1220,12 +1377,13 @@ def load_mpl_outputs(
         outputs: Dict[str, Union[str, Dict[str, str]]],
     ) -> Union[Component, List[Component]]:
         """
-        Reads in the raw data and prepares them for the layout.
+        Read in the raw data and prepare them for the layout.
 
         Note
         ----
         The passed `inputs` are cleaned and therefore differs compared to `load_inputs` or
-        `load_dependency_inputs`. Please see `_clean_inputs` for more information.
+        `load_dependency_inputs`. Inputs are cleaned s.t. only the first value is used.
+        Also, boolean values are casted to booleans.
 
         Parameters
         ----------
@@ -1248,7 +1406,7 @@ def load_mpl_outputs(
     @staticmethod
     def process(run: AbstractRun, inputs: Dict[str, Any]) -> Dict[str, Any]:
         """
-        Returns raw data based on a run and input data.
+        Return raw data based on a run and input data.
 
         Warning
         -------
@@ -1257,7 +1415,8 @@ def process(run: AbstractRun, inputs: Dict[str, Any]) -> Dict[str, Any]:
         Note
         ----
         The passed `inputs` are cleaned and therefore differs compared to `load_inputs` or
-        `load_dependency_inputs`. Please see `_clean_inputs` for more information.
+        `load_dependency_inputs`. Inputs are cleaned s.t. only the first value is used.
+        Also, boolean values are casted to booleans.
 
         Parameters
         ----------
@@ -1278,7 +1437,8 @@ def generate_outputs(
         cls, runs: Union[AbstractRun, List[AbstractRun]], inputs: Dict[str, Any]
     ) -> Union[Dict[str, Any], Dict[str, Dict[str, Any]]]:
         """
-        Checks whether run selection is active and accepts either one or multiple runs at once.
+        Check whether run selection is active and accepts either one or multiple runs at once.
+
         Calls `process` internally.
 
         Parameters
@@ -1320,7 +1480,8 @@ def generate_outputs(
 
     def generate_inputs(self, **kwargs: Any) -> Dict[str, Any]:
         """
-        Generates inputs for the `process` and `load_outputs` required for api mode.
+        Generate inputs for the `process` and `load_outputs` required for api mode.
+
         The arguments are validated against the input schema.
 
         Note
@@ -1336,11 +1497,17 @@ def generate_inputs(self, **kwargs: Any) -> Dict[str, Any]:
         -------
         Dict[str, Any]
             The inputs for the run.
+
+        Raises
+        ------
+        ValueError
+            If an unknown input is passed.
+            If an input is missing.
         """
         mapping = {}
-        for (id, attribute, *_) in self.inputs:
-            # Since `self.inputs` is ordered, we use the first occuring attribute and add
-            # the id so it is not used again.
+        for id, attribute, *_ in self.inputs:
+            # Since `self.inputs` is ordered, the first occurring attribute is used and
+            # the id is added, so it is not used again.
             if id not in mapping:
                 mapping[id] = attribute
 
diff --git a/deepcave/plugins/budget/__init__.py b/deepcave/plugins/budget/__init__.py
index e69de29b..150d0cb1 100644
--- a/deepcave/plugins/budget/__init__.py
+++ b/deepcave/plugins/budget/__init__.py
@@ -0,0 +1,9 @@
+#  noqa: D400
+"""
+# budget
+
+## Modules
+----------
+budget_correlation
+    This module provides utilities to visualize budget correlations.
+"""
diff --git a/deepcave/plugins/budget/budget_correlation.py b/deepcave/plugins/budget/budget_correlation.py
index 6688fbb4..d592d2f4 100644
--- a/deepcave/plugins/budget/budget_correlation.py
+++ b/deepcave/plugins/budget/budget_correlation.py
@@ -1,4 +1,16 @@
-from typing import Dict
+#  noqa: D400
+"""
+# BudgetCorrelation
+
+This module provides utilities to visualize budget correlations.
+
+Provided utilities include getting input and output layout, processing the data
+and loading the outputs.
+
+## Classes
+    - BudgetCorrelation: Can be used for visualizing the correlation of budgets.
+"""
+from typing import Any, Callable, DefaultDict, Dict, List
 
 from collections import defaultdict
 
@@ -10,7 +22,7 @@
 from deepcave import notification
 from deepcave.config import Config
 from deepcave.plugins.dynamic import DynamicPlugin
-from deepcave.runs import Status
+from deepcave.runs import AbstractRun, Status
 from deepcave.utils.layout import create_table, get_select_options
 from deepcave.utils.logs import get_logger
 from deepcave.utils.styled_plotty import get_color, save_image
@@ -19,6 +31,13 @@
 
 
 class BudgetCorrelation(DynamicPlugin):
+    """
+    Used for visualizing the correlation of budgets.
+
+    Provided utilities include getting input/output layout, data processing
+    and loading outputs.
+    """
+
     id = "budget_correlation"
     name = "Budget Correlation"
     icon = "fas fa-coins"
@@ -26,7 +45,20 @@ class BudgetCorrelation(DynamicPlugin):
     activate_run_selection = True
 
     @staticmethod
-    def check_run_compatibility(run) -> bool:
+    def check_run_compatibility(run: AbstractRun) -> bool:
+        """
+        Check if the run has more than one budget and is compatible.
+
+        Parameters
+        ----------
+        run : AbstractRun
+            The run to be checked.
+
+        Returns
+        -------
+        bool
+            True if the run is compatible, otherwise False.
+        """
         if len(run.get_budgets()) == 1:
             notification.update(f"{run.name} can not be selected because it has only one budget.")
             return False
@@ -34,7 +66,21 @@ def check_run_compatibility(run) -> bool:
         return True
 
     @staticmethod
-    def get_input_layout(register):
+    def get_input_layout(register: Callable) -> List[html.Div]:
+        """
+        Get the layout for the input block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[html.Div]
+            Layouts for the input block.
+        """
         return [
             html.Div(
                 [
@@ -47,7 +93,26 @@ def get_input_layout(register):
             ),
         ]
 
-    def load_dependency_inputs(self, run, _, inputs):
+    def load_dependency_inputs(self, run, _, inputs) -> Dict[str, Dict[str, Any]]:  # type: ignore
+        """
+        Work like 'load_inputs' but called after inputs have changed.
+
+        Note
+        ----
+        Only the changes are returned, they will be later merged with the inputs.
+
+        Parameters
+        ----------
+        run
+            The run to get the objective from.
+        inputs
+            The inputs containing the objective id and a value.
+
+        Returns
+        -------
+        Dict[str, Dict[str, Any]]
+            A dictionary with the changes.
+        """
         objective_names = run.get_objective_names()
         objective_ids = run.get_objective_ids()
         objective_options = get_select_options(objective_names, objective_ids)
@@ -64,14 +129,40 @@ def load_dependency_inputs(self, run, _, inputs):
         }
 
     @staticmethod
-    def process(run, inputs):
+    def process(run: AbstractRun, inputs: Dict[str, int]) -> Dict[str, Any]:
+        """
+        Return raw data based on a run and the input data.
+
+        Warning
+        -------
+        The returned data must be JSON serializable.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differ
+        compared to 'load_inputs' or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run : AbstractRun
+            The run to process.
+        inputs : Dict[str, int]
+            The input to get the objective id from.
+
+        Returns
+        -------
+        Dict[str, Any]
+            Serialized dictionary with the correlations
+            as well as the correlations symmetric.
+        """
         objective_id = inputs["objective_id"]
         budget_ids = run.get_budget_ids(include_combined=False)
 
         # Add symmetric correlations; table ready
-        correlations_symmetric: Dict[str, Dict[str, float]] = defaultdict(dict)
+        correlations_symmetric: DefaultDict[str, Dict[str, float]] = defaultdict(dict)
 
-        correlations: Dict[str, Dict[str, float]] = defaultdict(dict)
+        correlations: DefaultDict[str, Dict[str, float]] = defaultdict(dict)
         for budget1_id in budget_ids:
             budget1 = run.get_budget(budget1_id)
             budget1_readable = run.get_budget(budget1_id, human=True)
@@ -84,7 +175,7 @@ def process(run, inputs):
                 costs2 = run.get_all_costs(budget2, statuses=[Status.SUCCESS])
 
                 # Combine config ids
-                # So we make sure we have the same number of configs for each budget
+                # So it is guaranteed that there is the same number of configs for each budget
                 config_ids = set(costs1.keys()) & set(costs2.keys())
 
                 c1, c2 = [], []
@@ -93,14 +184,14 @@ def process(run, inputs):
                     c2 += [costs2[config_id][objective_id]]
 
                 correlation = round(stats.spearmanr(c1, c2).correlation, 2)
-                correlations_symmetric["Budget"][budget2_readable] = budget2_readable
-                correlations_symmetric[budget1_readable][budget2_readable] = correlation
+                correlations_symmetric["Budget"][budget2_readable] = budget2_readable  # type: ignore # noqa: E501
+                correlations_symmetric[budget1_readable][budget2_readable] = correlation  # type: ignore # noqa: E501
 
                 # Exclude if budget2 is higher than budget1
                 if budget2 > budget1:
                     continue
 
-                correlations[budget1_readable][budget2_readable] = correlation
+                correlations[budget1_readable][budget2_readable] = correlation  # type: ignore
 
         return {
             "correlations": correlations,
@@ -108,7 +199,21 @@ def process(run, inputs):
         }
 
     @staticmethod
-    def get_output_layout(register):
+    def get_output_layout(register: Callable) -> List[Any]:
+        """
+        Get the layout for the output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register outputs.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            Layout for the output block.
+        """
         return [
             html.Div(id=register("text", "children"), className="mb-3"),
             dbc.Tabs(
@@ -125,9 +230,30 @@ def get_output_layout(register):
         ]
 
     @staticmethod
-    def load_outputs(run, _, outputs):
+    def load_outputs(run, _, outputs) -> List[Any]:  # type: ignore
+        """
+        Read the raw data and prepare it for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differ
+        compared to 'load_inputs' or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        outputs
+            Raw output from the run.
+
+        Returns
+        -------
+        List[Any]
+            The text, the figure and the created table.
+        """
         traces = []
-        categories = defaultdict(list)
+        categories: defaultdict = defaultdict(list)
         correlations = outputs["correlations"]
         correlations_symmetric = outputs["correlations_symmetric"]
         for idx, (budget1, budgets) in enumerate(correlations.items()):
@@ -209,7 +335,6 @@ def load_outputs(run, _, outputs):
             text += f"{pairs_text} is {relation}"
             if i == n_categories - 1:
                 text += "."
-
         return [
             text,
             figure,
diff --git a/deepcave/plugins/dynamic.py b/deepcave/plugins/dynamic.py
index 7684680b..bd493c8d 100644
--- a/deepcave/plugins/dynamic.py
+++ b/deepcave/plugins/dynamic.py
@@ -1,5 +1,17 @@
+#  noqa: D400
+"""
+# Dynamic
+
+This module provides a plugin class for a dynamic plugin.
+
+Register and handle callbacks.
+
+## Classes
+    - DynamicPlugin: This class provides a dynamic plugin object.
+"""
+
 from abc import ABC
-from typing import List
+from typing import Any, List
 
 from dash.dependencies import Input, Output
 from dash.development.base_component import Component
@@ -9,6 +21,27 @@
 
 
 class DynamicPlugin(Plugin, ABC):
+    """
+    Provide a dynamic plugin object.
+
+    Register and handle callbacks.
+
+    Properties
+    ----------
+    outputs : List[Tuple[str, str, bool]]
+        The registered outputs.
+    inputs : List[Tuple[str, str, bool, Any]]
+        The registered inputs.
+    id : str
+        The identifier of the Plugin.
+    logger : Logger
+        The logger for debugging information.
+    use_cache : bool
+        Defines whether to use the cache.
+    raw_outputs : Dict[str, Any]
+        A dictionary of the raw outputs.
+    """
+
     use_cache = True
 
     def __init__(self) -> None:
@@ -16,6 +49,15 @@ def __init__(self) -> None:
 
     @interactive
     def register_callbacks(self) -> None:
+        """
+        Register basic callbacks for the plugin.
+
+        Following callbacks are registered:
+        - If inputs changes, the changes are pasted back.
+          This is in particular interest if input dependencies are used.
+        - Raw data dialog to display raw data.
+        - Callback to be redirected to the config if clicked on it.
+        """
         super().register_callbacks()
         from deepcave import app, c, rc
 
@@ -28,15 +70,26 @@ def register_callbacks(self) -> None:
             inputs.append(Input(self.get_internal_input_id(id), attribute))
 
         # Register updates from inputs
-        @app.callback(outputs, inputs)
-        def plugin_output_update(_, *inputs_list):  # type: ignore
+        @app.callback(outputs, inputs)  # type: ignore
+        def plugin_output_update(_: Any, *inputs_list: str) -> Any:
             """
-            Parameters:
-                *inputs_list: Values from user.
+            Update the outputs.
+
+            Parameters
+            ----------
+            *inputs_list
+                Input values from user.
+
+            Returns
+            -------
+            Any
+                The raw outputs.
             """
             # Map the list `inputs_list` to a dict s.t.
             # it's easier to access them.
-            inputs = self._list_to_dict(inputs_list, input=True)
+            inputs = self._list_to_dict(list(inputs_list), input=True)
+            # The Optional[str] would need tobe changed to a str as return type annotation
+            # of the function _dict_as_key.
             inputs_key = self._dict_as_key(inputs, remove_filters=True)
             cleaned_inputs = self._clean_inputs(inputs)
             runs = self.get_selected_runs(inputs)
@@ -66,4 +119,14 @@ def plugin_output_update(_, *inputs_list):  # type: ignore
 
     @interactive
     def __call__(self) -> List[Component]:  # type: ignore
+        """
+        Return the components for the plugin.
+
+        Basically, all blocks and elements of the plugin are stacked-up here.
+
+        Returns
+        -------
+        List[Component]
+            Layout as list of components.
+        """
         return super().__call__(False)
diff --git a/deepcave/plugins/hyperparameter/__init__.py b/deepcave/plugins/hyperparameter/__init__.py
index e69de29b..26df838e 100644
--- a/deepcave/plugins/hyperparameter/__init__.py
+++ b/deepcave/plugins/hyperparameter/__init__.py
@@ -0,0 +1,13 @@
+#  noqa: D400
+"""
+# hyperparameter
+
+## Modules
+----------
+importances
+    This module provides a plugin for the visualization of the importances.
+pdp
+    This module provides utilities for generating Partial Dependency Plots (PDP).
+symbolic_explanations
+    This module provides utilities for generating symbolic explanations.
+"""
diff --git a/deepcave/plugins/hyperparameter/importances.py b/deepcave/plugins/hyperparameter/importances.py
index ceb91f5a..03b07ea6 100644
--- a/deepcave/plugins/hyperparameter/importances.py
+++ b/deepcave/plugins/hyperparameter/importances.py
@@ -1,14 +1,30 @@
+#  noqa: D400
+"""
+# Importances
+
+This module provides a plugin for the visualization of the importances.
+
+Provided utilities include getting input and output layout (filtered or non-filtered),
+processing the data and loading the outputs. Also provides a matplotlib version.
+
+## Classes
+    - Importances: This class provides a plugin for the visualization of the importances.
+"""
+
+from typing import Any, Callable, Dict, List, Optional, Union
+
 import dash_bootstrap_components as dbc
 import numpy as np
 import plotly.graph_objs as go
+from ConfigSpace import ConfigurationSpace, Constant
 from dash import dcc, html
 from dash.exceptions import PreventUpdate
-from ConfigSpace import ConfigurationSpace, Constant
 
 from deepcave.config import Config
 from deepcave.evaluators.fanova import fANOVA as GlobalEvaluator
 from deepcave.evaluators.lpi import LPI as LocalEvaluator
 from deepcave.plugins.static import StaticPlugin
+from deepcave.runs import AbstractRun
 from deepcave.utils.cast import optional_int
 from deepcave.utils.layout import get_checklist_options, get_select_options, help_button
 from deepcave.utils.styled_plot import plt
@@ -16,6 +32,13 @@
 
 
 class Importances(StaticPlugin):
+    """
+    Provide a plugin for the visualization of the importances.
+
+    Provided utilities include getting input/output layout, data processing
+    and loading outputs. Also provides a matplotlib version.
+    """
+
     id = "importances"
     name = "Importances"
     icon = "far fa-star"
@@ -23,7 +46,21 @@ class Importances(StaticPlugin):
     activate_run_selection = True
 
     @staticmethod
-    def get_input_layout(register):
+    def get_input_layout(register: Callable) -> List[Any]:
+        """
+        Get the layout for the input block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            Layout for the input block.
+        """
         return [
             html.Div(
                 [
@@ -71,7 +108,21 @@ def get_input_layout(register):
         ]
 
     @staticmethod
-    def get_filter_layout(register):
+    def get_filter_layout(register: Callable) -> List[html.Div]:
+        """
+        Get the layout for the filter block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[html.Div]
+            Layout for the filter block.
+        """
         return [
             html.Div(
                 [
@@ -102,7 +153,19 @@ def get_filter_layout(register):
             ),
         ]
 
-    def load_inputs(self):
+    def load_inputs(self) -> Dict[str, Dict[str, Any]]:
+        """
+        Load the content for the defined inputs in 'get_input_layout' and 'get_filter_layout'.
+
+        This method is necessary to pre-load contents for the inputs.
+        If the plugin is called for the first time, or there are no results in the cache,
+        the plugin gets its content from this method.
+
+        Returns
+        -------
+        Dict[str, Dict[str, Any]]
+            Content to be filled.
+        """
         method_labels = ["Local Parameter Importance (local)", "fANOVA (global)"]
         method_values = ["local", "global"]
 
@@ -117,8 +180,28 @@ def load_inputs(self):
             "budget_ids": {"options": get_checklist_options(), "value": []},
         }
 
-    def load_dependency_inputs(self, run, _, inputs):
-        # Prepare objetives
+    def load_dependency_inputs(self, run, _: Any, inputs: Dict[str, Any]) -> Dict[str, Any]:  # type: ignore # noqa: E501
+        """
+        Work like 'load_inputs' but called after inputs have changed.
+
+        Note
+        ----
+        Only the changes have to be returned.
+        The returned dictionary will be merged with the inputs.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs : Dict[str, Any]
+            Current content of the inputs.
+
+        Returns
+        -------
+        Dict[str, Any]
+            A dictionary with the changes.
+        """
+        # Prepare objectives
         objective_names = run.get_objective_names()
         objective_ids = run.get_objective_ids()
         objective_options = get_select_options(objective_names, objective_ids)
@@ -170,7 +253,38 @@ def load_dependency_inputs(self, run, _, inputs):
         }
 
     @staticmethod
-    def process(run, inputs):
+    def process(run: AbstractRun, inputs: Dict[str, Any]) -> Dict[str, Any]:
+        """
+        Return raw data based on the run and input data.
+
+        Warning
+        -------
+        The returned data must be JSON serializable.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differ
+        compared to 'load_inputs' or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run : AbstractRun
+            The run to process.
+        inputs :  Dict[str, Any]
+            The input data.
+
+        Returns
+        -------
+        Dict[str, Any]
+            A serialzied dictionary.
+
+        Raises
+        ------
+        RuntimeError
+            If the number of trees is not specified.
+            If the method is not found.
+        """
         objective = run.get_objective(inputs["objective_id"])
         method = inputs["method"]
         n_trees = inputs["n_trees"]
@@ -178,14 +292,15 @@ def process(run, inputs):
         if n_trees is None:
             raise RuntimeError("Please specify the number of trees.")
 
-        # Handle constant values in fANOVA: As the fANOVA implementation relies on pyrfr and pyrfr cannot be applied
-        # to constant hyperparameters (see https://github.com/automl/fanova/issues/81), as a workaround we remove
-        # constant hyperparameters before calculation.
-        # Note: This will break if there are conditions or forbiddens including constant hyperparameters.
+        # Handle constant values in fANOVA: As the fANOVA implementation relies on pyrfr
+        # and pyrfr cannot be applied to constant hyperparameters
+        # (see https://github.com/automl/fanova/issues/81), as a workaround constant
+        # hyperparameters are removed before calculation.
+        # Note: This will break if there are conditions or forbiddens including
+        # constant hyperparameters.
         hp_dict = run.configspace.get_hyperparameters_dict()
         if method == "global" and any([type(v) == Constant for v in hp_dict.values()]):
-            hp_dict_wo_const = {
-                k: v for k, v in hp_dict.items() if type(v) != Constant}
+            hp_dict_wo_const = {k: v for k, v in hp_dict.items() if type(v) != Constant}
             configspace_wo_const = ConfigurationSpace()
             for k in hp_dict_wo_const.keys():
                 configspace_wo_const.add_hyperparameter(hp_dict_wo_const[k])
@@ -195,14 +310,17 @@ def process(run, inputs):
 
             configs_wo_const = []
             for n in range(len(run.configs)):
-                configs_wo_const.append({k: v for k,v in run.configs[n].items() if k in hp_dict_wo_const.keys()})
-            run.configs = configs_wo_const
+                configs_wo_const.append(
+                    {k: v for k, v in run.configs[n].items() if k in hp_dict_wo_const.keys()}
+                )
+            run.configs = dict(enumerate(configs_wo_const))
 
         hp_names = run.configspace.get_hyperparameter_names()
         budgets = run.get_budgets(include_combined=True)
 
+        evaluator: Optional[Union[LocalEvaluator, GlobalEvaluator]] = None
         if method == "local":
-            # Intiatize the evaluator
+            # Initialize the evaluator
             evaluator = LocalEvaluator(run)
         elif method == "global":
             evaluator = GlobalEvaluator(run)
@@ -212,19 +330,57 @@ def process(run, inputs):
         # Collect data
         data = {}
         for budget_id, budget in enumerate(budgets):
+            assert isinstance(budget, (int, float))
             evaluator.calculate(objective, budget, n_trees=n_trees, seed=0)
 
             importances = evaluator.get_importances(hp_names)
             data[budget_id] = importances
 
-        return data
+        return data  # type: ignore
 
     @staticmethod
-    def get_output_layout(register):
+    def get_output_layout(register: Callable) -> dcc.Graph:
+        """
+        Get the layout for the output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register outputs.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        dcc.Graph
+            Layout for the output block.
+        """
         return dcc.Graph(register("graph", "figure"), style={"height": Config.FIGURE_HEIGHT})
 
     @staticmethod
-    def load_outputs(run, inputs, outputs):
+    def load_outputs(run, inputs, outputs) -> go.Figure:  # type: ignore
+        """
+        Read in raw data and prepare for layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differ
+        compared to 'load_inputs' or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            Input and filter values from the user.
+        outputs
+            Raw output from the run.
+
+        Returns
+        -------
+        go.figure
+            The figure of the importances.
+        """
         # First selected, should always be shown first
         selected_hp_names = inputs["hyperparameter_names"]
         selected_budget_ids = inputs["budget_ids"]
@@ -301,14 +457,44 @@ def load_outputs(run, inputs, outputs):
         return figure
 
     @staticmethod
-    def get_mpl_output_layout(register):
+    def get_mpl_output_layout(register: Callable) -> html.Img:
+        """
+        Get the layout for the matplotlib output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register outputs.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        html.Img
+            The layout for the matplotlib output block.
+        """
         return html.Img(
             id=register("graph", "src"),
             className="img-fluid",
         )
 
     @staticmethod
-    def load_mpl_outputs(run, inputs, outputs):
+    def load_mpl_outputs(run, inputs: Dict[str, Any], outputs):  # type: ignore
+        """
+        Read the raw data and prepare it for the layout.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs : Dict[str, Any]
+            Input and filter values from the user.
+        outputs
+            Raw output from the run.
+
+        Returns
+        -------
+        The rendered matplotlib figure of the importances.
+        """
         # First selected, should always be shown first
         selected_hp_names = inputs["hyperparameter_names"]
         selected_budget_ids = inputs["budget_ids"]
@@ -354,7 +540,7 @@ def load_mpl_outputs(run, inputs, outputs):
                 hp_name = "..." + hp_name[-18:]
 
             x_labels += [hp_name]
-        x = np.arange(len(x_labels))
+        x_values = np.arange(len(x_labels))
 
         plt.figure()
         for budget_id, values in data.items():
@@ -366,10 +552,10 @@ def load_mpl_outputs(run, inputs, outputs):
 
             budget = run.get_budget(budget_id, human=True)
             plt.bar(
-                x,
+                x_values,
                 y,
                 yerr=y_err,
-                color=plt.get_color(budget_id),
+                color=plt.get_color(budget_id),  # type: ignore
                 label=budget,
                 error_kw=dict(lw=1, capsize=2, capthick=1),
             )
@@ -377,7 +563,7 @@ def load_mpl_outputs(run, inputs, outputs):
         plt.legend(title="Budgets")
 
         # Rotate x ticks
-        plt.xticks(x, x_labels, rotation=90)
+        plt.xticks(x_values, x_labels, rotation=90)
         plt.ylabel("Importance")
 
-        return plt.render()
+        return plt.render()  # type: ignore
diff --git a/deepcave/plugins/hyperparameter/pdp.py b/deepcave/plugins/hyperparameter/pdp.py
index 5783903f..fb2f57c9 100644
--- a/deepcave/plugins/hyperparameter/pdp.py
+++ b/deepcave/plugins/hyperparameter/pdp.py
@@ -1,3 +1,24 @@
+#  noqa: D400
+"""
+# PartialDependencies
+
+This module provides utilities for generating Partial Dependency Plots (PDP).
+
+Provided utilities include getting input and output layout (filtered or non-filtered),
+processing the data and loading the outputs.
+
+## Classes
+    - PartialDependencies: Generate a Partial Dependency Plot (PDP).
+
+## Constants
+    GRID_POINTS_PER_AXIS : int
+    SAMPLES_PER_HP : int
+    MAX_SAMPLES : int
+    MAX_SHOWN_SAMPLES : int
+"""
+
+from typing import Any, Callable, Dict, List
+
 import dash_bootstrap_components as dbc
 import numpy as np
 import plotly.graph_objs as go
@@ -18,6 +39,13 @@
 
 
 class PartialDependencies(StaticPlugin):
+    """
+    Generate Partial Dependency Plots (PDP).
+
+    Provided utilities include getting input and output layout (filtered or non-filtered),
+    processing the data and loading the outputs.
+    """
+
     id = "pdp"
     name = "Partial Dependencies"
     icon = "fas fa-grip-lines"
@@ -25,7 +53,21 @@ class PartialDependencies(StaticPlugin):
     activate_run_selection = True
 
     @staticmethod
-    def get_input_layout(register):
+    def get_input_layout(register: Callable) -> List[dbc.Row]:
+        """
+        Get the layout for the input block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[dbc.Row]
+            The layout for the input block.
+        """
         return [
             dbc.Row(
                 [
@@ -87,7 +129,21 @@ def get_input_layout(register):
         ]
 
     @staticmethod
-    def get_filter_layout(register):
+    def get_filter_layout(register: Callable) -> List[Any]:
+        """
+        Get the layout for the filter block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            The layout for the filter block.
+        """
         return [
             dbc.Row(
                 [
@@ -124,13 +180,48 @@ def get_filter_layout(register):
             ),
         ]
 
-    def load_inputs(self):
+    def load_inputs(self) -> Dict[str, Dict[str, Any]]:
+        """
+        Load the content for the defined inputs in 'get_input_layout' and 'get_filter_layout'.
+
+        This method is necessary to pre-load contents for the inputs.
+        If the plugin is called for the first time, or there are no results in the cache,
+        the plugin gets its content from this method.
+
+        Returns
+        -------
+        Dict[str, Dict[str, Any]]
+            Content to be filled.
+        """
         return {
             "show_confidence": {"options": get_select_options(binary=True), "value": "true"},
             "show_ice": {"options": get_select_options(binary=True), "value": "true"},
         }
 
-    def load_dependency_inputs(self, run, previous_inputs, inputs):
+    def load_dependency_inputs(self, run, previous_inputs, inputs) -> Dict[str, Any]:  # type: ignore # noqa: E501
+        """
+        Work like 'load_inputs' but called after inputs have changed.
+
+        Note
+        ----
+        Only the changes have to be returned. The returned dictionary
+        will be merged with the inputs.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            Current content of the inputs.
+        previous_inputs
+            Previous content of the inputs.
+            Not used in this specific function.
+
+        Returns
+        -------
+        Dict[str, Any]
+            Dictionary with the changes.
+        """
         objective_names = run.get_objective_names()
         objective_ids = run.get_objective_ids()
         objective_options = get_select_options(objective_names, objective_ids)
@@ -145,7 +236,6 @@ def load_dependency_inputs(self, run, previous_inputs, inputs):
         objective_value = inputs["objective_id"]["value"]
         budget_value = inputs["budget_id"]["value"]
         hp1_value = inputs["hyperparameter_name_1"]["value"]
-        hp2_value = inputs["hyperparameter_name_2"]["value"]
 
         if objective_value is None:
             objective_value = objective_ids[0]
@@ -165,7 +255,37 @@ def load_dependency_inputs(self, run, previous_inputs, inputs):
         }
 
     @staticmethod
-    def process(run, inputs):
+    def process(run, inputs) -> Dict[str, Any]:  # type: ignore
+        """
+        Return raw data based on a run and the input data.
+
+        Warning
+        -------
+        The returned data must be JSON serializable.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differ
+        compared to 'load_inputs' or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The run to process.
+        inputs
+            The input data.
+
+        Returns
+        -------
+        Dict[str, Any]
+            A serialized dictionary.
+
+        Raises
+        ------
+        RuntimeError
+            If the objective is None.
+        """
         # Surrogate
         hp_names = run.configspace.get_hyperparameter_names()
         objective = run.get_objective(inputs["objective_id"])
@@ -197,7 +317,7 @@ def process(run, inputs):
             selected_hyperparameters += [hp2]
 
         num_samples = SAMPLES_PER_HP * len(X)
-        # We limit the samples to max 10k
+        # The samples are limited to max 10k
         if num_samples > MAX_SAMPLES:
             num_samples = MAX_SAMPLES
 
@@ -213,7 +333,7 @@ def process(run, inputs):
         x = pdp.x_pdp.tolist()
         y = pdp.y_pdp.tolist()
 
-        # We have to cut the ICE curves because it's too much data
+        # The ICE curves have to be cut because it's too much data
         x_ice = pdp._ice.x_ice.tolist()
         y_ice = pdp._ice.y_ice.tolist()
 
@@ -230,11 +350,50 @@ def process(run, inputs):
         }
 
     @staticmethod
-    def get_output_layout(register):
+    def get_output_layout(register: Callable) -> dcc.Graph:
+        """
+        Get the layout for the output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register outputs.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        dcc.Graph
+            Layout for the output block.
+        """
         return dcc.Graph(register("graph", "figure"), style={"height": Config.FIGURE_HEIGHT})
 
     @staticmethod
-    def get_pdp_figure(run, inputs, outputs, show_confidence, show_ice, title=None):
+    def get_pdp_figure(  # type: ignore
+        run, inputs, outputs, show_confidence, show_ice, title=None
+    ) -> go.Figure:
+        """
+        Create a figure of the Partial Dependency Plot (PDP).
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            Input and filter values from the user.
+        outputs
+            Raw output from the run.
+        show_confidence
+            Whether to show confidence in the plot.
+        show_ice
+            Whether to show ice curves in the plot.
+        title
+            Title of the plot.
+
+        Returns
+        -------
+        go.Figure
+            The figure of the Partial Dependency Plot (PDP).
+        """
         # Parse inputs
         hp1_name = inputs["hyperparameter_name_1"]
         hp1_idx = run.configspace.get_idx_by_hyperparameter_name(hp1_name)
@@ -317,7 +476,7 @@ def get_pdp_figure(run, inputs, outputs, show_confidence, show_ice, title=None):
                     "yaxis": {
                         "title": objective_name,
                     },
-                    "title": title
+                    "title": title,
                 }
             )
         else:
@@ -344,7 +503,7 @@ def get_pdp_figure(run, inputs, outputs, show_confidence, show_ice, title=None):
                     xaxis=dict(tickvals=x_tickvals, ticktext=x_ticktext, title=hp1_name),
                     yaxis=dict(tickvals=y_tickvals, ticktext=y_ticktext, title=hp2_name),
                     margin=Config.FIGURE_MARGIN,
-                    title=title
+                    title=title,
                 )
             )
 
@@ -354,7 +513,30 @@ def get_pdp_figure(run, inputs, outputs, show_confidence, show_ice, title=None):
         return figure
 
     @staticmethod
-    def load_outputs(run, inputs, outputs):
+    def load_outputs(run, inputs, outputs):  # type: ignore
+        """
+        Read the raw data and prepare it for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differ
+        compared to 'load_inputs' or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            Input and filter values from the user.
+        outputs
+            Raw output from the run.
+
+        Returns
+        -------
+        go.Figure
+            The figure of the Partial Dependency Plot (PDP).
+        """
         show_confidence = inputs["show_confidence"]
         show_ice = inputs["show_ice"]
 
diff --git a/deepcave/plugins/hyperparameter/symbolic_explanations.py b/deepcave/plugins/hyperparameter/symbolic_explanations.py
index d9ba94f9..75c3764f 100644
--- a/deepcave/plugins/hyperparameter/symbolic_explanations.py
+++ b/deepcave/plugins/hyperparameter/symbolic_explanations.py
@@ -145,7 +145,7 @@ def get_input_layout(register: Callable) -> List[Union[dbc.Row, html.Details]]:
                                     ),
                                     dcc.Slider(
                                         id=register("parsimony", "value", type=int),
-                                        marks=dict([i, str(10**i)] for i in range(-8, 1)),
+                                        marks=dict((i, str(10**i)) for i in range(-8, 1)),
                                         min=-8,
                                         max=0,
                                         step=1,
@@ -448,10 +448,10 @@ def process(run, inputs) -> Dict[str, Any]:  # type: ignore
                 f"{objective.name} = "
                 f"{convert_symb(symb_model, n_decimals=3, hp_names=selected_hyperparameters)}"
             )
-        except:
+        except Exception as e:
             conv_expr = (
                 "Conversion of the expression failed. Please try another seed or increase "
-                "the parsimony hyperparameter."
+                f"the parsimony hyperparameter: {e}"
             )
 
         if len(conv_expr) > 150:
diff --git a/deepcave/plugins/objective/__init__.py b/deepcave/plugins/objective/__init__.py
index e69de29b..579f9fd2 100644
--- a/deepcave/plugins/objective/__init__.py
+++ b/deepcave/plugins/objective/__init__.py
@@ -0,0 +1,15 @@
+#  noqa: D400
+"""
+# objective
+
+## Modules
+----------
+configuration_cube
+    This module provides utilities for visualizing and creating a configruation cube.
+cost_over_time
+    This module provides utilities for visualizing the cost over time.
+parallel_coordinates
+    This module provides utilities for visualizing the parallel coordinates.
+pareto_front
+    This module provides utilities for creating a visualization of the Pareto Front.
+"""
diff --git a/deepcave/plugins/objective/configuration_cube.py b/deepcave/plugins/objective/configuration_cube.py
index 00cbf3dc..8c0cacb6 100644
--- a/deepcave/plugins/objective/configuration_cube.py
+++ b/deepcave/plugins/objective/configuration_cube.py
@@ -1,4 +1,16 @@
-from typing import Dict, Tuple
+#  noqa: D400
+"""
+# ConfigurationCube
+
+This module provides utilities for visualizing and creating a configuration cube.
+
+The configuration cube displays configurations and their score on the objective.
+
+## Classes
+    - ConfigurationCube: A plugin for visualizing a configuration cube.
+"""
+
+from typing import Any, Callable, Dict, List, Tuple
 
 import dash_bootstrap_components as dbc
 import numpy as np
@@ -9,7 +21,7 @@
 
 from deepcave.config import Config
 from deepcave.plugins.dynamic import DynamicPlugin
-from deepcave.runs import Status
+from deepcave.runs import AbstractRun, Status
 from deepcave.utils.compression import deserialize, serialize
 from deepcave.utils.layout import (
     get_checklist_options,
@@ -28,6 +40,8 @@
 
 
 class ConfigurationCube(DynamicPlugin):
+    """A plugin for visualizing a configuration cube."""
+
     id = "ccube"
     name = "Configuration Cube"
     icon = "fas fa-cube"
@@ -35,7 +49,21 @@ class ConfigurationCube(DynamicPlugin):
     help = "docs/plugins/configuration_cube.rst"
 
     @staticmethod
-    def get_input_layout(register):
+    def get_input_layout(register: Callable) -> List[dbc.Row]:
+        """
+        Get the layout for the input block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[dbc.Row]
+            Layouts for the input block.
+        """
         return [
             dbc.Row(
                 [
@@ -72,7 +100,21 @@ def get_input_layout(register):
         ]
 
     @staticmethod
-    def get_filter_layout(register):
+    def get_filter_layout(register: Callable) -> List[html.Div]:
+        """
+        Get the layout for the filter block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[html.Div]
+            Layouts for the filter block.
+        """
         return [
             html.Div(
                 [
@@ -98,14 +140,48 @@ def get_filter_layout(register):
             ),
         ]
 
-    def load_inputs(self):
+    def load_inputs(self) -> Dict[str, Any]:
+        """
+        Load the content for the defined inputs in 'get_input_layout' and 'get_filter_layout'.
+
+        This method is necessary to pre-load contents for the inputs.
+        So, if the plugin is called for the first time or there are no results in the cache,
+        the plugin gets its content from this method.
+
+        Returns
+        -------
+        Dict[str, Any]
+            The content to be filled.
+        """
         return {
             "n_configs": {"min": 0, "max": 0, "marks": get_slider_marks(), "value": 0},
             "hyperparameter_names": {"options": get_checklist_options(), "value": []},
         }
 
-    def load_dependency_inputs(self, run, _, inputs):
-        # Prepare objetives
+    def load_dependency_inputs(self, run, _, inputs) -> Dict[str, Any]:  # type: ignore
+        """
+        Work like 'load_inputs' but called after inputs have changed.
+
+        It is restricted to three Hyperparameters.
+
+        Note
+        ----
+        Only the changes have to be returned.
+        The returned dictionary will be merged with the inputs.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            Current content of the inputs.
+
+        Returns
+        -------
+        Dict[str, Any]
+           The dictionary with the changes.
+        """
+        # Prepare objectives
         objective_names = run.get_objective_names()
         objective_ids = run.get_objective_ids()
         objective_options = get_select_options(objective_names, objective_ids)
@@ -167,7 +243,32 @@ def load_dependency_inputs(self, run, _, inputs):
         }
 
     @staticmethod
-    def process(run, inputs):
+    def process(run: AbstractRun, inputs: Dict[str, Any]) -> Dict[str, str]:
+        """
+        Return raw data based on the run and input data.
+
+        Warning
+        -------
+        The returned data must be JSON serializable.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run : AbstractRun
+            The selected run.
+        inputs : Dict[str, Any]
+            The input data.
+
+        Returns
+        -------
+        Dict[str, str]
+            The serialized dictionary.
+        """
         budget = run.get_budget(inputs["budget_id"])
         objective = run.get_objective(inputs["objective_id"])
 
@@ -177,11 +278,48 @@ def process(run, inputs):
         return {"df": serialize(df)}
 
     @staticmethod
-    def get_output_layout(register):
+    def get_output_layout(register: Callable) -> Tuple[dcc.Graph,]:
+        """
+        Get the layout for the output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register output.
+            The register_output function is located in the Plugin superclass.
+
+        Returns
+        -------
+        Tuple[dcc.Graph,]
+            Layout for the output block.
+        """
         return (dcc.Graph(register("graph", "figure"), style={"height": Config.FIGURE_HEIGHT}),)
 
     @staticmethod
-    def load_outputs(run, inputs, outputs):
+    def load_outputs(run, inputs, outputs) -> go.Figure:  # type: ignore
+        """
+        Read in the raw data and prepares them for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            Input and filter values from the user.
+        outputs
+            Raw output from the run.
+
+        Returns
+        -------
+        go.Figure
+            The output figure.
+        """
         df = deserialize(outputs["df"], dtype=pd.DataFrame)
         hp_names = inputs["hyperparameter_names"]
         n_configs = inputs["n_configs"]
@@ -217,7 +355,7 @@ def load_outputs(run, inputs, outputs):
         # Transforms data to values
         values = np.transpose(np.array(data)).tolist()
 
-        # Now we want to filter duplicates
+        # Now the duplicates are filtered
         filtered_data: Dict[Tuple[int, float], Tuple] = {}
         for config_id, cost, v in zip(config_ids, costs, values):
             v = tuple(v)  # Make hashable
@@ -235,7 +373,7 @@ def load_outputs(run, inputs, outputs):
                     if old_cost > cost:
                         continue
 
-                # Otherwise we have to replace
+                # Otherwise it is replaced
                 del filtered_data[old_key]
 
             filtered_data[key] = v
diff --git a/deepcave/plugins/objective/cost_over_time.py b/deepcave/plugins/objective/cost_over_time.py
index 02293a56..d2a5bea2 100644
--- a/deepcave/plugins/objective/cost_over_time.py
+++ b/deepcave/plugins/objective/cost_over_time.py
@@ -1,4 +1,17 @@
-from typing import List
+#  noqa: D400
+"""
+# CostOverTime
+
+This module provides utilities for visualizing the cost over time.
+
+Visualized changes can be regarding to number of configurations or time.
+It includes a corresponding plugin class.
+
+## Classes
+    - CostOverTime: A plugin to provide a visualization for the cost over time.
+"""
+
+from typing import Any, Callable, Dict, List
 
 import dash_bootstrap_components as dbc
 import numpy as np
@@ -18,12 +31,44 @@
 
 
 class CostOverTime(DynamicPlugin):
+    """
+    A plugin to provide a visualization for the cost over time.
+
+    Properties
+    ----------
+    objective_options : List[Dict[str, Any]]
+        A list of dictionaries of the objective options.
+    budget_options : List[Dict[str, Any]]
+        A list of dictionaries of the budget options.
+    """
+
     id = "cost_over_time"
     name = "Cost Over Time"
     icon = "fas fa-chart-line"
     help = "docs/plugins/cost_over_time.rst"
 
     def check_runs_compatibility(self, runs: List[AbstractRun]) -> None:
+        """
+        Check if the runs are compatible.
+
+        This function is needed if all selected runs need something in common
+        (e.g. budget or objective).
+        Since this function is called before the layout is created,
+        it can be also used to set common values for the plugin.
+
+        Parameters
+        ----------
+        runs : List[AbstractRun]
+            A list containing the selected runs.
+
+        Raises
+        ------
+        NotMergeableError
+            If the meta data of the runs are not equal.
+            If the configuration spaces of the runs are not equal.
+            If the budgets of the runs are not equal.
+            If the objective of the runs are not equal.
+        """
         check_equality(runs, objectives=True, budgets=True)
 
         # Set some attributes here
@@ -38,7 +83,21 @@ def check_runs_compatibility(self, runs: List[AbstractRun]) -> None:
         self.budget_options = get_select_options(budgets, budget_ids)
 
     @staticmethod
-    def get_input_layout(register):
+    def get_input_layout(register: Callable) -> List[dbc.Row]:
+        """
+        Get the layout for the input block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[dbc.Row]
+            Layouts for the input block.
+        """
         return [
             dbc.Row(
                 [
@@ -71,7 +130,21 @@ def get_input_layout(register):
         ]
 
     @staticmethod
-    def get_filter_layout(register):
+    def get_filter_layout(register: Callable) -> List[Any]:
+        """
+        Get the layout for the filter block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            Layouts for the filter block.
+        """
         return [
             html.Div(
                 [
@@ -109,7 +182,19 @@ def get_filter_layout(register):
             ),
         ]
 
-    def load_inputs(self):
+    def load_inputs(self) -> Dict[str, Any]:
+        """
+        Load the content for the defined inputs in 'get_input_layout' and 'get_filter_layout'.
+
+        This method is necessary to pre-load contents for the inputs.
+        So, if the plugin is called for the first time or there are no results in the cache,
+        the plugin gets its content from this method.
+
+        Returns
+        -------
+        Dict[str, Any]
+            The content to be filled.
+        """
         return {
             "objective_id": {
                 "options": self.objective_options,
@@ -132,7 +217,32 @@ def load_inputs(self):
         }
 
     @staticmethod
-    def process(run, inputs):
+    def process(run, inputs) -> Dict[str, Any]:  # type: ignore
+        """
+        Return raw data based on a run and input data.
+
+        Warning
+        -------
+        The returned data must be JSON serializable.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run : AbstractRun
+            The selected run to process.
+        inputs : Dict[str, Any]
+            The input data.
+
+        Returns
+        -------
+        Dict[str, Any]
+            A serialized dictionary.
+        """
         budget = run.get_budget(inputs["budget_id"])
         objective = run.get_objective(inputs["objective_id"])
 
@@ -149,11 +259,48 @@ def process(run, inputs):
         }
 
     @staticmethod
-    def get_output_layout(register):
+    def get_output_layout(register: Callable) -> dcc.Graph:
+        """
+        Get the layout for the output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register outputs.
+            The register_output function is located in the Plugin superclass.
+
+        Returns
+        -------
+        dcc.Graph
+            The layouts for the output block.
+        """
         return dcc.Graph(register("graph", "figure"), style={"height": Config.FIGURE_HEIGHT})
 
     @staticmethod
-    def load_outputs(runs, inputs, outputs):
+    def load_outputs(runs, inputs, outputs) -> go.Figure:  # type: ignore
+        """
+        Read in the raw data and prepare them for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        runs :
+            The selected runs.
+        inputs :
+            The input and filter values from the user.
+        outputs :
+            The raw outputs from the runs.
+
+        Returns
+        -------
+        go.Figure
+            The output figure.
+        """
         show_runs = inputs["show_runs"]
         show_groups = inputs["show_groups"]
         objective = None
@@ -179,9 +326,9 @@ def load_outputs(runs, inputs, outputs):
             y_err = np.array(outputs[run.id]["costs_std"])
             y_upper = list(y + y_err)
             y_lower = list(y - y_err)
-            y = list(y)
+            y_list = list(y)
 
-            hovertext = ""
+            hovertext = None
             hoverinfo = "skip"
             symbol = None
             mode = "lines"
@@ -194,7 +341,7 @@ def load_outputs(runs, inputs, outputs):
             traces.append(
                 go.Scatter(
                     x=x,
-                    y=y,
+                    y=y_list,
                     name=run.name,
                     line_shape="hv",
                     line=dict(color=get_color(idx)),
diff --git a/deepcave/plugins/objective/parallel_coordinates.py b/deepcave/plugins/objective/parallel_coordinates.py
index 7afeaeab..ddfd5296 100644
--- a/deepcave/plugins/objective/parallel_coordinates.py
+++ b/deepcave/plugins/objective/parallel_coordinates.py
@@ -1,3 +1,16 @@
+#  noqa: D400
+
+"""
+# ParallelCoordinates
+
+This module provides utilities for visualizing the parallel coordinates.
+
+## Classes
+    - ParallelCoordinates : Can be used for visualizing the parallel coordinates.
+"""
+
+from typing import Any, Callable, Dict, List
+
 from collections import defaultdict
 
 import dash_bootstrap_components as dbc
@@ -20,6 +33,8 @@
 
 
 class ParallelCoordinates(StaticPlugin):
+    """Can be used for visualizing the parallel coordinates."""
+
     id = "parallel_coordinates"
     name = "Parallel Coordinates"
     icon = "far fa-map"
@@ -27,7 +42,21 @@ class ParallelCoordinates(StaticPlugin):
     help = "docs/plugins/parallel_coordinates.rst"
 
     @staticmethod
-    def get_input_layout(register):
+    def get_input_layout(register: Callable) -> List[Any]:
+        """
+        Get the layout for the input block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to regsiter (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            The layouts for the input block.
+        """
         return [
             dbc.Row(
                 [
@@ -79,7 +108,21 @@ def get_input_layout(register):
         ]
 
     @staticmethod
-    def get_filter_layout(register):
+    def get_filter_layout(register: Callable) -> List[Any]:
+        """
+        Get the layout for the filter block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            The layouts for the filter block.
+        """
         return [
             dbc.Row(
                 [
@@ -120,7 +163,19 @@ def get_filter_layout(register):
             ),
         ]
 
-    def load_inputs(self):
+    def load_inputs(self) -> Dict[str, Dict[str, Any]]:
+        """
+        Load the content for the defined inputs in 'get_input_layout' and 'get_filter_layout'.
+
+        This method is necessary to pre-load contents for the inputs.
+        So, if the plugin is called for the first time or there are no results in the cache,
+        the plugin gets its content from this method.
+
+        Returns
+        -------
+        Dict[str, Dict[str, Any]]
+            Content to be filled.
+        """
         return {
             "show_important_only": {"options": get_select_options(binary=True), "value": "true"},
             "show_unsuccessful": {"options": get_select_options(binary=True), "value": "false"},
@@ -129,8 +184,28 @@ def load_inputs(self):
             "hide_hps": {"hidden": True},
         }
 
-    def load_dependency_inputs(self, run, _, inputs):
-        # Prepare objetives
+    def load_dependency_inputs(self, run, _, inputs) -> Dict[str, Any]:  # type: ignore
+        """
+        Work like 'load_inputs' but called after inputs have changed.
+
+        Note
+        ----
+        Only the changes have to be returned.
+        The returned dictionary will be merged with the inputs.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            Current content of the inputs.
+
+        Returns
+        -------
+        Dict[str, Any]
+            The dictionary with the changes.
+        """
+        # Prepare objectives
         objective_names = run.get_objective_names()
         objective_ids = run.get_objective_ids()
         objective_options = get_select_options(objective_names, objective_ids)
@@ -186,29 +261,91 @@ def load_dependency_inputs(self, run, _, inputs):
         }
 
     @staticmethod
-    def process(run, inputs):
+    def process(run, inputs) -> Dict[str, Any]:  # type: ignore
+        """
+        Return raw data based on a run and input data.
+
+        Warning
+        -------
+        The returned data must be JSON serializable.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run : AbstractRun
+            The run to process.
+        inputs : Dict[str, Any]
+            The input data.
+
+        Returns
+        -------
+        Dict[str, Any]
+            The serialized dictionary.
+        """
         budget = run.get_budget(inputs["budget_id"])
         objective = run.get_objective(inputs["objective_id"])
         df = serialize(run.get_encoded_data(objective, budget))
-        result = {"df": df}
+        result: Dict[str, Any] = {"df": df}
 
         if inputs["show_important_only"]:
             # Let's run a quick fANOVA here
             evaluator = fANOVA(run)
             evaluator.calculate(objective, budget, n_trees=10, seed=0)
-            importances = evaluator.get_importances()
-            importances = {u: v[0] for u, v in importances.items()}
+            importances_dict = evaluator.get_importances()
+            importances = {u: v[0] for u, v in importances_dict.items()}
             important_hp_names = sorted(importances, key=lambda key: importances[key], reverse=True)
             result["important_hp_names"] = important_hp_names
 
         return result
 
     @staticmethod
-    def get_output_layout(register):
+    def get_output_layout(register: Callable) -> dcc.Graph:
+        """
+        Get the layout for the output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register outputs.
+            The register_output function is located in the Plugin superclass.
+
+        Returns
+        -------
+        dcc.Graph
+            The layouts for the output block.
+        """
         return dcc.Graph(register("graph", "figure"), style={"height": Config.FIGURE_HEIGHT})
 
     @staticmethod
-    def load_outputs(run, inputs, outputs):
+    def load_outputs(run, inputs, outputs) -> go.Figure:  # type: ignore
+        """
+        Read in the raw data and prepare them for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            The inputs and filter values fromt the user.
+        outputs
+            Raw output from the run.
+
+        Returns
+        -------
+        go.Figure
+            The output figure.
+        """
         objective = run.get_objective(inputs["objective_id"])
         objective_name = objective.name
 
@@ -238,7 +375,7 @@ def load_outputs(run, inputs, outputs):
             if b:
                 objective_values += [value]
 
-        data = defaultdict(dict)
+        data: defaultdict = defaultdict(dict)
         for hp_name in hp_names:
             values = []
             for hp_v, objective_v in zip(df[hp_name].values, df[objective_name].values):
diff --git a/deepcave/plugins/objective/pareto_front.py b/deepcave/plugins/objective/pareto_front.py
index db527cca..05b9b13b 100644
--- a/deepcave/plugins/objective/pareto_front.py
+++ b/deepcave/plugins/objective/pareto_front.py
@@ -1,4 +1,16 @@
-from typing import List, Union
+#  noqa: D400
+"""
+# ParetoFront
+
+This module provides utilities for creating a visualization of the Pareto Front.
+
+It includes the corresponding Pareto Front plugin.
+
+## Classes
+    - ParetoFront: Generate an interactive Pareto Front visualization.
+"""
+
+from typing import Any, Callable, Dict, List, Literal, Union
 
 import dash_bootstrap_components as dbc
 import numpy as np
@@ -7,7 +19,7 @@
 
 from deepcave.config import Config
 from deepcave.plugins.dynamic import DynamicPlugin
-from deepcave.runs import Status, check_equality
+from deepcave.runs import AbstractRun, Status, check_equality
 from deepcave.utils.layout import get_select_options, help_button
 from deepcave.utils.styled_plot import plt
 from deepcave.utils.styled_plotty import (
@@ -18,12 +30,44 @@
 
 
 class ParetoFront(DynamicPlugin):
+    """
+    Generate an interactive Pareto Front visualization.
+
+    Properties
+    ----------
+    objective_options : List[Dict[str, Any]]
+        A list of the objective options.
+    budget_options : List[Dict[str, Any]]
+        A list of the budget options.
+    """
+
     id = "pareto_front"
     name = "Pareto Front"
     icon = "fas fa-wind"
     help = "docs/plugins/pareto_front.rst"
 
-    def check_runs_compatibility(self, runs):
+    def check_runs_compatibility(self, runs: List[AbstractRun]) -> None:
+        """
+        Check if the runs are compatible.
+
+        This function is needed if all selected runs need something in common
+        (e.g. budget or objective).
+        Since this function is called before the layout is created,
+        it can be also used to set common values for the plugin.
+
+        Parameters
+        ----------
+        runs : List[AbstractRun]
+            A list containing the selected runs.
+
+        Raises
+        ------
+        NotMergeableError
+            If the meta data of the runs are not equal.
+            If the configuration spaces of the runs are not equal.
+            If the budgets of the runs are not equal.
+            If the objective of the runs are not equal.
+        """
         check_equality(runs, objectives=True, budgets=True)
 
         # Set some attributes here
@@ -38,7 +82,21 @@ def check_runs_compatibility(self, runs):
         self.budget_options = get_select_options(budgets, budget_ids)
 
     @staticmethod
-    def get_input_layout(register):
+    def get_input_layout(register: Callable) -> List[Any]:
+        """
+        Get layout for the input block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            The layouts for the input block.
+        """
         return [
             dbc.Row(
                 [
@@ -85,7 +143,21 @@ def get_input_layout(register):
         ]
 
     @staticmethod
-    def get_filter_layout(register):
+    def get_filter_layout(register: Callable) -> List[Any]:
+        """
+        Get the layout for the filter block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            The layouts for the filter block.
+        """
         return [
             html.Div(
                 [
@@ -128,7 +200,19 @@ def get_filter_layout(register):
             ),
         ]
 
-    def load_inputs(self):
+    def load_inputs(self) -> Dict[str, Dict[str, Any]]:
+        """
+        Load the content for the defined inputs in 'get_input_layout' and 'get_filter_layout'.
+
+        This method is necessary to pre-load contents for the inputs.
+        So, if the plugin is called for the first time or there are no results in the cache,
+        the plugin gets its content from this method.
+
+        Returns
+        -------
+        Dict[str, Dict[str, Any]]
+            The content to be filled.
+        """
         return {
             "objective_id_1": {
                 "options": self.objective_options,
@@ -148,7 +232,32 @@ def load_inputs(self):
         }
 
     @staticmethod
-    def process(run, inputs):
+    def process(run, inputs) -> Dict[str, Any]:  # type: ignore
+        """
+        Return raw data based on a run and input data.
+
+        Warning
+        -------
+        The returned data must be JSON serializable.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run : AbstractRun
+            The run to process.
+        inputs : Dict[str, Any]
+            The input data.
+
+        Returns
+        -------
+        Dict[str, Any]
+            The serialized dictionary.
+        """
         # Get budget
         budget = run.get_budget(inputs["budget_id"])
 
@@ -172,15 +281,14 @@ def process(run, inputs):
         points = points[sorted_idx]
         config_ids = config_ids[sorted_idx]
 
-        is_front: Union[List, np.ndarray] = np.ones(points.shape[0], dtype=bool)
+        is_front: np.ndarray = np.ones(points.shape[0], dtype=bool)
         for point_idx, costs in enumerate(points):
-
             if is_front[point_idx]:
                 # Keep any point with a lower/upper cost
                 # This loop is a little bit complicated than
                 # is_front[is_front] = np.any(points[is_front] < c, axis=1)
                 # because objectives can be optimized in different directions.
-                # We therefore have to check for each objective separately.
+                # Therefore it has to be checked for each objective separately.
                 select = None
                 for idx, (objective, cost) in enumerate(zip([objective_1, objective_2], costs)):
                     if objective.optimize == "upper":
@@ -205,11 +313,48 @@ def process(run, inputs):
         }
 
     @staticmethod
-    def get_output_layout(register):
+    def get_output_layout(register: Callable) -> dcc.Graph:
+        """
+        Get the layout for the output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register outputs.
+            The register_output function is located in the Plugin superclass.
+
+        Returns
+        -------
+        dcc.Graph
+            The layout for the output block.
+        """
         return dcc.Graph(register("graph", "figure"), style={"height": Config.FIGURE_HEIGHT})
 
     @staticmethod
-    def load_outputs(runs, inputs, outputs):
+    def load_outputs(runs, inputs, outputs) -> go.Figure:  # type: ignore
+        """
+        Read in the raw data and prepare them for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        runs :
+            The selected runs.
+        inputs :
+            The input and filter values from the user.
+        outputs :
+            Raw outputs from the runs.
+
+        Returns
+        -------
+        go.Figure
+            The output figure.
+        """
         show_all = inputs["show_all"]
 
         traces = []
@@ -256,7 +401,7 @@ def load_outputs(runs, inputs, outputs):
                     )
                 )
 
-            # Check if we need hv or vh
+            # Check if hv or vh is needed
             objective_1 = run.get_objective(inputs["objective_id_1"])
             objective_2 = run.get_objective(inputs["objective_id_2"])
             optimize1 = objective_1.optimize
@@ -302,14 +447,50 @@ def load_outputs(runs, inputs, outputs):
         return figure
 
     @staticmethod
-    def get_mpl_output_layout(register):
+    def get_mpl_output_layout(register: Callable) -> html.Img:
+        """
+        Get the layout for the matplotlib output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register outputs.
+            The register_output function is located in the Plugin superclass.
+
+        Returns
+        -------
+        html.Img
+            The layout for the matplotlib output block.
+        """
         return html.Img(
             id=register("graph", "src"),
             className="img-fluid",
         )
 
     @staticmethod
-    def load_mpl_outputs(runs, inputs, outputs):
+    def load_mpl_outputs(runs, inputs, outputs):  # type: ignore
+        """
+        Read in the raw data and prepare them for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        runs :
+            The selected runs.
+        inputs :
+            Input and filter values from the user.
+        outputs :
+            Raw outputs from the runs.
+
+        Returns
+        -------
+        The rendered matplotlib figure.
+        """
         show_all = inputs["show_all"] == "true"
 
         plt.figure()
@@ -337,18 +518,20 @@ def load_mpl_outputs(runs, inputs, outputs):
                     x += [points[point_idx][0]]
                     y += [points[point_idx][1]]
 
-            color = plt.get_color(idx)  # , alpha=0.1)
-            color_pareto = plt.get_color(idx)
+            # , alpha=0.1)
+            color = plt.get_color(idx)  # type: ignore
+            color_pareto = plt.get_color(idx)  # type: ignore
 
             if show_all:
                 plt.scatter(x, y, color=color, marker="o", alpha=0.1, s=3)
 
-            # Check if we need hv or vh
+            # Check if hv or vh is needed
             objective_1 = run.get_objective(inputs["objective_id_1"])
             objective_2 = run.get_objective(inputs["objective_id_2"])
             optimize1 = objective_1.optimize
             optimize2 = objective_2.optimize
 
+            line_shape: Union[Literal["post"], Literal["pre"], Literal["mid"]]
             if optimize1 == optimize2:
                 if objective_1.optimize == "lower":
                     line_shape = "post"
@@ -372,4 +555,4 @@ def load_mpl_outputs(runs, inputs, outputs):
 
         plt.legend()
 
-        return plt.render()
+        return plt.render()  # type: ignore
diff --git a/deepcave/plugins/static.py b/deepcave/plugins/static.py
index bf2c503b..66dac258 100644
--- a/deepcave/plugins/static.py
+++ b/deepcave/plugins/static.py
@@ -1,5 +1,18 @@
+#  noqa: D400
+"""
+# Static
+
+This module provides a plugin class for a static plugin.
+
+It provides an Enum used for the plugin state.
+
+## Classes
+    - PluginState: An Enum to define the state of the Plugin.
+    - StaticPlugin: This class provides a static plugin object.
+"""
+
 from abc import ABC
-from typing import Any, Callable, Dict, List
+from typing import Any, Callable, Dict, List, Optional, Tuple
 
 import traceback
 from enum import Enum
@@ -18,6 +31,8 @@
 
 
 class PluginState(Enum):
+    """An Enum to define the state of the Plugin."""
+
     UNSET = -1
     READY = 0
     NEEDS_PROCESSING = 1
@@ -27,7 +42,26 @@ class PluginState(Enum):
 
 def _process(
     process: Callable[[AbstractRun, Any], None], run: AbstractRun, inputs: Dict[str, Any]
-) -> Any:
+) -> None:
+    """
+    Process the run with the input data if possible.
+
+    Return raw data based on a run and input data.
+
+    Parameters
+    ----------
+    process : Callable[[AbstractRun, Any], None]
+        The process function.
+    run : AbstractRun
+        The run.
+    inputs : Dict[str, Any]
+        The inputs as a dictionary.
+
+    Raises
+    ------
+    Exception
+        If the process function fails.
+    """
     try:
         return process(run, inputs)
     except Exception:
@@ -37,7 +71,29 @@ def _process(
 
 class StaticPlugin(Plugin, ABC):
     """
+    Provide a static plugin object.
+
     Calculation with queue. Made for time-consuming tasks.
+    Register and handle callbacks.
+
+    Properties
+    ----------
+    outputs : List[Tuple[str, str, bool]]
+        The plugin specific outputs.
+    inputs : List[Tuple[str, str, bool, Any]]
+        The plugin specific inputs.
+    id : str
+        The plugin id.
+    raw_outputs : dict[str, Any]
+        The raw outputs of a run.
+    logger : Logger
+        The logger for the plugin.
+    name : str
+        The name of the plugin.
+    process : Callable
+        Return raw data based on a run and input data.
+    button_caption : str
+        The caption for the button.
     """
 
     def __init__(self) -> None:
@@ -45,14 +101,16 @@ def __init__(self) -> None:
         self._setup()
 
     def _setup(self) -> None:
+        """Set up the plugin."""
         self._state = PluginState.UNSET  # Set in the main loop to track what's going on right now
-        self._previous_state = None  # Used for updating status
+        self._previous_state: Optional[PluginState] = None  # Used for updating status
         self._refresh_required = True
         self._reset_button = False
         self._blocked = False
 
     @interactive
     def register_callbacks(self) -> None:
+        """Register different callbacks."""
         super().register_callbacks()
         self._callback_inputs_changed()
         self._callback_loop_update_status_label()
@@ -60,6 +118,7 @@ def register_callbacks(self) -> None:
 
     @interactive
     def _callback_inputs_changed(self) -> None:
+        """Handle callback if the inputs changed."""
         from deepcave import app, c, queue, rc, run_handler
 
         # Plugin specific outputs
@@ -77,13 +136,13 @@ def _callback_inputs_changed(self) -> None:
             inputs.append(Input(self.get_internal_input_id(id), attribute))
 
         # Register updates from inputs
-        @app.callback(outputs, inputs)
-        def plugin_process(n_clicks, _, *inputs_list):  # type: ignore
+        @app.callback(outputs, inputs)  # type: ignore
+        def plugin_process(n_clicks: int, _: Any, *inputs_list: str) -> Optional[Any]:
+            """Register updates from inputs."""
             self._blocked = True
-
             # Map the list `inputs_list` to a dict s.t.
             # it's easier to access them.
-            inputs = self._list_to_dict(inputs_list, input=True)
+            inputs = self._list_to_dict(list(inputs_list), input=True)
             inputs_key = self._dict_as_key(inputs, remove_filters=True)
             cleaned_inputs = self._clean_inputs(inputs)
             last_inputs = c.get("last_inputs", self.id)
@@ -98,7 +157,7 @@ def plugin_process(n_clicks, _, *inputs_list):  # type: ignore
             raw_outputs = {}
             raw_outputs_available = True
             for run in runs:
-                raw_outputs[run.id] = rc.get(run, self.id, inputs_key)
+                raw_outputs[run.id] = rc.get(run, self.id, inputs_key)  # same problem
 
                 if raw_outputs[run.id] is None:
                     raw_outputs_available = False
@@ -125,11 +184,11 @@ def plugin_process(n_clicks, _, *inputs_list):  # type: ignore
                 if button_pressed and self._state != PluginState.PROCESSING:
                     self.logger.debug("Button pressed.")
 
-                    # Check if we need to process
+                    # Check if processing is needed
                     for run in runs:
-                        job_id = self._get_job_id(run.id, inputs_key)
+                        job_id = self._get_job_id(run.id, inputs_key)  # same problem
 
-                        # We already got our results or it was already processed
+                        # Results are already achieved or it was already processed
                         if raw_outputs[run.id] is not None or queue.is_processed(job_id):
                             continue
 
@@ -170,6 +229,7 @@ def plugin_process(n_clicks, _, *inputs_list):  # type: ignore
                             run = run_handler.get_run(job_run_id)
 
                             # Save results in cache
+                            # Same optional string problem
                             rc.set(run, job_plugin_id, job_inputs_key, job_run_outputs)
                             self.logger.debug(f"Job {job_id} cached.")
 
@@ -221,8 +281,16 @@ def _callback_loop_trigger_main_loop(self) -> None:
         # Interval should not always run the main callback the whole time
         # Especially not if it's blocked because PreventUpdate
         # prevent output updates from previous callback calls.
-        @app.callback(output, inputs)
-        def plugin_check_blocked(_, data):  # type: ignore
+        @app.callback(output, inputs)  # type: ignore
+        def plugin_check_blocked(_: Any, data: Any) -> Any:
+            """
+            Check if blocked.
+
+            Raises
+            ------
+            PreventUpdate
+                If '_blocked' is True.
+            """
             if self._blocked:
                 raise PreventUpdate
 
@@ -242,15 +310,16 @@ def _callback_loop_update_status_label(self) -> None:
 
         # Update status label
         # Register updates from inputs
-        @app.callback(output, input)
-        def plugin_update_status(_):  # type: ignore
+        @app.callback(output, input)  # type: ignore
+        def plugin_update_status(_: Any) -> Tuple[List[Any], Optional[Any], bool]:
+            """Update the status of the plugin."""
             button_text = [html.Span(self.button_caption)]
 
             if self._state == PluginState.UNSET:
                 # Disable and reset button
                 return button_text, None, True
 
-            # Important so we don't update the button every time (would result in an ugly spinner)
+            # Important so the button is not updated every time (would result in an ugly spinner)
             if self._previous_state == self._state:
                 raise PreventUpdate
 
@@ -261,8 +330,8 @@ def plugin_update_status(_):  # type: ignore
                 self._previous_state == PluginState.PROCESSING
                 and self._state == PluginState.NEEDS_PROCESSING
             ):
-                # However: We have to unset the previous state so if we really change the inputs
-                # the visualizes will be updated.
+                # However: The previous state has to be unset, so if the inputs are really changed
+                # the visualizer will be updated.
                 self._previous_state = PluginState.UNSET
                 raise PreventUpdate
 
@@ -291,10 +360,35 @@ def plugin_update_status(_):  # type: ignore
             return button_text, button, disabled
 
     def _get_job_id(self, run_name: str, inputs_key: str) -> str:
+        """
+        Get the job id.
+
+        Parameters
+        ----------
+        run_name : str
+            The name of the run.
+        inputs_key : str
+            The inputs key.
+
+        Returns
+        -------
+        str
+            The job id.
+        """
         return f"{run_name}-{inputs_key}"
 
     @interactive
     def __call__(self) -> List[Component]:  # type: ignore
+        """
+        Return the components for the plugin.
+
+        Basically, all blocks and elements of the plugin are stacked-up here.
+
+        Returns
+        -------
+        List[Component]
+            Layout as list of components.
+        """
         from deepcave.config import Config
 
         self._setup()
diff --git a/deepcave/plugins/summary/__init__.py b/deepcave/plugins/summary/__init__.py
index e69de29b..2a11ffac 100644
--- a/deepcave/plugins/summary/__init__.py
+++ b/deepcave/plugins/summary/__init__.py
@@ -0,0 +1,13 @@
+#  noqa: D400
+"""
+# summary
+
+## Modules
+----------
+configurations
+    This module provides utilities to visualize the characteristics of a configuration within a run.
+footprint
+    This module provides utilities to visualize a configuration footprint.
+overview
+    This module provides utilities for visualizing an overview of the selected run.
+"""
diff --git a/deepcave/plugins/summary/configurations.py b/deepcave/plugins/summary/configurations.py
index bc8e9389..aba943ef 100644
--- a/deepcave/plugins/summary/configurations.py
+++ b/deepcave/plugins/summary/configurations.py
@@ -1,3 +1,17 @@
+#  noqa: D400
+"""
+# Configurations
+
+This module provides utilities to visualize the characteristics of a configuration within a run.
+
+The module provides a corresponding dynamic plugin.
+
+## Classes
+    - Configurations: Visualize the characteristics of a configuration.
+"""
+
+from typing import Any, Callable, Dict, List, Union
+
 from collections import defaultdict
 
 import dash_bootstrap_components as dbc
@@ -21,6 +35,8 @@
 
 
 class Configurations(DynamicPlugin):
+    """Visualize the characteristics of a configuration."""
+
     id = "configurations"
     name = "Configurations"
     icon = "fas fa-sliders-h"
@@ -31,14 +47,14 @@ class Configurations(DynamicPlugin):
     @staticmethod
     def get_link(run: AbstractRun, config_id: int) -> str:
         """
-        Creates a link to a specific configuration.
+        Create a link to a specific configuration.
 
         Parameters
         ----------
         run : AbstractRun
-            Selected run.
+            The selected run.
         config_id : int
-            Configuration, which should be visited.
+            Configuration which should be visited.
 
         Returns
         -------
@@ -56,7 +72,21 @@ def get_link(run: AbstractRun, config_id: int) -> str:
         return create_url(url, inputs)
 
     @staticmethod
-    def get_input_layout(register):
+    def get_input_layout(register: Callable) -> List[html.Div]:
+        """
+        Get the layout for the input block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[html.Div]
+            The layouts for the input block.
+        """
         return [
             html.Div(
                 [
@@ -66,12 +96,49 @@ def get_input_layout(register):
             ),
         ]
 
-    def load_inputs(self):
+    def load_inputs(self) -> Dict[str, Any]:
+        """
+        Load the content for the defined inputs in 'get_input_layout' and 'get_filter_layout'.
+
+        This method is necessary to pre-load contents for the inputs.
+        So, if the plugin is called for the first time or there are no results in the cache,
+        the plugin gets its content from this method.
+
+        Returns
+        -------
+        Dict[str, Any]
+            The content to be filled
+        """
         return {
             "config_id": {"min": 0, "max": 0, "marks": get_slider_marks(), "value": 0},
         }
 
-    def load_dependency_inputs(self, run, previous_inputs, inputs):
+    def load_dependency_inputs(  # type: ignore
+        self, run, previous_inputs: Dict[str, Any], inputs: Dict[str, Any]
+    ) -> Dict[str, Any]:
+        """
+        Work like 'load_inputs' but called after inputs have changed.
+
+        Note
+        ----
+        Only the changes have to be returned.
+        The returned dictionary will be merged with the inputs.
+
+        Parameters
+        ----------
+        run :
+            The selected run.
+        previous_inputs :
+            Previous content of the inputs.
+            Not used in this specific function
+        inputs : Dict[str, Any]
+            The current content of the inputs.
+
+        Returns
+        -------
+        Dict[str, Any]
+            A dictionary with the changes.
+        """
         # Get selected values
         config_id_value = inputs["config_id"]["value"]
         configs = run.get_configs()
@@ -88,7 +155,32 @@ def load_dependency_inputs(self, run, previous_inputs, inputs):
         }
 
     @staticmethod
-    def process(run, inputs):
+    def process(run, inputs) -> Dict[str, Any]:  # type: ignore
+        """
+        Return raw data based on a run and input data.
+
+        Warning
+        -------
+        The returned data must be JSON serializable.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run :
+            The selected run.
+        inputs :
+            The input data.
+
+        Returns
+        -------
+        Dict[str, Any]
+            A serialized dictionary.
+        """
         selected_config_id = int(inputs["config_id"])
         origin = run.get_origin(selected_config_id)
         objectives = run.get_objectives()
@@ -107,8 +199,8 @@ def process(run, inputs):
                 str(original_run.path) + f" (Configuration ID: {original_config_id})"
             ]
 
-        performances = {}
-        performances_table_data = {"Budget": []}
+        performances: Dict[str, Dict[Union[int, float], float]] = {}
+        performances_table_data: Dict[str, List[Any]] = {"Budget": []}
         for objective_id, objective in enumerate(objectives):
             if objective.name not in performances:
                 performances[objective.name] = {}
@@ -135,20 +227,20 @@ def process(run, inputs):
 
         # Let's start with the configspace
         X = []
-        cs_table_data = {"Hyperparameter": [], "Value": []}
-        for config_id, config in run.get_configs().items():
-            x = run.encode_config(config)
+        cs_table_data: Dict[str, List[Any]] = {"Hyperparameter": [], "Value": []}
+        for config_id, configuration in run.get_configs().items():
+            x = run.encode_config(configuration)
 
             highlight = 0
             if config_id == selected_config_id:
                 highlight = 1
 
-                for k, v in config.items():
+                for k, v in configuration.items():
                     # Add accurate data for our table here
                     cs_table_data["Hyperparameter"] += [k]
                     cs_table_data["Value"] += [v]
 
-            # We simply add highlight as a new column
+            # Highlight is simply added as a new column
             x += [highlight]
 
             # And add it to the lists
@@ -168,7 +260,23 @@ def process(run, inputs):
         }
 
     @staticmethod
-    def get_output_layout(register):
+    def get_output_layout(
+        register: Callable,
+    ) -> List[Any]:
+        """
+        Get the layout for the output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method used to register outputs.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            The layouts for the outputs.
+        """
         return [
             html.Div(id=register("overview_table", "children"), className="mb-3"),
             html.Hr(),
@@ -214,7 +322,24 @@ def get_output_layout(register):
         ]
 
     @staticmethod
-    def _get_objective_figure(_, outputs, run):
+    def _get_objective_figure(
+        _: Any, outputs: Dict[str, Dict[str, Dict[Any, Any]]], run: AbstractRun
+    ) -> go.Figure:
+        """
+        Get the figure for the visualization of the objectives.
+
+        Parameters
+        ----------
+        outputs : Dict[str, Dict[str, Dict[Any, Any]]]
+            Raw outputs from the run.
+        run : AbstractRun
+            The selected run.
+
+        Returns
+        -------
+        go.Figure
+            The figure of the objectives.
+        """
         objective_data = []
         for i, (metric, values) in enumerate(outputs["performances"].items()):
             trace_kwargs = {
@@ -230,12 +355,12 @@ def _get_objective_figure(_, outputs, run):
             trace = go.Scatter(**trace_kwargs)
             objective_data.append(trace)
 
-        layout_kwargs = {
+        layout_kwargs: Dict[str, Any] = {
             "margin": Config.FIGURE_MARGIN,
             "xaxis": {"title": "Budget", "domain": [0.05 * len(run.get_objectives()), 1]},
         }
 
-        # We create an axis for each objective now
+        # An axis is created for each objective now
         for id, objective in enumerate(run.get_objectives()):
             yaxis = "yaxis"
             if id > 0:
@@ -266,9 +391,25 @@ def _get_objective_figure(_, outputs, run):
         return objective_figure
 
     @staticmethod
-    def _get_configspace_figure(inputs, outputs, run):
-        df = outputs["cs_df"]
-        df = deserialize(df, dtype=pd.DataFrame)
+    def _get_configspace_figure(
+        inputs: Any, outputs: Dict[str, str], run: AbstractRun
+    ) -> go.Figure:
+        """
+        Get the figure for the visualization of the configuration space.
+
+        Parameters
+        ----------
+        outputs : Dict[str, str]
+            Raw outputs from the run.
+        run : AbstractRun
+            The selected run.
+
+        Returns
+        -------
+        go.Figure
+            The figure of the configuration space.
+        """
+        df = deserialize(outputs["cs_df"], dtype=pd.DataFrame)
 
         highlighted = df["highlighted"].values
         hp_names = run.configspace.get_hyperparameter_names()
@@ -276,7 +417,7 @@ def _get_configspace_figure(inputs, outputs, run):
         # Get highlighted column
         highlighted_df = df[df["highlighted"] == 1]
 
-        data = defaultdict(dict)
+        data: defaultdict = defaultdict(dict)
         for hp_name in hp_names:
             data[hp_name]["values"] = df[hp_name].values
             data[hp_name]["label"] = hp_name
@@ -312,7 +453,30 @@ def _get_configspace_figure(inputs, outputs, run):
         return fig
 
     @staticmethod
-    def load_outputs(run, inputs, outputs):
+    def load_outputs(run, inputs, outputs) -> List[Any]:  # type: ignore
+        """
+        Read in the raw data and prepare them for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            Input and filter values from the user.
+        outputs
+            Raw output from the run.
+
+        Returns
+        -------
+        List[Any]
+            A list of the created tables containing output information.
+        """
         config_id = inputs["config_id"]
         config = run.get_config(config_id)
 
diff --git a/deepcave/plugins/summary/footprint.py b/deepcave/plugins/summary/footprint.py
index abcb8bfb..5a5299b5 100644
--- a/deepcave/plugins/summary/footprint.py
+++ b/deepcave/plugins/summary/footprint.py
@@ -1,3 +1,17 @@
+# noqa: D400
+"""
+# FootPrint
+
+This module provides utilities to visualize a configuration footprint.
+
+The module contains a static plugin class for defining the footprint.
+
+## Classes
+    - FootPrint: A static plugin for the footprint of a configuration.
+"""
+
+from typing import Any, Callable, Dict, List
+
 import dash_bootstrap_components as dbc
 import plotly.graph_objs as go
 from dash import dcc, html
@@ -15,6 +29,12 @@
 
 
 class FootPrint(StaticPlugin):
+    """
+    Visualize the footprint of a configuration.
+
+    A static plugin for the footprint.
+    """
+
     id = "footprint"
     name = "Configuration Footprint"
     icon = "fas fa-shoe-prints"
@@ -22,7 +42,21 @@ class FootPrint(StaticPlugin):
     activate_run_selection = True
 
     @staticmethod
-    def get_input_layout(register):
+    def get_input_layout(register: Callable) -> List[Any]:
+        """
+        Get the layout for the input block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            The layouts for the input block.
+        """
         return [
             dbc.Row(
                 [
@@ -75,8 +109,22 @@ def get_input_layout(register):
         ]
 
     @staticmethod
-    def get_filter_layout(register):
-        return (
+    def get_filter_layout(register: Callable) -> List[dbc.Row]:
+        """
+        Get layout for the filter block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register (user) variables.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[dbc.Row]
+            The layouts for the filter block.
+        """
+        return [
             dbc.Row(
                 [
                     dbc.Col(
@@ -99,19 +147,54 @@ def get_filter_layout(register):
                         ],
                         md=6,
                     ),
-                ],
-            ),
-        )
+                ]
+            )
+        ]
+
+    def load_inputs(self) -> Dict[str, Dict[str, Any]]:
+        """
+        Load the content for the defined inputs in 'get_input_layout' and 'get_filter_layout'.
+
+        This method is necessary to pre-load contents for the inputs.
+        So, if the plugin is called for the first time or there are no results in the cache,
+        the plugin gets its content from this method.
 
-    def load_inputs(self):
+        Returns
+        -------
+        Dict[str, Dict[str, Any]]
+            The content to be filled.
+        """
         return {
             "details": {"value": 0.5},
             "show_borders": {"options": get_select_options(binary=True), "value": "true"},
             "show_supports": {"options": get_select_options(binary=True), "value": "true"},
         }
 
-    def load_dependency_inputs(self, run, previous_inputs, inputs):
-        # Prepare objetives
+    def load_dependency_inputs(self, run, previous_inputs, inputs) -> Dict[str, Any]:  # type: ignore # noqa: E501
+        """
+        Work like 'load_inputs' but called after inputs have changed.
+
+        Note
+        ----
+        Only the changes have to be returned.
+        The returned dictionary will be merged with the inputs.
+
+        Parameters
+        ----------
+        run :
+            The selected run.
+        previous_inputs :
+            Previous content of the inputs.
+            Not used in this specific function.
+        inputs :
+            Current content of the inputs.
+
+        Returns
+        -------
+        Dict[str, Any]
+            The dictionary with the changes.
+        """
+        # Prepare objectives
         objective_names = run.get_objective_names()
         objective_ids = run.get_objective_ids()
         objective_options = get_select_options(objective_names, objective_ids)
@@ -142,7 +225,32 @@ def load_dependency_inputs(self, run, previous_inputs, inputs):
         }
 
     @staticmethod
-    def process(run, inputs):
+    def process(run, inputs) -> Dict[str, Any]:  # type: ignore
+        """
+        Return raw data based on a run and input data.
+
+        Warning
+        -------
+        The returned data must be JSON serializable.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            The input data.
+
+        Returns
+        -------
+        Dict[str, Any]
+            A serialized dictionary.
+        """
         budget = run.get_budget(inputs["budget_id"])
         objective = run.get_objective(inputs["objective_id"])
         details = inputs["details"]
@@ -168,7 +276,21 @@ def process(run, inputs):
         }
 
     @staticmethod
-    def get_output_layout(register):
+    def get_output_layout(register: Callable) -> dbc.Tabs:
+        """
+        Get the layout for the output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method for registering outputs.
+            The register_output function is located in the Plugin superclass.
+
+        Returns
+        -------
+        dbc.Tabs
+            The layout for the output block.
+        """
         return dbc.Tabs(
             [
                 dbc.Tab(
@@ -187,7 +309,30 @@ def get_output_layout(register):
         )
 
     @staticmethod
-    def load_outputs(run, inputs, outputs):
+    def load_outputs(run, inputs, outputs) -> List[Any]:  # type: ignore
+        """
+        Read in the raw data and prepare them for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            Input and filter values from the user.
+        outputs
+            Raw output from the run.
+
+        Returns
+        -------
+        List[Any]
+            The plotly figure of the footprint performance and area.
+        """
         objective = run.get_objective(inputs["objective_id"])
         show_borders = inputs["show_borders"]
         show_supports = inputs["show_supports"]
@@ -276,7 +421,21 @@ def load_outputs(run, inputs, outputs):
         return [performance, area]
 
     @staticmethod
-    def get_mpl_output_layout(register):
+    def get_mpl_output_layout(register: Callable) -> List[dbc.Tabs]:
+        """
+        Get the layout for the matplotlib output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register the outputs.
+            The register_output function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[dbc.Tabs]
+            The layout for the output block.
+        """
         return [
             dbc.Tabs(
                 [
@@ -293,7 +452,29 @@ def get_mpl_output_layout(register):
         ]
 
     @staticmethod
-    def load_mpl_outputs(run, inputs, outputs):
+    def load_mpl_outputs(run, inputs, outputs):  # type: ignore
+        """
+        Read in the raw data and prepare them for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+        inputs
+            Input and filter values from the user.
+        outputs
+            Raw outputs from the run.
+
+        Returns
+        -------
+        The rendered matplotlib figure of the footprint
+        """
         objective = run.get_objective(inputs["objective_id"])
         show_borders = inputs["show_borders"]
         show_supports = inputs["show_supports"]
@@ -338,12 +519,12 @@ def load_mpl_outputs(run, inputs, outputs):
                     size = 10
                     marker_symbol = "^"
 
-                color = plt.get_color(color_id)
+                color = plt.get_color(color_id)  # type: ignore
                 plt.scatter(x, y, marker=marker_symbol, s=size, label=name, c=color)
 
             plt.axis("off")
             plt.legend(loc="lower right")
 
-            images += [plt.render()]
+            images += [plt.render()]  # type: ignore
 
         return images
diff --git a/deepcave/plugins/summary/overview.py b/deepcave/plugins/summary/overview.py
index 61ee452b..c9a49c37 100644
--- a/deepcave/plugins/summary/overview.py
+++ b/deepcave/plugins/summary/overview.py
@@ -1,3 +1,19 @@
+# noqa: D400
+"""
+# Overview
+
+This module provides utilities for visualizing an overview of the selected runs.
+
+It holds the most important information, e.g. meta data, objectives and statistics.
+
+The module includes a dynamic plugin for the overview.
+
+## Classes
+    - Overview: Visualize an overall overview of the selected run.
+"""
+
+from typing import Any, Callable, Dict, List
+
 import dash_bootstrap_components as dbc
 import numpy as np
 import plotly.graph_objs as go
@@ -23,6 +39,8 @@
 
 
 class Overview(DynamicPlugin):
+    """Visualize an overall overview of the selected run."""
+
     id = "overview"
     name = "Overview"
     icon = "fas fa-search"
@@ -31,7 +49,21 @@ class Overview(DynamicPlugin):
     activate_run_selection = True
 
     @staticmethod
-    def get_output_layout(register):
+    def get_output_layout(register: Callable) -> List[Any]:
+        """
+        Get the layout for the output block.
+
+        Parameters
+        ----------
+        register : Callable
+            Method to register the outputs.
+            The register_input function is located in the Plugin superclass.
+
+        Returns
+        -------
+        List[Any]
+            The layouts for the output block.
+        """
         return [
             html.Div(
                 id=register("card", "children"),
@@ -71,7 +103,26 @@ def get_output_layout(register):
         ]
 
     @staticmethod
-    def load_outputs(run, *_):
+    def load_outputs(run, *_: Any) -> List[Any]:  # type: ignore
+        """
+        Read in the raw data and prepare them for the layout.
+
+        Note
+        ----
+        The passed inputs are cleaned and therefore differs compared to 'load_inputs'
+        or 'load_dependency_inputs'.
+        Please see '_clean_inputs' for more information.
+
+        Parameters
+        ----------
+        run
+            The selected run.
+
+        Returns
+        -------
+        List[Any]
+            A list of the created tables of the overview.
+        """
         # Get best cost across all objectives, highest budget
         incumbent, _ = run.get_incumbent()
         config_id = run.get_config_id(incumbent)
@@ -142,7 +193,7 @@ def load_outputs(run, *_):
         )
 
         # Meta
-        meta = {"Attribute": [], "Value": []}
+        meta: Dict[str, List[str]] = {"Attribute": [], "Value": []}
         for k, v in run.get_meta().items():
             if k == "objectives":
                 continue
@@ -154,7 +205,7 @@ def load_outputs(run, *_):
             meta["Value"].append(str(v))
 
         # Objectives
-        objectives = {"Name": [], "Bounds": []}
+        objectives: Dict[str, List[str]] = {"Name": [], "Bounds": []}
         for objective in run.get_objectives():
             objectives["Name"].append(objective.name)
             objectives["Bounds"].append(f"[{objective.lower}, {objective.upper}]")
@@ -163,8 +214,13 @@ def load_outputs(run, *_):
         budgets = run.get_budgets(include_combined=False)
 
         # Statistics
-        status_statistics = {}
-        status_details = {"Configuration ID": [], "Budget": [], "Status": [], "Error": []}
+        status_statistics: Dict[float, Dict[Status, int]] = {}
+        status_details: Dict[str, List[Any]] = {
+            "Configuration ID": [],
+            "Budget": [],
+            "Status": [],
+            "Error": [],
+        }
         for budget in budgets:
             budget = round(budget, 2)
             if budget not in status_statistics:
@@ -241,8 +297,8 @@ def load_outputs(run, *_):
             str(round(count / len_trials * 100, 2)) + "%" for count in status_budget.values()
         ]
         status_budget_values_text = "/".join(status_budget_values)
-        status_budget_keys_text = [str(key) for key in status_budget.keys()]
-        status_budget_keys_text = "/".join(status_budget_keys_text)
+        status_budget_keys_text_list = [str(key) for key in status_budget.keys()]
+        status_budget_keys_text = "/".join(status_budget_keys_text_list)
 
         status_text = f"""
         Taking all evaluated trials into account, {successful_trials_rate}% have been successful.
@@ -283,7 +339,7 @@ def load_outputs(run, *_):
         config_statistics["Z_labels"] = z_labels
 
         # Prepare configspace table
-        configspace = {
+        configspace: Dict[str, List] = {
             "Hyperparameter": [],
             "Possible Values": [],
             "Default": [],
@@ -291,7 +347,6 @@ def load_outputs(run, *_):
         }
 
         for hp_name, hp in run.configspace.get_hyperparameters_dict().items():
-
             log = False
             value = None
             if (
@@ -310,12 +365,12 @@ def load_outputs(run, *_):
                 value = str(hp.value)
 
             default = str(hp.default_value)
-            log = str(log)
+            log_str = str(log)
 
             configspace["Hyperparameter"].append(hp_name)
             configspace["Possible Values"].append(value)
             configspace["Default"].append(default)
-            configspace["Log"].append(log)
+            configspace["Log"].append(log_str)
 
         stats_data = []
         for budget, stats in status_statistics.items():
diff --git a/deepcave/runs/__init__.py b/deepcave/runs/__init__.py
index 7443f84f..0ee084e9 100644
--- a/deepcave/runs/__init__.py
+++ b/deepcave/runs/__init__.py
@@ -1,3 +1,15 @@
+#  noqa: D400
+"""
+# AbstractRun
+
+This module provides utilities to create and handle an abstract run.
+
+It provides functions to get information of the run, as well as the used objectives.
+
+## Classes
+    - AbstractRun: Create a new run.
+"""
+
 from abc import ABC, abstractmethod
 from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
 
@@ -28,6 +40,37 @@
 
 
 class AbstractRun(ABC):
+    """
+    Create a new run.
+
+    Provide functions to get information of the run, as well as the used objectives.
+
+    Properties
+    ----------
+    name : str
+        The name of the run.
+    path : Optional[Path]
+        The path to the run.
+    logger : Logger
+        The logger for the run.
+    meta: Dict[str, Any]
+        Contains the run's meta information.
+    configspace: ConfigSpace.ConfigurationSpace
+        The configuration space of the run.
+    configs: Dict[int, Configuration]
+        Contains the configurations.
+    origins: Dict[int, str]
+        The origin of the configuration.
+    models: Dict[int, Optional[Union[str, "torch.nn.Module"]]]
+        Contains the modules.
+    history: List[Trial]
+        The history of Trials.
+    trial_keys: Dict[Tuple[str, int], int]
+        Contains config_id, budget and the corresponding trial_id.
+    models_dir : Path
+        The directory of the model.
+    """
+
     prefix: str
 
     def __init__(self, name: str) -> None:
@@ -39,14 +82,23 @@ def __init__(self, name: str) -> None:
         self.reset()
 
     def reset(self) -> None:
+        """
+        Reset the run to default values / empties.
+
+        Clear the initial data and configurations of the object.
+        """
         self.meta: Dict[str, Any] = {}
         self.configspace: ConfigSpace.ConfigurationSpace
-        self.configs: Dict[int, Configuration] = {}
-        self.origins: Dict[int, str] = {}
-        self.models: Dict[int, Optional[Union[str, "torch.nn.Module"]]] = {}
+        self.configs: Dict[int, Union[Configuration, Dict[Any, Any]]] = {}
+        self.origins: Dict[int, Optional[str]] = {}
+        self.models: Dict[  # type: ignore
+            int, Optional[Union[str, "torch.nn.Module"]]  # noqa: F821
+        ] = {}
 
         self.history: List[Trial] = []
-        self.trial_keys: Dict[Tuple[str, int], int] = {}  # (config_id, budget) -> trial_id
+        self.trial_keys: Dict[
+            Tuple[int, Union[int, float, None]], int
+        ] = {}  # (config_id, budget) -> trial_id
 
         # Cached data
         self._highest_budget: Dict[int, Union[int, float]] = {}  # config_id -> budget
@@ -54,6 +106,18 @@ def reset(self) -> None:
     def _update_highest_budget(
         self, config_id: int, budget: Union[int, float], status: Status
     ) -> None:
+        """
+        Update the highest budget.
+
+        Parameters
+        ----------
+        config_id : int
+            The identificator of the configuration.
+        budget : Union[int, float]
+            The new highest budget.
+        status : Status
+            The status of the run.
+        """
         if status == Status.SUCCESS:
             # Update highest budget
             if config_id not in self._highest_budget:
@@ -66,7 +130,9 @@ def _update_highest_budget(
     @abstractmethod
     def hash(self) -> str:
         """
-        Hash of the current run. If hash changes, cache has to be cleared. This ensures that
+        Hash of the current run.
+
+        If hash changes, cache has to be cleared. This ensures that
         the cache always holds the latest results of the run.
 
         Returns
@@ -80,7 +146,9 @@ def hash(self) -> str:
     @abstractmethod
     def id(self) -> str:
         """
-        Hash of the file. This is used to identify the file.
+        Hash of the file.
+
+        This is used to identify the file.
         In contrast to `hash`, this hash should not be changed throughout the run.
 
         Returns
@@ -92,31 +160,116 @@ def id(self) -> str:
 
     @property
     def latest_change(self) -> float:
+        """
+        Get the latest change.
+
+        Returns
+        -------
+        float
+            The latest change.
+        """
         return 0
 
     @staticmethod
-    def get_trial_key(config_id: int, budget: Union[int, float]):
+    def get_trial_key(
+        config_id: int, budget: Union[int, float, None]
+    ) -> Tuple[int, Union[int, float, None]]:
+        """
+        Get the trial key.
+
+        It is obtained through the assembly of configuration and budget.
+
+        Parameters
+        ----------
+        config_id : int
+            The identificator of the configuration.
+        budget : Union[int, float, None]
+            The budget of the Trial.
+
+        Returns
+        -------
+        Tuple[int, Union[int, float, None]]
+            Tuple representing the trial key, consisting of configuration id and the budget.
+        """
         return (config_id, budget)
 
-    def get_trial(self, trial_key) -> Optional[Trial]:
+    def get_trial(self, trial_key: Tuple[int, int]) -> Optional[Trial]:
+        """
+        Get the trial with the responding key if existing.
+
+        Parameters
+        ----------
+        trial_key : Tuple[int, int]
+            The key for the desired trial.
+
+        Returns
+        -------
+        Optional[Trial]
+            The trial object.
+        """
         if trial_key not in self.trial_keys:
             return None
 
         return self.history[self.trial_keys[trial_key]]
 
     def get_trials(self) -> Iterator[Trial]:
+        """
+        Get an iterator of all stored trials.
+
+        Returns
+        -------
+        Iterator[Trial]
+            An iterator over all stored trials.
+        """
         yield from self.history
 
     def get_meta(self) -> Dict[str, Any]:
+        """
+        Get a shallow copy of the meta information.
+
+        Returns
+        -------
+        Dict[str, Any]
+            A shallow copy of the meta information dictionary.
+        """
         return self.meta.copy()
 
     def empty(self) -> bool:
+        """
+        Check if the run's history is empty.
+
+        Returns
+        -------
+        bool
+            True if run history is empty, False otherwise.
+        """
         return len(self.history) == 0
 
-    def get_origin(self, config_id: int) -> str:
+    def get_origin(self, config_id: int) -> Optional[str]:
+        """
+        Get the origin, given a config id.
+
+        Parameters
+        ----------
+        config_id : int
+            The identificator of the configuration.
+
+        Returns
+        -------
+        Optional[str]
+            An origin string corresponding to the given configuration id.
+        """
         return self.origins[config_id]
 
     def get_objectives(self) -> List[Objective]:
+        """
+        Get a list of all objectives corresponding to the run.
+
+        Returns
+        -------
+        List[Objective]
+            A list containing all objectives associated with the run.
+        """
         objectives = []
         for d in self.meta["objectives"].copy():
             objective = Objective.from_json(d)
@@ -125,7 +278,8 @@ def get_objectives(self) -> List[Objective]:
         return objectives
 
     def get_objective(self, id: Union[str, int]) -> Optional[Objective]:
-        """Returns the objective based on the id or the name.
+        """
+        Return the objective based on the id or the name.
 
         Parameters
         ----------
@@ -134,10 +288,9 @@ def get_objective(self, id: Union[str, int]) -> Optional[Objective]:
 
         Returns
         -------
-        Objective
-            The objective object.
+        Optional[Objective]
+            The objective.
         """
-
         objectives = self.get_objectives()
         if type(id) == int:
             return objectives[id]
@@ -151,7 +304,7 @@ def get_objective(self, id: Union[str, int]) -> Optional[Objective]:
 
     def get_objective_id(self, objective: Union[Objective, str]) -> int:
         """
-        Returns the id of the objective if it is found.
+        Return the id of the objective if it is found.
 
         Parameters
         ----------
@@ -180,14 +333,34 @@ def get_objective_id(self, objective: Union[Objective, str]) -> int:
         raise RuntimeError("Objective was not found.")
 
     def get_objective_ids(self) -> List[int]:
+        """
+        Get the ids of the objectives.
+
+        Returns
+        -------
+        List[int]
+            A list of the ids of the objectives.
+        """
         return list(range(len(self.get_objectives())))
 
     def get_objective_name(self, objectives: Optional[List[Objective]] = None) -> str:
         """
         Get the cost name of given objective names.
+
         Returns "Combined Cost" if multiple objective names were involved.
-        """
 
+        Parameters
+        ----------
+        objectives : Optional[List[Objective]]
+            A list of the objectives.
+            By default, None.
+
+        Returns
+        -------
+        str
+            The name of the objective.
+            Returns "Combined Cost" if multiple objective names were involved.
+        """
         available_objective_names = self.get_objective_names()
 
         if objectives is None:
@@ -200,24 +373,35 @@ def get_objective_name(self, objectives: Optional[List[Objective]] = None) -> st
         return COMBINED_COST_NAME
 
     def get_objective_names(self) -> List[str]:
+        """
+        Get the names of the objectives.
+
+        Returns
+        -------
+        List[str]
+            A list containing the names of the objectives.
+        """
         return [obj.name for obj in self.get_objectives()]
 
-    def get_configs(self, budget: Union[int, float] = None) -> Dict[int, Configuration]:
+    def get_configs(self, budget: Optional[Union[int, float]] = None) -> Dict[int, Configuration]:
         """
-        Get configurations of the run. Optionally, only configurations which were evaluated
+        Get configurations of the run.
+
+        Optionally, only configurations which were evaluated
         on the passed budget are considered.
 
         Parameters
         ----------
-        budget : Union[int, float], optional
-            Considered budget. By default None (all configurations are included).
+        budget : Optiona[Union[int, float]]
+            Considered budget.
+            By default, None (all configurations are included).
 
         Returns
         -------
         Dict[int, Configuration]
             Configuration id and the configuration.
         """
-        # Include all configs if we have combined budget
+        # Include all configs if budget is a combined budget
         if budget == COMBINED_BUDGET:
             budget = None
 
@@ -237,10 +421,36 @@ def get_configs(self, budget: Union[int, float] = None) -> Dict[int, Configurati
         return configs
 
     def get_config(self, id: int) -> Configuration:
+        """
+        Retrieve the configuration with the corresponding id.
+
+        Parameters
+        ----------
+        id : int
+            The id of the configuration.
+
+        Returns
+        -------
+        Configuration
+            The corresponding Configuration.
+        """
         config = Configuration(self.configspace, self.configs[id])
         return config
 
     def get_config_id(self, config: Union[Configuration, Dict]) -> Optional[int]:
+        """
+        Get the id of the configuration.
+
+        Parameters
+        ----------
+        config : Union[Configuration, Dict]
+            The configuration for which to find the id.
+
+        Returns
+        -------
+        Optional[int]
+            The configuration id.
+        """
         if isinstance(config, Configuration):
             config = config.get_dictionary()
 
@@ -251,27 +461,66 @@ def get_config_id(self, config: Union[Configuration, Dict]) -> Optional[int]:
 
         return None
 
-    def get_num_configs(self, budget: Union[int, float] = None) -> int:
+    def get_num_configs(self, budget: Optional[Union[int, float]] = None) -> int:
+        """
+        Count the number of configurations stored in this run with a specific budget.
+
+        Parameters
+        ----------
+        budget : Optional[Union[int, float]]
+            The budget for which to count the configurations.
+            If not provided, counts all configurations.
+            Default is None.
+
+        Returns
+        -------
+        int
+            The number of all configurations with a given budget.
+            If budget is None, counts all configurations.
+        """
         return len(self.get_configs(budget=budget))
 
-    def get_budget(self, id: Union[int, str], human=False) -> float:
+    def get_budget(self, id: Union[int, str], human: bool = False) -> Union[int, float]:
         """
-        Gets the budget given an id.
+        Get the budget given an id.
 
         Parameters
         ----------
         id : Union[int, str]
-            Id of the wanted budget. If id is a string, it is converted to an integer.
+            The id of the wanted budget.
+            If id is a string, it is converted to an integer.
+        human : bool, optional
+            Make the output more readable.
+            By default False.
 
         Returns
         -------
-        float
-            Budget.
+        float, int
+            The budget.
+
+        Raises
+        ------
+        TypeError
+            If the budget with this id is invalid.
         """
         budgets = self.get_budgets(human=human)
-        return budgets[int(id)]
+        return budgets[int(id)]  # type: ignore
 
     def get_budget_ids(self, include_combined: bool = True) -> List[int]:
+        """
+        Get the corresponding ids for the budgets.
+
+        Parameters
+        ----------
+        include_combined : bool, optional
+            If False, cut last id of budget ids.
+            By default True.
+
+        Returns
+        -------
+        List[int]
+            A list of the budget ids.
+        """
         budget_ids = list(range(len(self.get_budgets())))
         if not include_combined:
             budget_ids = budget_ids[:-1]
@@ -280,26 +529,30 @@ def get_budget_ids(self, include_combined: bool = True) -> List[int]:
 
     def get_budgets(
         self, human: bool = False, include_combined: bool = True
-    ) -> List[Union[int, float]]:
+    ) -> List[Union[int, float, str]]:
         """
-        Returns the budgets from the meta data.
+        Return the budgets from the meta data.
 
         Parameters
         ----------
         human : bool, optional
-            Make the output better readable. By default False.
+            Make the output more readable.
+            By default False.
+        include_combined : bool, optional
+            If True, include the combined budget.
+            By default True.
 
         Returns
         -------
-        List[Union[int, float]]
-            List of budgets.
+        List[Union[int, float, str]]
+            List of budgets. In a readable form, if human is True.
         """
         budgets = self.meta["budgets"].copy()
         if include_combined and len(budgets) > 1 and COMBINED_BUDGET not in budgets:
             budgets += [COMBINED_BUDGET]
 
         if human:
-            readable_budgets = []
+            readable_budgets: List[Union[str, float]] = []
             for b in budgets:
                 if b == COMBINED_BUDGET:
                     readable_budgets += ["Combined"]
@@ -312,10 +565,17 @@ def get_budgets(
 
     def get_highest_budget(self, config_id: Optional[int] = None) -> Optional[Union[int, float]]:
         """
-        Returns the highest found budget for a config id. If no config id is specified then
+        Return the highest found budget for a config id.
+
+        If no config id is specified then
         the highest available budget is returned.
         Moreover, if no budget is available None is returned.
 
+        Parameters
+        ----------
+        config_id : Optional[int]
+            The config id for which the highest budget is returned.
+
         Returns
         -------
         Optional[Union[int, float]]
@@ -332,13 +592,16 @@ def get_highest_budget(self, config_id: Optional[int] = None) -> Optional[Union[
 
     def _process_costs(self, costs: List[float]) -> List[float]:
         """
-        Processes the costs to get rid of NaNs. NaNs are replaced by the worst value of the
+        Process the costs to get rid of NaNs.
+
+        NaNs are replaced by the worst value of the
         objective.
 
         Parameters
         ----------
         costs : List[float]
-            Costs, which should be processed. Must be the same length as the number of objectives.
+            Costs, which should be processed.
+            Must be the same length as the number of objectives.
 
         Returns
         -------
@@ -356,28 +619,30 @@ def _process_costs(self, costs: List[float]) -> List[float]:
 
     def get_costs(self, config_id: int, budget: Optional[Union[int, float]] = None) -> List[float]:
         """
-        Returns the costs of a configuration. In case of multi-objective, multiple costs are
+        Return the costs of a configuration.
+
+        In case of multi-objective, multiple costs are
         returned.
 
         Parameters
         ----------
         config_id : int
             Configuration id to get the costs for.
-        budget : Optional[Union[int, float]], optional
-            Budget to get the costs from the configuration id for. By default None. If budget is
+        budget : Optional[Union[int, float]]
+            Budget to get the costs from the configuration id for. By default, None. If budget is
             None, the highest budget is chosen.
 
+        Returns
+        -------
+        List[float]
+            List of costs from the associated configuration.
+
         Raises
         ------
         ValueError
             If the configuration id is not found.
         RuntimeError
             If the budget was not evaluated for the passed config id.
-
-        Returns
-        -------
-        List[float]
-            List of costs from the associated configuration.
         """
         if budget is None:
             budget = self.get_highest_budget()
@@ -397,15 +662,17 @@ def get_all_costs(
         statuses: Optional[Union[Status, List[Status]]] = None,
     ) -> Dict[int, List[float]]:
         """
-        Get all costs in the history with their config ids. Only configs from the given budget
+        Get all costs in the history with their config ids.
+
+        Only configs from the given budget
         and statuses are returned.
 
         Parameters
         ----------
-        budget : Optional[Union[int, float]], optional
+        budget : Optional[Union[int, float]]
             Budget to select the costs. If no budget is given, the highest budget is chosen.
-            By default None.
-        statuses : Optional[Union[Status, List[Status]]], optional
+            By default, None.
+        statuses : Optional[Union[Status, List[Status]]]
             Only selected stati are considered. If no status is given, all stati are considered.
             By default None.
 
@@ -417,7 +684,7 @@ def get_all_costs(
         if budget is None:
             budget = self.get_highest_budget()
 
-        # In case of COMBINED_BUDGET, we only keep the costs of the highest found budget
+        # In case of COMBINED_BUDGET, only the costs of the highest found budget are kept
         highest_evaluated_budget = {}
 
         results = {}
@@ -434,7 +701,7 @@ def get_all_costs(
                     highest_evaluated_budget[trial.config_id] = trial.budget
 
                 latest_budget = highest_evaluated_budget[trial.config_id]
-                # We only keep the highest budget
+                # Only the highest budget is kept
                 if trial.budget >= latest_budget:
                     results[trial.config_id] = trial.costs
             else:
@@ -448,25 +715,25 @@ def get_all_costs(
 
     def get_status(self, config_id: int, budget: Optional[Union[int, float]] = None) -> Status:
         """
-        Returns the status of a configuration.
+        Return the status of a configuration.
 
         Parameters
         ----------
         config_id : int
             Configuration id to get the status for.
-        budget : Optional[Union[int, float]], optional
-            Budget to get the status from the configuration id for. By default None. If budget is
+        budget : Optional[Union[int, float]]
+            Budget to get the status from the configuration id for. By default, None. If budget is
             None, the highest budget is chosen.
 
-        Raises
-        ------
-        ValueError
-            If the configuration id is not found.
-
         Returns
         -------
         Status
             Status of the configuration.
+
+        Raises
+        ------
+        ValueError
+            If the configuration id is not found.
         """
         if budget == COMBINED_BUDGET:
             return Status.NOT_EVALUATED
@@ -479,7 +746,7 @@ def get_status(self, config_id: int, budget: Optional[Union[int, float]] = None)
 
         trial_key = self.get_trial_key(config_id, budget)
 
-        # Unfortunately, we have to iterate through the history to find the status
+        # Unfortunately, it is necessary to iterate through the history to find the status
         # TODO: Cache the stati
         for trial in self.history:
             if trial_key == trial.get_key():
@@ -494,16 +761,16 @@ def get_incumbent(
         statuses: Optional[Union[Status, List[Status]]] = None,
     ) -> Tuple[Configuration, float]:
         """
-        Returns the incumbent with its normalized cost.
+        Return the incumbent with its normalized cost.
 
         Parameters
         ----------
-        objectives : Optional[Union[Objective, List[Objective]]], optional
-            Considerd objectives. By default None. If None, all objectives are considered.
-        budget : Optional[Union[int, float]], optional
-            Considered budget. By default None. If None, the highest budget is chosen.
-        statuses : Optional[Union[Status, List[Status]]], optional
-            Considered statuses. By default None. If None, all stati are considered.
+        objectives : Optional[Union[Objective, List[Objective]]]
+            Considered objectives. By default, None. If None, all objectives are considered.
+        budget : Optional[Union[int, float]]
+            Considered budget. By default, None. If None, the highest budget is chosen.
+        statuses : Optional[Union[Status, List[Status]]]
+            Considered statuses. By default, None. If None, all stati are considered.
 
         Returns
         -------
@@ -539,16 +806,18 @@ def merge_costs(
         self, costs: List[float], objectives: Optional[Union[Objective, List[Objective]]] = None
     ) -> float:
         """
-        Calculates one cost value from multiple costs.
-        Normalizes the costs first and weight every cost the same.
+        Calculate one cost value from multiple costs.
+
+        Normalizes the costs first and weigh every cost the same.
         The lower the normalized cost, the better.
 
         Parameters
         ----------
         costs : List[float]
-            The costs, which should be merged. Must be the same length as the original number of objectives.
-        objectives : Optional[List[Objective]], optional
-            The considered objectives to the costs. By default None.
+            The costs, which should be merged. Must be the same length as the original number of
+            objectives.
+        objectives : Optional[List[Objective]]
+            The considered objectives to the costs. By default, None.
             If None, all objectives are considered. The passed objectives can differ from the
             original number objectives.
 
@@ -556,6 +825,12 @@ def merge_costs(
         -------
         float
             Merged costs.
+
+        Raises
+        ------
+        RuntimeError
+            If the number of costs is different from the original number of objectives.
+            If the objective was not found.
         """
         # Get rid of NaN values
         costs = self._process_costs(costs)
@@ -593,8 +868,8 @@ def merge_costs(
             b = objective.upper - objective.lower
             normalized_cost = a / b
 
-            # We optimize the lower
-            # So we need to flip the normalized cost
+            # The lower is optimized
+            # So the normalized cost needs to be flipped
             if objective.optimize == "upper":
                 normalized_cost = 1 - normalized_cost
 
@@ -609,10 +884,23 @@ def merge_costs(
 
         return cost
 
-    def get_model(self, config_id: int) -> Optional["torch.nn.Module"]:
+    def get_model(self, config_id: int) -> Optional["torch.nn.Module"]:  # type: ignore # noqa: F821
+        """
+        Get a model associated with the configuration id.
+
+        Parameters
+        ----------
+        config_id : int
+            The configuration id.
+
+        Returns
+        -------
+        Optional["torch.nn.Module]
+            A model for the provided configuration id.
+        """
         import torch
 
-        filename = self.models_dir / f"{str(config_id)}.pth"
+        filename = self.models_dir / f"{str(config_id)}.pth"  # type: ignore
         if not filename.exists():
             return None
 
@@ -622,29 +910,30 @@ def get_trajectory(
         self, objective: Objective, budget: Optional[Union[int, float]] = None
     ) -> Tuple[List[float], List[float], List[float], List[int], List[int]]:
         """
-        Calculates the trajectory of the given objective and budget.
+        Calculate the trajectory of the given objective and budget.
 
         Parameters
         ----------
         objective : Objective
             Objective to calculate the trajectory for.
-        budget : Optional[Union[int, float]], optional
+        budget : Optional[Union[int, float]]
             Budget to calculate the trajectory for. If no budget is given, then the highest budget
             is chosen. By default None.
 
         Returns
         -------
-        times : List[float]
-            Times of the trajectory.
-        costs_mean : List[float]
-            Costs of the trajectory.
-        costs_std : List[float]
-            Standard deviation of the costs of the trajectory. This is particularly useful for
-            grouped runs.
-        ids : List[int]
-            The "global" ids of the selected trials.
-        config_ids : List[int]
-            Config ids of the selected trials.
+        Tuple[List[float], List[float], List[float], List[int], List[int]]
+            times : List[float]
+                Times of the trajectory.
+            costs_mean : List[float]
+                Costs of the trajectory.
+            costs_std : List[float]
+                Standard deviation of the costs of the trajectory. This is particularly useful for
+                grouped runs.
+            ids : List[int]
+                The "global" ids of the selected trials.
+            config_ids : List[int]
+                Config ids of the selected trials.
         """
         if budget is None:
             budget = self.get_highest_budget()
@@ -673,7 +962,7 @@ def get_trajectory(
         for id, _ in order:
             trial = self.history[id]
 
-            # We want to use all budgets
+            # All budgets should should be used
             if budget != COMBINED_BUDGET:
                 # Only consider selected/last budget
                 if trial.budget != budget:
@@ -693,7 +982,7 @@ def get_trajectory(
                 current_cost = cost
 
                 costs_mean.append(cost)
-                costs_std.append(0)
+                costs_std.append(0.0)
                 times.append(trial.end_time)
                 ids.append(id)
                 config_ids.append(trial.config_id)
@@ -704,14 +993,16 @@ def encode_config(
         self, config: Union[int, Dict[Any, Any], Configuration], specific: bool = False
     ) -> List:
         """
-        Encodes a given config (id) to a normalized list.
-        If a config is passed, no look-up has to be done.
+        Encode a given configuration (id) to a normalized list.
+
+        If a configuration is passed, no look-up has to be done.
 
         Parameters
         ----------
         config : Union[int, Dict[Any, Any], Configuration]
-            Either the config id, config as dict, or Configuration itself.
-        specific : bool, optional
+            Either the configuration id, as configuration as dict,
+            or a Configuration itself.
+        specific : bool
             Use specific encoding for fanova tree, by default False.
 
         Returns
@@ -748,12 +1039,25 @@ def encode_config(
         return x
 
     def encode_configs(self, configs: List[Configuration]) -> np.ndarray:
-        X = []
+        """
+        Encode a list of configurations into a corresponding numpy array.
+
+        Parameters
+        ----------
+        configs : List[Configuration]
+            A list containing the configurations to be encoded.
+
+        Returns
+        -------
+        np.ndarray
+            A numpy array with the encoded configurations.
+        """
+        x_set = []
         for config in configs:
             x = self.encode_config(config)
-            X.append(x)
+            x_set.append(x)
 
-        return np.array(X)
+        return np.array(x_set)
 
     def get_encoded_data(
         self,
@@ -763,30 +1067,30 @@ def get_encoded_data(
         specific: bool = False,
         include_config_ids: bool = False,
         include_combined_cost: bool = False,
-    ) -> Tuple[pd.DataFrame, pd.DataFrame]:
+    ) -> pd.DataFrame:
         """
-        Encodes configurations to process them further. After the configurations are encoded,
+        Encode configurations to process them further.
+
+        After the configurations are encoded,
         they can be used in model prediction.
 
         Parameters
         ----------
-        objectives : Optional[Union[Objective, List[Objective]]], optional
+        objectives : Optional[Union[Objective, List[Objective]]]
             Which objectives should be considered. If None, all objectives are
-            considered. By default None.
-        budget : Optional[List[Status]], optional
-            Which budget should be considered. By default None. If None, only the highest budget
+            considered. By default, None.
+        budget : Optional[List[Status]]
+            Which budget should be considered. By default, None. If None, only the highest budget
             is considered.
-        statuses : Optional[Union[Status, List[Status]]], optional
-            Which statuses should be considered. By default None. If None, all statuses are
+        statuses : Optional[Union[Status, List[Status]]]
+            Which statuses should be considered. By default, None. If None, all statuses are
             considered.
-        encode_y : bool, optional
-            Whether y should be normalized too. By default False.
-        specific : bool, optional
+        specific : bool
             Whether a specific encoding should be used. This encoding is compatible with pyrfr.
             A wrapper for pyrfr is implemented in ``deepcave.evaluators.epm``.
             By default False.
-        include_config_ids : bool, optional
-            Whether to include config ids. By default False.
+        include_config_ids : bool
+            Whether to include configuration ids. By default, False.
         include_combined_cost : bool, optional
             Whether to include combined cost. Note that the combined cost is calculated by the
             passed objectives only. By default False.
@@ -800,16 +1104,15 @@ def get_encoded_data(
         Raises
         ------
         ValueError
-            If a hyperparameter is not supported.
+            If a hyperparameter (HP) is not supported.
         """
-
         if objectives is None:
             objectives = self.get_objectives()
 
         if isinstance(objectives, Objective):
             objectives = [objectives]
 
-        X, Y = [], []
+        x_set, y_set = [], []
         config_ids = []
 
         results = self.get_all_costs(budget, statuses)
@@ -827,17 +1130,17 @@ def get_encoded_data(
             if include_combined_cost:
                 y += [self.merge_costs(costs, objectives)]
 
-            X.append(x)
-            Y.append(y)
+            x_set.append(x)
+            y_set.append(y)
             config_ids.append(config_id)
 
-        X = np.array(X)  # type: ignore
-        Y = np.array(Y)  # type: ignore
-        config_ids = np.array(config_ids).reshape(-1, 1)  # type: ignore
+        x_set_array = np.array(x_set)
+        y_set_array = np.array(y_set)
+        config_ids_array = np.array(config_ids).reshape(-1, 1)
 
         # Imputation: Easiest case is to replace all nans with -1
-        # However, since Stefan used different values for inactives
-        # we also have to use different inactives to be compatible
+        # However, since Stefan used different values for inactive hyperparameters,
+        # Also different inactive hyperparameters have to be used, to be compatible
         # with the random forests.
         # https://github.com/automl/SMAC3/blob/a0c89502f240c1205f83983c8f7c904902ba416d/smac/epm/base_rf.py#L45
         if specific:
@@ -864,10 +1167,10 @@ def get_encoded_data(
                             raise ValueError("Hyperparameter not supported.")
 
                 if conditional[idx] is True:
-                    nonfinite_mask = ~np.isfinite(X[:, idx])
-                    X[nonfinite_mask, idx] = impute_values[idx]
+                    non_finite_mask = ~np.isfinite(x_set_array[:, idx])
+                    x_set_array[non_finite_mask, idx] = impute_values[idx]
 
-        # Now we create dataframes for both values and labels
+        # Now dataframes are created for both values and labels
         # [CONFIG_ID, HP1, HP2, ..., HPn, OBJ1, OBJ2, ..., OBJm, COMBINED_COST]
         if include_config_ids:
             columns = ["config_id"]
@@ -881,9 +1184,9 @@ def get_encoded_data(
             columns += [COMBINED_COST_NAME]
 
         if include_config_ids:
-            data = np.concatenate((config_ids, X, Y), axis=1)
+            data: np.ndarray = np.concatenate((config_ids_array, x_set_array, y_set_array), axis=1)
         else:
-            data = np.concatenate((X, Y), axis=1)
+            data = np.concatenate((x_set_array, y_set_array), axis=1)
 
         data = pd.DataFrame(data=data, columns=columns)
 
@@ -898,28 +1201,37 @@ def check_equality(
     budgets: bool = True,
 ) -> Dict[str, Any]:
     """
-    Checks the passed runs on equality based on the selected runs and returns the requested
-    attributes.
+    Check the passed runs on equality based on the selected runs.
+
+    Return the requested attributes.
 
     Parameters
     ----------
     runs : list[AbstractRun]
         Runs to check for equality.
     meta : bool, optional
-        Meta-Data excluding objectives and budgets, by default True
+        Meta-Data excluding objectives and budgets, by default True.
     configspace : bool, optional
-        ConfigSpace, by default True
+        Wheter to include the configuration space, by default True.
     objectives : bool, optional
-        Objectives, by default True
+        Wheter to include the objectives, by default True.
     budgets : bool, optional
-        Budgets, by default True
+        Whether to include the budgets, by default True.
 
     Returns
     -------
     Dict[str, Any]
         Dictionary containing the checked attributes.
+
+    Raises
+    ------
+    NotMergeableError
+        If the meta data of the runs are not equal.
+        If the configuration spaces of the runs are not equal.
+        If the budgets of the runs are not equal.
+        If the objective of the runs are not equal.
     """
-    result = {}
+    result: Dict[str, Any] = {}
 
     if len(runs) == 0:
         return result
@@ -982,6 +1294,7 @@ def check_equality(
             for o1_, o2_ in zip(o1, o2):
                 o1_.merge(o2_)
 
+        assert o1 is not None
         serialized_objectives = [o.to_json() for o in o1]
         result["objectives"] = serialized_objectives
         if meta:
diff --git a/deepcave/runs/converters/__init__.py b/deepcave/runs/converters/__init__.py
index e69de29b..fd7a0de6 100644
--- a/deepcave/runs/converters/__init__.py
+++ b/deepcave/runs/converters/__init__.py
@@ -0,0 +1,18 @@
+#  noqa: D400
+"""
+# converters
+
+## Modules
+----------
+bohb
+    This module provides utilities for managing and processing data concerning a BOHB
+    (Bayesian Optimization and Hyperparameter Bandits) run.
+deepcave
+    This module defines the DeepCAVE run object.
+smac3v1
+    This module provides utilities to create a SMAC3v1
+    (Sequential Model-based Algorithm Configuration) run.
+smac3v2
+    This module provides utilities to create a SMAC3v2
+    (Sequential Model-based Algorithm Configuration) run.
+"""
diff --git a/deepcave/runs/converters/bohb.py b/deepcave/runs/converters/bohb.py
index e8fdc105..9cba0066 100644
--- a/deepcave/runs/converters/bohb.py
+++ b/deepcave/runs/converters/bohb.py
@@ -1,3 +1,16 @@
+# noqa: D400
+"""
+# BOHBRun
+
+This module provides utilities for managing and processing data concerning a BOHB
+(Bayesian Optimization and Hyperparameter Bandits) run.
+
+## Classes
+    - BOHBRun: Create a BOHB Run.
+"""
+
+from typing import Any, Dict, Union
+
 from pathlib import Path
 
 from deepcave.runs import Status
@@ -7,11 +20,31 @@
 
 
 class BOHBRun(Run):
+    """
+    Create a BOHB (Bayesian Optimization and Hyperparameter Bandits) run.
+
+    Properties
+    ----------
+    path : Path
+        The path to the run.
+    """
+
     prefix = "BOHB"
     _initial_order = 2
 
     @property
-    def hash(self):
+    def hash(self) -> str:
+        """
+        Get the hash of the current run.
+
+        If the hash changes, the cache has to be cleared.
+        This ensures that the cache always holds the latest results of the run.
+
+        Returns
+        -------
+        str
+            The hash of the run.
+        """
         if self.path is None:
             return ""
 
@@ -19,14 +52,34 @@ def hash(self):
         return file_to_hash(self.path / "results.json")
 
     @property
-    def latest_change(self):
+    def latest_change(self) -> Union[float, int]:
+        """
+        Get the timestamp of the latest change.
+
+        Returns
+        -------
+        Union[float, int]
+            The latest change.
+        """
         if self.path is None:
             return 0
 
         return Path(self.path / "results.json").stat().st_mtime
 
     @classmethod
-    def from_path(cls, path):
+    def from_path(cls, path: Union[Path, str]) -> "BOHBRun":
+        """
+        Create a new BOHB run from a given path and add a new trial to it.
+
+        Parameters
+        ----------
+        path : Union[Path, str]
+            The pathname to base the run on.
+
+        Returns
+        -------
+        The BOHB run
+        """
         path = Path(path)
 
         # Read configspace
@@ -35,7 +88,7 @@ def from_path(cls, path):
         configspace = cs_json.read((path / "configspace.json").read_text())
 
         # Read objectives
-        # We have to define it ourselves, because we don't know the type of the objective
+        # It has to be defined here, because the type of the objective is not known
         # Only lock lower
         objective = Objective("Cost", lower=0)
 
@@ -63,25 +116,31 @@ def from_path(cls, path):
             budget = bohb_run.budget
 
             if bohb_run.info is None:
-                status = "CRASHED"
+                status_string = "CRASHED"
             else:
-                status = bohb_run.info["state"]
+                status_string = bohb_run.info["state"]
 
             config = config_mapping[bohb_run.config_id]["config"]
 
-            origin = None
-            additional = {}
+            additional: Dict[str, Any] = {}
+            status: Status
 
             # QUEUED, RUNNING, CRASHED, REVIEW, TERMINATED, COMPLETED, SUCCESS
-            if "SUCCESS" in status or "TERMINATED" in status or "COMPLETED" in status:
+            if (
+                "SUCCESS" in status_string
+                or "TERMINATED" in status_string
+                or "COMPLETED" in status_string
+            ):
                 status = Status.SUCCESS
-            elif "RUNNING" in status or "QUEUED" in status or "REVIEW" in status:
-                continue
+            elif (
+                "RUNNING" in status_string or "QUEUED" in status_string or "REVIEW" in status_string
+            ):
+                status = status_string  # type: ignore
             else:
                 status = Status.CRASHED
 
             if status != Status.SUCCESS:
-                # We don't want cost included which are failed
+                # Costs which failed, should not be included
                 cost = None
 
             run.add(
@@ -91,7 +150,6 @@ def from_path(cls, path):
                 start_time=starttime,
                 end_time=endtime,
                 status=status,
-                origin=origin,
                 additional=additional,
             )
 
diff --git a/deepcave/runs/converters/deepcave.py b/deepcave/runs/converters/deepcave.py
index 923df8c8..ff8f70a3 100644
--- a/deepcave/runs/converters/deepcave.py
+++ b/deepcave/runs/converters/deepcave.py
@@ -1,3 +1,15 @@
+#  noqa: D400
+"""
+# DeepCAVE
+
+This module defines the DeepCAVE run object.
+
+## Classes
+    - DeepCAVERun: Create a DeepCAVE run and provide handling utilities.
+"""
+
+from typing import Union
+
 from pathlib import Path
 
 from deepcave.runs.run import Run
@@ -5,11 +17,31 @@
 
 
 class DeepCAVERun(Run):
+    """
+    Create a DeepCAVE run and provide handling utilities.
+
+    Properties
+    ----------
+    path : Path
+        The path the run.
+    """
+
     prefix = "DeepCAVE"
     _initial_order = 1
 
     @property
-    def hash(self):
+    def hash(self) -> str:
+        """
+        Hash of the current run.
+
+        If the hash changes, the cache has to be cleared.
+        This ensures that the cache always holds the latest results of the run.
+
+        Returns
+        -------
+        str
+            The hash of the run.
+        """
         if self.path is None:
             return ""
 
@@ -17,12 +49,32 @@ def hash(self):
         return file_to_hash(self.path / "history.jsonl")
 
     @property
-    def latest_change(self):
+    def latest_change(self) -> Union[float, int]:
+        """
+        Get the timestamp of the latest change.
+
+        Returns
+        -------
+        Union[float, int]
+            The latest change.
+        """
         if self.path is None:
             return 0
 
         return Path(self.path / "history.jsonl").stat().st_mtime
 
     @classmethod
-    def from_path(cls, path):
+    def from_path(cls, path: Path) -> "DeepCAVERun":
+        """
+        Get a DeepCAVE run from a given path.
+
+        Parameters
+        ----------
+        path : Path
+            The path to base the run on.
+
+        Returns
+        -------
+        The DeepCAVE run.
+        """
         return DeepCAVERun(path.stem, path=Path(path))
diff --git a/deepcave/runs/converters/smac3v1.py b/deepcave/runs/converters/smac3v1.py
index ea66cdc3..98122a99 100644
--- a/deepcave/runs/converters/smac3v1.py
+++ b/deepcave/runs/converters/smac3v1.py
@@ -1,3 +1,17 @@
+#  noqa: D400
+"""
+# SMAC3v1Run
+
+This module provides utilities to create a SMAC3v1
+(Sequential Model-based Algorithm Configuration) run.
+
+Version 1.4 is used.
+
+## Classes
+    - SMAC3v1Run: Define a SMAC3v1 run object.
+"""
+from typing import Optional, Union
+
 import json
 from pathlib import Path
 
@@ -10,11 +24,33 @@
 
 
 class SMAC3v1Run(Run):
+    """
+    Define a SMAC3v1 (Sequential Model-based Algorithm Configuration) run object.
+
+    Version 1.4 is used.
+
+    Properties
+    ----------
+    path : Path
+        The path to the run.
+    """
+
     prefix = "SMAC3v1"
     _initial_order = 2
 
     @property
-    def hash(self):
+    def hash(self) -> str:
+        """
+        Hash of the current run.
+
+        If the hash changes, the cache has to be cleared.
+        This ensures that the cache always holds the latest results of the run.
+
+        Returns
+        -------
+        str
+            The hash of the run.
+        """
         if self.path is None:
             return ""
 
@@ -22,16 +58,40 @@ def hash(self):
         return file_to_hash(self.path / "runhistory.json")
 
     @property
-    def latest_change(self):
+    def latest_change(self) -> Union[float, int]:
+        """
+        Get the timestamp of the latest change.
+
+        Returns
+        -------
+        Union[float, int]
+            The latest change.
+        """
         if self.path is None:
             return 0
 
         return Path(self.path / "runhistory.json").stat().st_mtime
 
     @classmethod
-    def from_path(cls, path):
+    def from_path(cls, path: Union[Path, str]) -> "SMAC3v1Run":
         """
         Based on working_dir/run_name/*, return a new trials object.
+
+        Parameters
+        ----------
+        path : Union[Path, str]
+            The path to base the run on.
+
+        Returns
+        -------
+        A SMAC3v1 run.
+
+        Raises
+        ------
+        RuntimeError
+            Instances are not supported.
+        RuntimeError
+            Multiple Seeds are not supported.
         """
         path = Path(path)
 
@@ -42,7 +102,7 @@ def from_path(cls, path):
             configspace = cs_json.read(f.read())
 
         # Read objectives
-        # We have to define it ourselves, because we don't know the type of the objective
+        # It has to be defined here, because the type of the objective is not known
         # Only lock lower
         objective1 = Objective("Cost", lower=0)
         objective2 = Objective("Time", lower=0)
@@ -69,7 +129,7 @@ def from_path(cls, path):
             name=path.stem, configspace=configspace, objectives=[objective1, objective2], meta=meta
         )
 
-        # We have to set the path manually
+        # The path has to be set manually
         run._path = path
 
         # Iterate over the runhistory
@@ -128,7 +188,7 @@ def from_path(cls, path):
                 status = Status.CRASHED
 
             if status != Status.SUCCESS:
-                # We don't want cost included which are failed
+                # Costs which failed, should not be included
                 cost = None
                 time = None
             else:
@@ -137,7 +197,7 @@ def from_path(cls, path):
             # Round budget
             budget = np.round(budget, 2)
 
-            origin = None
+            origin: Optional[str] = None
             if config_id in config_origins:
                 origin = config_origins[config_id]
 
diff --git a/deepcave/runs/converters/smac3v2.py b/deepcave/runs/converters/smac3v2.py
index e8ab250a..01450d16 100644
--- a/deepcave/runs/converters/smac3v2.py
+++ b/deepcave/runs/converters/smac3v2.py
@@ -1,3 +1,18 @@
+#  noqa: D400
+"""
+# SMAC3v2Run
+
+This module provides utilities to create a SMAC3v2
+(Sequential Model-based Algorithm Configuration) run.
+
+Version 2.0.0 is used.
+
+## Classes
+    - SMAC3v2Run: Define a SMAC3v2 run object.
+"""
+
+from typing import Union
+
 import json
 from pathlib import Path
 
@@ -10,11 +25,33 @@
 
 
 class SMAC3v2Run(Run):
+    """
+    Define a SMAC3v2 (Sequential Model-based Algorithm Configuration) run object.
+
+    Version 2.0.0 is used.
+
+    Properties
+    ----------
+    path : Path
+        The path to the run.
+    """
+
     prefix = "SMAC3v2"
     _initial_order = 2
 
     @property
-    def hash(self):
+    def hash(self) -> str:
+        """
+        Hash of the current run.
+
+        If the hash changes, the cache has to be cleared.
+        This ensures that the cache always holds the latest results of the run.
+
+        Returns
+        -------
+        str
+            The hash of the run.
+        """
         if self.path is None:
             return ""
 
@@ -22,16 +59,40 @@ def hash(self):
         return file_to_hash(self.path / "runhistory.json")
 
     @property
-    def latest_change(self):
+    def latest_change(self) -> Union[float, int]:
+        """
+        Get the timestamp of the latest change.
+
+        Returns
+        -------
+        Union[float, int]
+            The latest change.
+        """
         if self.path is None:
             return 0
 
         return Path(self.path / "runhistory.json").stat().st_mtime
 
     @classmethod
-    def from_path(cls, path):
+    def from_path(cls, path: Union[Path, str]) -> "SMAC3v2Run":
         """
         Based on working_dir/run_name/*, return a new trials object.
+
+        Parameters
+        ----------
+        path : Union[Path, str]
+            The path to base the trial object on.
+
+        Returns
+        -------
+        The SMAC3v2 run.
+
+        Raises
+        ------
+        RuntimeError
+            Instances are not supported.
+        RuntimeError
+            Multiple Seeds are not supported.
         """
         path = Path(path)
 
@@ -60,11 +121,9 @@ def from_path(cls, path):
             meta["run_objectives"] = meta.pop("objectives")
 
         # Let's create a new run object
-        run = SMAC3v2Run(
-            name=path.stem, configspace=configspace, objectives=obj_list, meta=meta
-        )
+        run = SMAC3v2Run(name=path.stem, configspace=configspace, objectives=obj_list, meta=meta)
 
-        # We have to set the path manually
+        # The path has to be set manually
         run._path = path
 
         # Iterate over the runhistory
@@ -124,7 +183,7 @@ def from_path(cls, path):
                 status = Status.CRASHED
 
             if status != Status.SUCCESS:
-                # We don't want cost included which are failed
+                # Costs which failed, should not be included
                 cost = [None] * len(cost) if isinstance(cost, list) else None
                 time = None
             else:
diff --git a/deepcave/runs/exceptions.py b/deepcave/runs/exceptions.py
index aefe3a20..d36d0e11 100644
--- a/deepcave/runs/exceptions.py
+++ b/deepcave/runs/exceptions.py
@@ -1,3 +1,18 @@
+# noqa: D400
+"""
+# Exceptions
+
+This module provides utilities for different errors concerning the runs.
+
+Exceptions will be raised, if a directory is not a valid run,
+as well as if runs are not mergeable.
+
+## Classes
+    - NotValidRunError: Raised if directory is not a valid run.
+    - NotMergeableError: Raised if two or more runs are not mergeable.
+"""
+
+
 class NotValidRunError(Exception):
     """Raised if directory is not a valid run."""
 
@@ -5,6 +20,6 @@ class NotValidRunError(Exception):
 
 
 class NotMergeableError(Exception):
-    """Raised if two or more runs are not mergeable"""
+    """Raised if two or more runs are not mergeable."""
 
     pass
diff --git a/deepcave/runs/group.py b/deepcave/runs/group.py
index 8ca639b8..8fdefc58 100644
--- a/deepcave/runs/group.py
+++ b/deepcave/runs/group.py
@@ -1,4 +1,15 @@
-from typing import Dict, List, Tuple
+#  noqa: D400
+"""
+# Group
+
+This module provides utilities for grouping and managing a group of runs.
+Utilities include getting attributes of the grouped runs, as well as the group itself.
+
+## Classes
+    - Group: Can group and manage a group of runs.
+"""
+
+from typing import Any, Dict, Iterator, List, Optional, Tuple
 
 from copy import deepcopy
 
@@ -9,6 +20,37 @@
 
 
 class Group(AbstractRun):
+    """
+    Can group and manage a group of runs.
+
+    Utilities include getting attributes of the grouped runs, as well as the group itself.
+
+    Properties
+    ----------
+    runs : List[AbstractRun]
+        A list of the runs.
+    meta : Dict[str, Any]
+        Contains budgets, objectives and their attributes.
+    configspace : ConfigurationSpace
+        The configuration space of the runs.
+    objectives : Objective
+        The objectives of the runs.
+    budgets : List[Union[int, float]]
+        The budgets of the runs.
+    configs : Dict[int, Any]
+        A dictionary of the configurations and their ids as key.
+    origins : Dict[int, str]
+        The origins of the configurations and their ids as key.
+    trial_keys : Dict[Tuple[str, int], int]
+        The keys of the trial.
+    history : List[Trial]
+        The trial history.
+    prefix : str
+        The prefix for the id of the group.
+    name : str
+        The name for the id of the group.
+    """
+
     prefix = "group"
 
     def __init__(self, name: str, runs: List[AbstractRun]):
@@ -27,7 +69,7 @@ def __init__(self, name: str, runs: List[AbstractRun]):
             self.objectives = attributes["objectives"]
             self.budgets = attributes["budgets"]
 
-            # We need new config ids
+            # New config ids are needed
             current_config_id = 0
 
             # Key: new_config_id; Value: (run_id, config_id)
@@ -69,7 +111,7 @@ def __init__(self, name: str, runs: List[AbstractRun]):
                     # Update config id
                     trial.config_id = new_config_id
 
-                    # Now we add it to the history
+                    # Now it is added to the history
                     trial_key = trial.get_key()
                     if trial_key not in self.trial_keys:
                         self.trial_keys[trial_key] = len(self.history)
@@ -86,28 +128,55 @@ def __init__(self, name: str, runs: List[AbstractRun]):
         except Exception as e:
             raise NotMergeableError(f"Runs can not be merged: {e}")
 
-    def __iter__(self):
+    def __iter__(self: "Group") -> Iterator[str]:
+        """Allow to iterate over the object."""
         for run in self.runs:
             yield run.name
 
     @property
     def hash(self) -> str:
+        """
+        Sorted hashes of the group.
+
+        Returns
+        -------
+        str
+            The sorted hash of the group.
+        """
         hashes = []
         for run in self.runs:
             hashes += [run.hash]
 
-        # We sort hashes now because we don't want to be dependent on the order
+        # Hashes are sorted now, so there is no dependence on the order
         hashes = sorted(hashes)
         return string_to_hash("-".join(hashes))
 
     @property
     def id(self) -> str:
-        # Groups do not have a path, therefore we use the name.
+        """
+        Get the hash as id of the group.
+
+        In contrast to hash, this hash should not be changed throughout the run.
+
+        Returns
+        -------
+        str
+            The hash of the group.
+        """
+        # Groups do not have a path, therefore the name is used.
         return string_to_hash(f"{self.prefix}:{self.name}")
 
     @property
-    def latest_change(self) -> int:
-        latest_change = 0
+    def latest_change(self) -> float:
+        """
+        Get the latest change made to the grouped runs.
+
+        Returns
+        -------
+        float
+            The latest change.
+        """
+        latest_change = 0.0
         for run in self.runs:
             if run.latest_change > latest_change:
                 latest_change = run.latest_change
@@ -116,46 +185,143 @@ def latest_change(self) -> int:
 
     @property
     def run_paths(self) -> List[str]:
+        """Get the path of the runs in the group."""
         return [str(run.path) for run in self.runs]
 
     @property
     def run_names(self) -> List[str]:
+        """
+        Get the names of the runs in the group.
+
+        Returns
+        -------
+        List[str]
+            A list of the names of the runs in the group.
+        """
         return [run.name for run in self.runs]
 
     def get_runs(self) -> List[AbstractRun]:
+        """
+        Get the runs in the group.
+
+        Returns
+        -------
+        List[AbstractRun]
+            A list of the grouped runs.
+        """
         return self.runs
 
     def get_new_config_id(self, run_id: int, original_config_id: int) -> int:
+        """
+        Get a new identificator for a configuration.
+
+        Parameters
+        ----------
+        run_id : int
+            The id of the run.
+        original_config_id : int
+            The original identificator of a configuration.
+
+        Returns
+        -------
+        int
+            The new identificator of a configuration.
+        """
         return self._new_config_mapping[(run_id, original_config_id)]
 
-    def get_original_config_id(self, config_id: int) -> id:
+    def get_original_config_id(self, config_id: int) -> int:
+        """
+        Get the original identificator of a configuration.
+
+        Parameters
+        ----------
+        config_id : int
+            The identificator of a configuration.
+
+        Returns
+        -------
+        int
+            The original identificator of a configuration.
+        """
         return self._original_config_mapping[config_id][1]
 
     def get_original_run(self, config_id: int) -> AbstractRun:
+        """
+        Get the original run.
+
+        Parameters
+        ----------
+        config_id : int
+            The identificator of the configuration.
+
+        Returns
+        -------
+        AbstractRun
+            The original run.
+        """
         run_id = self._original_config_mapping[config_id][0]
         return self.runs[run_id]
 
-    def get_model(self, config_id):
+    def get_model(self, config_id: int) -> Optional[Any]:
+        """
+        Get the model given the configuration id.
+
+        Parameters
+        ----------
+        config_id : int
+            The identificator of the configuration.
+
+        Returns
+        -------
+        Optional[Any]
+            The model.
+        """
         run_id, config_id = self._original_config_mapping[config_id]
         return self.runs[run_id].get_model(config_id)
 
-    def get_trajectory(self, *args, **kwargs):
+    # Types dont match superclass
+    def get_trajectory(self, *args, **kwargs):  # type: ignore
+        """
+        Calculate the trajectory of the given objective and budget.
+
+        This includes the times, the mean costs, and the standard deviation of the costs.
+
+        Parameters
+        ----------
+        *args
+            Should be the objective to calculate the trajectory from.
+        **kwargs
+            Should be the budget to calculate the trajectory for.
+
+        Returns
+        -------
+        times : List[float]
+            Times of the trajectory.
+        costs_mean : List[float]
+            Costs of the trajectory.
+        costs_std : List[float]
+            Standard deviation of the costs of the trajectory.
+        ids : List[int]
+            The "global" ids of the selected trial.
+        config_ids : List[int]
+            The configuration ids of the selected trials.
+        """
         # Cache costs
         run_costs = []
         run_times = []
 
-        # All x values on which we need y values
+        # All x values on which y values are needed
         all_times = []
 
         for _, run in enumerate(self.runs):
             times, costs_mean, _, _, _ = run.get_trajectory(*args, **kwargs)
 
-            # Cache s.t. we don't calculate it multiple times
+            # Cache s.t. calculate it is not calculated multiple times
             run_costs.append(costs_mean)
             run_times.append(times)
 
             # Add all times
-            # We want to calculate standard deviation on all times
+            # Standard deviation needs to be calculated on all times
             for time in times:
                 if time not in all_times:
                     all_times.append(time)
@@ -177,10 +343,10 @@ def get_trajectory(self, *args, **kwargs):
             all_costs.append(y)
 
         # Make numpy arrays
-        all_costs = np.array(all_costs)
+        all_costs_array = np.array(all_costs)
 
         times = all_times
-        costs_mean = np.mean(all_costs, axis=1)
-        costs_std = np.std(all_costs, axis=1)
+        costs_mean = np.mean(all_costs_array, axis=1)
+        costs_std = np.std(all_costs_array, axis=1)
 
         return times, list(costs_mean), list(costs_std), [], []
diff --git a/deepcave/runs/handler.py b/deepcave/runs/handler.py
index 7ebb83b4..9fcd3c48 100644
--- a/deepcave/runs/handler.py
+++ b/deepcave/runs/handler.py
@@ -1,6 +1,18 @@
+#  noqa: D400
+"""
+# Handler
+
+This module provides utilities to handle a run.
+
+It can retrieve working directories, run paths, run names, as well as groups of runs.
+It provides utilities to update and remove runs as well as groups of runs.
+
+# Classes
+    - RunHandler: Handle the runs.
+"""
+
 from typing import Dict, List, Optional, Type, Union
 
-import logging
 import time
 from pathlib import Path
 
@@ -8,13 +20,37 @@
 from deepcave.runs import AbstractRun
 from deepcave.runs.group import Group
 from deepcave.runs.run import Run
+from deepcave.utils.cache import Cache
 from deepcave.utils.logs import get_logger
+from deepcave.utils.run_caches import RunCaches
 
 
 class RunHandler:
     """
-    Handles the runs. Based on the meta data in the cache, automatically selects the right converter
+    Handle the runs.
+
+    Based on the meta data in the cache, automatically selects the right converter
     and switches to the right (plugin) cache.
+
+    Provides utilities to retrieve working directories, run paths, run names, and groups of runs.
+    Also update and remove runs as well a groups of runs.
+
+    Properties
+    ----------
+    c : Cache
+        The cache containing information about a run(s).
+    rc : RunCaches
+        The caches for the selected runs.
+    logger : Logger
+        The logger for the run handler.
+    available_run_yfes : List[Type[Run]]
+        A list of the available converters.
+    runs : Dict[str, AbstractRun]
+        A dictionary of runs with their path as key.
+    groups : Dict[str, Group]
+        A dictionary of the groups.
+    available_run_classes : List[Type[Run]]
+        Contains the available run classes.
     """
 
     def __init__(self, config: Config, cache: "Cache", run_cache: "RunCaches") -> None:
@@ -37,7 +73,7 @@ def __init__(self, config: Config, cache: "Cache", run_cache: "RunCaches") -> No
 
     def set_working_directory(self, working_directory: Union[Path, str]) -> None:
         """
-        Sets the working directoy to the meta cache.
+        Set the working directory to the meta cache.
 
         Parameters
         ----------
@@ -48,23 +84,36 @@ def set_working_directory(self, working_directory: Union[Path, str]) -> None:
 
     def get_working_directory(self) -> Path:
         """
-        Returns the current working directory in the cache.
+        Return the current working directory in the cache.
 
         Returns
         -------
         Path
             Path of the working directory.
+
+        Raises
+        ------
+        AssertionError
+            If the working directory is not a string or a Path, an error is thrown.
         """
-        return Path(self.c.get("working_dir"))
+        working_dir = self.c.get("working_dir")
+        assert isinstance(
+            working_dir, (str, Path)
+        ), "Working directory of cache must be a string or a Path like."
+        return Path(working_dir)
 
     def get_available_run_paths(self) -> Dict[str, str]:
         """
-        Returns the available run paths from the current directory.
+        Return the available run paths from the current directory.
 
         Returns
         -------
         Dict[str, str]
             Run path as key and run name as value.
+
+        Exceptions
+        ----------
+        FileNotFoundError
         """
         runs = {}
         working_dir = self.get_working_directory()
@@ -89,18 +138,27 @@ def get_available_run_paths(self) -> Dict[str, str]:
 
     def get_selected_run_paths(self) -> List[str]:
         """
-        Returns the selected run paths from the cache.
+        Return the selected run paths from the cache.
 
         Returns
         -------
-        Dict[str, str]
+        List[str]
             Run paths as a list.
+
+        Raises
+        ------
+        AssertionError.
+            If the selected run paths are not a list, an error is thrown.
         """
-        return self.c.get("selected_run_paths")
+        selected_run_paths = self.c.get("selected_run_paths")
+        assert isinstance(
+            selected_run_paths, list
+        ), "The selected run paths of the cache must be a list."
+        return selected_run_paths
 
     def get_selected_run_names(self) -> List[str]:
         """
-        Returns the run names of the selected runs.
+        Return the run names of the selected runs.
 
         Returns
         -------
@@ -111,7 +169,7 @@ def get_selected_run_names(self) -> List[str]:
 
     def get_run_name(self, run_path: Union[Path, str]) -> str:
         """
-        Returns the stem of the path.
+        Return the stem of the path.
 
         Parameters
         ----------
@@ -126,11 +184,30 @@ def get_run_name(self, run_path: Union[Path, str]) -> str:
         return Path(run_path).stem
 
     def get_selected_groups(self) -> Dict[str, List[str]]:
-        return self.c.get("groups")
+        """
+        Get the selected groups.
+
+        Returns
+        -------
+        Dict[str, List[str]]
+            Dictionary with the selected groups.
+
+        Raises
+        ------
+        AssertionError
+            If groups in cache is not a dictionary, an error is thrown.
+        """
+        selected_groups = self.c.get("groups")
+        assert isinstance(
+            selected_groups, dict
+        ), "The groups aquired from the cache must be a dictionary."
+        return selected_groups
 
     def add_run(self, run_path: str) -> bool:
         """
-        Adds a run path to the cache. If run path is already in cache, do nothing.
+        Add a run path to the cache.
+
+        If run path is already in cache, do nothing.
 
         Parameters
         ----------
@@ -153,36 +230,49 @@ def add_run(self, run_path: str) -> bool:
         return True
 
     def remove_run(self, run_path: str) -> None:
-        """Removes a run path from the cache. If run path is not in cache, do nothing.
+        """
+        Remove a run path from the cache.
+
+        If run path is not in cache, do nothing.
 
         Parameters
         ----------
         run_path : str
             Path of a run.
+
+        Raises
+        ------
+        TypeError
+            If `selected_run_paths` or `groups` is None, an error is thrown.
         """
         selected_run_paths = self.c.get("selected_run_paths")
 
+        if selected_run_paths is None:
+            raise TypeError("Selected run paths can not be None.")
+
         if run_path in selected_run_paths:
             selected_run_paths.remove(run_path)
             self.c.set("selected_run_paths", value=selected_run_paths)
 
-            # We have to check the groups here because the removed run_path may
+            # The groups have to be checked here because the removed run_path may
             # still be included
             groups = {}
-            for group_name, run_paths in self.c.get("groups").items():
+            group_it = self.c.get("groups")
+            if group_it is None:
+                raise TypeError("Groups can not be None.")
+            for group_name, run_paths in group_it.items():
                 if run_path in run_paths:
                     run_paths.remove(run_path)
                 groups[group_name] = run_paths
 
             self.c.set("groups", value=groups)
 
-            # We also remove last inputs here
+            # Last inputs are also removed here
             self.c.set("last_inputs", value={})
             self.update_runs()
 
     def update(self) -> None:
-        """Updates the internal run and group instances but only if a hash changed."""
-
+        """Update the internal run and group instances but only if a hash changed."""
         update_required = False
         for run_path in list(self.runs.keys()):
             run = self.runs[run_path]
@@ -201,17 +291,17 @@ def update(self) -> None:
 
     def update_runs(self) -> bool:
         """
-        Loads selected runs and update cache if files changed.
-
-        Raises
-        ------
-        NotValidRunError
-            If directory can not be transformed into a run, an error is thrown.
+        Load selected runs and update cache if files changed.
 
         Returns
         -------
         bool
             True if all selected runs could be loaded, False otherwise.
+
+        Raises
+        ------
+        NotValidRunError
+            If directory can not be transformed into a run, an error is thrown.
         """
         runs: Dict[str, AbstractRun] = {}  # run_path: Run
         success = True
@@ -240,15 +330,26 @@ def update_run(
         self, run_path: str, class_hint: Optional[Type[Run]] = None
     ) -> Optional[AbstractRun]:
         """
-        Loads the run from `self.runs` or creates a new one.
+        Load the run from `self.runs` or create a new one.
+
+        Parameters
+        ----------
+        run_path : str
+            The path of the run.
+        class_hint : Optional[Type[Run]], optional
+            A hint/suggestion of what the Type of the Run is.
+            Default is None.
+
+        Returns
+        -------
+        Optional[AbstractRun]
+            The Run added to the cache.
 
         Raises
         ------
         NotValidRunError
             If directory can not be transformed into a run, an error is thrown.
-
         """
-
         # Try to get run from current runs
         if run_path in self.runs:
             run = self.runs[run_path]
@@ -274,7 +375,8 @@ def update_run(
                 run = run_class.from_path(Path(run_path))
                 t2 = time.perf_counter()
                 self.logger.debug(
-                    f'Run "{Path(run_path).stem}" was successfully loaded (took {round(t2 - t1, 2)} seconds).'
+                    f'Run "{Path(run_path).stem}" was successfully loaded (took {round(t2 - t1, 2)}'
+                    f" seconds)."
                 )
             except KeyboardInterrupt:
                 # Pass KeyboardInterrupt through try-except, so it can actually interrupt.
@@ -295,21 +397,32 @@ def update_run(
 
         return run
 
-    def update_groups(self, groups: Optional[Dict[str, str]] = None) -> None:
+    def update_groups(self, groups: Optional[Dict[str, List[str]]] = None) -> None:
         """
-        Loads chosen groups. If `groups` is passed, it is used to instantiate the groups and
+        Load chosen groups.
+
+        If `groups` is passed, it is used to instantiate the groups and
         saved to the cache. Otherwise, `groups` is loaded from the cache.
 
+        Parameters
+        ----------
+        groups : Optional[Dict[str, str]], optional
+            A dictionary with the groups.
+            Default is None.
+
         Raises
         ------
         NotMergeableError
             If runs can not be merged, an error is thrown.
-
+        TypeError
+            If `groups` is None, an error is thrown.
         """
         instantiated_groups = {}
         if groups is None:
             groups = self.c.get("groups")
-
+        # This check is necessary because groups could still be None
+        if groups is None:
+            raise TypeError("Groups can not be None.")
         # Add grouped runs
         for group_name, run_paths in groups.items():
             runs = []
@@ -336,7 +449,7 @@ def update_groups(self, groups: Optional[Dict[str, str]] = None) -> None:
 
     def get_run(self, run_id: str) -> AbstractRun:
         """
-        Looks inside `self.runs` and `self.groups` and if the run id is found, returns the run.
+        Look inside `self.runs` and `self.groups` and if the run id is found, returns the run.
 
         Parameters
         ----------
@@ -346,7 +459,7 @@ def get_run(self, run_id: str) -> AbstractRun:
         Returns
         -------
         AbstractRun
-            Run
+            Run.
 
         Raises
         ------
@@ -362,7 +475,7 @@ def get_run(self, run_id: str) -> AbstractRun:
 
     def get_groups(self) -> List[Group]:
         """
-        Returns instantiated grouped runs.
+        Return instantiated grouped runs.
 
         Returns
         -------
@@ -372,15 +485,17 @@ def get_groups(self) -> List[Group]:
         self.update()
         return list(self.groups.values())
 
-    def get_runs(self, include_groups=False) -> List[AbstractRun]:
+    def get_runs(self, include_groups: bool = False) -> List[AbstractRun]:
         """
-        Returns the runs from the internal cache. The runs are already loaded and ready to use.
+        Return the runs from the internal cache.
+
+        The runs are already loaded and ready to use.
         Optional, if `include_groups` is set to True, the groups are also included.
 
         Parameters
         ----------
         include_groups : bool, optional
-            Includes the groups, by default False
+            Includes the groups, by default False.
 
         Returns
         -------
diff --git a/deepcave/runs/objective.py b/deepcave/runs/objective.py
index 3f62ce08..7f85cf16 100644
--- a/deepcave/runs/objective.py
+++ b/deepcave/runs/objective.py
@@ -1,3 +1,14 @@
+#  noqa: D400
+"""
+# Objective
+
+This module provides utilities to convert and create objectives.
+It also provides functions for merging and comparing objectives.
+
+## Classes
+    - Objective: Convert and create objectives.
+"""
+
 from typing import Any, Dict, Optional, Union
 
 from dataclasses import dataclass
@@ -9,6 +20,25 @@
 
 @dataclass
 class Objective:
+    """
+    Convert, create and merge objectives.
+
+    Properties
+    ----------
+    lower : Optional[Union[int, float]]
+        The lower bound of the objective.
+    upper : Optional[Union[int, float]]
+        The upper bound of the objective.
+    optimize : str
+        Define whether to optimize lower or upper.
+    lock_lower : bool
+        Whether to lock the lower bound.
+    lock_upper : bool
+        Whether to lock the upper bound.
+    name : str
+        The name of the objective.
+    """
+
     name: str
     lower: Optional[Union[int, float]] = None
     upper: Optional[Union[int, float]] = None
@@ -16,6 +46,8 @@ class Objective:
 
     def __post_init__(self) -> None:
         """
+        Check if bounds should be locked.
+
         Lock the lower bound if lower is not None.
         Lock the upper bound if upper is not None.
 
@@ -43,6 +75,14 @@ def __post_init__(self) -> None:
         self.lock_upper = lock_upper
 
     def to_json(self) -> Dict[str, Any]:
+        """
+        Convert objectives attributes to a dictionary in a JSON friendly format.
+
+        Returns
+        -------
+        Dict[str, Any]
+            A dictionary in a JSON friendly format with the objects attributes.
+        """
         return {
             "name": self.name,
             "lower": self.lower,
@@ -54,6 +94,19 @@ def to_json(self) -> Dict[str, Any]:
 
     @staticmethod
     def from_json(d: Dict[str, Any]) -> "Objective":
+        """
+        Create an objective from a JSON friendly dictionary format.
+
+        Parameters
+        ----------
+        d : Dict[str, Any]
+            A dictionary in a JSON friendly format containing the attributes.
+
+        Returns
+        -------
+        Objective
+            An objective created from the provided data.
+        """
         objective = Objective(
             name=d["name"],
             lower=d["lower"],
@@ -67,6 +120,19 @@ def from_json(d: Dict[str, Any]) -> "Objective":
         return objective
 
     def __eq__(self, other: Any) -> bool:
+        """
+        Compare if two instances are equal based on their attributes.
+
+        Parameters
+        ----------
+        other : Any
+            The other instance to compare.
+
+        Returns
+        -------
+        bool
+            True if equal, else False.
+        """
         attributes = ["name", "lock_lower", "lock_upper", "optimize"]
         for a in attributes:
             if getattr(self, a) != getattr(other, a):
@@ -75,30 +141,59 @@ def __eq__(self, other: Any) -> bool:
         return True
 
     def merge(self, other: Any) -> None:
+        """
+        Merge two Objectives over their attributes.
+
+        Parameters
+        ----------
+        other : Any
+            The other Objective to merge.
+
+        Raises
+        ------
+        NotMergeableError
+            If parts of the two Objectives are not mergeable.
+        ValueError
+            If the lower bound of one Objective is None.
+            If the upper bound of one Objective is None.
+        """
         if not isinstance(other, Objective):
             raise NotMergeableError("Objective can only be merged with another Objective.")
 
         attributes = ["name", "lock_lower", "lock_upper", "optimize"]
         for attribute in attributes:
             if getattr(self, attribute) != getattr(other, attribute):
-                raise NotMergeableError(f"Objective {attribute} is not mergeable.")
+                raise NotMergeableError(f"Objective {attribute} can not be merged.")
+
+        if self.lower is None or other.lower is None:
+            raise ValueError("The lower bound of one Objective is None.")
+        if self.upper is None or other.upper is None:
+            raise ValueError("The upper bound of one Objective is None.")
 
         if self.lock_lower and self.lock_lower == other.lock_lower:
             if self.lower != other.lower:
-                raise NotMergeableError(f"Objective {other.name}'s lower bound is not mergeable.")
+                raise NotMergeableError(f"Objective {other.name}'s lower bound can not be merged.")
         else:
-            if self.lower > other.lower:  # type: ignore
+            if self.lower > other.lower:
                 self.lower = other.lower
 
         if self.lock_upper and self.lock_upper == other.lock_upper:
             if self.upper != other.upper:
-                raise NotMergeableError(f"Objective {other.name}'s upper bound is not mergeable.")
+                raise NotMergeableError(f"Objective {other.name}'s upper bound can not be merged.")
         else:
-            if self.upper < other.upper:  # type: ignore
+            if self.upper < other.upper:
                 self.upper = other.upper
 
-    def get_worst_value(self) -> float:
+    def get_worst_value(self) -> Optional[Union[int, float]]:
+        """
+        Get the worst value based on the optimization setting.
+
+        Returns
+        -------
+        float
+            The worst value based on the optimization setting.
+        """
         if self.optimize == "lower":
-            return self.upper  # type: ignore
+            return self.upper
         else:
-            return self.lower  # type: ignore
+            return self.lower
diff --git a/deepcave/runs/recorder.py b/deepcave/runs/recorder.py
index e64bddaa..767373de 100644
--- a/deepcave/runs/recorder.py
+++ b/deepcave/runs/recorder.py
@@ -1,4 +1,14 @@
-from typing import Optional, Union
+#  noqa: D400
+"""
+# Recorder
+
+This module provides utilities to record information.
+
+## Classes
+    - Recorder: Define a Recorder for recording information.
+"""
+
+from typing import Any, Dict, List, Optional, Tuple, Union
 
 import time
 from pathlib import Path
@@ -6,65 +16,120 @@
 import ConfigSpace
 import numpy as np
 from ConfigSpace import Configuration
+from typing_extensions import Self
 
 from deepcave.runs import Status
 from deepcave.runs.converters.deepcave import DeepCAVERun
-from deepcave.runs.run import Run
+from deepcave.runs.objective import Objective
 
 
 class Recorder:
+    """
+    Define a Recorder for recording information.
+
+    Properties
+    ----------
+    path : Path
+        The path to the recorded information.
+    last_trial_id : tuple[Any, Optional[float]]
+        The last id containing the configuration and the budget.
+    start_time : float
+        The current time in seconds since the epoch.
+    start_times : Dict[Any, Any]
+        A dictionary containing the start times with their id as key.
+    models : Dict[Any, Any|
+        The models used with their id as key.
+    origins : Dict[Any, Any]
+        The origins with their id as key.
+    additionals : Dict[Any, Any]
+        Additional information with the id as key.
+    run : DeepCAVERun
+        The deepcave run container.
+    """
+
     def __init__(
         self,
         configspace: ConfigSpace.ConfigurationSpace,
-        objectives=None,
-        meta=None,
-        save_path="logs",
-        prefix="run",
-        overwrite=False,
+        objectives: Optional[List[Objective]] = None,
+        meta: Optional[Dict[str, Any]] = None,
+        save_path: str = "logs",
+        prefix: str = "run",
+        overwrite: bool = False,
     ):
         """
         All objectives follow the scheme the lower the better.
-        If file
-
-        Parameters:
-            save_path (str):
-            configspace (ConfigSpace):
-            objectives (list of Objective):
-            prefix: Name of the trial. If not given, trial_x will be used.
-            overwrite: Uses the prefix as name and overwrites the file.
+
+        Parameters
+        ----------
+        save_path : str, otpional
+            The path to the recording.
+            Default is "logs".
+        configspace : ConfigSpace.ConfigurationSpace
+            The configuration space.
+        objectives Optional[List[Objective]], optional
+            The objectives of the run.
+            Default is None.
+        prefix ; str, optional
+            Name of the trial. If not given, trial_x will be used.
+            Default is "run".
+        overwrite : bool, optional
+            Uses the prefix as name and overwrites the file.
+            Default is False.
         """
         if objectives is None:
             objectives = []
         if meta is None:
             meta = {}
 
-        self.path: Path = None
+        self.path: Path
         self._set_path(save_path, prefix, overwrite)
 
         # Set variables
-        self.last_trial_id = None
+        self.last_trial_id: Optional[
+            Tuple[Union[Dict[Any, Any], Configuration], Optional[float]]
+        ] = None
         self.start_time = time.time()
-        self.start_times = {}
-        self.models = {}
-        self.origins = {}
-        self.additionals = {}
+        self.start_times: Dict[
+            Tuple[Union[Dict[Any, Any], Configuration], Optional[float]], float
+        ] = {}
+        self.models: Dict[
+            Tuple[Union[Dict[Any, Any], Configuration], Optional[float]], Optional[Any]
+        ] = {}
+        self.origins: Dict[
+            Tuple[Union[Dict[Any, Any], Configuration], Optional[float]], Optional[str]
+        ] = {}
+        self.additionals: Dict[
+            Tuple[Union[Dict[Any, Any], Configuration], Optional[float]], Dict[Any, Any]
+        ] = {}
 
         # Define trials container
         self.run = DeepCAVERun(
             self.path.stem, configspace=configspace, objectives=objectives, meta=meta
         )
 
-    def __enter__(self):
+    def __enter__(self) -> Self:
         return self
 
-    def __exit__(self, type, value, traceback):
+    def __exit__(self, type, value, traceback) -> None:  # type: ignore
         pass
 
-    def _set_path(self, path: Union[str, Path], prefix="run", overwrite=False):
-        """
-        Identifies the latest run and sets the path with increased id.
+    def _set_path(
+        self, path: Union[str, Path], prefix: str = "run", overwrite: bool = False
+    ) -> None:
         """
+        Set the path.
 
+        Parameters
+        ----------
+        path : Union[str, Path]
+            The path to set.
+        prefix, optional
+            The prefix for the path.
+            Default is "run".
+        overwrite, optional
+            To determine whether to overwrite an existing folder.
+            Default is False.
+        """
         # Make sure the word is interpreted as folder
         path = Path(path)
         path.mkdir(parents=True, exist_ok=True)
@@ -76,9 +141,9 @@ def _set_path(self, path: Union[str, Path], prefix="run", overwrite=False):
                     continue
                 idx = file.name.split("_")[-1]
                 if idx.isnumeric():
-                    idx = int(idx)
-                    if idx > new_idx:
-                        new_idx = idx
+                    idx_int = int(idx)
+                    if idx_int > new_idx:
+                        new_idx = idx_int
 
             # And increase the id
             new_idx += 1
@@ -88,17 +153,40 @@ def _set_path(self, path: Union[str, Path], prefix="run", overwrite=False):
 
     def start(
         self,
-        config: Union[dict, Configuration],
+        config: Configuration,
         budget: Optional[float] = None,
-        model=None,
-        origin=None,
+        model: Optional[Any] = None,
+        origin: Optional[str] = None,
         additional: Optional[dict] = None,
         start_time: Optional[float] = None,
-    ):
+    ) -> None:
+        """
+        Start recording the information.
+
+        Parameters
+        ----------
+        config : Configuration
+            Holds the configuration settings.
+        budget : Optional[float], optional
+            The budget.
+            Default is None.
+        model : Optional[Any], optional
+            The model used.
+            Default is None.
+        origin : Optional[str], optional
+            The origin.
+            Default is None.
+        additional : Optional[dict], optional
+            Additional information.
+            Default is None.
+        start_time : Optional[float], optional
+            The start time.
+            Default is None.
+        """
         if additional is None:
             additional = {}
 
-        id = (config, budget)
+        id: Tuple[Union[Dict[Any, Any], Configuration], Optional[float]] = (config, budget)
 
         if start_time is None:
             start_time = time.time() - self.start_time
@@ -115,14 +203,42 @@ def end(
         self,
         costs: float = np.inf,
         status: Status = Status.SUCCESS,
-        config: Union[dict, Configuration] = None,
-        budget: float = np.inf,
+        config: Optional[Union[dict, Configuration]] = None,
+        budget: Optional[float] = np.inf,
         additional: Optional[dict] = None,
         end_time: Optional[float] = None,
-    ):
+    ) -> None:
         """
-        In case of multi-processing, config+budget should be passed as otherwise
-        it can't be matched correctly.
+        End the recording and add it to the trial history.
+
+        In case of multi-processing, config+budget should be passed.
+        If it can't be passed, it can't be matched correctly.
+
+        Parameters
+        ----------
+        costs : float, optional
+            The costs.
+            Default is np.inf.
+        status : Status, optional
+            The status.
+            Default is Status.Success.
+        config : Union[dict, Configuration], optional
+            The configuration.
+            Default is None.
+        budget : float, optional
+            The budget.
+            Default is np.inf.
+        additional : Optional[dict], optional
+            Additional information.
+            Default is None.
+        end_time : Optional[float], optional
+            The end time.
+            Default is None.
+
+        Raises
+        ------
+        AssertionError
+            If no trial was started yet.
         """
         if additional is None:
             additional = {}
@@ -130,6 +246,7 @@ def end(
         if config is not None:
             id = (config, budget)
         else:
+            assert self.last_trial_id is not None, "No trial started yet."
             id = self.last_trial_id
             config, budget = id[0], id[1]
 
@@ -141,6 +258,8 @@ def end(
         if end_time is None:
             end_time = time.time() - self.start_time
 
+        assert budget is not None
+
         # Add to trial history
         self.run.add(
             costs=costs,
diff --git a/deepcave/runs/run.py b/deepcave/runs/run.py
index 436bb130..4dd5c8a3 100644
--- a/deepcave/runs/run.py
+++ b/deepcave/runs/run.py
@@ -1,3 +1,13 @@
+#  noqa: D400
+"""
+# Run
+
+This module provides utilities to create a new run and get its attributes.
+
+## Classes
+    - Run: Create a new run.
+"""
+
 from abc import ABC, abstractmethod
 from typing import Any, Dict, List, Optional, Union
 
@@ -18,26 +28,36 @@
 
 class Run(AbstractRun, ABC):
     """
-    Creates a new run.
+    Create a new run and get its attributes.
+
     If path is given, runs are loaded from the path.
 
-    Parameters
+    Properties
     ----------
-    name : str
-        Name of the run.
-    configspace : ConfigSpace, optional
-        Configuration space of the run. Should be None if `path` is used. By default None.
-    objectives : Union[Objective, List[Objective]], optional
-        Objectives of the run. Should be None if `path` is used. By default None
-    meta : Dict[str, Any], optional
-        Meta data of the run. Should be None if `path` is used. By default None.
-    path : Optional[Union[str, Path]], optional
-        If a path is specified, the run is loaded from there. By default None.
-
-    Raises
-    ------
-    RuntimeError
-        If no configuration space is provided or found.
+    configspace : ConfigurationSpace
+        The configuration space of the run.
+    path : Optional[Union[str, Path]]
+        The path of a run to be loaded.
+    meta : Dict[str, Any]
+        Contains serialized objectives and budgets.
+    prefix : str
+        The prefix for the id.
+    meta_fn : Path
+        The path to the meta data.
+    configspace_fn : Path
+        The path to the configuration space file.
+    configs_fn : Path
+        The path to the configurations file.
+    origins_fn : Path
+        The path to the origins file.
+    history_fn : Path
+        The path to the history file.
+    models_dir : Path
+        The path to the models directory.
+    configs : Dict[int, Configuration]
+        Containing the configurations.
+    models : Dict[int, Optional[Union[str, "torch.nn.Module"]]]
+        Contains the models.
     """
 
     prefix = "run"
@@ -46,10 +66,10 @@ class Run(AbstractRun, ABC):
     def __init__(
         self,
         name: str,
-        configspace: ConfigSpace = None,
-        objectives: Union[Objective, List[Objective]] = None,
-        meta: Dict[str, Any] = None,
-        path: Optional[Union[str, Path]] = None,
+        configspace: Optional[ConfigSpace.ConfigurationSpace] = None,
+        objectives: Optional[Union[Objective, List[Objective]]] = None,
+        meta: Optional[Dict[str, Any]] = None,
+        path: Optional[Path] = None,
     ) -> None:
         super(Run, self).__init__(name)
 
@@ -60,7 +80,8 @@ def __init__(
 
         # Reset and load configspace/path
         self.reset()
-        self.configspace = configspace
+        if configspace is not None:
+            self.configspace = configspace
         self.path = path
         if self.path is not None:
             self.load()
@@ -89,23 +110,53 @@ def __init__(
     def from_path(cls, path: Path) -> "Run":
         """
         Based on a path, return a new Run object.
+
+        Parameters
+        ----------
+        path : Path
+            The path to get the run from.
+
+        Returns
+        -------
+        "Run"
+            The run loaded from the path.
         """
         pass
 
     @property
     def id(self) -> str:
+        """
+        Get a hash as id.
+
+        Returns
+        -------
+        str
+            The hashed id.
+        """
         return string_to_hash(f"{self.prefix}:{self.path}")
 
     @property
     def path(self) -> Optional[Path]:
+        """
+        Return the path of the run if it exists.
+
+        Returns
+        -------
+        Optional[Path]
+            The path of the run.
+        """
         return self._path
 
     @path.setter
-    def path(self, value: Optional[Union[str, Path]]):
-        """
-        If path is changed, also change the filenames of all created files.
+    def path(self, value: Optional[Union[str, Path]]) -> None:
         """
+        Set the paths of the run and the JSON files.
 
+        Parameters
+        ----------
+        value : Optional[Union[str, Path]]
+            The path for the directory.
+        """
         if value is None:
             self._path = None
             return
@@ -122,7 +173,7 @@ def path(self, value: Optional[Union[str, Path]]):
 
     def exists(self) -> bool:
         """
-        Checks if the run exists based on the internal path.
+        Check if the run exists based on the internal path.
 
         Returns
         -------
@@ -151,12 +202,13 @@ def add(
         start_time: float = 0.0,
         end_time: float = 0.0,
         status: Status = Status.SUCCESS,
-        origin: str = None,
-        model: Union[str, "torch.nn.Module"] = None,  # type: ignore
+        origin: Optional[str] = None,
+        model: Union[str, "torch.nn.Module"] = None,  # type: ignore # noqa: F821
         additional: Optional[Dict] = None,
     ) -> None:
         """
-        Adds a trial to the run.
+        Add a trial to the run.
+
         If combination of config and budget already exists, it will be overwritten.
         Not successful runs are added with `None` costs.
 
@@ -167,17 +219,17 @@ def add(
         config : Union[Dict, Configuration]
             The corresponding configuration.
         start_time : float, optional
-            Start time. By default 0.0
+            Start time. By default, 0.0
         end_time : float, optional
-            End time. By default 0.0
+            End time. By default, 0.0
         status : Status, optional
-            Status of the trial. By default Status.SUCCESS
+            Status of the trial. By default, Status.SUCCESS
         origin : str, optional
-            Origin of the trial. By default None
-        model : Union[str, &quot;torch.nn.Module&quot;], optional
-            Model of the trial. By default None
+            Origin of the trial. By default, None
+        model : Union[str, "torch.nn.Module"], optional
+            Model of the trial. By default, None
         additional : Optional[Dict], optional
-            Additional information of the trial. By default None.
+            Additional information of the trial. By default, None.
             Following information is used by DeepCAVE:
             * traceback
 
@@ -185,6 +237,8 @@ def add(
         ------
         RuntimeError
             If number of costs does not match number of objectives.
+        ValueError
+            If config id is None.
         """
         if additional is None:
             additional = {}
@@ -208,12 +262,12 @@ def add(
             # If cost is none, replace it later with the highest cost
             if cost is not None:
                 # Update bounds here
-                if not objective.lock_lower:
-                    if cost < objective.lower:  # type: ignore
+                if not objective.lock_lower and objective.lower is not None:
+                    if cost < objective.lower:
                         objective.lower = cost
 
-                if not objective.lock_upper:
-                    if cost > objective.upper:  # type: ignore
+                if not objective.lock_upper and objective.upper is not None:
+                    if cost > objective.upper:
                         objective.upper = cost
 
             updated_objectives += [objective.to_json()]
@@ -224,11 +278,14 @@ def add(
             config = config.get_dictionary()
 
         if config not in self.configs.values():
-            config_id = len(self.configs)
-            self.configs[config_id] = config
-            self.origins[config_id] = origin
+            config_id_len = len(self.configs)
+            self.configs[config_id_len] = config
+            self.origins[config_id_len] = origin
 
         config_id = self.get_config_id(config)
+        if config_id is None:
+            raise ValueError("Config id is None.")
+
         trial = Trial(
             config_id=config_id,
             budget=budget,
@@ -255,13 +312,26 @@ def add(
         self._update_highest_budget(config_id, budget, status)
 
         # Update models
-        # Problem: We don't want to have the model in the cache.
-        # Therefore, we first keep the model as it is,
+        # Problem: The model should not be in the cache.
+        # Therefore, first the model is kept as it is,
         # but remove it from the dict and save it to the disk later on.
         if model is not None:
             self.models[config_id] = model
 
-    def save(self, path: Optional[Union[str, Path]] = None):
+    def save(self, path: Union[str, Path]) -> None:
+        """
+        Save the run and its information.
+
+        Parameters
+        ----------
+        path : Optional[Union[str, Path]]
+            The path in which to save the trials.
+
+        Raises
+        ------
+        RuntimeError
+            If the path is not specified.
+        """
         if path is None:
             raise RuntimeError("Please specify a path to save the trials.")
 
@@ -286,7 +356,7 @@ def save(self, path: Optional[Union[str, Path]] = None):
 
         # Models
         if len(self.models) > 0:
-            # We import torch here because we don't want to have it as requirement.
+            # torch is imported here, because it is not wanted as requirement.
             import torch
 
             # Iterate over models and save them if they are a module.
@@ -305,6 +375,21 @@ def save(self, path: Optional[Union[str, Path]] = None):
                 del self.models[config_id]
 
     def load(self, path: Optional[Union[str, Path]] = None) -> None:
+        """
+        Load the run.
+
+        Parameters
+        ----------
+        path : Optional[Union[str, Path]], optional
+            The path where to load the run from.
+            Default is None.
+
+        Raises
+        ------
+        RuntimeError
+            If the path is None.
+            If the trials were not found.
+        """
         self.reset()
 
         if path is None and self.path is None:
diff --git a/deepcave/runs/status.py b/deepcave/runs/status.py
index b67d1718..c6d7d2d3 100644
--- a/deepcave/runs/status.py
+++ b/deepcave/runs/status.py
@@ -1,7 +1,38 @@
+# noqa: D400
+"""
+# Status
+
+This module provides the information about the status of a run.
+
+A utility to convert a string text to a simpler, lower case text format is provided.
+
+## Classes
+    - Status: Represent the status of a run as an Enum.
+
+## Constants
+    SUCCESS: int
+    TIMEOUT: int
+    MEMORYOUT: int
+    CRASHED: int
+    ABORTED: int
+    NOT_EVALUATED: int
+"""
+
 from enum import IntEnum
 
 
 class Status(IntEnum):
+    """
+    Represent the status of a run as an Enum.
+
+    A utility to convert a string text to a simpler, lower case text format is provided.
+
+    Properties
+    ----------
+    name : str
+        The status name.
+    """
+
     SUCCESS = 1
     TIMEOUT = 2
     MEMORYOUT = 3
@@ -10,4 +41,12 @@ class Status(IntEnum):
     NOT_EVALUATED = 6
 
     def to_text(self) -> str:
+        """
+        Convert name to simpler, lower case text format.
+
+        Returns
+        -------
+        str
+            The converted name in lower case with spaces added
+        """
         return self.name.lower().replace("_", " ")
diff --git a/deepcave/runs/trial.py b/deepcave/runs/trial.py
index 2647c29a..2bb6b703 100644
--- a/deepcave/runs/trial.py
+++ b/deepcave/runs/trial.py
@@ -1,3 +1,14 @@
+#  noqa: D400
+"""
+# Trial
+
+This module provides the trial object.
+Utilities for handling the trial are provided.
+
+## Classes
+    - Trial: This class provides the trial object itself and multiple handling utilities.
+"""
+
 from typing import Any, Dict, List, Tuple, Union
 
 from dataclasses import dataclass
@@ -7,6 +18,27 @@
 
 @dataclass
 class Trial:
+    """
+    Provide the trial object itself and multiple handling utilities.
+
+    Properties
+    ----------
+    status : Status
+        The status of the trial.
+    config_id : int
+        The identificator of the configuration.
+    budget : Union[int, float]
+        The budget for the trial.
+    costs : List[float]
+        A list of the costs of the trial.
+    start_time : float
+        The start time of the trial.
+    end_time : float
+        The end time of the trial.
+    additional : Dict[str, Any]
+        A dictionary of additional information of the trial.
+    """
+
     config_id: int
     budget: Union[int, float]
     costs: List[float]
@@ -16,17 +48,34 @@ class Trial:
     additional: Dict[str, Any]
 
     def __post_init__(self) -> None:
+        """Set the status."""
         if isinstance(self.status, int):
             self.status = Status(self.status)
 
         assert isinstance(self.status, Status)
 
-    def get_key(self) -> Tuple[int, int]:
+    def get_key(self) -> Tuple[int, Union[int, float, None]]:
+        """
+        Generate a key based on the configuration id and the budget.
+
+        Returns
+        -------
+        Tupel[int, int]
+            A Tuple representing a unique key based on the configuration id and the budget.
+        """
         from deepcave.runs import AbstractRun
 
         return AbstractRun.get_trial_key(self.config_id, self.budget)
 
     def to_json(self) -> List[Any]:
+        """
+        Convert trial object to JSON-compatible representation.
+
+        Returns
+        -------
+        List[Any]
+            A JSON-compatible list with the Trials attributes.
+        """
         return [
             self.config_id,
             self.budget,
diff --git a/deepcave/server.py b/deepcave/server.py
index de786638..e0b56d7a 100644
--- a/deepcave/server.py
+++ b/deepcave/server.py
@@ -1,3 +1,10 @@
+#  noqa: D400
+"""
+# Server
+
+This module starts the server.
+"""
+
 from deepcave import app, config
 from deepcave.layouts.main import MainLayout
 
diff --git a/deepcave/utils/__init__.py b/deepcave/utils/__init__.py
index e69de29b..49a1ec8e 100644
--- a/deepcave/utils/__init__.py
+++ b/deepcave/utils/__init__.py
@@ -0,0 +1,43 @@
+#  noqa: D400
+"""
+# utils
+
+## Modules
+----------
+cache
+    This module provides utilities to handle the cache.
+cast
+    This module provides a utilitiy to convert any value to an int if possible.
+compression
+    This module provides utilities for serializing and deserializing a dataframe from/to a string.
+configs
+    This module is for parsing the configurations from a filename.
+configspace
+    This module samples random as well as border configurations.
+dash
+    This module provides utilities to return and flash alerts.
+data_structure
+    This module can be used for updating one dictionary with another dictionary inplace.
+docs
+    This module provides a function to convert reStructuredText (RST) to MarkDown (MD).
+files
+    This module provides a utility to create directories from a filename.
+hash
+    This module provides utilities to convert strings and files to hash.
+layout
+    This module provides utilities to customize the layout of the Dash.
+logs
+    This module sets up and gets the logging configuration.
+notification
+    This module provides utilities for creating, updating and resetting notifications.
+run_caches
+    This module defines a class for holding the caches for selected runs.
+styled_plot
+    This module provides utilities to customize functions from the matplotlib.
+styled_plotty
+    This module provides utilities for styling and customizing different plots with plotly.
+url
+    This module creates and parses an URL according to its input information.
+util
+    This module provides utilities for string generation and shortening.
+"""
diff --git a/deepcave/utils/cache.py b/deepcave/utils/cache.py
index a88edd96..88b33d1c 100644
--- a/deepcave/utils/cache.py
+++ b/deepcave/utils/cache.py
@@ -1,7 +1,18 @@
+#  noqa: D400
+"""
+# Cache
+
+This module provides utilities to handle the cache.
+
+This includes reading, writing, set and get utilities, as well as clearing the cache.
+The cache handles a json file.
+
+## Classes
+    - Cache: Cache handles a json file.
+"""
 from typing import Any, Dict, Optional
 
 import json
-import logging
 from copy import deepcopy
 from pathlib import Path
 
@@ -14,14 +25,15 @@
 
 class Cache:
     """
-    Cache handles a json file. Decided not to use flask_caching
-    since code is easier to change to our needs.
+    Cache handles a json file.
+
+    Decided not to use flask_caching since code is easier to change to our needs.
     """
 
     def __init__(
         self,
         filename: Optional[Path] = None,
-        defaults: Dict = None,
+        defaults: Optional[Dict] = None,
         debug: bool = False,
         write_file: bool = True,
     ) -> None:
@@ -36,6 +48,17 @@ def __init__(
         self._setup(filename, write_file)
 
     def _setup(self, filename: Optional[Path], write_file: bool = True) -> None:
+        """
+        Initialize the setup.
+
+        Parameters
+        ----------
+        filename : Optional[Path]
+            The filename to be set.
+        write_file : bool, optional
+            Define whether do write the content of cache into a file.
+            Default is True.
+        """
         self._data = {}
         self._filename = filename
 
@@ -45,7 +68,7 @@ def _setup(self, filename: Optional[Path], write_file: bool = True) -> None:
             self.read()
 
     def read(self) -> None:
-        """Reads content from a file and load into cache as dictionary."""
+        """Read content from a file and load into cache as dictionary."""
         if self._filename is None or not self._filename.exists():
             return
 
@@ -65,11 +88,27 @@ def write(self) -> None:
             else:
                 json.dump(self._data, f, separators=JSON_DENSE_SEPARATORS)
 
-    def set(self, *keys, value: Any, write_file: bool = True) -> None:
+    def set(self, *keys: str, value: Any, write_file: bool = True) -> None:
         """
         Set a value from a chain of keys.
+
         E.g. set("a", "b", "c", value=4) creates following dictionary:
         {"a": {"b": {"c": 4}}}
+
+        Parameters
+        ----------
+        *keys : str
+            The keys to set the value from.
+        value : Any
+            The value to be set.
+        write_file : bool, optional
+            Whether to write the constant of the cache into a file.
+            Default is True.
+
+        Raises
+        ------
+        RuntimeError
+            If the type of the key is not a string.
         """
         name = "(empty)"
         if self._filename is not None:
@@ -93,14 +132,36 @@ def set(self, *keys, value: Any, write_file: bool = True) -> None:
             self.write()
 
     def set_dict(self, d: Dict, write_file: bool = True) -> None:
-        """Updates cache to a specific value"""
+        """
+        Update cache to a specific value.
+
+        Parameters
+        ----------
+        d : Dict
+            The dictionary to be set.
+        write_file : bool, optional
+            Whether to write the constant of the cache into a file.
+            Default is True.
+        """
         self._data.update(d)
 
         if write_file:
             self.write()
 
-    def get(self, *keys) -> Optional[Any]:
-        """Retrieve value for a specific key"""
+    def get(self, *keys: str) -> Optional[Any]:
+        """
+        Retrieve value for a specific key.
+
+        Parameters
+        ----------
+        *keys : str
+            The key to retrieve the value from.
+
+        Returns
+        -------
+        Optional[Any]
+            The value of the key.
+        """
         d = deepcopy(self._data)
         for key in keys:
             if key not in d:
@@ -110,8 +171,20 @@ def get(self, *keys) -> Optional[Any]:
 
         return d
 
-    def has(self, *keys) -> bool:
-        """Check whether cache has specific key"""
+    def has(self, *keys: str) -> bool:
+        """
+        Check whether cache has specific key.
+
+        Parameters
+        ----------
+        *keys : str
+            The key to check for.
+
+        Returns
+        -------
+        bool
+            Whether cache has specific key.
+        """
         d = self._data
         for key in keys:
             if key not in d:
@@ -121,10 +194,19 @@ def has(self, *keys) -> bool:
         return True
 
     def clear(self, write_file: bool = True) -> None:
-        """Clear all cache and reset to defaults"""
+        """
+        Clear all cache and reset to defaults.
+
+        Parameters
+        ----------
+        write_file : bool, optional
+            Whether to write the constant of the cache into a file.
+            Default is True.
+        """
         filename = self._filename
 
-        if filename is not None and filename.exists():
-            self._filename.unlink()
+        if self._filename is not None:
+            if self._filename.exists():
+                self._filename.unlink()
 
         self._setup(filename, write_file=write_file)
diff --git a/deepcave/utils/cast.py b/deepcave/utils/cast.py
index f1caea1c..61f13235 100644
--- a/deepcave/utils/cast.py
+++ b/deepcave/utils/cast.py
@@ -1,7 +1,27 @@
-from typing import Any
+#  noqa: D400
+"""
+# Cast
 
+This module provides a utility to convert any value to an int if possible.
+"""
+from typing import Any, Optional
 
-def optional_int(value: Any):
+
+def optional_int(value: Any) -> Optional[int]:
+    """
+    Convert a value to an int if possible.
+
+    Parameters
+    ----------
+    value : Any
+        The value to be turned into an int.
+
+    Returns
+    -------
+    Optional[int]
+        The converted int value.
+        If not possible, return None.
+    """
     if value is None:
         return None
 
diff --git a/deepcave/utils/compression.py b/deepcave/utils/compression.py
index 9200de1e..33110152 100644
--- a/deepcave/utils/compression.py
+++ b/deepcave/utils/compression.py
@@ -1,4 +1,19 @@
-from typing import Dict, List, TypeVar, Union
+#  noqa: D400
+"""
+# Compression
+
+This module provides utilities for serializing and deserializing a dataframe from/to a string.
+
+## Classes
+    - Encoder: This class defines a custom JSON Encoder.
+
+## Constants
+    - JSON_DENSE_SEPARATORS: Tuple(str, str)
+    - JSON_DEFAULT_SEPARATORS: Tuple(str, str)
+    - TYPE: TypeVar
+"""
+
+from typing import Any, Dict, List, TypeVar, Union
 
 import json
 
@@ -13,10 +28,35 @@
 def serialize(data: Union[Dict, List, pd.DataFrame]) -> str:
     """
     Serialize a dataframe to a string.
+
+    Parameters
+    ----------
+    data : Union[Dict, List, pd.DataFrame]
+        The dataframe to be serialized.
+
+    Returns
+    -------
+    str
+        The serialized object as a JSON formatted string.
     """
 
     class Encoder(json.JSONEncoder):
-        def default(self, obj):
+        """Define a custom JSON Encoder."""
+
+        def default(self, obj: Any) -> Any:
+            """
+            Return the object as list if np.ndarray.
+
+            Parameters
+            ----------
+            obj : Any
+                The object to be converted.
+
+            Returns
+            -------
+            Any
+                The converted object.
+            """
             if isinstance(obj, np.ndarray):
                 return obj.tolist()
             return json.JSONEncoder.default(self, obj)
@@ -31,6 +71,19 @@ def default(self, obj):
 def deserialize(string: str, dtype: TYPE = pd.DataFrame) -> TYPE:
     """
     Deserialize a dataframe from a string.
+
+    Parameters
+    ----------
+    string : str
+        The string to be deserialized.
+    dtype : TYPE, optional
+        The type of the object.
+        Default is pd.DataFrame.
+
+    Returns
+    -------
+    TYPE
+        The deserialized object.
     """
     if dtype == pd.DataFrame:
         return pd.DataFrame.from_dict(json.loads(string))
diff --git a/deepcave/utils/configs.py b/deepcave/utils/configs.py
index 069b8b06..e5a50f81 100644
--- a/deepcave/utils/configs.py
+++ b/deepcave/utils/configs.py
@@ -1,3 +1,10 @@
+#  noqa: D400
+"""
+# Configs
+
+This module provides a utility for parsing the configurations from a filename.
+"""
+
 from typing import Optional
 
 import importlib
@@ -10,7 +17,9 @@
 
 def parse_config(filename: Optional[str] = None) -> Config:
     """
-    Parses the config given the filename. Both relative and absolute paths are possible.
+    Parse the config given the filename.
+
+    Both relative and absolute paths are possible.
 
     Parameters
     ----------
@@ -49,7 +58,7 @@ def parse_config(filename: Optional[str] = None) -> Config:
             script_dir = path.stem  # That's the path without the script name
             module_name = p.stem  # That's the script name without the extension
 
-            # Now we add to sys path
+            # Now it is added to sys path
             sys.path.append(str(path))
 
             module = importlib.import_module(f"{script_dir}.{module_name}")
diff --git a/deepcave/utils/configspace.py b/deepcave/utils/configspace.py
index 096cea69..cfbc0560 100644
--- a/deepcave/utils/configspace.py
+++ b/deepcave/utils/configspace.py
@@ -1,7 +1,11 @@
-from typing import Generator, Iterator, List, Optional
+#  noqa: D400
+"""
+# ConfigSpace
 
-import random
-from itertools import islice, product
+This module samples random as well as border configurations.
+"""
+
+from typing import Iterator, Optional
 
 import numpy as np
 from ConfigSpace.configuration_space import Configuration, ConfigurationSpace
@@ -16,12 +20,12 @@
 
 def sample_border_config(configspace: ConfigurationSpace) -> Iterator[Configuration]:
     """
-    Generates border configurations from the configuration space.
+    Generate border configurations from the configuration space.
 
     Parameters
     ----------
     configspace : ConfigurationSpace
-        The configspace from which the hyperparameters are drawn from.
+        The configuration space from which the hyperparameters are drawn from.
 
     Yields
     ------
@@ -50,19 +54,19 @@ def sample_border_config(configspace: ConfigurationSpace) -> Iterator[Configurat
             config[hp_name] = value
 
         try:
-            config = deactivate_inactive_hyperparameters(config, configspace)
-            config.is_valid_configuration()
+            configuration = deactivate_inactive_hyperparameters(config, configspace)
+            configuration.is_valid_configuration()
         except Exception:
             continue
 
-        yield config
+        yield configuration
 
 
 def sample_random_config(
     configspace: ConfigurationSpace, d: Optional[int] = None
 ) -> Iterator[Configuration]:
     """
-    Generates random configurations from the configuration space.
+    Generate random configurations from the configuration space.
 
     Parameters
     ----------
@@ -70,8 +74,8 @@ def sample_random_config(
         The configspace from which the hyperparameters are drawn from.
     d : Optional[int], optional
         The possible hyperparameter values can be reduced by this argument as the range gets
-        discretized. For example, an integer or float hyperparameter has only four possible values
-        if d=4. By default None (no discretization is done).
+        discretized. For example, an integer or float hyperparameter has only four possible
+        values if d=4. By default, None (no discretization is done).
 
     Yields
     ------
@@ -87,7 +91,7 @@ def sample_random_config(
     rng = np.random.RandomState(0)
 
     while True:
-        config = {}
+        config_dict = {}
 
         # Iterates over the hyperparameters to get considered values
         for hp_name, hp in zip(
@@ -117,12 +121,12 @@ def sample_random_config(
 
             # Get a random choice
             value = rng.choice(values)
-            config[hp_name] = value
+            config_dict[hp_name] = value
 
         try:
-            config = deactivate_inactive_hyperparameters(config, configspace)
-            config.is_valid_configuration()
+            configuration = deactivate_inactive_hyperparameters(config_dict, configspace)
+            configuration.is_valid_configuration()
         except Exception:
             continue
 
-        yield config
+        yield configuration
diff --git a/deepcave/utils/dash.py b/deepcave/utils/dash.py
index 7aa9cb4a..1a22b5bd 100644
--- a/deepcave/utils/dash.py
+++ b/deepcave/utils/dash.py
@@ -1,11 +1,30 @@
+#  noqa: D400
+"""
+# Dash
+
+This module provides utilities to return and flash alerts.
+"""
+
 import dash_bootstrap_components as dbc
 
 
-def flash(message: str, category: str = "info"):
+def flash(message: str, category: str = "info") -> dbc.Alert:
     """
     Flask style flash-message with Alerts.
-    """
 
+    Parameters
+    ----------
+    message : str
+        The message to be displayed.
+    category : str, optional
+        The category of the alert.
+        Default is "info".
+
+    Returns
+    -------
+    dbc.Alert
+        The alert object.
+    """
     return dbc.Alert(
         message,
         id=f"alert_{hash(message)}",
@@ -16,5 +35,18 @@ def flash(message: str, category: str = "info"):
     )
 
 
-def alert(message: str):
+def alert(message: str) -> dbc.Alert:
+    """
+    Return an alert message.
+
+    Parameters
+    ----------
+    message : str
+        The message of the alert.
+
+    Returns
+    -------
+    dbc.Alert
+        The alert object.
+    """
     return flash(message, "danger")
diff --git a/deepcave/utils/data_structures.py b/deepcave/utils/data_structures.py
index 9134354a..c9427e93 100644
--- a/deepcave/utils/data_structures.py
+++ b/deepcave/utils/data_structures.py
@@ -1,9 +1,16 @@
+#  noqa: D400
+"""
+# Data Structures
+
+This module can be used for updating one dictionary with another dictionary inplace.
+"""
+
 from typing import Dict
 
 
 def update_dict(a: Dict[str, Dict], b: Dict[str, Dict]) -> None:
     """
-    Updates dictionary a with dictionary b inplace.
+    Update dictionary a with dictionary b inplace.
 
     Parameters
     ----------
diff --git a/deepcave/utils/docs.py b/deepcave/utils/docs.py
index b6c37f3f..0aeaadb1 100644
--- a/deepcave/utils/docs.py
+++ b/deepcave/utils/docs.py
@@ -1,3 +1,10 @@
+#  noqa: D400
+"""
+# Docs
+
+This module provides a function to convert reStructuredText (RST) to MarkDown (MD).
+"""
+
 from __future__ import annotations
 
 import re
@@ -5,6 +12,19 @@
 
 
 def rst_to_md(filename: str | Path) -> str:
+    """
+    Convert a subset of reStructuredText (RST) to MarkDown (MD).
+
+    Parameters
+    ----------
+    filename : str | Path
+        The path to the reStructuredText (RST) file.
+
+    Returns
+    -------
+    str
+        The converted data in MarkDown (MD) format.
+    """
     if isinstance(filename, Path):
         filename = str(filename)
 
diff --git a/deepcave/utils/files.py b/deepcave/utils/files.py
index b3c887dd..c0618f2a 100644
--- a/deepcave/utils/files.py
+++ b/deepcave/utils/files.py
@@ -1,9 +1,27 @@
+#  noqa: D400
+"""
+# Files
+
+This module provides a utility to create directories from a filename.
+"""
+
 from typing import Union
 
 from pathlib import Path
 
 
-def make_dirs(filename: Union[str, Path], parents=True):
+def make_dirs(filename: Union[str, Path], parents: bool = True) -> None:
+    """
+    Create a directory.
+
+    Parameters
+    ----------
+    filename : Union[str, Path]
+        The name and path of the file.
+    parents : bool, optional
+        Whether intermediate directories should be created.
+        Default is True.
+    """
     path = Path(filename)
     if path.suffix != "":  # Is file
         path = path.parent
diff --git a/deepcave/utils/hash.py b/deepcave/utils/hash.py
index 912b997e..98e0efa6 100644
--- a/deepcave/utils/hash.py
+++ b/deepcave/utils/hash.py
@@ -1,13 +1,46 @@
+#  noqa: D400
+"""
+# Hash
+
+This module provides utilities to convert strings and files to hash.
+"""
+
 import hashlib
 from pathlib import Path
 
 
 def string_to_hash(string: str) -> str:
+    """
+    Convert a string to a hash.
+
+    Parameters
+    ----------
+    string : str
+        The string to be converted to hash.
+
+    Returns
+    -------
+    str
+        The hash object.
+    """
     hash_object = hashlib.md5(string.encode())
     return hash_object.hexdigest()
 
 
 def file_to_hash(filename: Path) -> str:
+    """
+    Convert a file to a hash.
+
+    Parameters
+    ----------
+    filename : Path
+        The path to the file to be converted.
+
+    Returns
+    -------
+    str
+        The hash object.
+    """
     hash = hashlib.md5()
     with Path(filename).open("rb") as f:
         while chunk := f.read(4082):
diff --git a/deepcave/utils/layout.py b/deepcave/utils/layout.py
index 4c094252..880bccc1 100644
--- a/deepcave/utils/layout.py
+++ b/deepcave/utils/layout.py
@@ -1,4 +1,12 @@
-from typing import Any, Dict, List, Optional
+#  noqa: D400
+"""
+# Layout
+
+This module provides utilities to customize the layout.
+
+This includes a help button, slider marks, select/checklist/radio options.
+"""
+from typing import Any, Dict, List, Optional, Union
 
 import uuid
 
@@ -9,7 +17,24 @@
 from deepcave.utils.hash import string_to_hash
 
 
-def help_button(text: str, placement="top"):
+def help_button(text: str, placement: str = "top") -> html.Span:
+    """
+    Generate button with help icon.
+
+    Displays popover when hovered over that contains the provided text.
+
+    Parameters
+    ----------
+    text : str
+        The text that will be displayed in the popover.
+    placement : str, optional
+        The placement of the button, default is Top.
+
+    Returns
+    -------
+    html.Span
+        An html structure, that wraps the icon and the popover.
+    """
     id = "help-button-" + string_to_hash(text)
     id += str(uuid.uuid1())
 
@@ -26,16 +51,40 @@ def help_button(text: str, placement="top"):
     )
 
 
-def render_table(df):
+def render_table(df: pd.DataFrame) -> None:  # noqa: D103
     pass
 
 
 def get_slider_marks(
-    strings: Optional[List[Dict[str, Any]]] = None, steps=10, access_all=False
-) -> Dict[int, str]:
+    strings: Optional[List[Any]] = None,
+    steps: int = 10,
+    access_all: bool = False,
+) -> Dict[int, Dict[str, str]]:
+    """
+    Generate a dictionary containing slider marks.
+
+    The slider marks are based on the provided list of dictionaries.
+
+    Parameters
+    ----------
+    strings : Optional[List[Dict[str, Any]]], optional
+        List of dictionaries containing information about the marks.
+        Default value is None.
+    steps : int, optional
+        Number of steps or marks on the slider.
+        Default is 10.
+    access_all : bool, optional
+        Indicates whether to create marks for all items.
+        Default is False.
+
+    Returns
+    -------
+    Dict[int, Dict[str, str]]
+        Contains information about the positions and labels of the marks.
+    """
     marks = {}
     if strings is None:
-        marks[0] = {"label": str("None")}
+        marks[0] = {"label": "None"}
         return marks
 
     if len(strings) < steps:
@@ -55,13 +104,44 @@ def get_slider_marks(
 
 
 def get_select_options(
-    labels=None, values=None, disabled=None, binary=False
+    labels: Any = None,
+    values: Any = None,
+    disabled: Union[List[bool], None] = None,
+    binary: bool = False,
 ) -> List[Dict[str, Any]]:
     """
-    If values are none use labels as values.
-    If both are none return empty list.
-    """
+    Get dictionaries of the selected options.
 
+    If values are none use labels as values. If both are none return empty list.
+
+    Parameters
+    ----------
+    labels : Any, optional
+        To specify the labels of the options.
+        If labels are None, use values als labels.
+        Default is None.
+    values : Any, optional
+        To specify the values corresponding to the options.
+        If values are None, use labels as values.
+        Default is None
+    disabled : Union[List[Bool], None], optional
+        A list containing information whether each option should be disabled or not.
+        Default is None.
+    binary : bool, optional
+        Define whether to return two dictionaries with binary choices.
+        Only when both values and labels are None.
+        Default is False.
+
+    Returns
+    -------
+    List[Dict[str, Any]]
+        A list of dictionaries of the selected options.
+
+    Raises
+    ------
+    ValueError
+        If the labels and values do not have the same length.
+    """
     if labels is None and values is None:
         if binary:
             return [{"label": "Yes", "value": True}, {"label": "No", "value": False}]
@@ -87,17 +167,106 @@ def get_select_options(
     return options
 
 
-def get_checklist_options(labels=None, values=None, binary=False):
+def get_checklist_options(
+    labels: Any = None, values: Any = None, binary: bool = False
+) -> List[Dict[str, Any]]:
+    """
+    Get a list of dictionaries of checklist options.
+
+    Parameters
+    ----------
+    labels : Any, optional
+        To specify the labels of the options.
+        If labels are None, use values als labels.
+        Default is None.
+    values : Any, optional
+        To specify the values corresponding to the options.
+        If values are None, use labels as values.
+        Default is None
+    binary : bool, optional
+        Define whether to return two dictionaries with binary choices.
+        Only when both values and labels are None.
+        Default is False.
+
+    Returns
+    -------
+    List[Dict[str, Any]]
+        A list of dictionaries of the checklist options.
+
+    Raises
+    ------
+    ValueError
+        If the labels and values do not have the same length.
+    """
     return get_select_options(labels=labels, values=values, binary=binary)
 
 
-def get_radio_options(labels=None, values=None, binary=False):
+def get_radio_options(
+    labels: Any = None, values: Any = None, binary: Any = False
+) -> List[Dict[str, Any]]:
+    """
+    Get a list of dictionaries of radio options.
+
+    Parameters
+    ----------
+    labels : Any, optional
+        To specify the labels of the options.
+        If labels are None, use values als labels.
+        Default is None.
+    values : Any, optional
+        To specify the values corresponding to the options.
+        If values are None, use labels as values.
+        Default is None
+    binary : bool, optional
+        Define whether to return two dictionaries with binary choices.
+        Only when both values and labels are None.
+        Default is False.
+
+    Returns
+    -------
+    List[Dict[str, Any]]
+        A list of dictionaries of the radio options.
+
+    Raises
+    ------
+    ValueError
+        If the labels and values do not have the same length.
+    """
     return get_select_options(labels=labels, values=values, binary=binary)
 
 
 def create_table(
-    output: Dict[str, str], fixed=False, head=True, striped=True, mb=True
+    output: Dict[str, List[str]],
+    fixed: bool = False,
+    head: bool = True,
+    striped: bool = True,
+    mb: bool = True,
 ) -> dbc.Table:
+    """
+    Create a table from the given data.
+
+    Parameters
+    ----------
+    output : Dict[str, List[str]]
+        Containing the information for the table.
+    fixed : bool, optional
+        Indicates whether the table has a fixed layout.
+        Default is False.
+    head : bool, optional
+        Indicates whether the head will be included.
+        Default is True.
+    striped : bool, optional
+        Indicates whether the rows will be striped.
+        Default is True.
+    mb : bool, optional
+        Indicates whether the table has a margin.
+        Default is True.
+
+    Returns
+    -------
+    dbc.Table
+        The created dash bootstrap component (DBC) table.
+    """
     className = ""
     if not head:
         className += "exclude-head "
diff --git a/deepcave/utils/logs.py b/deepcave/utils/logs.py
index eaebc8e5..d7a62c23 100644
--- a/deepcave/utils/logs.py
+++ b/deepcave/utils/logs.py
@@ -1,3 +1,10 @@
+#  noqa: D400
+"""
+# Logs
+
+This module sets up and gets the logging configuration.
+"""
+
 import logging
 import logging.config
 from pathlib import Path
@@ -14,4 +21,17 @@
 
 
 def get_logger(logger_name: str) -> logging.Logger:
+    """
+    Get the logger corresponding to the logger name.
+
+    Parameters
+    ----------
+    logger_name : str
+        The name of the logger.
+
+    Returns
+    -------
+    logging.Logger
+        The logger corresponding to the logger name.
+    """
     return logging.getLogger(logger_name)
diff --git a/deepcave/utils/notification.py b/deepcave/utils/notification.py
index d1c34868..d5bdfa82 100644
--- a/deepcave/utils/notification.py
+++ b/deepcave/utils/notification.py
@@ -1,22 +1,55 @@
+#  noqa: D400
+"""
+# Notification
+
+This module provides utilities for creating, updating and resetting notifications.
+
+## Classes
+    - Notification: Can create, update and reset a notification.
+"""
 from typing import Optional, Tuple
 
 
 class Notification:
+    """Can create, update and reset a notification."""
+
     def __init__(self) -> None:
-        self.reset()
+        self._update_required = False
+        self._message: Optional[str] = None
+        self._color: Optional[str] = None
 
     def reset(self) -> None:
+        """Reset the notification."""
         self._update_required = False
         self._message = None
         self._color = None
 
     def update(self, message: str, color: str = "danger") -> None:
+        """
+        Update the notification.
+
+        Parameters
+        ----------
+        message : str
+            The message of the notification.
+        color : str, optional
+            The color of the notification.
+            Default is "danger".
+        """
         self._update_required = True
         self._message = message
         self._color = color
 
     def get_latest(self) -> Optional[Tuple[str, str]]:
-        if self._update_required:
+        """
+        Retrieve the latest notification and reset.
+
+        Returns
+        -------
+        Optional[Tuple[str, str]]
+            The latest notification
+        """
+        if self._update_required and self._message is not None and self._color is not None:
             result = (self._message, self._color)
             self.reset()
 
diff --git a/deepcave/utils/run_caches.py b/deepcave/utils/run_caches.py
index 8acd88a3..d4d05b74 100644
--- a/deepcave/utils/run_caches.py
+++ b/deepcave/utils/run_caches.py
@@ -1,20 +1,41 @@
-from typing import Any, Dict, Iterator, Optional, Union
+#  noqa: D400
+"""
+# RunCaches
+
+This module defines a class for holding the caches for selected runs.
+
+Utilities provided include updating, getting, setting and clearing.
+
+## Classes
+    - RunCaches: Hold the caches for the selected runs.
+"""
+
+from typing import Any, Dict, Optional
 
 import shutil
 
+from deepcave.config import Config
 from deepcave.runs import AbstractRun
 from deepcave.utils.cache import Cache
-from deepcave.utils.hash import string_to_hash
 from deepcave.utils.logs import get_logger
 
 
 class RunCaches:
     """
-    Holds the caches for the selected runs. The caches are used for the plugins to store the
+    Hold the caches for the selected runs.
+
+    The caches are used for the plugins to store the
     raw outputs so that raw outputs must not be calculated again.
 
     Each input has its own cache. This change was necessary because it ensures that not all data
-    are loaded if they are not needed.
+    is loaded if not needed.
+
+    Properties
+    ----------
+    cache_dir : Path
+        The path to the cache directory of the run.
+    logger : Logger
+        The logger for the run cache.
     """
 
     def __init__(self, config: "Config"):
@@ -24,7 +45,8 @@ def __init__(self, config: "Config"):
 
     def update(self, run: AbstractRun) -> bool:
         """
-        Updates the cache for the given run. If the cache does not exists it will be created.
+        Update the cache for the given run. If the cache does not exists it will be created.
+
         If the run hash is different from the saved variant the cache will be reset.
 
         Parameters
@@ -86,9 +108,9 @@ def _reset(self, run: AbstractRun, cache: Cache) -> None:
 
         cache.write()
 
-    def get(self, run: AbstractRun, plugin_id: str, inputs_key: str) -> Dict[str, Any]:
+    def get(self, run: AbstractRun, plugin_id: str, inputs_key: str) -> Optional[Dict[str, Any]]:
         """
-        Returns the raw outputs for the given run, plugin and inputs key.
+        Return the raw outputs for the given run, plugin and inputs key.
 
         Parameters
         ----------
@@ -101,8 +123,13 @@ def get(self, run: AbstractRun, plugin_id: str, inputs_key: str) -> Dict[str, An
 
         Returns
         -------
-        Dict[str, Any]
+        Optional[Dict[str, Any]]
             Raw outputs for the given run, plugin and inputs key.
+
+        Raises
+        ------
+        AssertionError
+            If the outputs of the cache are not a dict.
         """
         filename = self.cache_dir / run.id / plugin_id / f"{inputs_key}.json"
 
@@ -110,11 +137,14 @@ def get(self, run: AbstractRun, plugin_id: str, inputs_key: str) -> Dict[str, An
             return None
 
         cache = Cache(filename, debug=self._debug, write_file=False)
-        return cache.get("outputs")
+        outputs = cache.get("outputs")
+        assert isinstance(outputs, dict), "Outputs of cache must be a dict."
+        return outputs
 
     def set(self, run: AbstractRun, plugin_id: str, inputs_key: str, value: Any) -> None:
         """
-        Sets the value for the given run, plugin and inputs key.
+        Set the value for the given run, plugin and inputs key.
+
         Since each input key has it's own cache, only necessary data are loaded.
 
         Parameters
@@ -133,15 +163,11 @@ def set(self, run: AbstractRun, plugin_id: str, inputs_key: str, value: Any) ->
         cache.set("outputs", value=value)
 
     def clear_run(self, run: AbstractRun) -> None:
-        """
-        Removes all caches for the given run.
-        """
+        """Remove all caches for the given run."""
         shutil.rmtree(self.cache_dir / run.id)
 
     def clear(self) -> None:
-        """
-        Removes all caches.
-        """
+        """Remove all caches."""
         try:
             shutil.rmtree(self.cache_dir)
         except Exception:
diff --git a/deepcave/utils/styled_plot.py b/deepcave/utils/styled_plot.py
index b9568f22..669309f1 100644
--- a/deepcave/utils/styled_plot.py
+++ b/deepcave/utils/styled_plot.py
@@ -1,15 +1,35 @@
-from distutils.spawn import find_executable
+#  noqa: D400
+"""
+# StyledPlot
 
-import matplotlib
+This module provides utilities to customize functions from the matplotlib.
+Plots can be created and different parameters of the plots can be defined.
+
+## Classes
+    - StyledPlot: Overwrites default settings from matplotlib.pyplot.
+
+## Constants
+    FIG_WIDTH: float
+    FIG_HEIGHT: float
+"""
+
+from typing import Any, Dict, Optional, Union
 
-matplotlib.use("Agg")
 import base64
 import io
+from distutils.spawn import find_executable
+from pathlib import Path
 
+import matplotlib
 import matplotlib.pyplot as plt
+import numpy as np
+from numpy.typing import ArrayLike
 
 from deepcave.utils.logs import get_logger
 
+matplotlib.use("Agg")
+
+
 # IEEETrans double column standard
 FIG_WIDTH = 252.0 / 72.27  # 1pt is 1/72.27 inches
 FIG_HEIGHT = FIG_WIDTH / 1.618  # golden ratio
@@ -21,10 +41,17 @@
 class StyledPlot:
     """
     Overwrites default settings from matplotlib.pyplot.
+
     If a function is not overwritten, the default function will be used.
+
+    Properties
+    ----------
+    plt : Module("matplotlib.pyplot")
+        The matplotlib plot.
+        The style used is seaborn.
     """
 
-    def __init__(self):
+    def __init__(self) -> None:
         plt.style.use("seaborn-v0_8")
 
         # Set MatPlotLib defaults
@@ -47,7 +74,27 @@ def __init__(self):
 
         self.plt = plt
 
-    def figure(self, cols=1, rows=1, dpi=200):
+    def figure(self, cols: int = 1, rows: int = 1, dpi: int = 200) -> plt.Figure:
+        """
+        Create a new figure using the input values.
+
+        Parameters
+        ----------
+        cols : int, optional
+            The number of the columns.
+            Default is 1.
+        rows : int, optional
+            The number of the rows.
+            Default is 1.
+        dpi : int, optional
+            The dots per inches (dpi).
+            Default is 200.
+
+        Returns
+        -------
+        plt.Figure
+            The figure created with the input information.
+        """
         # Clean all
         self.plt.cla()
         self.plt.clf()
@@ -57,12 +104,28 @@ def figure(self, cols=1, rows=1, dpi=200):
 
         return f
 
-    def save_figure(self, filename):
+    def save_figure(self, filename: Union[str, Path]) -> None:
+        """
+        Save the figure/plot at the given filename.
+
+        Parameters
+        ----------
+        filename : Union[str, Path]
+            The name of the file the plot will be saved at.
+        """
         self.plt.savefig(filename, dpi=400, bbox_inches="tight")
         self.plt.close()
 
-    def render(self):
-        # Ccreate a virtual file which matplotlib can use to save the figure
+    def render(self) -> str:
+        """
+        Render the Styled Plot for displaying.
+
+        Returns
+        -------
+        str
+            The rendered plot.
+        """
+        # Create a virtual file which matplotlib can use to save the figure
         buffer = io.BytesIO()
         self.plt.savefig(buffer, dpi=400, bbox_inches="tight")
         buffer.seek(0)
@@ -72,12 +135,35 @@ def render(self):
         encoded_image = base64.b64encode(buffer.read())
         return "data:image/png;base64,{}".format(encoded_image.decode())
 
-    def xlim(self, xmin, xmax):
+    def xlim(self, xmin: Union[float, int], xmax: Union[float, int]) -> None:
+        """
+        Set the x-axis limits with a margin of a matplotlib plot.
+
+        Parameters
+        ----------
+        xmin : Union[float, int]
+            The lower x-axis limit.
+        xmax : Union[float, int]
+            The upper x-axis limit.
+        """
         xmin_with_margin = xmin - 0.05 * (xmax - xmin)
         xmax_with_margin = xmax + 0.05 * (xmax - xmin)
         self.plt.xlim(xmin_with_margin, xmax_with_margin)
 
-    def ylim(self, ymin, ymax, margin=True):
+    def ylim(self, ymin: Union[float, int], ymax: Union[float, int], margin: bool = True) -> None:
+        """
+        Set the y-axis limit of a matplotlib plot.
+
+        Parameters
+        ----------
+        ymin : Union[float, int]
+            The lower y-axis limit.
+        ymax : Union[float, int]
+            The upper y-axis limit.
+        margin : bool, optional
+            Determines whether a margin should be added to the limits.
+            Default is True.
+        """
         if margin:
             ymin_with_margin = ymin - 0.05 * (ymax - ymin)
             ymax_with_margin = ymax + 0.05 * (ymax - ymin)
@@ -87,9 +173,27 @@ def ylim(self, ymin, ymax, margin=True):
 
     # def grid(self):
     #    pass
-    #    #self.plt.grid(b=True, color='black', linestyle='--', linewidth=0.5, axis='y', zorder=0, alpha=0.5)
+    #    # self.plt.grid(b=True, color='black', linestyle='--', linewidth=0.5, axis='y', zorder=0,
+    #    # alpha=0.5)
 
-    def boxplot(self, values, positions, color, widths=0.5):
+    def boxplot(
+        self, values: np.ndarray, positions: ArrayLike, color: str, widths: float = 0.5
+    ) -> None:
+        """
+        Create a boxplot on a matplotlib plot.
+
+        Parameters
+        ----------
+        values : np.ndarray
+            Values to create the boxplot.
+        positions : ArrayLike
+            The position of the boxplot.
+        color : str
+            The color of the boxes as well as other elements in the plot.
+        widths : float, optional
+            The width of the boxes.
+            Default is 0.5.
+        """
         bp = self.plt.boxplot(values, positions=positions, patch_artist=True, widths=widths)
 
         for box in bp["boxes"]:
@@ -116,8 +220,34 @@ def boxplot(self, values, positions, color, widths=0.5):
                 alpha=0.5,
             )
 
-    def legend(self, cols=1, loc=None, title=None, outside=False):
-        kwargs = {
+    def legend(
+        self,
+        cols: int = 1,
+        loc: Optional[str] = None,
+        title: Optional[str] = None,
+        outside: bool = False,
+    ) -> None:
+        """
+        Customize and add a legend to a matplot plot.
+
+        Customize the placement and appearance of the legend.
+
+        Parameters
+        ----------
+        cols : int, optional
+            The number of the columns.
+            Default is 1
+        loc : Optional[str], optional
+            The location of the legend.
+            Default is None.
+        title : Optional[str], optional
+            The title for the legend.
+            Default is None.
+        outside : bool, optional
+            Determines if a legend is placed outside of plot area.
+            Default is False.
+        """
+        kwargs: Dict[str, Any] = {
             "ncol": cols,
             "columnspacing": 0.8,
             "labelspacing": 0,
@@ -146,23 +276,32 @@ def legend(self, cols=1, loc=None, title=None, outside=False):
             legend.get_frame().set_linewidth(0.5)
             legend.get_frame().set_edgecolor("gray")
 
-    def get_color(self, id):
+    def get_color(self, id: int) -> str:
+        """
+        Get the color from color palette based on the given id.
+
+        Parameters
+        ----------
+        id : int
+            id for retrieving a specific color.
+
+        Returns
+        -------
+        str
+            The color from the color palette.
+        """
         import seaborn as sns
 
         pal = sns.color_palette()
         hex_codes = pal.as_hex()
-
         return hex_codes[id % len(hex_codes)]
 
-    def __getattr__(self, name):
-        """
-        Make sure we access self.plt directly.
-        """
-
+    def __getattr__(self, name: str) -> Any:
+        """Make sure self.plt is accessed directly."""
         try:
             return self.__getattribute__(name)
-        except:
+        except AttributeError:
             return self.plt.__getattribute__(name)
 
 
-plt = StyledPlot()
+plot = StyledPlot()
diff --git a/deepcave/utils/styled_plotty.py b/deepcave/utils/styled_plotty.py
index 0f06ca03..6d823099 100644
--- a/deepcave/utils/styled_plotty.py
+++ b/deepcave/utils/styled_plotty.py
@@ -1,3 +1,11 @@
+# noqa: D400
+"""
+# Styled Plotty
+
+This module provides utilities for styling and customizing different plots with plotly.
+For this, it uses plotly as well as dash.
+"""
+
 from typing import Any, Callable, List, Optional, Tuple, Union
 
 import itertools
@@ -17,6 +25,7 @@
 
 from deepcave import interactive
 from deepcave.constants import CONSTANT_VALUE, NAN_LABEL, NAN_VALUE
+from deepcave.runs import AbstractRun
 from deepcave.utils.logs import get_logger
 
 logger = get_logger(__name__)
@@ -25,24 +34,25 @@
 @interactive
 def save_image(figure: go.Figure, name: str) -> None:
     """
-    Saves a plotly figure as an image.
+    Save a plotly figure as an image.
 
     Parameters
     ----------
-    fig : go.Figure
+    figure : go.Figure
         Plotly figure.
     name : str
-        Name of the image with extension. Will be automatically saved to the cache.
+        Name of the image with extension.
+        Will be automatically saved to the cache.
     """
-    from deepcave.config import Config
+    from deepcave import config
 
-    if not Config.SAVE_IMAGES:
+    if not config.SAVE_IMAGES:
         return
 
     ratio = 16 / 9
     width = 500
     height = int(width / ratio)
-    path = Config.CACHE_DIR / "figures" / name
+    path = config.CACHE_DIR / "figures" / name
 
     figure.write_image(path, width=width, height=height)
     logger.info(f"Saved figure {name} to {path}.")
@@ -50,10 +60,27 @@ def save_image(figure: go.Figure, name: str) -> None:
 
 def hex_to_rgb(hex_string: str) -> Tuple[int, int, int]:
     """
-    Converts a hex_string to a tuple of rgb values.
+    Convert a hex_string to a tuple of rgb values.
+
     Requires format including #, e.g.:
     #000000
     #ff00ff
+
+    Parameters
+    ----------
+    hex_string : str
+        The hex string to be converted.
+
+    Returns
+    -------
+    Tuple[int, int, int]
+        A Tuple of the converted RGB values
+
+    Raises
+    ------
+    ValueError
+        If the hex string is longer than 7.
+        If there are invalid characters in the hex string.
     """
     if len(hex_string) != 7:
         raise ValueError(f"Invalid length for #{hex_string}")
@@ -69,7 +96,19 @@ def hex_to_rgb(hex_string: str) -> Tuple[int, int, int]:
 
 def get_color(id_: int, alpha: float = 1) -> Union[str, Tuple[float, float, float, float]]:
     """
-    Using Plotly palette for the first 10 ids and Alphabet palette for the next 26, currently 36 colors are possible.
+    Get an RGBA Color, currently (Plotly version 5.3.1) there are 10 possible colors.
+
+    Parameters
+    ----------
+    id_ : int
+        ID for retrieving a specific color.
+    alpha : float, optional
+        Alpha value for the color, by default 1.
+
+    Returns
+    -------
+    Union[str, Tuple[float, float, float, float]]
+        The color from the color palette.
     """
     if id_ < 10:
         color = px.colors.qualitative.Plotly[id_]
@@ -80,19 +119,27 @@ def get_color(id_: int, alpha: float = 1) -> Union[str, Tuple[float, float, floa
     return f"rgba({r}, {g}, {b}, {alpha})"
 
 
-def get_discrete_heatmap(x, y, values: List[Any], labels: List[Any]):
+def get_discrete_heatmap(
+    x: List[Union[float, int]], y: List[int], values: List[Any], labels: List[Any]
+) -> go.Heatmap:
     """
     Generate a discrete colorscale from a (nested) list or numpy array of values.
 
     Parameters
     ----------
-    values : _type_
-        _description_
+    x : List[Union[float, int]]
+        List of values that present the x-axis of the heatmap.
+    y : List[int]
+         List of values that present the y-axis of the heatmap.
+    values : List[Any]
+        Contains the data values for the heatmap.
+    labels : List[Any]
+        Contains the labels corresponding to the values.
 
     Returns
     -------
-    _type_
-        _description_
+    go.Heatmap
+        A Plotly Heatmap object corresponding to the input.
     """
     flattened_values = list(itertools.chain(*values))
     flattened_labels = list(itertools.chain(*labels))
@@ -112,8 +159,8 @@ def get_discrete_heatmap(x, y, values: List[Any], labels: List[Any]):
         unique_sorted_values += [unique_values[idx]]
         unique_sorted_labels += [unique_labels[idx]]
 
-    # Now we give them new ids and we want to create new z values
-    # For that we need a mapping from old to new
+    # Now they are given new ids, and new z values should be created
+    # For that a mapping from old to new is needed
     mapping = {}
     v = []
     for new, old in enumerate(unique_sorted_values):
@@ -125,8 +172,11 @@ def get_discrete_heatmap(x, y, values: List[Any], labels: List[Any]):
         for i2, v2 in enumerate(v1):
             z[i1][i2] = mapping[v2]
 
-    n_intervals = v + [len(v)]
-    n_intervals = [(i - n_intervals[0]) / (n_intervals[-1] - n_intervals[0]) for i in n_intervals]
+    n_intervals_int = v + [len(v)]
+    n_intervals = [
+        (i - n_intervals_int[0]) / (n_intervals_int[-1] - n_intervals_int[0])
+        for i in n_intervals_int
+    ]
     colors = [get_color(i) for i in range(len(n_intervals))]
 
     discrete_colorscale = []
@@ -136,12 +186,12 @@ def get_discrete_heatmap(x, y, values: List[Any], labels: List[Any]):
     tickvals = [np.mean(n_intervals[k : k + 2]) for k in range(len(n_intervals) - 1)]
     ticktext = unique_sorted_labels
 
-    x = [str(i) for i in x]
-    y = [str(i) for i in y]
+    x_str = [str(i) for i in x]
+    y_str = [str(i) for i in y]
 
     return go.Heatmap(
-        x=x,
-        y=y,
+        x=x_str,
+        y=y_str,
         z=z,
         showscale=True,
         colorscale=discrete_colorscale,
@@ -154,7 +204,9 @@ def get_discrete_heatmap(x, y, values: List[Any], labels: List[Any]):
 
 def prettify_label(label: Union[str, float, int]) -> str:
     """
-    Takes a label and prettifies it. E.g. floats are shortened.
+    Take a label and prettifies it.
+
+    E.g. floats are shortened.
 
     Parameters
     ----------
@@ -191,9 +243,11 @@ def get_hyperparameter_ticks(
     include_nan: bool = True,
 ) -> Tuple[List, List]:
     """
-    Generates tick data for both tickvals and ticktext. The background is that
-    you might have encoded data but you don't want to show all of them.
-    With this function, only 6 (default) values are shown. This behaviour is
+    Generate tick data for both tickvals and ticktext.
+
+    The background is that
+    you might have encoded data, but you don't want to show all of them.
+    With this function, only 6 (default) values are shown. This behavior is
     ignored if `hp` is categorical.
 
     Parameters
@@ -201,9 +255,9 @@ def get_hyperparameter_ticks(
     hp : Hyperparameter
         Hyperparameter to generate ticks from.
     additional_values : Optional[List], optional
-        Additional values, which are forced in addition. By default None.
+        Additional values, which are forced in addition. By default, None.
     ticks : int, optional
-        Number of ticks, by default 6
+        Number of ticks, by default 4
     include_nan : bool, optional
         Whether "nan" as tick should be included. By default True.
 
@@ -212,8 +266,8 @@ def get_hyperparameter_ticks(
     Tuple[List, List]
         tickvals and ticktext.
     """
-
     # This is basically the inverse of `encode_config`.
+    tickvals: List[Union[float, int]]
     if isinstance(hp, CategoricalHyperparameter):
         ticktext = hp.choices
         if len(ticktext) == 1:
@@ -230,7 +284,7 @@ def get_hyperparameter_ticks(
         min_v = 0
         max_v = 1
 
-        values = [min_v]
+        values: List[Union[float, int]] = [min_v]
 
         # Get values for each tick
         factors = [i / (ticks - 1) for i in range(1, ticks - 1)]
@@ -248,7 +302,7 @@ def get_hyperparameter_ticks(
         for value in values:
             inverse_values += [hp._transform_scalar(value)]
 
-        # Integers are rounded, so we map then back
+        # Integers are rounded, they are mapped
         if isinstance(hp, IntegerHyperparameter):
             for label in inverse_values:
                 value = hp._inverse_transform(label)
@@ -258,7 +312,7 @@ def get_hyperparameter_ticks(
                     ticktext += [label]
 
             if additional_values is not None:
-                # Now we add additional values
+                # Now add additional values are added
                 for value in additional_values:
                     if not (value is None or np.isnan(value) or value == NAN_VALUE):
                         label = hp._transform_scalar(value)
@@ -273,7 +327,7 @@ def get_hyperparameter_ticks(
                 ticktext += [label]
 
             if additional_values is not None:
-                # Now we add additional values
+                # Now additional values are added
                 for value in additional_values:
                     if (
                         not (value is None or np.isnan(value) or value == NAN_VALUE)
@@ -295,9 +349,11 @@ def get_hyperparameter_ticks_from_values(
     values: List, labels: List, forced: Optional[List[bool]] = None, ticks: int = 6
 ) -> Tuple[List, List]:
     """
-    Generates tick data for both values and labels. The background is that
-    you might have encoded data but you don't want to show all of them.
-    With this function, only 6 (default) values are shown. This behaviour is
+    Generate tick data for both values and labels.
+
+    The background is that
+    you might have encoded data, but you don't want to show all of them.
+    With this function, only 6 (default) values are shown. This behavior is
     ignored if `values` is a list of strings.
 
     Parameters
@@ -306,9 +362,9 @@ def get_hyperparameter_ticks_from_values(
         List of values.
     labels : List
         List of labels. Must be the same size as `values`.
-    forced : List[bool], optional
+    forced : Optional[List[bool]], optional
         List of booleans. If True, displaying the particular tick is enforced.
-        Independent from `ticks`.
+        Independent of `ticks`.
     ticks : int, optional
         Number of ticks and labels to show. By default 6.
 
@@ -316,6 +372,11 @@ def get_hyperparameter_ticks_from_values(
     -------
     Tuple[List, List]
         Returns tickvals and ticktext as list.
+
+    Raises
+    ------
+    RuntimeError
+        If values contain both strings and non-strings.
     """
     assert len(values) == len(labels)
 
@@ -337,9 +398,9 @@ def get_hyperparameter_ticks_from_values(
     tickvals = []
     ticktext = []
 
-    # If we have less than x values, we also show them
+    # If there are less than x values, they are also shown
     if return_all or len(unique_values) <= ticks:
-        # Make sure we don't have multiple (same) labels for the same value
+        # Make sure there are no multiple (same) labels for the same value
         for value, label in zip(unique_values, unique_labels):
             tickvals.append(value)
             ticktext.append(label)
@@ -349,8 +410,8 @@ def get_hyperparameter_ticks_from_values(
             tickvals.append(values[idx])
             ticktext.append(labels[idx])
 
-        # After we added min and max values, we want to add
-        # intermediate values too
+        # After min and max values are added,
+        # intermediate values should be added too
         min_v = np.min(values)
         max_v = np.max(values)
 
@@ -379,7 +440,25 @@ def get_hyperparameter_ticks_from_values(
     return tickvals, ticktext
 
 
-def get_hovertext_from_config(run: "AbstractRun", config_id: int) -> str:
+def get_hovertext_from_config(run: AbstractRun, config_id: int) -> str:
+    """
+    Generate hover text with metrics for a configuration.
+
+    The method gets information about a given configuration, including a link, its objectives,
+    budget, costs and hyperparameters.
+
+    Parameters
+    ----------
+    run : AbstractRun
+        The run instance
+    config_id : int
+        The id of the configuration
+
+    Returns
+    -------
+    str
+        The hover text string containing the configuration information.
+    """
     if config_id < 0:
         return ""
 
@@ -396,6 +475,7 @@ def get_hovertext_from_config(run: "AbstractRun", config_id: int) -> str:
     budget = run.get_highest_budget(config_id)
     costs = run.get_costs(config_id, budget)
 
+    assert budget is not None
     string += f"<b>Objectives</b> (on highest found budget {round(budget, 2)})<br>"
     for objective, cost in zip(objectives, costs):
         string += f"{objective.name}: {cost}<br>"
@@ -410,6 +490,22 @@ def get_hovertext_from_config(run: "AbstractRun", config_id: int) -> str:
 
 
 def generate_config_code(register: Callable, variables: List[str]) -> List[Component]:
+    """
+    Generate HTML components to display code.
+
+    Parameters
+    ----------
+    register : Callable
+        A Callable for registering Dash components.
+        The register_input function is located in the Plugin class.
+    variables : List[str]
+        A List of variable names.
+
+    Returns
+    -------
+    List[Component]
+        A List of Dash components.
+    """
     code = """
     from ConfigSpace.configuration_space import ConfigurationSpace, Configuration
     from ConfigSpace.read_and_write import cs_json
diff --git a/deepcave/utils/symbolic_regression.py b/deepcave/utils/symbolic_regression.py
index 33781f60..b236e0c6 100644
--- a/deepcave/utils/symbolic_regression.py
+++ b/deepcave/utils/symbolic_regression.py
@@ -5,7 +5,7 @@
 This module provides utilities for running symbolic regression with gplearn.
 """
 
-from typing import List, Union
+from typing import List, Optional, Union
 
 import numpy as np
 import sympy
@@ -18,22 +18,26 @@
 logger = get_logger(__name__)
 
 
-def exp(x):
+def exp(x: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
     """
     Get a safe exp function with a maximum value of 100000 to avoid overflow.
 
     Parameters
     ----------
-    x : float
+    x : Union[float, np.ndarray]
         The value to calculate the exponential of.
 
     Returns
     -------
-    float
+    Union[float, np.ndarray]
         The safe exponential of x.
     """
     with np.errstate(all="ignore"):
-        max_value = np.full(shape=x.shape, fill_value=100000)
+        max_value: Union[float, np.ndarray]
+        if isinstance(x, np.ndarray):
+            max_value = np.full(shape=x.shape, fill_value=100000)
+        else:
+            max_value = 100000.0
         return np.minimum(np.exp(x), max_value)
 
 
@@ -54,11 +58,10 @@ def get_function_set() -> List[Union[str, _Function]]:
 
 
 def convert_symb(
-    symb: SymbolicRegressor, n_decimals: int = None, hp_names: list = None
-) -> sympy.core.expr:
+    symb: SymbolicRegressor, n_decimals: Optional[int] = None, hp_names: Optional[List[str]] = None
+) -> str:
     """
-    Convert a fitted symbolic regression to a simplified and potentially rounded mathematical
-    expression.
+    Convert a fitted symbolic regression to a simplified and rounded mathematical expression.
 
     Warning: eval is used in this function, thus it should not be used on unsanitized input (see
     https://docs.sympy.org/latest/modules/core.html?highlight=eval#module-sympy.core.sympify).
@@ -74,7 +77,7 @@ def convert_symb(
 
     Returns
     -------
-    SymbolicRegressor
+    str
         Converted mathematical expression.
     """
     # sqrt is protected function in gplearn, always returning sqrt(abs(x))
@@ -126,8 +129,8 @@ def convert_symb(
             symb_conv = symb_conv.subs(X1, hp1)
         else:
             raise ValueError(
-                f"Numer of hyperparameters to be explained by symbolic explanations must not "
-                f"be larger than 2"
+                "Numer of hyperparameters to be explained by symbolic explanations must not "
+                "be larger than 2"
             )
 
     # Simplify the formula
@@ -148,4 +151,4 @@ def convert_symb(
             if isinstance(a, sympy.core.numbers.Float):
                 symb_simpl = symb_simpl.subs(a, round(a, n_decimals))
 
-    return symb_simpl
+    return str(symb_simpl)
diff --git a/deepcave/utils/url.py b/deepcave/utils/url.py
index a3a419bf..7e683aa4 100644
--- a/deepcave/utils/url.py
+++ b/deepcave/utils/url.py
@@ -1,3 +1,10 @@
+#  noqa: D400
+"""
+# URL
+
+This module creates and parses an URL according to its input information.
+"""
+
 from typing import Any, Dict, Optional
 
 import json
@@ -5,6 +12,22 @@
 
 
 def create_url(pathname: str, inputs: Dict[str, Any]) -> str:
+    """
+    Create an URL with the given pathname and inputs.
+
+    Parameters
+    ----------
+    pathname : str
+        The name of the path for the URL.
+    inputs : Dict[str, Any]
+        A dictionary containing the input parameters for the URL.
+
+    Returns
+    -------
+    str
+        The URL.
+
+    """
     string = json.dumps(inputs)
     converted_string = urllib.parse.quote(string)
 
@@ -12,6 +35,20 @@ def create_url(pathname: str, inputs: Dict[str, Any]) -> str:
 
 
 def parse_url(pathname: str) -> Optional[Dict[str, Any]]:
+    """
+    Parse the URL and extract input information if possible.
+
+    Parameters
+    ----------
+    pathname : str
+        The name of the URL.
+
+    Returns
+    -------
+    Optional[Dict[str, Any]]
+        A dictionary with the extracted inputs if available.
+        Otherwise return None.
+    """
     url = urllib.parse.urlparse(pathname)
     query = urllib.parse.parse_qs(url.query)
 
diff --git a/deepcave/utils/util.py b/deepcave/utils/util.py
index e75f6a96..8cb362a0 100644
--- a/deepcave/utils/util.py
+++ b/deepcave/utils/util.py
@@ -1,45 +1,95 @@
-from typing import Any, Dict, Optional, Tuple, Union
+#  noqa: D400
+"""
+# Util
+
+This module provides utilities for string generation and shortening.
+It also provides a function to get the difference between now and a given timestamp.
+"""
+from typing import Any
 
-import base64
 import datetime
 import random
 import string
-from io import BytesIO
-
-import pandas as pd
-from ConfigSpace import ConfigurationSpace
-from ConfigSpace.hyperparameters import (
-    CategoricalHyperparameter,
-    FloatHyperparameter,
-    IntegerHyperparameter,
-    OrdinalHyperparameter,
-)
-from dash import html
-from matplotlib import pyplot as plt
-from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder
 
 
 def get_random_string(length: int) -> str:
+    """
+    Get a random string with a specific length.
+
+    Parameters
+    ----------
+    length : int
+        The length of the string.
+
+    Returns
+    -------
+    str
+        The random string with the given length.
+
+    Raises
+    ------
+    ValueError
+        If the length is smaller 0.
+    """
     if length < 0:
         raise ValueError("Length has to be greater than 0")
     letters = string.ascii_lowercase
-    return "".join(random.choice(letters) for i in range(length))
+    return "".join(random.choice(letters) for _ in range(length))
+
 
+def short_string(value: Any, length: int = 30, *, mode: str = "prefix") -> str:
+    """
+    Shorten the given string.
 
-def short_string(value: Any, length: int = 30, *, mode="prefix") -> str:
+    Cut either at prefix or at suffix if necessary.
+
+    Parameters
+    ----------
+    value : Any
+        The value or string to shorten.
+    length : int, optional
+        The length of the returned string.
+        Default is 30.
+    mode : str, optional
+        Define how to shorten the string.
+        Default is "prefix".
+
+    Returns
+    -------
+    str
+        The shortened string.
+
+    Raises
+    ------
+    ValueError
+        If the given mode is unknown.
+    """
     value = str(value)
     if len(value) <= length:
         return value
 
-    cutlength = length - 3  # For 3 dots (...)
+    cut_length = length - 3  # For 3 dots (...)
     if mode == "prefix":
-        return f"...{value[-cutlength:]}"
+        return f"...{value[-cut_length:]}"
     elif mode == "suffix":
-        return f"{value[:cutlength]}..."
+        return f"{value[:cut_length]}..."
     raise ValueError(f"Unknown mode '{mode}'")
 
 
 def get_latest_change(st_mtime: int) -> str:
+    """
+    Get the difference between now and a given timestamp.
+
+    Parameters
+    ----------
+    st_mtime : int
+        A timestamp to calculate the difference from.
+
+    Returns
+    -------
+    str
+        A string containing the passed time.
+    """
     t = datetime.datetime.fromtimestamp(st_mtime)
     s_diff = (datetime.datetime.now() - t).seconds
     d_diff = (datetime.datetime.now() - t).days
diff --git a/deepcave/worker.py b/deepcave/worker.py
index 264fc5d6..df83e854 100644
--- a/deepcave/worker.py
+++ b/deepcave/worker.py
@@ -1,3 +1,9 @@
+"""
+# Worker.
+
+This module can create a redis queue worker.
+"""
+
 from rq import Connection, Worker
 
 from deepcave import queue
diff --git a/examples/api/importances.py b/examples/api/importances.py
index 981f5d87..be61312d 100644
--- a/examples/api/importances.py
+++ b/examples/api/importances.py
@@ -26,7 +26,7 @@
         budget_ids=budget_ids,
         method="global",
         n_hps=3,
-        n_trees=10
+        n_trees=10,
     )
     # Note: Filter variables are not considered.
     outputs = plugin.generate_outputs(run, inputs)
@@ -34,5 +34,5 @@
     # Finally, you can load the figure. Here, the filter variables play a role.
     # Alternatively: Use the matplotlib output (`load_mpl_outputs`) if available.
     figure = plugin.load_outputs(run, inputs, outputs)  # plotly.go figure
-    figure.write_image("examples/api/importances.png", scale=2.)
-    # figure.show()
\ No newline at end of file
+    figure.write_image("examples/api/importances.png", scale=2.0)
+    # figure.show()
diff --git a/examples/api/parallel_coordinates.py b/examples/api/parallel_coordinates.py
index 6df3f406..693254e8 100644
--- a/examples/api/parallel_coordinates.py
+++ b/examples/api/parallel_coordinates.py
@@ -34,5 +34,5 @@
     # Finally, you can load the figure. Here, the filter variables play a role.
     # Alternatively: Use the matplotlib output (`load_mpl_outputs`) if available.
     figure = plugin.load_outputs(run, inputs, outputs)  # plotly.go figure
-    figure.write_image("examples/api/parallel_coordinates.png", scale=2.)
+    figure.write_image("examples/api/parallel_coordinates.png", scale=2.0)
     # figure.show()
diff --git a/examples/api/pdp.py b/examples/api/pdp.py
index 83479592..957e8d96 100644
--- a/examples/api/pdp.py
+++ b/examples/api/pdp.py
@@ -34,4 +34,4 @@
     # Alternatively: Use the matplotlib output (`load_mpl_outputs`) if available.
     figure = plugin.load_outputs(run, inputs, outputs)  # plotly.go figure
     figure.write_image("examples/api/pdp.png")
-    #figure.show()
+    # figure.show()
diff --git a/pyproject.toml b/pyproject.toml
index f30400d1..fc6b64e0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -48,31 +48,20 @@ multi_line_output = 3 # https://pycqa.github.io/isort/docs/configuration/multi_l
 [tool.pydocstyle]
 convention = "numpy"
 add-ignore = [ # http://www.pydocstyle.org/en/stable/error_codes.html
-    "D100", # Missing docstring in public module
-    "D101", # Missing docstring in public class
-    "D104", # Missing docstring in public package
     "D105", # Missing docstring in magic method
-    "D203", # 1 blank line required before class docstring
-    "D205", # 1 blank line required between summary and description
-    "D210", # No whitespaces allowed surrounding docstring text
     "D212", # Multi-line docstring summary should start at the first line
-    "D213", # Multi-line docstring summary should start at the second line
-    "D400", # First line should end with a period
-    "D401", # First line should be in imperative mood
-    "D404", # First word of docstring should not be this
-    "D413", # Missing blank line after last section
-    "D415", # First line should end with a period, question mark, or exclamation point
 ]
 
 [tool.mypy]
-python_version = "3.8"
+python_version = "3.9"
 show_error_codes = true
 warn_unused_configs = true         # warn about unused [tool.mypy] lines
 follow_imports = "normal"          # Type check top level api code we use from imports
-ignore_missing_imports = false     # prefer explicit ignores
+ignore_missing_imports = true      # prefer explicit ignores
 disallow_untyped_defs = true       # All functions must have types
 disallow_untyped_decorators = true # ... even decorators
 disallow_incomplete_defs = true    # ... all types
+check_untyped_defs = true          # Problems are shown, even if def is check_untyped_defs
 
 [[tool.mypy.overrides]]
 module = ["setuptools.*"]     # Add modules that give import errors here
diff --git a/requirements.txt b/requirements.txt
index 93bd6cc3..2920f10c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,5 @@
+# If something is changed here, change it in the pre-commit-hook under mypy additonal dependencies as well
+# otherwise mypy wont check the dependencies
 wheel>=0.41.2
 setuptools==68.2.2
 absl-py>=1.0.0
@@ -10,7 +12,6 @@ pyyaml>=6.0.1
 kaleido>=0.2.1
 gplearn>=0.4.2
 sympy>=1.12
-kaleido>=0.2.1
 
 # AutoML packages
 ConfigSpace==0.6.1


Test patch:
diff --git a/tests/test_evaluators/test_epm.py b/tests/test_evaluators/test_epm.py
index d152a7f2..621655d3 100644
--- a/tests/test_evaluators/test_epm.py
+++ b/tests/test_evaluators/test_epm.py
@@ -1,8 +1,5 @@
 import unittest
 
-import numpy as np
-import pytest
-
 from deepcave.constants import COMBINED_COST_NAME
 from deepcave.evaluators.epm.fanova_forest import FanovaForest
 from deepcave.evaluators.epm.random_forest import RandomForest
diff --git a/tests/test_evaluators/test_fanova.py b/tests/test_evaluators/test_fanova.py
index 0869e25e..92dc1301 100644
--- a/tests/test_evaluators/test_fanova.py
+++ b/tests/test_evaluators/test_fanova.py
@@ -1,9 +1,5 @@
 import unittest
 
-import numpy as np
-import pytest
-
-from deepcave.constants import COMBINED_COST_NAME
 from deepcave.evaluators.fanova import fANOVA as Evaluator
 from deepcave.runs import AbstractRun
 from deepcave.runs.converters.smac3v1 import SMAC3v1Run
diff --git a/tests/test_evaluators/test_lpi.py b/tests/test_evaluators/test_lpi.py
index bc467f1d..bf957fbf 100644
--- a/tests/test_evaluators/test_lpi.py
+++ b/tests/test_evaluators/test_lpi.py
@@ -1,9 +1,5 @@
 import unittest
 
-import numpy as np
-import pytest
-
-from deepcave.constants import COMBINED_COST_NAME
 from deepcave.evaluators.lpi import LPI as Evaluator
 from deepcave.runs import AbstractRun
 from deepcave.runs.converters.smac3v1 import SMAC3v1Run
@@ -22,9 +18,9 @@ def test(self):
 
         # Calculate
         self.evaluator.calculate(objective, budget)
-        importances = self.evaluator.get_importances(self.hp_names)
+        importance_dict = self.evaluator.get_importances(self.hp_names)
 
-        print(importances)
+        print(importance_dict)
 
 
 if __name__ == "__main__":
diff --git a/tests/test_runs/test_groups.py b/tests/test_runs/test_groups.py
index a2e402f9..94bbbaae 100644
--- a/tests/test_runs/test_groups.py
+++ b/tests/test_runs/test_groups.py
@@ -1,11 +1,5 @@
-from typing import List
-
 import unittest
 
-import numpy as np
-import pytest
-from requests import check_compatibility
-
 from deepcave.runs import AbstractRun, check_equality
 from deepcave.runs.converters.smac3v1 import SMAC3v1Run
 from deepcave.runs.converters.smac3v2 import SMAC3v2Run
diff --git a/tests/test_utils/test_cache.py b/tests/test_utils/test_cache.py
index 3d7c3939..2446662d 100644
--- a/tests/test_utils/test_cache.py
+++ b/tests/test_utils/test_cache.py
@@ -4,7 +4,6 @@
 import unittest
 from pathlib import Path
 
-import matplotlib.pyplot as plt
 import pandas as pd
 
 from deepcave.utils.cache import Cache