From 9ed7fc60db364ce5eca97767551d92fdec264907 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 19 Feb 2026 13:47:06 -0500 Subject: [PATCH 01/11] build: deprecate addNewItem --- news/deprecate-addNewItem.rst | 23 +++++++++++++++++++++++ requirements/conda.txt | 1 + requirements/pip.txt | 1 + src/diffpy/structure/parsers/p_cif.py | 2 +- src/diffpy/structure/parsers/p_discus.py | 2 +- src/diffpy/structure/parsers/p_pdb.py | 2 +- src/diffpy/structure/parsers/p_pdffit.py | 2 +- src/diffpy/structure/parsers/p_rawxyz.py | 2 +- src/diffpy/structure/parsers/p_xcfg.py | 2 +- src/diffpy/structure/parsers/p_xyz.py | 2 +- src/diffpy/structure/structure.py | 22 ++++++++++++++++++++++ 11 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 news/deprecate-addNewItem.rst diff --git a/news/deprecate-addNewItem.rst b/news/deprecate-addNewItem.rst new file mode 100644 index 00000000..7aeb4b68 --- /dev/null +++ b/news/deprecate-addNewItem.rst @@ -0,0 +1,23 @@ +**Added:** + +* No News Added: deprecate CamelCase function for addNewItem + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/requirements/conda.txt b/requirements/conda.txt index 0fdcecb6..4c4ff50e 100644 --- a/requirements/conda.txt +++ b/requirements/conda.txt @@ -1,2 +1,3 @@ numpy pycifrw +diffpy.utils diff --git a/requirements/pip.txt b/requirements/pip.txt index 0fdcecb6..4c4ff50e 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -1,2 +1,3 @@ numpy pycifrw +diffpy.utils diff --git a/src/diffpy/structure/parsers/p_cif.py b/src/diffpy/structure/parsers/p_cif.py index 814074f2..b542e69e 100644 --- a/src/diffpy/structure/parsers/p_cif.py +++ b/src/diffpy/structure/parsers/p_cif.py @@ -497,7 +497,7 @@ def _parse_atom_site_label(self, block): if curlabel == "?": continue self.labelindex[curlabel] = len(self.stru) - self.stru.addNewAtom() + self.stru.add_new_atom() a = self.stru.getLastAtom() for fset, val in zip(prop_setters, values): fset(a, val) diff --git a/src/diffpy/structure/parsers/p_discus.py b/src/diffpy/structure/parsers/p_discus.py index a87f99d3..665c096f 100644 --- a/src/diffpy/structure/parsers/p_discus.py +++ b/src/diffpy/structure/parsers/p_discus.py @@ -264,7 +264,7 @@ def _parse_atom(self, words): element = words[0][0:1].upper() + words[0][1:].lower() xyz = [float(w) for w in words[1:4]] Biso = float(words[4]) - self.stru.addNewAtom(element, xyz) + self.stru.add_new_atom(element, xyz) a = self.stru.getLastAtom() a.Bisoequiv = Biso return diff --git a/src/diffpy/structure/parsers/p_pdb.py b/src/diffpy/structure/parsers/p_pdb.py index 73ea41c1..9f2ecc47 100644 --- a/src/diffpy/structure/parsers/p_pdb.py +++ b/src/diffpy/structure/parsers/p_pdb.py @@ -197,7 +197,7 @@ def parseLines(self, lines): # get element from the first 2 characters of name element = line[12:14].strip() element = element[0].upper() + element[1:].lower() - stru.addNewAtom(element, occupancy=occupancy, label=name) + stru.add_new_atom(element, occupancy=occupancy, label=name) last_atom = stru.getLastAtom() last_atom.xyz_cartn = rc last_atom.Uisoequiv = uiso diff --git a/src/diffpy/structure/parsers/p_pdffit.py b/src/diffpy/structure/parsers/p_pdffit.py index ae95d6f5..84b55fce 100644 --- a/src/diffpy/structure/parsers/p_pdffit.py +++ b/src/diffpy/structure/parsers/p_pdffit.py @@ -132,7 +132,7 @@ def parseLines(self, lines): element = wl1[0][0].upper() + wl1[0][1:].lower() xyz = [float(w) for w in wl1[1:4]] occ = float(wl1[4]) - stru.addNewAtom(element, xyz=xyz, occupancy=occ) + stru.add_new_atom(element, xyz=xyz, occupancy=occ) a = stru.getLastAtom() p_nl += 1 wl2 = next(ilines).split() diff --git a/src/diffpy/structure/parsers/p_rawxyz.py b/src/diffpy/structure/parsers/p_rawxyz.py index 24ad293b..ab28ec41 100644 --- a/src/diffpy/structure/parsers/p_rawxyz.py +++ b/src/diffpy/structure/parsers/p_rawxyz.py @@ -103,7 +103,7 @@ def parseLines(self, lines): xyz = [float(f) for f in fields[x_idx : x_idx + 3]] if len(xyz) == 2: xyz.append(0.0) - stru.addNewAtom(element, xyz=xyz) + stru.add_new_atom(element, xyz=xyz) except ValueError: emsg = "%d: invalid number" % p_nl exc_type, exc_value, exc_traceback = sys.exc_info() diff --git a/src/diffpy/structure/parsers/p_xcfg.py b/src/diffpy/structure/parsers/p_xcfg.py index 4f24c420..08c108a9 100644 --- a/src/diffpy/structure/parsers/p_xcfg.py +++ b/src/diffpy/structure/parsers/p_xcfg.py @@ -269,7 +269,7 @@ def parseLines(self, lines): elif len(words) == xcfg_entry_count and p_element is not None: fields = [float(w) for w in words] xyz = [xcfg_A * xi for xi in fields[:3]] - stru.addNewAtom(p_element, xyz=xyz) + stru.add_new_atom(p_element, xyz=xyz) a = stru[-1] _assign_auxiliaries( a, diff --git a/src/diffpy/structure/parsers/p_xyz.py b/src/diffpy/structure/parsers/p_xyz.py index 5c08f99b..a91c431f 100644 --- a/src/diffpy/structure/parsers/p_xyz.py +++ b/src/diffpy/structure/parsers/p_xyz.py @@ -109,7 +109,7 @@ def parseLines(self, lines): element = fields[0] element = element[0].upper() + element[1:].lower() xyz = [float(f) for f in fields[1:4]] - stru.addNewAtom(element, xyz=xyz) + stru.add_new_atom(element, xyz=xyz) except ValueError: exc_type, exc_value, exc_traceback = sys.exc_info() emsg = "%d: invalid number format" % p_nl diff --git a/src/diffpy/structure/structure.py b/src/diffpy/structure/structure.py index 3c7b725f..930b5149 100644 --- a/src/diffpy/structure/structure.py +++ b/src/diffpy/structure/structure.py @@ -21,9 +21,19 @@ from diffpy.structure.atom import Atom from diffpy.structure.lattice import Lattice from diffpy.structure.utils import _linkAtomAttribute, atomBareSymbol, isiterable +from diffpy.utils._deprecator import build_deprecation_message, deprecated # ---------------------------------------------------------------------------- +base = "diffpy.structure.Structure" +removal_version = "4.0.0" +addNewAtom_deprecation_msg = build_deprecation_message( + base, + "addNewAtom", + "add_new_atom", + removal_version, +) + class Structure(list): """Define group of atoms in a specified lattice. Structure --> group @@ -145,7 +155,19 @@ def __str__(self): s_atoms = "\n".join([str(a) for a in self]) return s_lattice + "\n" + s_atoms + @deprecated(addNewAtom_deprecation_msg) def addNewAtom(self, *args, **kwargs): + """This function has been deprecated and will be removed in + version 4.0.0. + + Please use diffpy.structure.Structure.add_new_atom instead. + """ + kwargs["lattice"] = self.lattice + a = Atom(*args, **kwargs) + self.append(a, copy=False) + return + + def add_new_atom(self, *args, **kwargs): """Add new `Atom` instance to the end of this `Structure`. Parameters From 6e990822ff4f8fa73f09f8ec469c58cceedbf4d3 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 19 Feb 2026 16:13:46 -0500 Subject: [PATCH 02/11] build: change body of deprecated function --- src/diffpy/structure/structure.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/diffpy/structure/structure.py b/src/diffpy/structure/structure.py index 930b5149..865e1862 100644 --- a/src/diffpy/structure/structure.py +++ b/src/diffpy/structure/structure.py @@ -162,9 +162,7 @@ def addNewAtom(self, *args, **kwargs): Please use diffpy.structure.Structure.add_new_atom instead. """ - kwargs["lattice"] = self.lattice - a = Atom(*args, **kwargs) - self.append(a, copy=False) + self.add_new_atom(*args, **kwargs) return def add_new_atom(self, *args, **kwargs): From 95138f709ee3d79f56d5f14cc0b0f9af20cd175d Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 19 Feb 2026 16:52:24 -0500 Subject: [PATCH 03/11] build: add test cases for addNewAtom to test it & show deprecation message. --- tests/test_structure.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_structure.py b/tests/test_structure.py index 65d028c4..d772c5d1 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -120,6 +120,38 @@ def test___copy__(self): # """check Structure.getLastAtom()""" # return + def test_add_new_atom(self): + s_lat = Lattice() + other_lat = Lattice() + expected = Structure(lattice=s_lat) + + length = len(expected) + expected.add_new_atom(atype="C", xyz=[0.1, 0.2, 0.3]) + actual = length + 1 + assert len(expected) == actual + + object = expected[-1] + if hasattr(object, "element"): + assert object.element == "C" + if hasattr(object, "xyz"): + assert numpy.allclose(object.xyz, [0.1, 0.2, 0.3]) + + def test_addNewAtom(self): + s_lat = Lattice() + other_lat = Lattice() + expected = Structure(lattice=s_lat) + + length = len(expected) + expected.addNewAtom(atype="C", xyz=[0.1, 0.2, 0.3]) + actual = length + 1 + assert len(expected) == actual + + object = expected[-1] + if hasattr(object, "element"): + assert object.element == "C" + if hasattr(object, "xyz"): + assert numpy.allclose(object.xyz, [0.1, 0.2, 0.3]) + def test_assignUniqueLabels(self): """Check Structure.assignUniqueLabels()""" self.assertEqual("", "".join([a.label for a in self.stru])) From 3f285b6ef555f504ae487972a54361e67bdcfe96 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 19 Feb 2026 17:00:11 -0500 Subject: [PATCH 04/11] fix: refine test code to avoid if logic --- tests/test_structure.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/test_structure.py b/tests/test_structure.py index d772c5d1..28a3fb25 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -122,7 +122,6 @@ def test___copy__(self): def test_add_new_atom(self): s_lat = Lattice() - other_lat = Lattice() expected = Structure(lattice=s_lat) length = len(expected) @@ -131,26 +130,20 @@ def test_add_new_atom(self): assert len(expected) == actual object = expected[-1] - if hasattr(object, "element"): - assert object.element == "C" - if hasattr(object, "xyz"): - assert numpy.allclose(object.xyz, [0.1, 0.2, 0.3]) + assert object.element == "C" + assert numpy.allclose(object.xyz, [0.1, 0.2, 0.3]) def test_addNewAtom(self): s_lat = Lattice() - other_lat = Lattice() expected = Structure(lattice=s_lat) length = len(expected) expected.addNewAtom(atype="C", xyz=[0.1, 0.2, 0.3]) actual = length + 1 assert len(expected) == actual - object = expected[-1] - if hasattr(object, "element"): - assert object.element == "C" - if hasattr(object, "xyz"): - assert numpy.allclose(object.xyz, [0.1, 0.2, 0.3]) + assert object.element == "C" + assert numpy.allclose(object.xyz, [0.1, 0.2, 0.3]) def test_assignUniqueLabels(self): """Check Structure.assignUniqueLabels()""" From 1fc8a341d8a052197a207ef65f66c72d63c15eef Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 19 Feb 2026 17:34:25 -0500 Subject: [PATCH 05/11] test: add docstring and comments to the test. --- tests/test_structure.py | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/tests/test_structure.py b/tests/test_structure.py index 28a3fb25..5cc880d7 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -121,29 +121,39 @@ def test___copy__(self): # return def test_add_new_atom(self): + """Check Structure.add_new_item()""" + # Case: We initialize a Structure object, after calling + # add_new_item method, we check whether length of Structure + # object is added by 1. Moreover, we check added Atom's attributes + # are properly loaded into Structure object. s_lat = Lattice() - expected = Structure(lattice=s_lat) + structure = Structure(lattice=s_lat) - length = len(expected) - expected.add_new_atom(atype="C", xyz=[0.1, 0.2, 0.3]) + length = len(structure) + structure.add_new_atom(atype="C", xyz=[0.1, 0.2, 0.3]) + expected = len(structure) # length of structure should add by 1 actual = length + 1 - assert len(expected) == actual - - object = expected[-1] - assert object.element == "C" - assert numpy.allclose(object.xyz, [0.1, 0.2, 0.3]) + assert expected == actual + atom_object = structure[-1] + assert atom_object.element == "C" + assert numpy.allclose(atom_object.xyz, [0.1, 0.2, 0.3]) def test_addNewAtom(self): + """Duplicate test for the deprecated addNewAtom method. + + Remove this test in version 4.0.0 + """ s_lat = Lattice() - expected = Structure(lattice=s_lat) + structure = Structure(lattice=s_lat) - length = len(expected) - expected.addNewAtom(atype="C", xyz=[0.1, 0.2, 0.3]) + length = len(structure) + structure.addNewAtom(atype="C", xyz=[0.1, 0.2, 0.3]) + expected = len(structure) actual = length + 1 - assert len(expected) == actual - object = expected[-1] - assert object.element == "C" - assert numpy.allclose(object.xyz, [0.1, 0.2, 0.3]) + assert expected == actual + atom_object = structure[-1] + assert atom_object.element == "C" + assert numpy.allclose(atom_object.xyz, [0.1, 0.2, 0.3]) def test_assignUniqueLabels(self): """Check Structure.assignUniqueLabels()""" From cb7aec062df2ae420ea77d84aa92b28b1f850504 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 19 Feb 2026 21:35:32 -0500 Subject: [PATCH 06/11] fix: add more test cases and refine behavior of add_new_atom --- news/deprecate-addNewItem.rst | 4 ++-- src/diffpy/structure/structure.py | 15 +++++++++------ tests/test_structure.py | 30 ++++++++++++++++++++++++------ 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/news/deprecate-addNewItem.rst b/news/deprecate-addNewItem.rst index 7aeb4b68..922d41c9 100644 --- a/news/deprecate-addNewItem.rst +++ b/news/deprecate-addNewItem.rst @@ -1,6 +1,6 @@ **Added:** -* No News Added: deprecate CamelCase function for addNewItem +* Added `diffpy.structure.Structure.add_new_atom` in replace of `addNewAtom` **Changed:** @@ -8,7 +8,7 @@ **Deprecated:** -* +* Deprecated `diffpy.structure.Structure.addNewAtom` method for removal in version 4.0.0 **Removed:** diff --git a/src/diffpy/structure/structure.py b/src/diffpy/structure/structure.py index 865e1862..9719fe03 100644 --- a/src/diffpy/structure/structure.py +++ b/src/diffpy/structure/structure.py @@ -168,14 +168,17 @@ def addNewAtom(self, *args, **kwargs): def add_new_atom(self, *args, **kwargs): """Add new `Atom` instance to the end of this `Structure`. - Parameters - ---------- - *args, **kwargs : - See `Atom` class constructor. + Raises + ------ + ValueError + If an atom with the same element/type and coordinates already exists. """ kwargs["lattice"] = self.lattice - a = Atom(*args, **kwargs) - self.append(a, copy=False) + atom = Atom(*args, **kwargs) + for existing in self: + if existing.element == atom.element and numpy.allclose(existing.xyz, atom.xyz): + raise ValueError(f"Duplicate atom {atom.element} already exists at {atom.xyz!r}") + self.append(atom, copy=False) return def getLastAtom(self): diff --git a/tests/test_structure.py b/tests/test_structure.py index 5cc880d7..9cac4b92 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -122,22 +122,40 @@ def test___copy__(self): def test_add_new_atom(self): """Check Structure.add_new_item()""" - # Case: We initialize a Structure object, after calling - # add_new_item method, we check whether length of Structure - # object is added by 1. Moreover, we check added Atom's attributes - # are properly loaded into Structure object. + # Case 1: valid atom added to an empty structure. + # Expect the atom list length to go from 0 to 1. + # Expect the atom attributes are successfully loaded. s_lat = Lattice() structure = Structure(lattice=s_lat) - length = len(structure) structure.add_new_atom(atype="C", xyz=[0.1, 0.2, 0.3]) - expected = len(structure) # length of structure should add by 1 + expected = len(structure) actual = length + 1 assert expected == actual atom_object = structure[-1] assert atom_object.element == "C" assert numpy.allclose(atom_object.xyz, [0.1, 0.2, 0.3]) + # Case 2: valid atom added to existing atom list. + # Expect the atom list length to go from 1 to 2. + # Expect the atom attributes are successfully loaded. + length = len(structure) + structure.add_new_atom(atype="Ni", xyz=[0.8, 1.2, 0.9]) + expected = len(structure) + actual = length + 1 + assert expected == actual + atom_object = structure[-1] + assert atom_object.element == "Ni" + assert numpy.allclose(atom_object.xyz, [0.8, 1.2, 0.9]) + + # Case 3: duplicated atom added to the existing atom list. + # Expect the atom not to be added and gives an ValueError. + with pytest.raises(ValueError, match=r"Duplicate atom C already exists at array\(\[0\.1, 0\.2, 0\.3\]\)"): + structure.add_new_atom(atype="C", xyz=[0.1, 0.2, 0.3]) + actual = len(structure) + expected = 2 + assert expected == actual + def test_addNewAtom(self): """Duplicate test for the deprecated addNewAtom method. From 114f3d62a7a38cc7590067325d1669191ec1fe86 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 19 Feb 2026 21:42:39 -0500 Subject: [PATCH 07/11] fix: add docstring for hyperparamter for add_new_atom --- src/diffpy/structure/structure.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/diffpy/structure/structure.py b/src/diffpy/structure/structure.py index 9719fe03..6caa33ac 100644 --- a/src/diffpy/structure/structure.py +++ b/src/diffpy/structure/structure.py @@ -168,6 +168,11 @@ def addNewAtom(self, *args, **kwargs): def add_new_atom(self, *args, **kwargs): """Add new `Atom` instance to the end of this `Structure`. + Parameters + ---------- + *args, **kwargs : + See `Atom` class constructor. + Raises ------ ValueError From 40e877366412867fa9c35c19a817813dfa0ee49a Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 19 Feb 2026 21:46:01 -0500 Subject: [PATCH 08/11] fix: fix comment with syntax error --- tests/test_structure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_structure.py b/tests/test_structure.py index 9cac4b92..0aae764b 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -149,7 +149,7 @@ def test_add_new_atom(self): assert numpy.allclose(atom_object.xyz, [0.8, 1.2, 0.9]) # Case 3: duplicated atom added to the existing atom list. - # Expect the atom not to be added and gives an ValueError. + # Expect the atom not to be added and gives a ValueError. with pytest.raises(ValueError, match=r"Duplicate atom C already exists at array\(\[0\.1, 0\.2, 0\.3\]\)"): structure.add_new_atom(atype="C", xyz=[0.1, 0.2, 0.3]) actual = len(structure) From 8f8253c3a356a160c13e0e02c69aef20c33c5f51 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 19 Feb 2026 23:49:16 -0500 Subject: [PATCH 09/11] fix: change behavior to warning and use pytest parametrize to test it. --- src/diffpy/structure/structure.py | 10 +++- tests/test_structure.py | 84 ++++++++++++++++++------------- 2 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/diffpy/structure/structure.py b/src/diffpy/structure/structure.py index 6caa33ac..6b3a9cad 100644 --- a/src/diffpy/structure/structure.py +++ b/src/diffpy/structure/structure.py @@ -17,6 +17,7 @@ import copy as copymod import numpy +import warnings from diffpy.structure.atom import Atom from diffpy.structure.lattice import Lattice @@ -175,14 +176,19 @@ def add_new_atom(self, *args, **kwargs): Raises ------ - ValueError + UserWarning If an atom with the same element/type and coordinates already exists. """ kwargs["lattice"] = self.lattice atom = Atom(*args, **kwargs) for existing in self: if existing.element == atom.element and numpy.allclose(existing.xyz, atom.xyz): - raise ValueError(f"Duplicate atom {atom.element} already exists at {atom.xyz!r}") + warnings.warn( + f"Duplicate atom {atom.element} already exists at {atom.xyz!r}", + category=UserWarning, + stacklevel=2, + ) + break self.append(atom, copy=False) return diff --git a/tests/test_structure.py b/tests/test_structure.py index 0aae764b..3f9b2864 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -120,41 +120,7 @@ def test___copy__(self): # """check Structure.getLastAtom()""" # return - def test_add_new_atom(self): - """Check Structure.add_new_item()""" - # Case 1: valid atom added to an empty structure. - # Expect the atom list length to go from 0 to 1. - # Expect the atom attributes are successfully loaded. - s_lat = Lattice() - structure = Structure(lattice=s_lat) - length = len(structure) - structure.add_new_atom(atype="C", xyz=[0.1, 0.2, 0.3]) - expected = len(structure) - actual = length + 1 - assert expected == actual - atom_object = structure[-1] - assert atom_object.element == "C" - assert numpy.allclose(atom_object.xyz, [0.1, 0.2, 0.3]) - # Case 2: valid atom added to existing atom list. - # Expect the atom list length to go from 1 to 2. - # Expect the atom attributes are successfully loaded. - length = len(structure) - structure.add_new_atom(atype="Ni", xyz=[0.8, 1.2, 0.9]) - expected = len(structure) - actual = length + 1 - assert expected == actual - atom_object = structure[-1] - assert atom_object.element == "Ni" - assert numpy.allclose(atom_object.xyz, [0.8, 1.2, 0.9]) - - # Case 3: duplicated atom added to the existing atom list. - # Expect the atom not to be added and gives a ValueError. - with pytest.raises(ValueError, match=r"Duplicate atom C already exists at array\(\[0\.1, 0\.2, 0\.3\]\)"): - structure.add_new_atom(atype="C", xyz=[0.1, 0.2, 0.3]) - actual = len(structure) - expected = 2 - assert expected == actual def test_addNewAtom(self): """Duplicate test for the deprecated addNewAtom method. @@ -655,6 +621,56 @@ def test_pickling(self): # End of class TestStructure # ---------------------------------------------------------------------------- +@pytest.mark.parametrize( + "existing, atype, xyz, expected_len, expected_element, expected_xyz", + [ + # Case 1: valid atom added to an empty structure. + # Expect the atom list length to go from 0 to 1. + # Expect the atom attributes are successfully loaded. + ( + None, + "C", + [0.1, 0.2, 0.3], + 1, + "C", + [0.1, 0.2, 0.3], + ), + # Case 2: valid atom added to existing atom list. + # Expect the atom list length to go from 1 to 2. + # Expect the atom attributes are successfully loaded. + ( + [Atom("C", [0, 0, 0])], + "Ni", + [0.8, 1.2, 0.9], + 2, + "Ni", + [0.8, 1.2, 0.9], + ), + ], +) +def test_add_new_atom(existing, atype, xyz, expected_len, expected_element, expected_xyz): + """Check Structure.add_new_item()""" + structure = Structure(existing, lattice=Lattice()) + structure.add_new_atom(atype=atype, xyz=xyz) + actual_length = len(structure) + assert expected_len == actual_length + atom_object = structure[-1] + assert atom_object.element == expected_element + assert numpy.allclose(atom_object.xyz, expected_xyz) + + +def test_add_new_atom_duplicate(): + # Case 3: duplicated atom added to the existing atom list. + # Expect the atom not to be added and gives a ValueError. + structure = Structure( + [Atom("C", [0.1, 0.2, 0.3]), Atom("Ni", [0.8, 1.2, 0.9])], + lattice=Lattice(), + ) + with pytest.warns(UserWarning): + structure.add_new_atom(atype="C", xyz=[0.1, 0.2, 0.3]) + assert len(structure) == 3 + assert structure[-1].element == "C" + assert numpy.allclose(structure[-1].xyz, [0.1, 0.2, 0.3]) if __name__ == "__main__": unittest.main() From 15ccba1545c6fe077389fcab68fd632182ab48d3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 04:49:45 +0000 Subject: [PATCH 10/11] [pre-commit.ci] auto fixes from pre-commit hooks --- src/diffpy/structure/structure.py | 2 +- tests/test_structure.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/diffpy/structure/structure.py b/src/diffpy/structure/structure.py index 6b3a9cad..9e84bc83 100644 --- a/src/diffpy/structure/structure.py +++ b/src/diffpy/structure/structure.py @@ -15,9 +15,9 @@ """This module defines class `Structure`.""" import copy as copymod +import warnings import numpy -import warnings from diffpy.structure.atom import Atom from diffpy.structure.lattice import Lattice diff --git a/tests/test_structure.py b/tests/test_structure.py index 3f9b2864..358c58dd 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -120,8 +120,6 @@ def test___copy__(self): # """check Structure.getLastAtom()""" # return - - def test_addNewAtom(self): """Duplicate test for the deprecated addNewAtom method. @@ -620,6 +618,7 @@ def test_pickling(self): # End of class TestStructure + # ---------------------------------------------------------------------------- @pytest.mark.parametrize( "existing, atype, xyz, expected_len, expected_element, expected_xyz", @@ -672,5 +671,6 @@ def test_add_new_atom_duplicate(): assert structure[-1].element == "C" assert numpy.allclose(structure[-1].xyz, [0.1, 0.2, 0.3]) + if __name__ == "__main__": unittest.main() From 52c99e64f965f9d608f6345b32e04397a96f0278 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 19 Feb 2026 23:53:16 -0500 Subject: [PATCH 11/11] fix: fix comment for UserWarning. --- tests/test_structure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_structure.py b/tests/test_structure.py index 358c58dd..648f684b 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -660,7 +660,7 @@ def test_add_new_atom(existing, atype, xyz, expected_len, expected_element, expe def test_add_new_atom_duplicate(): # Case 3: duplicated atom added to the existing atom list. - # Expect the atom not to be added and gives a ValueError. + # Expect the atom to be added and gives a UserWarning. structure = Structure( [Atom("C", [0.1, 0.2, 0.3]), Atom("Ni", [0.8, 1.2, 0.9])], lattice=Lattice(),