Repository: pymzml/pymzML

Issue:
Cannot get precursor information from MS3 spectra
Hi! Thank you for this software--it's been tremendously useful for my work.

**Describe the bug**
Cannot get precursor information from Spectrum object.

**To Reproduce**
Steps to reproduce the behavior:

```
import pymzml
run = pymzml.run.Reader('KL-NB1-12_A.mzML', MS_precisions={1: 1e-6, 2: 1e-6, 3: 1e-6})
# get some spectrum, ms3, with MS level == 3
ms3.precursors
```

```
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-25a2f3d1799b> in <module>
----> 1 ms3.precursors

/usr/local/miniconda3/lib/python3.8/site-packages/pymzml/spec.py in precursors(self)
    169             precursor(list): list of precursor ids for this spectrum.
    170         """
--> 171         if self._precursors is None:
    172             precursors = self.element.findall(
    173                 "./{ns}precursorList/{ns}precursor".format(ns=self.ns)

AttributeError: 'Spectrum' object has no attribute '_precursors'
```

**Expected behavior**
spectrum.precursors should return a list of SpectrumRef ids.

**Desktop (please complete the following information):**
 - OS: Linux 5.4.0-53-generic #59-Ubuntu SMP x86_64 GNU/Linux

**Additional context**
It appears that the issue is self._precursors is never initialized in the Spectrum class. Adding
`self._precursors = None` appears to fix.


Comments:

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b1834c7..ae7c981 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
 repos:
 -   repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v2.5.0
+    rev: v3.3.0
     hooks:
     -   id: trailing-whitespace
     -   id: end-of-file-fixer
diff --git a/pymzml/spec.py b/pymzml/spec.py
index 8d81573..9740fa8 100755
--- a/pymzml/spec.py
+++ b/pymzml/spec.py
@@ -160,26 +160,6 @@ class MS_Spectrum(object):
         else:
             raise Exception("Unknown data Type ({0})".format(d_type))
 
-    @property
-    def precursors(self):
-        """
-        List the precursor information of this spectrum, if available.
-
-        Returns:
-            precursor(list): list of precursor ids for this spectrum.
-        """
-        if self._precursors is None:
-            precursors = self.element.findall(
-                "./{ns}precursorList/{ns}precursor".format(ns=self.ns)
-            )
-            self._precursors = []
-            for prec in precursors:
-                spec_ref = prec.get("spectrumRef")
-                self._precursors.append(
-                    regex_patterns.SPECTRUM_ID_PATTERN.search(spec_ref).group(1)
-                )
-        return self._precursors
-
     def _get_encoding_parameters(self, array_type):
         """
         Find the correct parameter for decoding and return them as tuple.
@@ -409,7 +389,6 @@ class Spectrum(MS_Spectrum):
             "_index",
             "_measured_precision",
             "_peaks",
-            "_precursors",
             "_profile",
             "_reprofiled_peaks",
             "_t_mass_set",
@@ -936,10 +915,14 @@ class Spectrum(MS_Spectrum):
             selected_precursor_cs = self.element.findall(
                 ".//*[@accession='MS:1000041']"
             )
+            precursors = self.element.findall(
+                "./{ns}precursorList/{ns}precursor".format(ns=self.ns)
+            )
 
             mz_values = []
             i_values = []
             charges = []
+            ids = []
             for obj in selected_precursor_mzs:
                 mz = obj.get("value")
                 mz_values.append(float(mz))
@@ -949,10 +932,19 @@ class Spectrum(MS_Spectrum):
             for obj in selected_precursor_cs:
                 c = obj.get("value")
                 charges.append(int(c))
+            for prec in precursors:
+                spec_ref = prec.get("spectrumRef")
+                if spec_ref is not None:
+                    ids.append(
+                        regex_patterns.SPECTRUM_ID_PATTERN.search(spec_ref).group(1)
+                    )
+                else:
+                    ids.append(None)
+                # ids.append()
             self._selected_precursors = []
             for pos, mz in enumerate(mz_values):
                 dict_2_save = {"mz": mz}
-                for key, list_of_values in [("i", i_values), ("charge", charges)]:
+                for key, list_of_values in [("i", i_values), ("charge", charges), ('precursor id', ids)]:
                     try:
                         dict_2_save[key] = list_of_values[pos]
                     except:
@@ -960,6 +952,26 @@ class Spectrum(MS_Spectrum):
                 self._selected_precursors.append(dict_2_save)
 
         return self._selected_precursors
+    
+    @property
+    def precursors(self):
+        """
+        List the precursor information of this spectrum, if available.
+        Returns:
+            precursor(list): list of precursor ids for this spectrum.
+        """
+        self.deprecation_warning(sys._getframe().f_code.co_name)
+        if self._precursors is None:
+            precursors = self.element.findall(
+                "./{ns}precursorList/{ns}precursor".format(ns=self.ns)
+            )
+            self._precursors = []
+            for prec in precursors:
+                spec_ref = prec.get("spectrumRef")
+                self._precursors.append(
+                    regex_patterns.SPECTRUM_ID_PATTERN.search(spec_ref).group(1)
+                )
+        return self._precursors
 
     def remove_precursor_peak(self):
         peaks = self.peaks("centroided")
@@ -1627,6 +1639,7 @@ class Spectrum(MS_Spectrum):
             "removeNoise": "remove_noise",
             "newPlot": "new_plot",
             "centroidedPeaks": "peaks",
+            "precursors": "selected_precursors",
         }
         warnings.warn(
             """


Test patch:
diff --git a/tests/ms2_spec_test.py b/tests/ms2_spec_test.py
index 42aab35..9984dcf 100755
--- a/tests/ms2_spec_test.py
+++ b/tests/ms2_spec_test.py
@@ -47,7 +47,7 @@ class SpectrumMS2Test(unittest.TestCase):
         self.assertIsInstance(selected_precursor[0]["i"], float)
         self.assertIsInstance(selected_precursor[0]["charge"], int)
         self.assertEqual(
-            selected_precursor, [{"mz": 443.711242675781, "i": 0.0, "charge": 2}]
+            selected_precursor, [{"mz": 443.711242675781, "i": 0.0, "charge": 2, 'precursor id': None}]
         )
 
     def test_ion_mode(self):