Skip to content

Commit 298e630

Browse files
authored
Merge pull request diffpy#196 from bobleesj/setter-property
Use `@property` to prevent direct modification of `all_arrays`
2 parents ed9fb03 + d20d2bc commit 298e630

File tree

4 files changed

+89
-23
lines changed

4 files changed

+89
-23
lines changed

news/setter-property.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* prevent direct modification of `all_arrays` using `@property`
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>

requirements/test.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ pytest-env
66
pytest-mock
77
pytest-cov
88
freezegun
9+
DeepDiff

src/diffpy/utils/diffraction_objects.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,17 @@ def __rtruediv__(self, other):
181181
divided.on_q[1] = other.on_q[1] / self.on_q[1]
182182
return divided
183183

184+
@property
185+
def all_arrays(self):
186+
return self._all_arrays
187+
188+
@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+
)
194+
184195
def set_angles_from_list(self, angles_list):
185196
self.angles = angles_list
186197
self.n_steps = len(angles_list) - 1.0
@@ -259,25 +270,25 @@ def get_angle_index(self, angle):
259270
raise IndexError(f"WARNING: no angle {angle} found in angles list")
260271

261272
def _set_xarrays(self, xarray, xtype):
262-
self.all_arrays = np.empty(shape=(len(xarray), 4))
273+
self._all_arrays = np.empty(shape=(len(xarray), 4))
263274
if xtype.lower() in QQUANTITIES:
264-
self.all_arrays[:, 1] = xarray
265-
self.all_arrays[:, 2] = q_to_tth(xarray, self.wavelength)
266-
self.all_arrays[:, 3] = q_to_d(xarray)
275+
self._all_arrays[:, 1] = xarray
276+
self._all_arrays[:, 2] = q_to_tth(xarray, self.wavelength)
277+
self._all_arrays[:, 3] = q_to_d(xarray)
267278
elif xtype.lower() in ANGLEQUANTITIES:
268-
self.all_arrays[:, 2] = xarray
269-
self.all_arrays[:, 1] = tth_to_q(xarray, self.wavelength)
270-
self.all_arrays[:, 3] = tth_to_d(xarray, self.wavelength)
279+
self._all_arrays[:, 2] = xarray
280+
self._all_arrays[:, 1] = tth_to_q(xarray, self.wavelength)
281+
self._all_arrays[:, 3] = tth_to_d(xarray, self.wavelength)
271282
elif xtype.lower() in DQUANTITIES:
272-
self.all_arrays[:, 3] = xarray
273-
self.all_arrays[:, 1] = d_to_q(xarray)
274-
self.all_arrays[:, 2] = d_to_tth(xarray, self.wavelength)
275-
self.qmin = np.nanmin(self.all_arrays[:, 1], initial=np.inf)
276-
self.qmax = np.nanmax(self.all_arrays[:, 1], initial=0.0)
277-
self.tthmin = np.nanmin(self.all_arrays[:, 2], initial=np.inf)
278-
self.tthmax = np.nanmax(self.all_arrays[:, 2], initial=0.0)
279-
self.dmin = np.nanmin(self.all_arrays[:, 3], initial=np.inf)
280-
self.dmax = np.nanmax(self.all_arrays[:, 3], initial=0.0)
283+
self._all_arrays[:, 3] = xarray
284+
self._all_arrays[:, 1] = d_to_q(xarray)
285+
self._all_arrays[:, 2] = d_to_tth(xarray, self.wavelength)
286+
self.qmin = np.nanmin(self._all_arrays[:, 1], initial=np.inf)
287+
self.qmax = np.nanmax(self._all_arrays[:, 1], initial=0.0)
288+
self.tthmin = np.nanmin(self._all_arrays[:, 2], initial=np.inf)
289+
self.tthmax = np.nanmax(self._all_arrays[:, 2], initial=0.0)
290+
self.dmin = np.nanmin(self._all_arrays[:, 3], initial=np.inf)
291+
self.dmax = np.nanmax(self._all_arrays[:, 3], initial=0.0)
281292

282293
def insert_scattering_quantity(
283294
self,
@@ -309,7 +320,7 @@ def insert_scattering_quantity(
309320
310321
"""
311322
self._set_xarrays(xarray, xtype)
312-
self.all_arrays[:, 0] = yarray
323+
self._all_arrays[:, 0] = yarray
313324
self.input_xtype = xtype
314325
# only update these optional values if non-empty quantities are passed to avoid overwriting
315326
# valid data inadvertently

tests/test_diffraction_objects.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import numpy as np
44
import pytest
5+
from deepdiff import DeepDiff
56
from freezegun import freeze_time
67

78
from diffpy.utils.diffraction_objects import DiffractionObject
@@ -248,7 +249,7 @@ def test_dump(tmp_path, mocker):
248249
(
249250
{},
250251
{
251-
"all_arrays": np.empty(shape=(0, 4)), # instantiate empty
252+
"_all_arrays": np.empty(shape=(0, 4)), # instantiate empty
252253
"metadata": {},
253254
"input_xtype": "",
254255
"name": "",
@@ -265,7 +266,7 @@ def test_dump(tmp_path, mocker):
265266
( # instantiate just non-array attributes
266267
{"name": "test", "scat_quantity": "x-ray", "metadata": {"thing": "1", "another": "2"}},
267268
{
268-
"all_arrays": np.empty(shape=(0, 4)),
269+
"_all_arrays": np.empty(shape=(0, 4)),
269270
"metadata": {"thing": "1", "another": "2"},
270271
"input_xtype": "",
271272
"name": "test",
@@ -287,7 +288,7 @@ def test_dump(tmp_path, mocker):
287288
"wavelength": 4.0 * np.pi,
288289
},
289290
{
290-
"all_arrays": np.array(
291+
"_all_arrays": np.array(
291292
[
292293
[1.0, 0.0, 0.0, np.float64(np.inf)],
293294
[2.0, 1.0 / np.sqrt(2), 90.0, np.sqrt(2) * 2 * np.pi],
@@ -316,7 +317,7 @@ def test_dump(tmp_path, mocker):
316317
"scat_quantity": "x-ray",
317318
},
318319
{
319-
"all_arrays": np.array(
320+
"_all_arrays": np.array(
320321
[
321322
[1.0, 0.0, 0.0, np.float64(np.inf)],
322323
[2.0, 1.0 / np.sqrt(2), 90.0, np.sqrt(2) * 2 * np.pi],
@@ -341,5 +342,35 @@ def test_dump(tmp_path, mocker):
341342

342343
@pytest.mark.parametrize("inputs, expected", tc_params)
343344
def test_constructor(inputs, expected):
344-
actualdo = DiffractionObject(**inputs)
345-
compare_dicts(actualdo.__dict__, expected)
345+
actual_do = DiffractionObject(**inputs)
346+
diff = DeepDiff(actual_do.__dict__, expected, ignore_order=True, significant_digits=13)
347+
assert diff == {}
348+
349+
350+
def test_all_array_getter():
351+
actual_do = DiffractionObject(
352+
xarray=np.array([0.0, 90.0, 180.0]),
353+
yarray=np.array([1.0, 2.0, 3.0]),
354+
xtype="tth",
355+
wavelength=4.0 * np.pi,
356+
)
357+
expected_all_arrays = np.array(
358+
[
359+
[1.0, 0.0, 0.0, np.float64(np.inf)],
360+
[2.0, 1.0 / np.sqrt(2), 90.0, np.sqrt(2) * 2 * np.pi],
361+
[3.0, 1.0, 180.0, 1.0 * 2 * np.pi],
362+
]
363+
)
364+
assert np.allclose(actual_do.all_arrays, expected_all_arrays)
365+
366+
367+
def test_all_array_setter():
368+
actual_do = DiffractionObject()
369+
370+
# Attempt to directly modify the property
371+
with pytest.raises(
372+
AttributeError,
373+
match="Direct modification of attribute 'all_arrays' is not allowed."
374+
"Please use 'insert_scattering_quantity' to modify `all_arrays`.",
375+
):
376+
actual_do.all_arrays = np.empty((4, 4))

0 commit comments

Comments
 (0)