Repository: matchms/matchms
Pull request #640 (2024-05-08 07:40:39)

Propose dependency changes


Patch:
diff --git a/.github/workflows/CI_build.yml b/.github/workflows/CI_build.yml
index 2327da4a..4a6c2481 100644
--- a/.github/workflows/CI_build.yml
+++ b/.github/workflows/CI_build.yml
@@ -108,7 +108,7 @@ jobs:
       fail-fast: false
       matrix:
         os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
-        python-version: ['3.8', '3.9', '3.10', '3.11']
+        python-version: ['3.9', '3.10', '3.11', '3.12']
     steps:
       - uses: actions/checkout@v4
       - name: Set up Python
@@ -143,7 +143,7 @@ jobs:
         uses: conda-incubator/setup-miniconda@v3
         with:
           auto-update-conda: true
-          python-version: 3.8
+          python-version: 3.9
       - name: Show conda config
         shell: bash -l {0}
         run: |
diff --git a/matchms/Metadata.py b/matchms/Metadata.py
index ad3e6862..e0c3e798 100644
--- a/matchms/Metadata.py
+++ b/matchms/Metadata.py
@@ -86,8 +86,8 @@ class Metadata:
     def harmonize_keys(self):
         """Runs default harmonization of metadata.
 
-        Method harmonized metadata field names which includes setting them to lower-case
-        and runing a series of regex replacements followed by default field name
+        Method harmonized metadata field names which include setting them to lower-case
+        and running a series of regex replacements followed by the default field name
         replacements (such as precursor_mass --> precursor_mz).
 
         """
@@ -98,7 +98,7 @@ class Metadata:
         """Runs default harmonization of metadata.
 
         This includes harmonizing entries for ionmode, retention time and index,
-        charge, as well as the removal of invalid entried ("", "NA", "N/A", "NaN").
+        charge, as well as the removal of invalid entries ("", "NA", "N/A", "NaN").
         """
         metadata_filtered = _interpret_pepmass_metadata(self.data)
         metadata_filtered = _add_precursor_mz_metadata(metadata_filtered)
@@ -121,7 +121,7 @@ class Metadata:
             metadata_filtered["charge"] = charge_int
 
         invalid_entries = ["", "NA", "N/A", "NaN"]
-        metadata_filtered = {k:v for k,v in metadata_filtered.items() if v not in invalid_entries}
+        metadata_filtered = {k: v for k, v in metadata_filtered.items() if not (isinstance(v, str) and v in invalid_entries)}
 
         self.data = metadata_filtered
 
diff --git a/matchms/Scores.py b/matchms/Scores.py
index 6c84e44e..1bc95f59 100644
--- a/matchms/Scores.py
+++ b/matchms/Scores.py
@@ -206,8 +206,13 @@ class Scores:
         """
         if name is None and len(self.score_names) > 1 and sort is True:
             raise IndexError("For sorting, score must be specified")
-        assert reference in self.references, "Given input not found in references."
-        selected_idx = int(np.where(self.references == reference)[0])
+
+        idx_array = np.where(self.references == reference)[0]
+        if idx_array.size > 0:
+            selected_idx = int(idx_array[0])
+        else:
+            raise ValueError("Given input not found in references.")
+
         _, r, scores_for_ref = self._scores[selected_idx, :]
         if sort:
             if name is None:
@@ -270,8 +275,13 @@ class Scores:
         """
         if name is None and len(self.score_names) > 1 and sort is True:
             raise IndexError("For sorting, score must be specified")
-        assert query in self.queries, "Given input not found in queries."
-        selected_idx = int(np.where(self.queries == query)[0])
+
+        idx_array = np.where(self.queries == query)[0]
+        if idx_array.size > 0:
+            selected_idx = int(idx_array[0])
+        else:
+            raise ValueError("Given input not found in queries.")
+
         c, _, scores_for_query = self._scores[:, selected_idx]
         if sort:
             if name is None:
diff --git a/matchms/filtering/SpectrumProcessor.py b/matchms/filtering/SpectrumProcessor.py
index affea0e8..4c48c6bb 100644
--- a/matchms/filtering/SpectrumProcessor.py
+++ b/matchms/filtering/SpectrumProcessor.py
@@ -318,7 +318,12 @@ class ProcessingReport:
             if filter_name not in processing_report["filter"].values:
                 processing_report.loc[len(processing_report)] = {"filter": filter_name}
 
-        processing_report = processing_report.set_index("filter").fillna(0)
+        try:
+            with pd.option_context("future.no_silent_downcasting", True):
+                processing_report = processing_report.set_index("filter").infer_objects().fillna(0)
+        except pd.errors.OptionError:
+            processing_report = processing_report.set_index("filter").fillna(0)
+
         return processing_report.astype(int)
 
     def __str__(self):
diff --git a/matchms/networking/SimilarityNetwork.py b/matchms/networking/SimilarityNetwork.py
index 8bc98852..e8018ac6 100644
--- a/matchms/networking/SimilarityNetwork.py
+++ b/matchms/networking/SimilarityNetwork.py
@@ -107,8 +107,13 @@ class SimilarityNetwork:
         if score_name is None:
             score_name = scores.scores.guess_score_name()
         assert self.top_n >= self.max_links, "top_n must be >= max_links"
-        assert np.all(scores.queries == scores.references), \
-            "Expected symmetric scores object with queries==references"
+
+        if scores.queries.shape != scores.references.shape:
+            raise TypeError("Expected symmetric scores")
+
+        if not np.all(scores.queries == scores.references):
+            raise ValueError("Queries and references do not match")
+
         unique_ids = list({s.get(self.identifier_key) for s in scores.queries})
 
         # Initialize network graph, add nodes
diff --git a/pyproject.toml b/pyproject.toml
index 6dc5cc5d..923e10ac 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -24,23 +24,23 @@ classifiers = [
 ]
 
 [tool.poetry.dependencies]
-python = ">=3.8,<3.12"
-numpy = "<1.27"
-scipy = "<1.11"
-pandas = "^2.0.3"
+python = ">=3.9,<3.13"
+numpy = ">1.22, <1.27"
+scipy = ">=1.11"
+pandas = ">=2.0.3"
 pillow = "!=9.4.0"
 lxml = "^4.9.3"
-matplotlib = "^3.7.2"
-networkx = "^3.1"
+matplotlib = ">=3.7"
+networkx = ">=3.0"
 numba = ">=0.57.1,<0.60"
-pickydict = "^0.4.0"
-pyteomics = "^4.6"
-requests = "^2.31.0"
-sparsestack = "^0.4.1"
-tqdm = "^4.65.0"
-rdkit = ">=2023.3.2,<2023.9.5"
-pyyaml = "^6.0.1"
-deprecated = "^1.2.14"
+pickydict = ">=0.4.0"
+pyteomics = ">=4.6"
+requests = ">=2.31.0"
+sparsestack = ">=0.4.1"
+tqdm = ">=4.65.0"
+rdkit = ">=2023.9.6"
+pyyaml = ">=6.0.1"
+deprecated = ">=1.2.14"
 pubchempy = "*"
 
 [tool.poetry.group.dev.dependencies]


Test patch:
diff --git a/tests/networking/test_SimilarityNetwork.py b/tests/networking/test_SimilarityNetwork.py
index e3605a07..ec073241 100644
--- a/tests/networking/test_SimilarityNetwork.py
+++ b/tests/networking/test_SimilarityNetwork.py
@@ -63,6 +63,18 @@ def create_dummy_scores_symmetric():
     return scores
 
 
+def create_dummy_scores_symmetric_faulty():
+    scores = create_dummy_scores_symmetric()
+    faulty_spec = Spectrum(mz=np.array([100, 400.]),
+                           intensities=np.array([0.5, 0.1 * 4]),
+                           metadata={"spectrum_id": 'query_spec_400',
+                                     "fingerprint": np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 0], [1, 0, 1]]),
+                                     "smiles": 'CC1=C(C=C(C=C1)NC(=O)N(C)C)Cl',
+                                     "precursor_mz": 110 + 50 * 4})
+    scores.queries[0] = faulty_spec
+
+    return scores
+
 def create_dummy_scores_symmetric_modified_cosine():
     spectrums = create_dummy_spectrums()
 
@@ -76,10 +88,17 @@ def test_create_network_symmetric_wrong_input():
     """Test if function is used with non-symmetric scores object"""
     scores = create_dummy_scores()
     msnet = SimilarityNetwork()
-    with pytest.raises(AssertionError) as msg:
+    with pytest.raises(TypeError) as msg:
+        msnet.create_network(scores)
+
+    expected_msg = "Expected symmetric scores"
+    assert expected_msg in str(msg), "Expected different exception"
+
+    scores = create_dummy_scores_symmetric_faulty()
+    with pytest.raises(ValueError) as msg:
         msnet.create_network(scores)
 
-    expected_msg = "Expected symmetric scores object with queries==references"
+    expected_msg = "Queries and references do not match"
     assert expected_msg in str(msg), "Expected different exception"
 
 
diff --git a/tests/test_scores.py b/tests/test_scores.py
index 91ef5c2b..f12adc9b 100644
--- a/tests/test_scores.py
+++ b/tests/test_scores.py
@@ -228,6 +228,22 @@ def test_scores_by_referencey_non_tuple_score():
     assert selected_scores == expected_result, "Expected different scores."
 
 
+def test_scores_by_references_exception():
+    spectrum_1, spectrum_2, spectrum_3, spectrum_4 = spectra()
+    references = [spectrum_1, spectrum_2, spectrum_3]
+    queries = [spectrum_3, spectrum_4]
+
+    builder = SpectrumBuilder()
+    faulty_spectrum = builder.with_mz(np.array([200, 350, 400.])).with_intensities(
+        np.array([0.7, 0.2, 0.1])).with_metadata({'id': 'spectrum5'}).build()
+
+    scores = calculate_scores(references, queries, CosineGreedy())
+    name_score = scores.score_names[0]
+
+    with pytest.raises(ValueError, match="Given input not found in references."):
+        scores.scores_by_reference(faulty_spectrum, name_score)
+
+
 def test_scores_by_query():
     "Test scores_by_query method."
     spectrum_1, spectrum_2, spectrum_3, spectrum_4 = spectra()
@@ -296,6 +312,22 @@ def test_sort_without_name_exception():
     assert "For sorting, score must be specified" in exception.value.args[0]
 
 
+def test_scores_by_query_exception():
+    spectrum_1, spectrum_2, spectrum_3, spectrum_4 = spectra()
+    references = [spectrum_1, spectrum_2, spectrum_3]
+    queries = [spectrum_2, spectrum_3, spectrum_4]
+
+    builder = SpectrumBuilder()
+    faulty_spectrum = builder.with_mz(np.array([200, 350, 400.])).with_intensities(
+        np.array([0.7, 0.2, 0.1])).with_metadata({'id': 'spectrum5'}).build()
+
+    scores = calculate_scores(references, queries, CosineGreedy())
+    name_score = scores.score_names[0]
+
+    with pytest.raises(ValueError, match="Given input not found in queries."):
+        scores.scores_by_query(faulty_spectrum, name_score)
+
+
 @pytest.mark.parametrize(
     "similarity_function_a,similarity_function_b",
     [(CosineGreedy(), IntersectMz()), (IntersectMz(), CosineGreedy())])