@@ -184,8 +184,8 @@ class ProteomeSearchSpace(BaseModel):
184184 fasta_file : Path
185185 min_length : int = 8
186186 max_length : int = 30
187- min_precursor_mz : float | None = 0
188- max_precursor_mz : float | None = np .inf
187+ min_precursor_mz : float = 0
188+ max_precursor_mz : float = np .inf
189189 cleavage_rule : str = "trypsin"
190190 missed_cleavages : int = 2
191191 semi_specific : bool = False
@@ -194,7 +194,31 @@ class ProteomeSearchSpace(BaseModel):
194194 max_variable_modifications : int = 3
195195 charges : list [int ] = [2 , 3 ]
196196
197- _peptidoform_spaces : list [_PeptidoformSearchSpace ] = PrivateAttr (default_factory = list )
197+ _peptidoform_spaces : list [_PeptidoformSearchSpace ] | None = PrivateAttr (default = None )
198+
199+ @field_validator ("min_precursor_mz" , mode = "before" )
200+ @classmethod
201+ def _coerce_min_precursor_mz (cls , v ):
202+ return 0.0 if v is None else v
203+
204+ @field_validator ("max_precursor_mz" , mode = "before" )
205+ @classmethod
206+ def _coerce_max_precursor_mz (cls , v ):
207+ return np .inf if v is None else v
208+
209+ @field_validator ("min_length" )
210+ @classmethod
211+ def _validate_min_length (cls , v ):
212+ if v > 3 :
213+ return v
214+ raise ValueError ("Minimum peptide length must be greater than 3." )
215+
216+ @field_validator ("max_length" )
217+ @classmethod
218+ def _validate_max_length (cls , v ):
219+ if v <= 100 :
220+ return v
221+ raise ValueError ("Maximum peptide length must be less than or equal to 100." )
198222
199223 @field_validator ("modifications" )
200224 @classmethod
@@ -219,7 +243,7 @@ def _validate_unspecific_cleavage(self):
219243 return self
220244
221245 def __len__ (self ):
222- if not self ._peptidoform_spaces :
246+ if self ._peptidoform_spaces is None :
223247 raise ValueError ("Search space must be built before length can be determined." )
224248 return sum (len (pep_space ) for pep_space in self ._peptidoform_spaces )
225249
@@ -276,7 +300,7 @@ def __iter__(self) -> Generator[PSM, None, None]: # type: ignore[ty:invalid-met
276300
277301 """
278302 # Build search space if not already built
279- if not self ._peptidoform_spaces :
303+ if self ._peptidoform_spaces is None :
280304 raise ValueError ("Search space must be built before PSMs can be generated." )
281305
282306 spectrum_id = 0
@@ -295,25 +319,26 @@ def filter_psms_by_mz(self, psms: PSMList) -> PSMList:
295319 psm_list = [
296320 psm
297321 for psm in psms
298- if self .min_precursor_mz <= psm .peptidoform .theoretical_mz <= self .max_precursor_mz # type: ignore[ty:unsupported-operator]
322+ if psm .peptidoform .theoretical_mz is not None
323+ and self .min_precursor_mz <= psm .peptidoform .theoretical_mz <= self .max_precursor_mz
299324 ]
300325 )
301326
302327 def _digest_fasta (self , processes : int = 1 ):
303328 """Digest FASTA file to peptides and populate search space."""
304329 # Convert to string to avoid issues with Path objects
305- self . fasta_file = str (self .fasta_file ) # type: ignore[ty:invalid-assignment]
330+ fasta_file = str (self .fasta_file )
306331 n_proteins = _count_fasta_entries (self .fasta_file )
307332 if self .add_decoys :
308333 fasta_db = pyteomics .fasta .decoy_db (
309- self . fasta_file ,
334+ fasta_file ,
310335 mode = "reverse" ,
311336 decoy_only = False ,
312337 keep_nterm = True ,
313338 )
314339 n_proteins *= 2
315340 else :
316- fasta_db = pyteomics .fasta .FASTA (self . fasta_file )
341+ fasta_db = pyteomics .fasta .FASTA (fasta_file )
317342
318343 # Read proteins and digest to peptides
319344 with _get_pool (processes ) as pool :
@@ -335,6 +360,7 @@ def _digest_fasta(self, processes: int = 1):
335360
336361 def _remove_redundancy (self ):
337362 """Remove redundancy in peptides and combine protein lists."""
363+ assert self ._peptidoform_spaces is not None # for type checker
338364 peptide_dict = dict ()
339365 for peptide in track (
340366 self ._peptidoform_spaces ,
@@ -351,6 +377,7 @@ def _remove_redundancy(self):
351377
352378 def _add_modifications (self , processes : int = 1 ):
353379 """Add modifications to peptides in search space."""
380+ assert self ._peptidoform_spaces is not None # for type checker
354381 modifications_by_target = _restructure_modifications_by_target (self .modifications )
355382 modification_options = []
356383 with _get_pool (processes ) as pool :
@@ -373,6 +400,7 @@ def _add_modifications(self, processes: int = 1):
373400
374401 def _add_charges (self ):
375402 """Add charge permutations to peptides in search space."""
403+ assert self ._peptidoform_spaces is not None # for type checker
376404 for peptide in track (
377405 self ._peptidoform_spaces ,
378406 description = "Adding charge permutations..." ,
0 commit comments