Skip to content

Commit 965e657

Browse files
committed
feat: failing to load a pattern will now raise an error instead of returning -1
1 parent 51cc053 commit 965e657

File tree

4 files changed

+52
-27
lines changed

4 files changed

+52
-27
lines changed

glassure/pattern.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def load(self, filename: str, skiprows: int = 0):
6464
6565
:param filename: path to the file
6666
:param skiprows: number of rows to skip when loading the data (header)
67+
:raises PatternLoadError: if the file cannot be loaded
6768
"""
6869
try:
6970
if filename.endswith(".chi"):
@@ -73,18 +74,25 @@ def load(self, filename: str, skiprows: int = 0):
7374
self.y = data.T[1]
7475
self.name = os.path.basename(filename).split(".")[:-1][0]
7576

76-
except ValueError:
77-
print("Wrong data format for pattern file! - " + filename)
78-
return -1
77+
except FileNotFoundError:
78+
raise PatternLoadError(filename, "File not found")
79+
except OSError as e:
80+
raise PatternLoadError(filename, f"OS error: {e}")
81+
except ValueError as e:
82+
raise PatternLoadError(filename, f"Wrong data format: {e}")
83+
except Exception as e:
84+
raise PatternLoadError(filename, f"Unexpected error: {e}")
7985

8086
@staticmethod
81-
def from_file(filename: str, skip_rows: int = 0) -> Pattern | int:
87+
def from_file(filename: str, skip_rows: int = 0) -> Pattern:
8288
"""
8389
Loads a pattern from a file. The file can be either a .xy or a .chi file. The .chi file will be loaded with
8490
skiprows=4 by default.
8591
8692
:param filename: path to the file
8793
:param skip_rows: number of rows to skip when loading the data (header)
94+
:return: Pattern object loaded from the file
95+
:raises PatternLoadError: if the file cannot be loaded
8896
"""
8997
try:
9098
if filename.endswith(".chi"):
@@ -95,9 +103,14 @@ def from_file(filename: str, skip_rows: int = 0) -> Pattern | int:
95103
name = os.path.basename(filename).split(".")[:-1][0]
96104
return Pattern(x, y, name)
97105

98-
except ValueError:
99-
print("Wrong data format for pattern file! - " + filename)
100-
return -1
106+
except FileNotFoundError:
107+
raise PatternLoadError(filename, "File not found")
108+
except OSError as e:
109+
raise PatternLoadError(filename, f"OS error: {e}")
110+
except ValueError as e:
111+
raise PatternLoadError(filename, f"Wrong data format: {e}")
112+
except Exception as e:
113+
raise PatternLoadError(filename, f"Unexpected error: {e}")
101114

102115
def save(self, filename: str, header: str = ""):
103116
"""
@@ -369,6 +382,17 @@ def __str__(self):
369382
)
370383

371384

385+
class PatternLoadError(Exception):
386+
"""Exception raised when a pattern file cannot be loaded."""
387+
388+
def __init__(self, filename: str, reason: str):
389+
self.filename = filename
390+
self.reason = reason
391+
392+
def __str__(self):
393+
return f"Failed to load pattern from file '{self.filename}': {self.reason}"
394+
395+
372396
def validate(value):
373397
"""
374398
Validates a numpy array. If the value is a list, it is converted to a numpy array.

tests/test_calc.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ def prepare_input():
1717
data_pattern = Pattern.from_file(data_path)
1818
bkg_pattern = Pattern.from_file(bkg_path)
1919

20-
if not isinstance(data_pattern, Pattern):
21-
raise ValueError(f"Failed to load data pattern from {data_path}")
22-
if not isinstance(bkg_pattern, Pattern):
23-
raise ValueError(f"Failed to load background pattern from {bkg_path}")
24-
2520
data_config, calculation_config = create_calculate_pdf_configs(
2621
data_pattern,
2722
composition={"Mg": 2, "Si": 1, "O": 4},

tests/test_optimization.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ def sample(data_path, bkg_path):
4848
"""Create a sample pattern by subtracting background from data."""
4949
data = Pattern.from_file(data_path)
5050
bkg = Pattern.from_file(bkg_path)
51-
assert isinstance(data, Pattern)
52-
assert isinstance(bkg, Pattern)
5351

5452
sample = data - bkg
5553
return sample.limit(1, 17)
@@ -169,8 +167,6 @@ def test_optimize_sq_fit_SiO2(sq, atomic_density):
169167
def test_optimize_density_SiO2(data_path, bkg_path):
170168
data = Pattern.from_file(data_path)
171169
background = Pattern.from_file(bkg_path)
172-
assert isinstance(data, Pattern)
173-
assert isinstance(background, Pattern)
174170
composition = {"Si": 1.0, "O": 2.0}
175171
density = 2.2
176172

@@ -198,8 +194,6 @@ def test_optimize_density_SiO2(data_path, bkg_path):
198194
def test_optimize_density_Mg2SiO4():
199195
data = Pattern.from_file(data_path_glass)
200196
background = Pattern.from_file(background_path_glass)
201-
assert isinstance(data, Pattern)
202-
assert isinstance(background, Pattern)
203197

204198
composition = {"Mg": 2.0, "Si": 1.0, "O": 4.0}
205199
density = 3.21
@@ -227,8 +221,6 @@ def test_optimize_density_Mg2SiO4():
227221
def test_optimize_density_method():
228222
data = Pattern.from_file(data_path_glass)
229223
background = Pattern.from_file(background_path_glass)
230-
assert isinstance(data, Pattern)
231-
assert isinstance(background, Pattern)
232224
composition = {"Mg": 2.0, "Si": 1.0, "O": 4.0}
233225
density = 3.21
234226

@@ -263,8 +255,6 @@ def test_optimize_density_method():
263255
def test_optimize_density_method_nelder():
264256
data = Pattern.from_file(data_path_glass)
265257
background = Pattern.from_file(background_path_glass)
266-
assert isinstance(data, Pattern)
267-
assert isinstance(background, Pattern)
268258

269259
composition = {"Mg": 2.0, "Si": 1.0, "O": 4.0}
270260
density = 3.21
@@ -310,8 +300,6 @@ def test_optimize_density_method_nelder():
310300
def test_optimize_density_vary_bkg_scaling():
311301
data = Pattern.from_file(data_path_glass)
312302
background = Pattern.from_file(background_path_glass)
313-
assert isinstance(data, Pattern)
314-
assert isinstance(background, Pattern)
315303

316304
composition = {"Mg": 2.0, "Si": 1.0, "O": 4.0}
317305
density = 3.21
@@ -342,8 +330,6 @@ def test_optimize_density_vary_bkg_scaling():
342330
def test_invalid_optimize_density_method():
343331
data = Pattern.from_file(data_path_glass)
344332
background = Pattern.from_file(background_path_glass)
345-
assert isinstance(data, Pattern)
346-
assert isinstance(background, Pattern)
347333

348334
composition = {"Mg": 2.0, "Si": 1.0, "O": 4.0}
349335
density = 3.21

tests/test_pattern.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# -*- coding: utf-8 -*-
22
import numpy as np
3+
import pytest
34
from pytest import approx
45
from pydantic import BaseModel
5-
from glassure.pattern import PydanticNpArray
6+
from glassure.pattern import PydanticNpArray, PatternLoadError
67

78
from glassure import Pattern
89

@@ -147,3 +148,22 @@ def test_pydantic_nparray_from_json():
147148
input = {"x": [1, 2, 3]}
148149
t = DummyModel(**input)
149150
assert np.array_equal(t.x, np.array([1, 2, 3]))
151+
152+
153+
def test_from_file_raises_exception_for_nonexistent_file():
154+
"""Test that from_file raises PatternLoadError for nonexistent files."""
155+
with pytest.raises(PatternLoadError) as exc_info:
156+
Pattern.from_file("nonexistent_file.xy")
157+
assert "nonexistent_file.xy" in str(exc_info.value)
158+
assert "File not found" in str(exc_info.value)
159+
160+
161+
def test_from_file_raises_exception_for_invalid_format(tmp_path):
162+
"""Test that from_file raises PatternLoadError for invalid file format."""
163+
invalid_file = tmp_path / "invalid.xy"
164+
invalid_file.write_text("not a valid pattern file\ninvalid data")
165+
166+
with pytest.raises(PatternLoadError) as exc_info:
167+
Pattern.from_file(str(invalid_file))
168+
assert "invalid.xy" in str(exc_info.value)
169+
assert "Wrong data format" in str(exc_info.value)

0 commit comments

Comments
 (0)