@@ -340,20 +340,78 @@ def __init__(
340340 don't match to within comp_tol.
341341 """
342342
343+ # Load CIF content as text
344+ if isinstance (filename , (str | Path )):
345+ with zopen (filename , mode = "rt" , encoding = "utf-8" , errors = "replace" ) as f :
346+ cif_string : str = cast ("str" , f .read ())
347+ elif isinstance (filename , StringIO ):
348+ warnings .warn (
349+ "Initializing CifParser from StringIO is deprecated, use `from_str()` instead." ,
350+ DeprecationWarning ,
351+ stacklevel = 2 ,
352+ )
353+ cif_string = filename .getvalue ()
354+ else :
355+ raise TypeError ("Unsupported file format. Expect str or Path." )
356+
357+ parser = type (self ).from_str (
358+ cif_string ,
359+ occupancy_tolerance = occupancy_tolerance ,
360+ site_tolerance = site_tolerance ,
361+ frac_tolerance = frac_tolerance ,
362+ check_cif = check_cif ,
363+ comp_tol = comp_tol ,
364+ )
365+
366+ self .__dict__ .update (parser .__dict__ )
367+
368+ @classmethod
369+ def from_str (
370+ cls ,
371+ cif_string : str ,
372+ * ,
373+ occupancy_tolerance : float = 1.0 ,
374+ site_tolerance : float = 1e-4 ,
375+ frac_tolerance : float = 1e-4 ,
376+ check_cif : bool = True ,
377+ comp_tol : float = 0.01 ,
378+ ) -> Self :
379+ """Create a CifParser from a string.
380+
381+ Args:
382+ cif_string (str): String representation of a CIF.
383+
384+ Returns:
385+ CifParser
386+ """
387+
388+ self = cls .__new__ (cls )
389+
390+ self ._cif = CifFile .from_str (cif_string )
391+
392+ # Take tolerances
393+ self ._occupancy_tolerance = occupancy_tolerance
394+ self ._site_tolerance = site_tolerance
395+ self ._frac_tolerance = frac_tolerance
396+
397+ # Options related to checking CIFs for missing elements
398+ # or incorrect stoichiometries
399+ self .check_cif = check_cif
400+ self .comp_tol = comp_tol
401+
343402 def is_magcif () -> bool :
344403 """Check if a file is a magCIF file (heuristic)."""
345404 # Doesn't seem to be a canonical way to test if file is magCIF or
346405 # not, so instead check for magnetic symmetry datanames
347- prefixes = [
406+ prefixes = (
348407 "_space_group_magn" ,
349408 "_atom_site_moment" ,
350409 "_space_group_symop_magn" ,
351- ]
410+ )
352411 for data in self ._cif .data .values ():
353412 for key in data .data :
354- for prefix in prefixes :
355- if prefix in key :
356- return True
413+ if any (prefix in key for prefix in prefixes ):
414+ return True
357415 return False
358416
359417 def is_magcif_incommensurate () -> bool :
@@ -364,57 +422,28 @@ def is_magcif_incommensurate() -> bool:
364422 # Doesn't seem to be a canonical way to test if magCIF file
365423 # describes incommensurate structure or not, so instead check
366424 # for common datanames
367- if not self .feature_flags [ "magcif" ] :
425+ if not self .feature_flags . get ( "magcif" , False ) :
368426 return False
369427 prefixes = ["_cell_modulation_dimension" , "_cell_wave_vector" ]
370428 for data in self ._cif .data .values ():
371429 for key in data .data :
372- for prefix in prefixes :
373- if prefix in key :
374- return True
430+ if any (prefix in key for prefix in prefixes ):
431+ return True
375432 return False
376433
377- # Take tolerances
378- self ._occupancy_tolerance = occupancy_tolerance
379- self ._site_tolerance = site_tolerance
380- self ._frac_tolerance = frac_tolerance
381-
382- # Read CIF file
383- if isinstance (filename , str | Path ):
384- self ._cif = CifFile .from_file (filename )
385- elif isinstance (filename , StringIO ):
386- self ._cif = CifFile .from_str (filename .read ())
387- else :
388- raise TypeError ("Unsupported file format." )
389-
390- # Options related to checking CIFs for missing elements
391- # or incorrect stoichiometries
392- self .check_cif = check_cif
393- self .comp_tol = comp_tol
394-
395434 # Store features from non-core CIF dictionaries, e.g. magCIF
396- self .feature_flags : dict [Literal [" magcif" , " magcif_incommensurate" ], bool ] = {}
435+ self .feature_flags = cast ( " dict[Literal[' magcif', ' magcif_incommensurate' ], bool]" , {})
397436 self .feature_flags ["magcif" ] = is_magcif ()
398437 self .feature_flags ["magcif_incommensurate" ] = is_magcif_incommensurate ()
399438
400439 # Store warnings during parsing
401- self .warnings : list [str ] = []
440+ self .warnings = cast ( " list[str]" , [])
402441
403442 # Pass individual CifBlocks to _sanitize_data
404443 for key in self ._cif .data :
405444 self ._cif .data [key ] = self ._sanitize_data (self ._cif .data [key ])
406445
407- @classmethod
408- def from_str (cls , cif_string : str , ** kwargs ) -> Self :
409- """Create a CifParser from a string.
410-
411- Args:
412- cif_string (str): String representation of a CIF.
413-
414- Returns:
415- CifParser
416- """
417- return cls (StringIO (cif_string ), ** kwargs )
446+ return self
418447
419448 def _sanitize_data (self , data : CifBlock ) -> CifBlock :
420449 """Some CIF files do not conform to spec. This method corrects
0 commit comments