Skip to content

Commit 3efd93b

Browse files
authored
Merge pull request #209 from bobleesj/scattering-obj-valid
Check valid x and y array lenghts and xtypes in `insert_scattering_quantity`
2 parents 77c0b9e + 9bf5329 commit 3efd93b

File tree

3 files changed

+104
-28
lines changed

3 files changed

+104
-28
lines changed

Diff for: news/scattering-obj-valid.rst

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* validate xtype belongs to XQUANTITIES during DiffractionObject init
4+
5+
**Changed:**
6+
7+
* <news item>
8+
9+
**Deprecated:**
10+
11+
* <news item>
12+
13+
**Removed:**
14+
15+
* <news item>
16+
17+
**Fixed:**
18+
19+
* <news item>
20+
21+
**Security:**
22+
23+
* <news item>

Diff for: src/diffpy/utils/diffraction_objects.py

+44-17
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,15 @@
2121

2222
def _xtype_wmsg(xtype):
2323
return (
24-
f"I don't know how to handle the xtype, '{xtype}'. Please rerun specifying an "
25-
f"xtype from {*XQUANTITIES, }"
24+
f"I don't know how to handle the xtype, '{xtype}'. "
25+
f"Please rerun specifying an xtype from {*XQUANTITIES, }"
26+
)
27+
28+
29+
def _setter_wmsg(attribute):
30+
return (
31+
f"Direct modification of attribute '{attribute}' is not allowed. "
32+
f"Please use 'insert_scattering_quantity' to modify '{attribute}'.",
2633
)
2734

2835

@@ -45,6 +52,7 @@ def __init__(
4552
xarray = np.empty(0)
4653
if yarray is None:
4754
yarray = np.empty(0)
55+
4856
self.insert_scattering_quantity(xarray, yarray, xtype)
4957

5058
def __eq__(self, other):
@@ -186,11 +194,16 @@ def all_arrays(self):
186194
return self._all_arrays
187195

188196
@all_arrays.setter
189-
def all_arrays(self, value):
190-
raise AttributeError(
191-
"Direct modification of attribute 'all_arrays' is not allowed."
192-
"Please use 'insert_scattering_quantity' to modify `all_arrays`."
193-
)
197+
def all_arrays(self, _):
198+
raise AttributeError(_setter_wmsg("all_arrays"))
199+
200+
@property
201+
def input_xtype(self):
202+
return self._input_xtype
203+
204+
@input_xtype.setter
205+
def input_xtype(self, _):
206+
raise AttributeError(_setter_wmsg("input_xtype"))
194207

195208
def set_angles_from_list(self, angles_list):
196209
self.angles = angles_list
@@ -261,7 +274,7 @@ def _set_array_from_range(self, begin, end, step_size=None, n_steps=None):
261274

262275
def get_array_index(self, value, xtype=None):
263276
"""
264-
returns the index of the closest value in the array associated with the specified xtype
277+
Return the index of the closest value in the array associated with the specified xtype.
265278
266279
Parameters
267280
----------
@@ -276,7 +289,7 @@ def get_array_index(self, value, xtype=None):
276289
"""
277290

278291
if xtype is None:
279-
xtype = self.input_xtype
292+
xtype = self._input_xtype
280293
array = self.on_xtype(xtype)[0]
281294
if len(array) == 0:
282295
raise ValueError(f"The '{xtype}' array is empty. Please ensure it is initialized.")
@@ -333,9 +346,18 @@ def insert_scattering_quantity(
333346
Nothing. Updates the object in place.
334347
335348
"""
349+
350+
# Check xarray and yarray have the same length
351+
if len(xarray) != len(yarray):
352+
raise ValueError(
353+
"'xarray' and 'yarray' must have the same length. "
354+
"Please re-initialize 'DiffractionObject' or re-run the method 'insert_scattering_quantity' "
355+
"with 'xarray' and 'yarray' of identical length."
356+
)
357+
336358
self._set_xarrays(xarray, xtype)
337359
self._all_arrays[:, 0] = yarray
338-
self.input_xtype = xtype
360+
self._input_xtype = xtype
339361
# only update these optional values if non-empty quantities are passed to avoid overwriting
340362
# valid data inadvertently
341363
if metadata:
@@ -347,12 +369,17 @@ def insert_scattering_quantity(
347369
if wavelength is not None:
348370
self.wavelength = wavelength
349371

372+
# Check xtype is valid. An empty string is the default value.
373+
if xtype != "":
374+
if xtype not in XQUANTITIES:
375+
raise ValueError(_xtype_wmsg(xtype))
376+
350377
def _get_original_array(self):
351-
if self.input_xtype in QQUANTITIES:
378+
if self._input_xtype in QQUANTITIES:
352379
return self.on_q(), "q"
353-
elif self.input_xtype in ANGLEQUANTITIES:
380+
elif self._input_xtype in ANGLEQUANTITIES:
354381
return self.on_tth(), "tth"
355-
elif self.input_xtype in DQUANTITIES:
382+
elif self._input_xtype in DQUANTITIES:
356383
return self.on_d(), "d"
357384

358385
def on_q(self):
@@ -365,8 +392,8 @@ def on_d(self):
365392
return [self.all_arrays[:, 3], self.all_arrays[:, 0]]
366393

367394
def scale_to(self, target_diff_object, xtype=None, xvalue=None):
368-
f"""
369-
returns a new diffraction object which is the current object but recaled in y to the target
395+
"""
396+
Return a new diffraction object which is the current object but recaled in y to the target
370397
371398
Parameters
372399
----------
@@ -401,8 +428,8 @@ def scale_to(self, target_diff_object, xtype=None, xvalue=None):
401428
return scaled
402429

403430
def on_xtype(self, xtype):
404-
f"""
405-
return a list of two 1D np array with x and y data, raise an error if the specified xtype is invalid
431+
"""
432+
Return a list of two 1D np array with x and y data, raise an error if the specified xtype is invalid
406433
407434
Parameters
408435
----------

Diff for: tests/test_diffraction_objects.py

+37-11
Original file line numberDiff line numberDiff line change
@@ -211,16 +211,15 @@ def test_on_xtype():
211211
assert np.allclose(test.on_xtype("d"), [np.array([12.13818, 6.28319]), np.array([1, 2])])
212212

213213

214-
def test_on_xtype_bad():
215-
test = DiffractionObject()
214+
def test_init_invalid_xtype():
216215
with pytest.raises(
217216
ValueError,
218217
match=re.escape(
219-
f"I don't know how to handle the xtype, 'invalid'. Please rerun specifying an "
220-
f"xtype from {*XQUANTITIES, }"
218+
f"I don't know how to handle the xtype, 'invalid_type'. "
219+
f"Please rerun specifying an xtype from {*XQUANTITIES, }"
221220
),
222221
):
223-
test.on_xtype("invalid")
222+
DiffractionObject(xtype="invalid_type")
224223

225224

226225
params_index = [
@@ -287,7 +286,7 @@ def test_dump(tmp_path, mocker):
287286
{
288287
"_all_arrays": np.empty(shape=(0, 4)), # instantiate empty
289288
"metadata": {},
290-
"input_xtype": "",
289+
"_input_xtype": "",
291290
"name": "",
292291
"scat_quantity": None,
293292
"qmin": np.float64(np.inf),
@@ -304,7 +303,7 @@ def test_dump(tmp_path, mocker):
304303
{
305304
"_all_arrays": np.empty(shape=(0, 4)),
306305
"metadata": {"thing": "1", "another": "2"},
307-
"input_xtype": "",
306+
"_input_xtype": "",
308307
"name": "test",
309308
"scat_quantity": "x-ray",
310309
"qmin": np.float64(np.inf),
@@ -332,7 +331,7 @@ def test_dump(tmp_path, mocker):
332331
]
333332
),
334333
"metadata": {},
335-
"input_xtype": "tth",
334+
"_input_xtype": "tth",
336335
"name": "",
337336
"scat_quantity": None,
338337
"qmin": np.float64(0.0),
@@ -361,7 +360,7 @@ def test_dump(tmp_path, mocker):
361360
]
362361
),
363362
"metadata": {},
364-
"input_xtype": "d",
363+
"_input_xtype": "d",
365364
"name": "",
366365
"scat_quantity": "x-ray",
367366
"qmin": np.float64(0.0),
@@ -406,12 +405,39 @@ def test_all_array_setter():
406405
# Attempt to directly modify the property
407406
with pytest.raises(
408407
AttributeError,
409-
match="Direct modification of attribute 'all_arrays' is not allowed."
410-
"Please use 'insert_scattering_quantity' to modify `all_arrays`.",
408+
match="Direct modification of attribute 'all_arrays' is not allowed. "
409+
"Please use 'insert_scattering_quantity' to modify 'all_arrays'.",
411410
):
412411
actual_do.all_arrays = np.empty((4, 4))
413412

414413

414+
def test_xarray_yarray_length_mismatch():
415+
with pytest.raises(
416+
ValueError,
417+
match="'xarray' and 'yarray' must have the same length. "
418+
"Please re-initialize 'DiffractionObject' or re-run the method 'insert_scattering_quantity' "
419+
"with 'xarray' and 'yarray' of identical length",
420+
):
421+
DiffractionObject(xarray=np.array([1.0, 2.0]), yarray=np.array([0.0, 0.0, 0.0]))
422+
423+
424+
def test_input_xtype_getter():
425+
do = DiffractionObject(xtype="tth")
426+
assert do.input_xtype == "tth"
427+
428+
429+
def test_input_xtype_setter():
430+
do = DiffractionObject(xtype="tth")
431+
432+
# Attempt to directly modify the property
433+
with pytest.raises(
434+
AttributeError,
435+
match="Direct modification of attribute 'input_xtype' is not allowed. "
436+
"Please use 'insert_scattering_quantity' to modify 'input_xtype'.",
437+
):
438+
do.input_xtype = "q"
439+
440+
415441
def test_copy_object():
416442
do = DiffractionObject(
417443
name="test",

0 commit comments

Comments
 (0)