Repository: matchms/matchms
Pull request #505 (2023-08-23 08:44:03)

Add repair annotation


Patch:
diff --git a/.readthedocs.yml b/.readthedocs.yml
index 133fb870..c73f37df 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -5,15 +5,19 @@
 # Required
 version: 2
 
+
+# Set the OS, Python version and other tools you might need
+build:
+  os: ubuntu-22.04
+  tools:
+    python: '3.8'
+  jobs:
+    post_install:
+      - pip install poetry
+      - poetry config virtualenvs.create false
+      - poetry install --with docs
+
 # Build documentation in the docs/ directory with Sphinx
 sphinx:
   builder: html
   configuration: readthedocs/conf.py
-
-python:
-  version: 3.7
-  install:
-    - method: pip
-      path: .
-      extra_requirements:
-        - dev
diff --git a/matchms/Scores.py b/matchms/Scores.py
index c35bafb8..274983f9 100644
--- a/matchms/Scores.py
+++ b/matchms/Scores.py
@@ -111,6 +111,9 @@ class Scores:
         self._index = 0
         raise StopIteration
 
+    def __repr__(self):
+        return self._scores.__repr__()
+
     def __str__(self):
         return self._scores.__str__()
 
diff --git a/matchms/Spectrum.py b/matchms/Spectrum.py
index cdbb3230..a125d4c7 100644
--- a/matchms/Spectrum.py
+++ b/matchms/Spectrum.py
@@ -27,9 +27,11 @@ class Spectrum:
 
         spectrum = Spectrum(mz=np.array([100, 150, 200.]),
                             intensities=np.array([0.7, 0.2, 0.1]),
-                            metadata={'id': 'spectrum1',
+                            metadata={"id": 'spectrum1',
+                                      "precursor_mz": 222.333,
                                       "peak_comments": {200.: "the peak at 200 m/z"}})
 
+        print(spectrum)
         print(spectrum.peaks.mz[0])
         print(spectrum.peaks.intensities[0])
         print(spectrum.get('id'))
@@ -39,6 +41,7 @@ class Spectrum:
 
     .. testoutput::
 
+        Spectrum(precursor m/z=222.33, 3 fragments between 100.0 and 200.0)
         100.0
         0.7
         spectrum1
@@ -101,6 +104,16 @@ class Spectrum:
         combined_hash = self.metadata_hash() + self.spectrum_hash()
         return int.from_bytes(bytearray(combined_hash, 'utf-8'), 'big')
 
+    def __repr__(self):
+        num_peaks = len(self.peaks)
+        min_mz = min(self.peaks.mz)
+        max_mz = max(self.peaks.mz)
+        precursor_mz = self.get("precursor_mz")
+        return f"Spectrum(precursor m/z={precursor_mz:.2f}, {num_peaks} fragments between {min_mz:.1f} and {max_mz:.1f})"
+
+    def __str__(self):
+        return self.__repr__()
+
     def spectrum_hash(self):
         """Return a (truncated) sha256-based hash which is generated
         based on the spectrum peaks (mz:intensity pairs).
diff --git a/matchms/filtering/SpectrumProcessor.py b/matchms/filtering/SpectrumProcessor.py
index f8ce9280..3c0c2659 100644
--- a/matchms/filtering/SpectrumProcessor.py
+++ b/matchms/filtering/SpectrumProcessor.py
@@ -1,3 +1,4 @@
+import inspect
 from collections import defaultdict
 from functools import partial
 from typing import Dict, Optional, Tuple, Union
@@ -34,7 +35,7 @@ class SpectrumProcessor:
             for filter_name in PREDEFINED_PIPELINES[predefined_pipeline]:
                 self.add_matchms_filter(filter_name)
 
-    def add_filter(self, filter_function: Union[Tuple[str], str]):
+    def add_filter(self, filter_function: Union[Tuple[str, Dict[str, any]], str]):
         """Add a filter to the processing pipeline. Takes both matchms filter names (and parameters)
         as well as custom-made functions.
         """
@@ -67,12 +68,12 @@ class SpectrumProcessor:
             filter_func.__name__ = FILTER_FUNCTION_NAMES[filter_name].__name__
         else:
             raise TypeError("filter_spec should be a string or a tuple or list")
-
+        check_all_parameters_given(filter_func)
         self.filters.append(filter_func)
         # Sort filters according to their order in self.filter_order
         self.filters.sort(key=lambda f: self.filter_order.index(f.__name__))
 
-    def add_custom_filter(self, filter_function, filter_params=None):
+    def add_custom_filter(self, filter_function, filter_params=None, filter_position: Optional[int] = None):
         """
         Add a custom filter function to the processing pipeline.
 
@@ -86,20 +87,31 @@ class SpectrumProcessor:
             processing pipeline.
         filter_params: dict
             If needed, add dictionary with all filter parameters. Default is set to None.
+        filter_position:
+            The position this filter should be inserted in the filter order.
+            If None, it will be appended at the end of the current list of filters.
         """
         if not callable(filter_function):
             raise TypeError("Expected callable filter function.")
-        filter_position = 0
-        for filter_func in self.filters[::-1]:
-            if filter_func.__name__ in self.filter_order:
-                filter_position = self.filter_order.index(filter_func.__name__)
-        self.filter_order.insert(filter_position + 1, filter_function.__name__)
-        if filter_params is None:
-            self.filters.append(filter_function)
+        if filter_position is None:
+            self.filter_order.append(filter_function.__name__)
+        elif not isinstance(filter_position, int):
+            raise TypeError("Expected filter_position to be an integer.")
         else:
-            filter_func = partial(filter_function, **filter_params)
-            filter_func.__name__ = filter_function.__name__
-            self.filters.append(filter_func)
+            if filter_position >= len(self.filters):
+                self.filter_order.append(filter_function.__name__)
+            else:
+                current_filter_at_position = self.filters[filter_position].__name__
+                order_index = self.filter_order.index(current_filter_at_position)
+                self.filter_order.insert(order_index, filter_function.__name__)
+
+        if filter_params is not None:
+            partial_filter_func = partial(filter_function, **filter_params)
+            partial_filter_func.__name__ = filter_function.__name__
+            filter_function = partial_filter_func
+        check_all_parameters_given(filter_function)
+        self.filters.append(filter_function)
+        self.filters.sort(key=lambda f: self.filter_order.index(f.__name__))
 
     def process_spectrum(self, spectrum,
                          processing_report: Optional["ProcessingReport"] = None):
@@ -174,9 +186,9 @@ class SpectrumProcessor:
     def processing_steps(self):
         filter_list = []
         for filter_step in self.filters:
-            if isinstance(filter_step, partial):
-                filter_params = filter_step.keywords
-                filter_list.append((filter_step.__name__, filter_params))
+            parameter_settings = get_parameter_settings(filter_step)
+            if parameter_settings is not None:
+                filter_list.append((filter_step.__name__, parameter_settings))
             else:
                 filter_list.append(filter_step.__name__)
         return filter_list
@@ -195,6 +207,33 @@ class SpectrumProcessor:
         return summary_string
 
 
+def check_all_parameters_given(func):
+    """Asserts that all added parameters for a function are given (except spectrum_in)"""
+    signature = inspect.signature(func)
+    parameters_without_value = []
+    for parameter, value in signature.parameters.items():
+        if value.default is inspect.Parameter.empty:
+            parameters_without_value.append(parameter)
+    assert len(parameters_without_value) == 1, \
+        f"More than one parameter of the function {func.__name__} is not specified, " \
+        f"the parameters not specified are {parameters_without_value}"
+
+
+def get_parameter_settings(func):
+    """Returns all parameters and parameter values for a function
+
+    This includes default parameter settings and, but also the settings stored in partial"""
+    signature = inspect.signature(func)
+    parameter_settings = {
+            parameter: value.default
+            for parameter, value in signature.parameters.items()
+            if value.default is not inspect.Parameter.empty
+        }
+    if parameter_settings == {}:
+        return None
+    return parameter_settings
+
+
 class ProcessingReport:
     """Class to keep track of spectrum changes during filtering.
     """
diff --git a/matchms/filtering/__init__.py b/matchms/filtering/__init__.py
index 9ef6b9e9..c7a459f3 100644
--- a/matchms/filtering/__init__.py
+++ b/matchms/filtering/__init__.py
@@ -105,6 +105,8 @@ from .metadata_processing.repair_adduct_based_on_smiles import \
     repair_adduct_based_on_smiles
 from .metadata_processing.repair_inchi_inchikey_smiles import \
     repair_inchi_inchikey_smiles
+from .metadata_processing.repair_not_matching_annotation import \
+    repair_not_matching_annotation
 from .metadata_processing.repair_parent_mass_is_mol_wt import \
     repair_parent_mass_is_mol_wt
 from .metadata_processing.repair_parent_mass_match_smiles_wrapper import \
@@ -121,8 +123,8 @@ from .metadata_processing.require_parent_mass_match_smiles import \
 from .metadata_processing.require_precursor_below_mz import \
     require_precursor_below_mz
 from .metadata_processing.require_precursor_mz import require_precursor_mz
-from .metadata_processing.require_valid_annotation import (
-    repair_not_matching_annotation, require_valid_annotation)
+from .metadata_processing.require_valid_annotation import \
+    require_valid_annotation
 from .peak_processing.add_losses import add_losses
 from .peak_processing.normalize_intensities import normalize_intensities
 from .peak_processing.reduce_to_number_of_peaks import \
diff --git a/matchms/filtering/metadata_processing/repair_not_matching_annotation.py b/matchms/filtering/metadata_processing/repair_not_matching_annotation.py
new file mode 100644
index 00000000..d6955f49
--- /dev/null
+++ b/matchms/filtering/metadata_processing/repair_not_matching_annotation.py
@@ -0,0 +1,90 @@
+import logging
+from matchms import Spectrum
+from matchms.filtering.filter_utils.smile_inchi_inchikey_conversions import (
+    _check_fully_annotated, convert_inchi_to_inchikey, convert_inchi_to_smiles,
+    convert_smiles_to_inchi)
+from matchms.filtering.metadata_processing.require_parent_mass_match_smiles import \
+    _check_smiles_and_parent_mass_match
+
+
+logger = logging.getLogger("matchms")
+
+
+def repair_not_matching_annotation(spectrum_in: Spectrum):
+    """
+    Repairs mismatches in a spectrum's annotations related to SMILES, InChI, and InChIKey.
+
+    Given a spectrum, this function ensures that the provided SMILES, InChI, and InChIKey
+    annotations are consistent with one another. If there are discrepancies, they are resolved 
+    as follows:
+
+    1. If the SMILES and InChI do not match:
+        - Both SMILES and InChI are checked against the parent mass.
+        - The annotation that matches the parent mass is retained, and the other is regenerated.
+    2. If the InChIKey does not match the InChI:
+        - A new InChIKey is generated from the InChI and replaces the old one.
+
+    Warnings and information logs are generated to track changes and potential issues.
+
+    Parameters:
+    ----------
+    spectrum_in : Spectrum
+        The input spectrum containing annotations to be checked and repaired.
+
+    Returns:
+    -------
+    Spectrum
+        A cloned version of the input spectrum with corrected annotations. If the input
+        spectrum is `None`, it returns `None`.
+    """
+    if spectrum_in is None:
+        return None
+
+    spectrum = spectrum_in.clone()
+
+    if not _check_fully_annotated(spectrum):
+        logger.warning("First run derive inchi_from_smiles, derive_inchikey_from_inchi, and derive_smiles_from_inchi")
+
+    smiles = spectrum.get("smiles")
+    inchi = spectrum.get("inchi")
+    inchikey = spectrum.get("inchikey")
+    parent_mass = spectrum.get("parent_mass")
+
+    inchi_from_smiles = convert_smiles_to_inchi(smiles)
+
+    # Check if SMILES and InChI match
+    if inchi_from_smiles != inchi:
+        smiles_from_inchi = convert_inchi_to_smiles(inchi)
+        spectrum = _handle_smiles_inchi_mismatch(spectrum, smiles, inchi, smiles_from_inchi, inchi_from_smiles, parent_mass)
+
+    # Check if the InChIKey matches the InChI
+    correct_inchikey = convert_inchi_to_inchikey(spectrum.get("inchi"))
+    if correct_inchikey and (inchikey[:14] == correct_inchikey[:14]):
+        return spectrum
+
+    logger.info("The inchikey has been changed from %s to %s", inchikey, correct_inchikey)
+    spectrum.set("inchikey", correct_inchikey)
+    return spectrum
+
+
+def _handle_smiles_inchi_mismatch(spectrum, smiles, inchi,
+                                  smiles_from_inchi, inchi_from_smiles,
+                                  parent_mass):
+    # pylint: disable=too-many-arguments
+    smiles_correct = _check_smiles_and_parent_mass_match(smiles, parent_mass, 0.1)
+    inchi_correct = _check_smiles_and_parent_mass_match(smiles_from_inchi, parent_mass, 0.1)
+
+    if smiles_correct and inchi_correct:
+        logger.warning("The SMILES and InChI are not matching, but both match the parent mass. "
+                       "SMILES = %s, InChI = %s", smiles, inchi)
+    elif smiles_correct and not inchi_correct:
+        # Repair by using inchi generated from SMILES
+        logger.info("The InChI has been changed from %s to %s. "
+                    "The new InChI matches the parent mass, while the old one did not", inchi, inchi_from_smiles)
+        spectrum.set("inchi", inchi_from_smiles)
+    else:
+        # Repair by using SMILES generated from the inchi
+        logger.info("The SMILES has been changed from %s to %s to match the InChI. "
+                    "The new SMILES matches the parent mass, while the old one did not", smiles, smiles_from_inchi)
+        spectrum.set("smiles", smiles_from_inchi)
+    return spectrum
diff --git a/matchms/filtering/metadata_processing/require_valid_annotation.py b/matchms/filtering/metadata_processing/require_valid_annotation.py
index 0233f0b3..0e98730f 100644
--- a/matchms/filtering/metadata_processing/require_valid_annotation.py
+++ b/matchms/filtering/metadata_processing/require_valid_annotation.py
@@ -1,63 +1,13 @@
 import logging
 from matchms import Spectrum
 from matchms.filtering.filter_utils.smile_inchi_inchikey_conversions import (
-    _check_fully_annotated, convert_inchi_to_inchikey, convert_inchi_to_smiles,
-    convert_smiles_to_inchi, is_valid_inchi, is_valid_inchikey,
-    is_valid_smiles)
-from matchms.filtering.metadata_processing.require_parent_mass_match_smiles import \
-    _check_smiles_and_parent_mass_match
+    convert_inchi_to_inchikey, convert_smiles_to_inchi, is_valid_inchi,
+    is_valid_inchikey, is_valid_smiles)
 
 
 logger = logging.getLogger("matchms")
 
 
-def repair_not_matching_annotation(spectrum_in: Spectrum):
-    """When smiles and inchi do not match the annotation that matches parent mass will be kept"""
-    if spectrum_in is None:
-        return None
-    spectrum = spectrum_in.clone()
-    # Check if smiles, inchi and inchikey are valid
-    if _check_fully_annotated(spectrum):
-        logger.warning("First run derive inchi_from_smiles, derive_inchikey_from_inchi and derive_smiles_from_inchi")
-    smiles = spectrum.get("smiles")
-    inchi = spectrum.get("inchi")
-    inchikey = spectrum.get("inchikey")
-
-    # Check if smiles and inchi match
-    if convert_smiles_to_inchi(smiles) != inchi:
-        # There is a mismatch between smiles and inchi
-        parent_mass = spectrum.get("parent_mass")
-        smiles_correct = _check_smiles_and_parent_mass_match(smiles, parent_mass, 0.1)
-        inchi_correct = _check_smiles_and_parent_mass_match(convert_inchi_to_smiles(inchi), parent_mass, 0.1)
-        if smiles_correct and inchi_correct:
-            logger.warning("The smiles and inchi are not matching, but both match the parent mass. "
-                           "Smiles = %s, inchi = %s, inchikey = %s", smiles, inchi, inchikey)
-            return spectrum
-        if smiles_correct and not inchi_correct:
-            # repair the inchi from the smiles
-            new_inchi = convert_smiles_to_inchi(smiles)
-            spectrum.set("inchi", new_inchi)
-            logger.info("The inchi has been changed from %s to %s"
-                        "The new inchi matches the parent mass, while the old one did not", inchi, new_inchi)
-        else:
-            # repair the smiles from the inchi
-            new_smiles = convert_inchi_to_smiles(inchi)
-            spectrum.set("smiles", new_smiles)
-            logger.info("The smiles has been changed from %s to %s, to match the inchi. "
-                        "The new smiles matches the parent mass, while the old one did not", smiles, new_smiles)
-
-    # Check if the inchikey matches the inchi
-    if inchikey[:14] == convert_inchi_to_inchikey(spectrum.get("inchi"))[:14]:
-        # The inchi, smiles and inchikey all already matched
-        return spectrum
-    # The smiles and inchi (now) match, but the inchikey is still wrong
-    # Repair inchikey
-    new_inchikey = convert_inchi_to_inchikey(spectrum.get("inchi"))
-    logger.info("The inchikey has been changed from %s to %s", inchikey, new_inchikey)
-    spectrum.set("inchikey", new_inchikey)
-    return spectrum
-
-
 def require_valid_annotation(spectrum: Spectrum):
     """Removes spectra that are not fully annotated (correct and matching, smiles, inchi and inchikey)"""
     if spectrum is None:
diff --git a/pyproject.toml b/pyproject.toml
index 07a8d7fc..3c9469de 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -42,7 +42,6 @@ rdkit = "^2023.3.2"
 pyyaml = "^6.0.1"
 deprecated = "^1.2.14"
 
-
 [tool.poetry.group.dev.dependencies]
 decorator = "^5.1.1"
 isort = "^5.12.0"
@@ -59,7 +58,7 @@ poetry-bumpversion = "^0.3.1"
 [tool.poetry.group.docs.dependencies]
 sphinxcontrib-apidoc = "^0.3.0"
 sphinx-rtd-theme = "^1.2.2"
-sphinx = "<7"
+sphinx = "^7"
 
 
 [build-system]
diff --git a/readthedocs/conf.py b/readthedocs/conf.py
index fcf34b61..46c6e91d 100644
--- a/readthedocs/conf.py
+++ b/readthedocs/conf.py
@@ -23,8 +23,8 @@ import matchms
 # -- Project information -----------------------------------------------------
 
 project = "matchms"
-copyright = "2020, Netherlands eScience Center"
-author = "Netherlands eScience Center"
+copyright = "2023, Düsseldorf University of Applied Sciences & Netherlands eScience Center"
+author = "matchms development team"
 
 
 # -- General configuration ---------------------------------------------------
diff --git a/readthedocs/index.rst b/readthedocs/index.rst
index a397ee12..2ea6361a 100644
--- a/readthedocs/index.rst
+++ b/readthedocs/index.rst
@@ -28,7 +28,7 @@ Installation
 
 Prerequisites:
 
-- Python 3.7, 3.8, or 3.9
+- Python 3.8 - 3.11
 - Anaconda
 
 Install matchms from Anaconda Cloud with
@@ -36,7 +36,7 @@ Install matchms from Anaconda Cloud with
 .. code-block:: console
 
   # install matchms in a new virtual environment to avoid dependency clashes
-  conda create --name matchms python=3.8
+  conda create --name matchms python=3.9
   conda activate matchms
   conda install --channel nlesc --channel bioconda --channel conda-forge matchms
 


Test patch:
diff --git a/tests/filtering/test_spectrum_processor.py b/tests/filtering/test_spectrum_processor.py
index 5c6ef7ed..1d9c5168 100644
--- a/tests/filtering/test_spectrum_processor.py
+++ b/tests/filtering/test_spectrum_processor.py
@@ -22,34 +22,53 @@ def spectrums():
 
 def test_filter_sorting_and_output():
     processing = SpectrumProcessor("default")
-    expected_filters = [
-        'make_charge_int',
-        'add_compound_name',
-        'derive_adduct_from_name',
-        'derive_formula_from_name',
-        'clean_compound_name',
-        'interpret_pepmass',
-        'add_precursor_mz',
-        'derive_ionmode',
-        'correct_charge',
-        'require_precursor_mz',
-        'add_parent_mass',
-        'harmonize_undefined_inchikey',
-        'harmonize_undefined_inchi',
-        'harmonize_undefined_smiles',
-        'repair_inchi_inchikey_smiles',
-        'normalize_intensities'
-        ]
-    actual_filters = [x.__name__ for x in processing.filters]
-    assert actual_filters == expected_filters
-    # 2nd way to access the filter names via processing_steps attribute:
+    expected_filters = ['make_charge_int',
+                        'add_compound_name',
+                        ('derive_adduct_from_name', {'remove_adduct_from_name': True}),
+                        ('derive_formula_from_name', {'remove_formula_from_name': True}),
+                        'clean_compound_name',
+                        'interpret_pepmass',
+                        'add_precursor_mz',
+                        'derive_ionmode',
+                        'correct_charge',
+                        ('require_precursor_mz', {'minimum_accepted_mz': 10.0}),
+                        ('add_parent_mass',
+                         {'estimate_from_adduct': True, 'overwrite_existing_entry': False}),
+                        ('harmonize_undefined_inchikey', {'aliases': None, 'undefined': ''}),
+                        ('harmonize_undefined_inchi', {'aliases': None, 'undefined': ''}),
+                        ('harmonize_undefined_smiles', {'aliases': None, 'undefined': ''}),
+                        'repair_inchi_inchikey_smiles',
+                        'normalize_intensities']
     assert processing.processing_steps == expected_filters
 
 
+@pytest.mark.parametrize("filter_step, expected", [
+    [("add_parent_mass", {'estimate_from_adduct': False}),
+     ('add_parent_mass', {'estimate_from_adduct': False, 'overwrite_existing_entry': False})],
+    ["derive_adduct_from_name",
+     ('derive_adduct_from_name', {'remove_adduct_from_name': True})],
+    [("require_correct_ionmode", {"ion_mode_to_keep": "both"}),
+     ("require_correct_ionmode", {"ion_mode_to_keep": "both"})],
+])
+def test_overwrite_default_settings(filter_step, expected):
+    """Test if both default settings and set settings are returned in processing steps"""
+    processor = SpectrumProcessor(None)
+    processor.add_filter(filter_step)
+    expected_filters = [expected]
+    assert processor.processing_steps == expected_filters
+
+
+def test_incomplete_parameters():
+    """Test if an error is raised when running an incomplete command"""
+    with pytest.raises(AssertionError):
+        processor = SpectrumProcessor(None)
+        processor.add_filter("require_correct_ionmode")
+
+
 def test_string_output():
     processing = SpectrumProcessor("minimal")
-    expected_str = "SpectrumProcessor\nProcessing steps:\n- make_charge_int\n- interpret_pepmass"\
-        "\n- derive_ionmode\n- correct_charge"
+    expected_str = "SpectrumProcessor\nProcessing steps:\n- make_charge_int\n- interpret_pepmass" \
+                   "\n- derive_ionmode\n- correct_charge"
     assert str(processing) == expected_str
 
 
@@ -62,7 +81,7 @@ def test_add_matchms_filter(metadata, expected):
     spectrum_in = SpectrumBuilder().with_metadata(metadata).build()
     processor = SpectrumProcessor("minimal")
     processor.add_matchms_filter(("require_correct_ionmode",
-                                 {"ion_mode_to_keep": "both"}))
+                                  {"ion_mode_to_keep": "both"}))
     spectrum = processor.process_spectrum(spectrum_in)
     if expected is None:
         assert spectrum is None
@@ -154,6 +173,28 @@ def test_adding_custom_filter_with_parameters(spectrums):
     assert spectrums[0].get("inchikey") == "NONSENSENONSENSE", "Custom filter not executed properly"
 
 
+@pytest.mark.parametrize("filter_position, expected", [
+    [0, 0],
+    [1, 1],
+    [2, 2],
+    [3, 3],
+    [None, 4],
+    [5, 4],
+    [6, 4]
+])
+def test_add_custom_filter_in_position(filter_position, expected):
+    def nonsense_inchikey_multiple(s, number):
+        s.set("inchikey", number * "NONSENSE")
+        return s
+
+    processor = SpectrumProcessor("minimal")
+    processor.add_custom_filter(nonsense_inchikey_multiple, {"number": 2},
+                                filter_position=filter_position)
+    filters = processor.filters
+
+    assert filters[expected].__name__ == "nonsense_inchikey_multiple"
+
+
 def test_add_filter_with_custom(spectrums):
     def nonsense_inchikey_multiple(s, number):
         s.set("inchikey", number * "NONSENSE")
@@ -162,6 +203,7 @@ def test_add_filter_with_custom(spectrums):
     processor = SpectrumProcessor("minimal")
     processor.add_filter((nonsense_inchikey_multiple, {"number": 2}))
     filters = processor.filters
+
     assert filters[-1].__name__ == "nonsense_inchikey_multiple"
     spectrums, _ = processor.process_spectrums(spectrums, create_report=True)
     assert spectrums[0].get("inchikey") == "NONSENSENONSENSE", "Custom filter not executed properly"
@@ -170,7 +212,7 @@ def test_add_filter_with_custom(spectrums):
 def test_add_filter_with_matchms_filter(spectrums):
     processor = SpectrumProcessor("minimal")
     processor.add_filter(("require_correct_ionmode",
-                         {"ion_mode_to_keep": "both"}))
+                          {"ion_mode_to_keep": "both"}))
     filters = processor.filters
     assert filters[-1].__name__ == "require_correct_ionmode"
     spectrums, _ = processor.process_spectrums(spectrums, create_report=True)