Skip to content

Commit 7134328

Browse files
authored
Deprecate CifParser init support of filename as StringIO (#4531)
* move cif parsing from __init__ of CifParser to from_str * deprecate support of StringIO in __init__ of CifParser * revert support of TextIOWrapper * Revert "revert support of TextIOWrapper" This reverts commit 1911a75.
1 parent b1ea39b commit 7134328

File tree

2 files changed

+80
-40
lines changed

2 files changed

+80
-40
lines changed

src/pymatgen/io/cif.py

Lines changed: 69 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -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

tests/io/test_cif.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
from io import StringIO
4+
35
import numpy as np
46
import pytest
57
from pytest import approx
@@ -168,6 +170,15 @@ def test_cif_parser(self):
168170
for struct in parser.parse_structures():
169171
assert struct.formula == "V4 O6"
170172

173+
# Test init from StringIO
174+
with open(f"{TEST_FILES_DIR}/cif/V2O3.cif", encoding="utf-8") as f:
175+
cif_text = f.read()
176+
177+
with pytest.warns(DeprecationWarning, match="Initializing CifParser from StringIO"):
178+
parser = CifParser(StringIO(cif_text))
179+
180+
for struct in parser.parse_structures():
181+
assert struct.formula == "V4 O6"
171182
bibtex_str = """
172183
@article{cifref0,
173184
author = "Andersson, G.",

0 commit comments

Comments
 (0)