From 17e37477f93572a7884e51eaece23ad4c106fa6a Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 20 Oct 2025 13:33:11 -0400 Subject: [PATCH 01/25] rm api from cli.py --- src/diffpy/cmi/cli.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/diffpy/cmi/cli.py b/src/diffpy/cmi/cli.py index 5037a61..539b249 100644 --- a/src/diffpy/cmi/cli.py +++ b/src/diffpy/cmi/cli.py @@ -16,7 +16,7 @@ import argparse from pathlib import Path from shutil import copytree -from typing import Dict, List, Optional, Tuple +from typing import List, Optional, Tuple from diffpy.cmi import __version__ from diffpy.cmi.conda import env_info @@ -25,22 +25,6 @@ from diffpy.cmi.profilesmanager import ProfilesManager -def copy_examples( - examples_dict: Dict[str, List[Tuple[str, Path]]], target_dir: Path = None -) -> None: - """Copy an example into the the target or current working directory. - - Parameters - ---------- - examples_dict : dict - Dictionary mapping pack name -> list of (example, path) tuples. - target_dir : pathlib.Path, optional - Target directory to copy examples into. Defaults to current - working directory. - """ - return - - # Examples def _get_examples_dir() -> Path: """Return the absolute path to the installed examples directory. From 82f7394c6f655fa7475db32bee0ef610d1efc41d Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 20 Oct 2025 13:33:44 -0400 Subject: [PATCH 02/25] add copy_examples api to packsmanager --- src/diffpy/cmi/packsmanager.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index 9c25e9e..c45e4cc 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -133,6 +133,24 @@ def available_examples(self) -> dict[str, List[tuple[str, Path]]]: ) return examples_dict + def copy_examples( + self, + examples_to_copy: List[str], + target_dir: Path = None, + ) -> None: + """Copy examples or packs into the target or current working + directory. + + Parameters + ---------- + examples_to_copy : list of str + User-specified pack(s), example(s), or "all" to copy all. + target_dir : pathlib.Path, optional + Target directory to copy examples into. Defaults to current + working directory. + """ + return + def _resolve_pack_file(self, identifier: Union[str, Path]) -> Path: """Resolve a pack identifier to an absolute .txt path. From d973c830cdcdd6b954c66497e42f63d66b38483a Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 20 Oct 2025 13:44:39 -0400 Subject: [PATCH 03/25] add tests for copy_examples --- tests/test_packsmanager.py | 180 +++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index 986b060..4bce34c 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -1,3 +1,7 @@ +import os +import re +from pathlib import Path + import pytest from diffpy.cmi.packsmanager import PacksManager @@ -63,9 +67,12 @@ def paths_and_names_match(expected, actual, root): { "packA": [ ("ex1", "case5/docs/examples/packA/ex1"), + ("ex2", "case5/docs/examples/packA/ex2"), ], "packB": [ ("ex1", "case5/docs/examples/packB/ex1"), + ("ex3", "case5/docs/examples/packB/ex3"), + ("ex4", "case5/docs/examples/packB/ex4"), ], }, ), @@ -92,3 +99,176 @@ def test_tmp_file_structure(input, expected, example_cases): assert path.is_file() else: assert path.is_dir() + + +copy_params = [ + # Test various use cases to copy_examples on case5 + # 1) copy one example (ambiguous) + # 2) copy list of examples from same pack (ambiguous) + # 3) copy list of examples from different pack (ambiguous) + # 4) copy one example (unambiguous) + # 5) copy list of examples from same pack (unambiguous) + # 6) copy list of examples from different pack (unambiguous) + # 7) copy all examples from a pack + # 8) copy all examples from list of packs + # 9) copy all examples from all packs + ( # 1) copy one example, (ambiguous) + ["ex1"], + [ + Path("packA/ex1/path1/script1.py"), + Path("packB/ex1/path2/script2.py"), + ], + ), + ( # 2) copy list of examples from same pack (ambiguous) + ["ex1", "ex2"], + [ + Path("packA/ex1/path1/script1.py"), + Path("packB/ex1/path2/script2.py"), + Path("packA/ex2/script3.py"), + ], + ), + ( # 3) copy list of examples from different packs (ambiguous) + ["ex1", "ex1"], + [ + Path("packA/ex1/path1/script1.py"), + Path("packB/ex1/path2/script2.py"), + ], + ), + ( # 4) copy one example (unambiguous) + ["ex2"], + [ + Path("packA/ex2/script3.py"), + ], + ), + ( # 5) copy list of examples from same pack (unambiguous) + ["ex3", "ex4"], + [ + Path("packB/ex3/script4.py"), + Path("packB/ex4/script5.py"), + ], + ), + ( # 6) copy list of examples from different packs (unambiguous) + ["ex2", "ex3"], + [ + Path("packA/ex2/script3.py"), + Path("packB/ex3/script4.py"), + ], + ), + ( # 7) copy all examples from a pack + ["packA"], + [ + Path("packA/ex1/path1/script1.py"), + Path("packA/ex2/script3.py"), + ], + ), + ( # 8) copy all examples from list of packs + ["packA", "packB"], + [ + Path("packA/ex1/path1/script1.py"), + Path("packA/ex2/script3.py"), + Path("packB/ex1/path2/script2.py"), + Path("packB/ex3/script4.py"), + Path("packB/ex4/script5.py"), + ], + ), + ( # 9) copy all examples from all packs + ["all"], + [ + Path("packA/ex1/path1/script1.py"), + Path("packA/ex2/script3.py"), + Path("packB/ex1/path2/script2.py"), + Path("packB/ex3/script4.py"), + Path("packB/ex4/script5.py"), + ], + ), +] + + +# input: list of str - cli input(s) to copy_examples +# expected_paths: list of Path - expected relative paths to copied examples +@pytest.mark.parametrize("input,expected_paths", copy_params) +def test_copy_examples(input, expected_paths, example_cases): + examples_dir = example_cases / "case5" + pm = PacksManager(root_path=examples_dir) + target_dir = example_cases / "user_target" + pm.copy_examples(input, target_dir=target_dir) + actual = sorted(list(target_dir.rglob("*"))) + expected = sorted([target_dir / path for path in expected_paths]) + assert actual == expected + for path in expected_paths: + copied_path = target_dir / path + original_path = examples_dir / path + if copied_path.is_file() and original_path.is_file(): + assert copied_path.read_text() == original_path.read_text() + + +# Test default and targeted copy_example location on case5 +# input: str or None - path arg to copy_examples +# expected: Path - expected relative path to copied example +@pytest.mark.parametrize( + "input,expected_path", + [ + (None, Path("cwd/packA/ex1/path1/script1.py")), + ("user_target", Path("user_target/packA/ex1/path1/script1.py")), + ], +) +def test_copy_examples_location(input, expected_path, example_cases): + examples_dir = example_cases / "case5" + os.chdir(example_cases / "cwd") + pm = PacksManager(root_path=examples_dir) + paths = pm.copy_examples(["packA"], target_dir=input) + actual = paths[0] + expected = example_cases / expected_path + assert actual == expected + + +# Test bad inputs to copy_examples on case3 +# These include: +# 1) Input not found (example or pack) +# 2) Mixed good and bad inputs +# 3) Path to directory already exists +# 4) No input provided +@pytest.mark.parametrize( + "bad_inputs,expected,path", + [ + ( + # 1) Input not found (example or pack). + # Expected: Raise an error with the message. + ["bad_example"], + "No examples or packs found for input: 'bad_example'", + None, + ), + ( + # 2) Mixed good example and bad input. + # Expected: Raise an error with the message. + ["ex1", "bad_example"], + "No examples or packs found for input: 'bad_example'", + None, + ), + ( + # 3) Mixed good pack and bad input. + # Expected: Raise an error with the message. + ["packA", "bad_example"], + "No examples or packs found for input: 'bad_example'", + None, + ), + ( + # 4) Path to directory already exists. + # Expected: Raise a warning with the message. + ["ex1"], + ( + "Example directory(ies): 'ex1' already exist. " + " Current versions of existing files have " + "been left unchanged. To overwrite, please rerun " + "and specify --force." + ), + Path("docs/examples/"), + ), + ], +) +def test_copy_examples_bad(bad_inputs, expected, path, example_cases): + examples_dir = example_cases / "case3" + pm = PacksManager(root_path=examples_dir) + target_dir = None if path is None else examples_dir / path + with pytest.raises(Exception, match=re.escape(expected)): + pm.copy_examples(bad_inputs, target_dir=target_dir) From 375606c8662a50c41f0d0d500a00e87b60a9ea8c Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 20 Oct 2025 13:45:44 -0400 Subject: [PATCH 04/25] add additional dirs to temp structure --- tests/conftest.py | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 5a4edca..f83e5b6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,7 +22,7 @@ def tmp_examples(tmp_path_factory): yield tmp_examples -@pytest.fixture(scope="session") +@pytest.fixture(scope="function") def example_cases(tmp_path_factory): """Copy the entire examples tree into a temp directory once per test session. @@ -30,6 +30,10 @@ def example_cases(tmp_path_factory): Returns the path to that copy. """ root_temp_dir = tmp_path_factory.mktemp("temp") + cwd = root_temp_dir / "cwd" + cwd.mkdir(parents=True, exist_ok=True) + user_target = root_temp_dir / "user_target" + user_target.mkdir(parents=True, exist_ok=True) # case 1: pack with no examples case1ex_dir = root_temp_dir / "case1" / "docs" / "examples" @@ -44,13 +48,15 @@ def example_cases(tmp_path_factory): case2ex_dir / "full_pack" / "ex1" / "solution" / "diffpy-cmi" ) # full_pack, ex1 case2a.mkdir(parents=True, exist_ok=True) - (case2a / "script1.py").touch() + (case2a / "script1.py").write_text(f"# {case2a.name} script1\n") + case2b = ( case2ex_dir / "full_pack" / "ex2" / "random" / "path" ) # full_pack, ex2 case2b.mkdir(parents=True, exist_ok=True) - (case2b / "script1.py").touch() - (case2b / "script2.py").touch() + (case2b / "script1.py").write_text(f"# {case2b.name} script1\n") + (case2b / "script2.py").write_text(f"# {case2b.name} script2\n") + case2req_dir = root_temp_dir / "case2" / "requirements" / "packs" case2req_dir.mkdir(parents=True, exist_ok=True) @@ -58,16 +64,19 @@ def example_cases(tmp_path_factory): case3ex_dir = root_temp_dir / "case3" / "docs" / "examples" case3a = case3ex_dir / "packA" / "ex1" # packA, ex1 case3a.mkdir(parents=True, exist_ok=True) - (case3a / "script1.py").touch() + (case3a / "script1.py").write_text(f"# {case3a.name} script1\n") + case3b = case3ex_dir / "packA" / "ex2" / "solutions" # packA, ex2 case3b.mkdir(parents=True, exist_ok=True) - (case3b / "script2.py").touch() + (case3b / "script2.py").write_text(f"# {case3b.name} script2\n") + case3c = ( case3ex_dir / "packB" / "ex3" / "more" / "random" / "path" ) # packB, ex3 case3c.mkdir(parents=True, exist_ok=True) - (case3c / "script3.py").touch() - (case3c / "script4.py").touch() + (case3c / "script3.py").write_text(f"# {case3c.name} script3\n") + (case3c / "script4.py").write_text(f"# {case3c.name} script4\n") + case3req_dir = root_temp_dir / "case3" / "requirements" / "packs" case3req_dir.mkdir(parents=True, exist_ok=True) @@ -80,12 +89,27 @@ def example_cases(tmp_path_factory): # Case 5: multiple packs with the same example names case5ex_dir = root_temp_dir / "case5" / "docs" / "examples" + case5a = case5ex_dir / "packA" / "ex1" / "path1" # packA, ex1 case5a.mkdir(parents=True, exist_ok=True) - (case5a / "script1.py").touch() + (case5a / "script1.py").write_text(f"# {case5a.name} script1\n") + case5b = case5ex_dir / "packB" / "ex1" / "path2" # packB, ex1 case5b.mkdir(parents=True, exist_ok=True) - (case5b / "script2.py").touch() + (case5b / "script2.py").write_text(f"# {case5b.name} script2\n") + + case5c = case5ex_dir / "packA" / "ex2" # packA, ex2 + case5c.mkdir(parents=True, exist_ok=True) + (case5c / "script3.py").write_text(f"# {case5c.name} script3\n") + + case5d = case5ex_dir / "packB" / "ex3" + case5d.mkdir(parents=True, exist_ok=True) + (case5d / "script4.py").write_text(f"# {case5d.name} script4\n") + + case5e = case5ex_dir / "packB" / "ex4" + case5e.mkdir(parents=True, exist_ok=True) + (case5e / "script5.py").write_text(f"# {case5e.name} script5\n") + case5req_dir = root_temp_dir / "case5" / "requirements" / "packs" case5req_dir.mkdir(parents=True, exist_ok=True) From 711c24ef32da7c3df7a3a2e6dff660005c933ba9 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 20 Oct 2025 13:46:03 -0400 Subject: [PATCH 05/25] rm old tests --- tests/test_cli.py | 65 ----------------------------------------------- 1 file changed, 65 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 9eb9712..e69de29 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,65 +0,0 @@ -import os -from pathlib import Path - -import pytest - -from diffpy.cmi import cli - - -def test_map_pack_to_examples_structure(): - """Test that map_pack_to_examples returns the right shape of - data.""" - actual = cli.map_pack_to_examples() - assert isinstance(actual, dict) - for pack, exdirs in actual.items(): - assert isinstance(pack, str) - assert isinstance(exdirs, list) - for ex in exdirs: - assert isinstance(ex, str) - # Check for known packs - assert "core" in actual.keys() - assert "pdf" in actual.keys() - # Check for known examples - assert ["linefit"] in actual.values() - - -@pytest.mark.parametrize( - "input_valid_str", - [ - "core/linefit", - "pdf/ch03NiModelling", - ], -) -def test_copy_example_success(tmp_path, input_valid_str): - """Given a valid example format (/), test that its copied - to the temp dir.""" - os.chdir(tmp_path) - actual = cli.copy_example(input_valid_str) - expected = tmp_path / Path(input_valid_str).name - assert expected.exists() and expected.is_dir() - assert actual == expected - - -def test_copy_example_fnferror(): - """Test that FileNotFoundError is raised when the example does not - exist.""" - with pytest.raises(FileNotFoundError): - cli.copy_example("pack/example1") - - -@pytest.mark.parametrize( - "input_bad_str", - [ - "", # empty string - "/", # missing pack and example - "corelinefit", # missing slash - "linefit", # missing pack and slash - "core/", # missing example - "/linefit", # missing pack - "core/linefit/extra", # too many slashes - ], -) -def test_copy_example_valueerror(input_bad_str): - """Test that ValueError is raised when the format is invalid.""" - with pytest.raises(ValueError): - cli.copy_example(input_bad_str) From 987c3e3d8c4d5e394276d2358c342997f3d512e0 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 20 Oct 2025 13:46:30 -0400 Subject: [PATCH 06/25] add news --- news/copy-func-test.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 news/copy-func-test.rst diff --git a/news/copy-func-test.rst b/news/copy-func-test.rst new file mode 100644 index 0000000..22d461a --- /dev/null +++ b/news/copy-func-test.rst @@ -0,0 +1,23 @@ +**Added:** + +* Add test for ``copy_examples``. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* From 9e65a2743f622b2e609ca3f515657b4322bdc95d Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 20 Oct 2025 17:27:34 -0400 Subject: [PATCH 07/25] glob only files and not dirs --- tests/test_packsmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index 4bce34c..02dc701 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -192,7 +192,7 @@ def test_copy_examples(input, expected_paths, example_cases): pm = PacksManager(root_path=examples_dir) target_dir = example_cases / "user_target" pm.copy_examples(input, target_dir=target_dir) - actual = sorted(list(target_dir.rglob("*"))) + actual = sorted(list(target_dir.rglob("*.py"))) expected = sorted([target_dir / path for path in expected_paths]) assert actual == expected for path in expected_paths: From 5354831f188f16c4a4e9028f26bab2ed7c82e8b4 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Wed, 22 Oct 2025 17:20:58 -0400 Subject: [PATCH 08/25] fix target dir location to be under cwd and change name for new test case --- tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index f83e5b6..c58b653 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -32,8 +32,8 @@ def example_cases(tmp_path_factory): root_temp_dir = tmp_path_factory.mktemp("temp") cwd = root_temp_dir / "cwd" cwd.mkdir(parents=True, exist_ok=True) - user_target = root_temp_dir / "user_target" - user_target.mkdir(parents=True, exist_ok=True) + existing_dir = cwd / "existing_target" + existing_dir.mkdir(parents=True, exist_ok=True) # case 1: pack with no examples case1ex_dir = root_temp_dir / "case1" / "docs" / "examples" From 47d87fb436f274a54688a762eb3740df50c90396 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Wed, 22 Oct 2025 17:31:32 -0400 Subject: [PATCH 09/25] Add additional test case, fix how copy location is tested --- tests/test_packsmanager.py | 40 +++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index 02dc701..72597fd 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -206,19 +206,45 @@ def test_copy_examples(input, expected_paths, example_cases): # input: str or None - path arg to copy_examples # expected: Path - expected relative path to copied example @pytest.mark.parametrize( - "input,expected_path", + "input,expected_paths", [ - (None, Path("cwd/packA/ex1/path1/script1.py")), - ("user_target", Path("user_target/packA/ex1/path1/script1.py")), + ( + None, + [ + Path("cwd/packA/ex1/path1/script1.py"), + Path("cwd/packA/ex2/script3.py"), + ], + ), + # input is a target dir that doesn't exist yet + # expected target dir to be created and examples copied there + ( + Path("user_target"), + [ + Path("cwd/user_target/packA/ex1/path1/script1.py"), + Path("cwd/user_target/packA/ex2/script3.py"), + ], + ), + # input is a target dir that already exists + # expected examples copied into existing dir + ( + Path("existing_target"), + [ + Path("cwd/existing_target/packA/ex1/path1/script1.py"), + Path("cwd/existing_target/packA/ex2/script3.py"), + ], + ), ], ) -def test_copy_examples_location(input, expected_path, example_cases): +def test_copy_examples_location(input, expected_paths, example_cases): examples_dir = example_cases / "case5" os.chdir(example_cases / "cwd") pm = PacksManager(root_path=examples_dir) - paths = pm.copy_examples(["packA"], target_dir=input) - actual = paths[0] - expected = example_cases / expected_path + pm.copy_examples(["packA"], target_dir=input) + target_directory = ( + Path.cwd() if input is None else example_cases / "cwd" / input + ) + actual = sorted(target_directory.rglob("*.py")) + expected = [example_cases / path for path in expected_paths] assert actual == expected From 0a490b92e66faa4edd11db3bbb5543860a2fac2c Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Wed, 22 Oct 2025 17:34:54 -0400 Subject: [PATCH 10/25] copy_examples function --- src/diffpy/cmi/packsmanager.py | 65 +++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index c45e4cc..682a566 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -12,7 +12,7 @@ # See LICENSE.rst for license information. # ############################################################################## - +import shutil from importlib.resources import as_file from pathlib import Path from typing import List, Union @@ -149,8 +149,71 @@ def copy_examples( Target directory to copy examples into. Defaults to current working directory. """ + self._target_dir = target_dir.resolve() if target_dir else Path.cwd() + + if "all" in examples_to_copy: + self._copy_all() + return + + for item in examples_to_copy: + if item in self.available_examples(): + self._copy_pack(item) + elif self._is_example_name(item): + self._copy_example(item) + else: + self._not_found_error(item) + del self._target_dir return + def _copy_all(self): + """Copy all packs and examples.""" + for pack_name in self.available_examples(): + self._copy_pack(pack_name) + + def _copy_pack(self, pack_name): + """Copy all examples in a single pack.""" + examples = self.available_examples().get(pack_name, []) + for ex_name, ex_path in examples: + self._copy_tree_to_target(pack_name, ex_name, ex_path) + + def _copy_example(self, example_name): + """Copy a single example by its name.""" + example_found = False + for pack_name, examples in self.available_examples().items(): + for ex_name, ex_path in examples: + if ex_name == example_name: + self._copy_tree_to_target(pack_name, ex_name, ex_path) + example_found = True + if not example_found: + self._not_found_error(example_name) + + def _is_example_name(self, name): + """Return True if the given name matches any known example.""" + for pack_name, examples in self.available_examples().items(): + for example_name, _ in examples: + if example_name == name: + return True + return False + + def _copy_tree_to_target(self, pack_name, example_name, src_path): + """Helper to handle the actual filesystem copy.""" + dest_dir = self._target_dir / pack_name / example_name + if dest_dir.exists(): + plog.warning( + f"Example directory(ies): '{dest_dir.stem}' already exist. " + " Current versions of existing files have " + "been left unchanged. To overwrite, please rerun " + "and specify --force." + ) + return + dest_dir.parent.mkdir(parents=True, exist_ok=True) + shutil.copytree(src_path, dest_dir) + + def _not_found_error(self, name): + raise FileNotFoundError( + f"No examples or packs found for input: '{name}'" + ) + def _resolve_pack_file(self, identifier: Union[str, Path]) -> Path: """Resolve a pack identifier to an absolute .txt path. From 6e2695d5667cd991346d89902aa552ad96e05c77 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 23 Oct 2025 10:20:09 -0400 Subject: [PATCH 11/25] rm error message function --- src/diffpy/cmi/packsmanager.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index 682a566..4200a48 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -161,7 +161,9 @@ def copy_examples( elif self._is_example_name(item): self._copy_example(item) else: - self._not_found_error(item) + raise FileNotFoundError( + f"No examples or packs found for input: '{item}'" + ) del self._target_dir return @@ -185,7 +187,9 @@ def _copy_example(self, example_name): self._copy_tree_to_target(pack_name, ex_name, ex_path) example_found = True if not example_found: - self._not_found_error(example_name) + raise FileNotFoundError( + f"No examples or packs found for input: '{example_name}'" + ) def _is_example_name(self, name): """Return True if the given name matches any known example.""" @@ -209,11 +213,6 @@ def _copy_tree_to_target(self, pack_name, example_name, src_path): dest_dir.parent.mkdir(parents=True, exist_ok=True) shutil.copytree(src_path, dest_dir) - def _not_found_error(self, name): - raise FileNotFoundError( - f"No examples or packs found for input: '{name}'" - ) - def _resolve_pack_file(self, identifier: Union[str, Path]) -> Path: """Resolve a pack identifier to an absolute .txt path. From e96988fbce5de894c17bd81dceb6c2f0a8e9ee1e Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 23 Oct 2025 10:53:56 -0400 Subject: [PATCH 12/25] add test for forced overwrite of copied files --- tests/test_packsmanager.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index 72597fd..1a9a92c 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -192,7 +192,7 @@ def test_copy_examples(input, expected_paths, example_cases): pm = PacksManager(root_path=examples_dir) target_dir = example_cases / "user_target" pm.copy_examples(input, target_dir=target_dir) - actual = sorted(list(target_dir.rglob("*.py"))) + actual = sorted(target_dir.rglob("*.py")) expected = sorted([target_dir / path for path in expected_paths]) assert actual == expected for path in expected_paths: @@ -244,7 +244,7 @@ def test_copy_examples_location(input, expected_paths, example_cases): Path.cwd() if input is None else example_cases / "cwd" / input ) actual = sorted(target_directory.rglob("*.py")) - expected = [example_cases / path for path in expected_paths] + expected = sorted([example_cases / path for path in expected_paths]) assert actual == expected @@ -298,3 +298,22 @@ def test_copy_examples_bad(bad_inputs, expected, path, example_cases): target_dir = None if path is None else examples_dir / path with pytest.raises(Exception, match=re.escape(expected)): pm.copy_examples(bad_inputs, target_dir=target_dir) + + +def test_copy_examples_force(example_cases): + examples_dir = example_cases / "case3" + pm = PacksManager(root_path=examples_dir) + target_dir = examples_dir / "docs" / "examples" + pm.copy_examples(["packA"], target_dir=target_dir, force=True) + expected_paths = [ + Path("packA/ex1/script1.py"), + Path("packA/ex2/solution/script2.py"), + ] + actual = sorted(target_dir.rglob("*.py")) + expected = sorted([target_dir / path for path in expected_paths]) + assert actual == expected + for path in expected_paths: + copied_path = target_dir / path + original_path = examples_dir / path + if copied_path.is_file() and original_path.is_file(): + assert copied_path.read_text() == original_path.read_text() From abfba685011691c019e510cbfd8872005e82b252 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 23 Oct 2025 10:55:31 -0400 Subject: [PATCH 13/25] Add 'force' param to copy_examples --- src/diffpy/cmi/packsmanager.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index 4200a48..38e6c84 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -137,6 +137,7 @@ def copy_examples( self, examples_to_copy: List[str], target_dir: Path = None, + force: bool = False, ) -> None: """Copy examples or packs into the target or current working directory. @@ -148,8 +149,11 @@ def copy_examples( target_dir : pathlib.Path, optional Target directory to copy examples into. Defaults to current working directory. + force : bool, optional + If ``True``, overwrite existing files. Defaults to ``False``. """ self._target_dir = target_dir.resolve() if target_dir else Path.cwd() + self._force = force if "all" in examples_to_copy: self._copy_all() @@ -165,6 +169,7 @@ def copy_examples( f"No examples or packs found for input: '{item}'" ) del self._target_dir + del self._force return def _copy_all(self): From d4cddf5797ccf5f24d3b08405e9f566e85d4f3b4 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 27 Oct 2025 14:37:39 -0400 Subject: [PATCH 14/25] fix copy_tree to handle warnings and errors properly --- src/diffpy/cmi/packsmanager.py | 19 +++++----- tests/test_packsmanager.py | 66 ++++++++++++++++++---------------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index 38e6c84..21f93e2 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -13,6 +13,7 @@ # ############################################################################## import shutil +import warnings from importlib.resources import as_file from pathlib import Path from typing import List, Union @@ -150,7 +151,9 @@ def copy_examples( Target directory to copy examples into. Defaults to current working directory. force : bool, optional - If ``True``, overwrite existing files. Defaults to ``False``. + Defaults to ``False``. If ``True``, existing files are + overwritten and directories are merged + (extra files in the target are preserved). """ self._target_dir = target_dir.resolve() if target_dir else Path.cwd() self._force = force @@ -207,16 +210,16 @@ def _is_example_name(self, name): def _copy_tree_to_target(self, pack_name, example_name, src_path): """Helper to handle the actual filesystem copy.""" dest_dir = self._target_dir / pack_name / example_name - if dest_dir.exists(): - plog.warning( + dest_dir.parent.mkdir(parents=True, exist_ok=True) + if dest_dir.exists() and not self._force: + warn_message = ( f"Example directory(ies): '{dest_dir.stem}' already exist. " - " Current versions of existing files have " - "been left unchanged. To overwrite, please rerun " - "and specify --force." + "Current versions of existing files have been left unchanged. " + "To overwrite, please rerun and specify --force." ) + warnings.warn(warn_message, UserWarning) return - dest_dir.parent.mkdir(parents=True, exist_ok=True) - shutil.copytree(src_path, dest_dir) + shutil.copytree(src_path, dest_dir, dirs_exist_ok=self._force) def _resolve_pack_file(self, identifier: Union[str, Path]) -> Path: """Resolve a pack identifier to an absolute .txt path. diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index 1a9a92c..c8483c9 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -105,13 +105,12 @@ def test_tmp_file_structure(input, expected, example_cases): # Test various use cases to copy_examples on case5 # 1) copy one example (ambiguous) # 2) copy list of examples from same pack (ambiguous) - # 3) copy list of examples from different pack (ambiguous) - # 4) copy one example (unambiguous) - # 5) copy list of examples from same pack (unambiguous) - # 6) copy list of examples from different pack (unambiguous) - # 7) copy all examples from a pack - # 8) copy all examples from list of packs - # 9) copy all examples from all packs + # 3) copy one example (unambiguous) + # 4) copy list of examples from same pack (unambiguous) + # 5) copy list of examples from different packs (unambiguous) + # 6) copy all examples from a pack + # 7) copy all examples from list of packs + # 8) copy all examples from all packs ( # 1) copy one example, (ambiguous) ["ex1"], [ @@ -127,41 +126,34 @@ def test_tmp_file_structure(input, expected, example_cases): Path("packA/ex2/script3.py"), ], ), - ( # 3) copy list of examples from different packs (ambiguous) - ["ex1", "ex1"], - [ - Path("packA/ex1/path1/script1.py"), - Path("packB/ex1/path2/script2.py"), - ], - ), - ( # 4) copy one example (unambiguous) + ( # 3) copy one example (unambiguous) ["ex2"], [ Path("packA/ex2/script3.py"), ], ), - ( # 5) copy list of examples from same pack (unambiguous) + ( # 4) copy list of examples from same pack (unambiguous) ["ex3", "ex4"], [ Path("packB/ex3/script4.py"), Path("packB/ex4/script5.py"), ], ), - ( # 6) copy list of examples from different packs (unambiguous) + ( # 5) copy list of examples from different packs (unambiguous) ["ex2", "ex3"], [ Path("packA/ex2/script3.py"), Path("packB/ex3/script4.py"), ], ), - ( # 7) copy all examples from a pack + ( # 6) copy all examples from a pack ["packA"], [ Path("packA/ex1/path1/script1.py"), Path("packA/ex2/script3.py"), ], ), - ( # 8) copy all examples from list of packs + ( # 7) copy all examples from list of packs ["packA", "packB"], [ Path("packA/ex1/path1/script1.py"), @@ -171,7 +163,7 @@ def test_tmp_file_structure(input, expected, example_cases): Path("packB/ex4/script5.py"), ], ), - ( # 9) copy all examples from all packs + ( # 8) copy all examples from all packs ["all"], [ Path("packA/ex1/path1/script1.py"), @@ -255,7 +247,7 @@ def test_copy_examples_location(input, expected_paths, example_cases): # 3) Path to directory already exists # 4) No input provided @pytest.mark.parametrize( - "bad_inputs,expected,path", + "bad_inputs,expected,path,is_warning", [ ( # 1) Input not found (example or pack). @@ -263,6 +255,7 @@ def test_copy_examples_location(input, expected_paths, example_cases): ["bad_example"], "No examples or packs found for input: 'bad_example'", None, + False, ), ( # 2) Mixed good example and bad input. @@ -270,6 +263,7 @@ def test_copy_examples_location(input, expected_paths, example_cases): ["ex1", "bad_example"], "No examples or packs found for input: 'bad_example'", None, + False, ), ( # 3) Mixed good pack and bad input. @@ -277,6 +271,7 @@ def test_copy_examples_location(input, expected_paths, example_cases): ["packA", "bad_example"], "No examples or packs found for input: 'bad_example'", None, + False, ), ( # 4) Path to directory already exists. @@ -284,36 +279,45 @@ def test_copy_examples_location(input, expected_paths, example_cases): ["ex1"], ( "Example directory(ies): 'ex1' already exist. " - " Current versions of existing files have " + "Current versions of existing files have " "been left unchanged. To overwrite, please rerun " "and specify --force." ), Path("docs/examples/"), + True, ), ], ) -def test_copy_examples_bad(bad_inputs, expected, path, example_cases): +def test_copy_examples_bad( + bad_inputs, expected, path, is_warning, example_cases +): examples_dir = example_cases / "case3" pm = PacksManager(root_path=examples_dir) target_dir = None if path is None else examples_dir / path - with pytest.raises(Exception, match=re.escape(expected)): - pm.copy_examples(bad_inputs, target_dir=target_dir) + if is_warning: + with pytest.warns(UserWarning, match=re.escape(expected)): + pm.copy_examples(bad_inputs, target_dir=target_dir) + else: + with pytest.raises(FileNotFoundError, match=re.escape(expected)): + pm.copy_examples(bad_inputs, target_dir=target_dir) def test_copy_examples_force(example_cases): examples_dir = example_cases / "case3" pm = PacksManager(root_path=examples_dir) - target_dir = examples_dir / "docs" / "examples" - pm.copy_examples(["packA"], target_dir=target_dir, force=True) + case5dir = example_cases / "case5" / "docs" / "examples" + pm.copy_examples(["packA"], target_dir=case5dir, force=True) expected_paths = [ + Path("packA/ex1/path1/script1.py"), Path("packA/ex1/script1.py"), - Path("packA/ex2/solution/script2.py"), + Path("packA/ex2/solutions/script2.py"), + Path("packA/ex2/script3.py"), ] - actual = sorted(target_dir.rglob("*.py")) - expected = sorted([target_dir / path for path in expected_paths]) + actual = sorted((case5dir / "packA").rglob("*.py")) + expected = sorted([case5dir / path for path in expected_paths]) assert actual == expected for path in expected_paths: - copied_path = target_dir / path + copied_path = case5dir / path original_path = examples_dir / path if copied_path.is_file() and original_path.is_file(): assert copied_path.read_text() == original_path.read_text() From f4fe60590285824fa2dfc4e93ffe187c1fe0cfca Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 30 Oct 2025 14:29:51 -0400 Subject: [PATCH 15/25] pin workflow to python 3.13 --- .github/workflows/tests-on-pr.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml index e8ef0fc..08533f1 100644 --- a/.github/workflows/tests-on-pr.yml +++ b/.github/workflows/tests-on-pr.yml @@ -11,6 +11,7 @@ jobs: project: diffpy.cmi c_extension: false headless: false + python_version: 3.13 run: | set -Eeuo pipefail echo "Test cmds" From 07cbcd736c15e4c158a0c45f2506a126caf10c1c Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 31 Oct 2025 12:37:19 -0400 Subject: [PATCH 16/25] add test for force and non-force to make sure files are copied correctly --- tests/test_packsmanager.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index c8483c9..78814c9 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -302,17 +302,32 @@ def test_copy_examples_bad( pm.copy_examples(bad_inputs, target_dir=target_dir) -def test_copy_examples_force(example_cases): +@pytest.mark.parametrize( + "expected_paths,force", + [ + ( + [ + Path("packA/ex1/script1.py"), + Path("packA/ex2/solutions/script2.py"), + ], + True, + ), + ( + [ + Path("packA/ex1/path1/script1.py"), + Path("packA/ex1/script1.py"), + Path("packA/ex2/solutions/script2.py"), + Path("packA/ex2/script3.py"), + ], + False, + ), + ], +) +def test_copy_examples_force(example_cases, expected_paths, force): examples_dir = example_cases / "case3" pm = PacksManager(root_path=examples_dir) case5dir = example_cases / "case5" / "docs" / "examples" - pm.copy_examples(["packA"], target_dir=case5dir, force=True) - expected_paths = [ - Path("packA/ex1/path1/script1.py"), - Path("packA/ex1/script1.py"), - Path("packA/ex2/solutions/script2.py"), - Path("packA/ex2/script3.py"), - ] + pm.copy_examples(["packA"], target_dir=case5dir, force=force) actual = sorted((case5dir / "packA").rglob("*.py")) expected = sorted([case5dir / path for path in expected_paths]) assert actual == expected From 078b13c3ee1b7a343b87ddad2482fd95b6b4942f Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 31 Oct 2025 13:15:13 -0400 Subject: [PATCH 17/25] update copy_tree_to_target to handle different copying cases --- src/diffpy/cmi/packsmanager.py | 66 +++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index 21f93e2..91f468e 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -13,11 +13,12 @@ # ############################################################################## import shutil -import warnings from importlib.resources import as_file from pathlib import Path from typing import List, Union +from rich.console import Console + from diffpy.cmi.installer import ( ParsedReq, install_requirements, @@ -80,6 +81,7 @@ class PacksManager: def __init__(self, root_path=None) -> None: self.packs_dir = _installed_packs_dir(root_path) self.examples_dir = self._get_examples_dir() + self.console = Console() def _get_examples_dir(self) -> Path: """Return the absolute path to the installed examples directory. @@ -207,19 +209,59 @@ def _is_example_name(self, name): return True return False - def _copy_tree_to_target(self, pack_name, example_name, src_path): - """Helper to handle the actual filesystem copy.""" - dest_dir = self._target_dir / pack_name / example_name - dest_dir.parent.mkdir(parents=True, exist_ok=True) - if dest_dir.exists() and not self._force: - warn_message = ( - f"Example directory(ies): '{dest_dir.stem}' already exist. " - "Current versions of existing files have been left unchanged. " - "To overwrite, please rerun and specify --force." + def _copy_tree_to_target(self, pack_name, example_name, example_origin): + """Copy an example folder from source to the user's target + directory.""" + target_dir = self._target_dir / pack_name / example_name + target_dir.parent.mkdir(parents=True, exist_ok=True) + if target_dir.exists() and self._force: + self._overwrite_example( + example_origin, target_dir, pack_name, example_name + ) + return + if target_dir.exists(): + self._copy_missing_files(example_origin, target_dir) + print( + f"WARNING: Example '{pack_name}/{example_name}' " + "already exists at the specified target directory. " + "Existing files were left unchanged; " + "new or missing files were copied. To overwrite everything, " + "rerun with --force." ) - warnings.warn(warn_message, UserWarning) return - shutil.copytree(src_path, dest_dir, dirs_exist_ok=self._force) + self._copy_new_example( + example_origin, target_dir, pack_name, example_name + ) + + def _overwrite_example( + self, example_origin, target, pack_name, example_name + ): + """Delete target and copy example.""" + shutil.rmtree(target) + shutil.copytree(example_origin, target) + self.console.print( + f"[green]Overwriting example '{pack_name}/{example_name}'.[/]" + ) + + def _copy_missing_files(self, example_origin, target): + """Copy only files and directories that are missing in the + target.""" + for example_item in example_origin.rglob("*"): + rel_path = example_item.relative_to(example_origin) + target_item = target / rel_path + if example_item.is_dir(): + target_item.mkdir(parents=True, exist_ok=True) + elif example_item.is_file() and not target_item.exists(): + target_item.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(example_item, target_item) + + def _copy_new_example( + self, example_origin, target, pack_name, example_name + ): + shutil.copytree(example_origin, target) + self.console.print( + f"[green]Copied example '{pack_name}/{example_name}'.[/]" + ) def _resolve_pack_file(self, identifier: Union[str, Path]) -> Path: """Resolve a pack identifier to an absolute .txt path. From e7444d7623dfee6dec8f132aa991867e98b2a182 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 31 Oct 2025 13:15:38 -0400 Subject: [PATCH 18/25] capture warning output in print statement --- tests/test_packsmanager.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index 78814c9..bdc9300 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -278,10 +278,11 @@ def test_copy_examples_location(input, expected_paths, example_cases): # Expected: Raise a warning with the message. ["ex1"], ( - "Example directory(ies): 'ex1' already exist. " - "Current versions of existing files have " - "been left unchanged. To overwrite, please rerun " - "and specify --force." + "WARNING: Example 'packA/ex1' already exists at " + "the specified target directory. " + "Existing files were left unchanged; new or missing " + "files were copied. " + "To overwrite everything, rerun with --force." ), Path("docs/examples/"), True, @@ -289,14 +290,16 @@ def test_copy_examples_location(input, expected_paths, example_cases): ], ) def test_copy_examples_bad( - bad_inputs, expected, path, is_warning, example_cases + bad_inputs, expected, path, is_warning, example_cases, capsys ): examples_dir = example_cases / "case3" pm = PacksManager(root_path=examples_dir) target_dir = None if path is None else examples_dir / path if is_warning: - with pytest.warns(UserWarning, match=re.escape(expected)): - pm.copy_examples(bad_inputs, target_dir=target_dir) + pm.copy_examples(bad_inputs, target_dir=target_dir) + captured = capsys.readouterr() + actual = captured.out + assert re.search(re.escape(expected), actual) else: with pytest.raises(FileNotFoundError, match=re.escape(expected)): pm.copy_examples(bad_inputs, target_dir=target_dir) From c792c0b37040d2b8eabecc91f02f0470e4ca8e69 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 31 Oct 2025 13:21:17 -0400 Subject: [PATCH 19/25] add rich to conda.txt --- requirements/conda.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/conda.txt b/requirements/conda.txt index e9bdbc1..3248c7e 100644 --- a/requirements/conda.txt +++ b/requirements/conda.txt @@ -4,3 +4,4 @@ PyYAML diffpy.utils diffpy.srfit diffpy.structure +rich From 7d7a2a3ad7f2eef700700aaf09cb15a8cad23b8f Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 31 Oct 2025 14:48:23 -0400 Subject: [PATCH 20/25] add UC comments --- tests/test_packsmanager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index bdc9300..62d2168 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -309,14 +309,16 @@ def test_copy_examples_bad( "expected_paths,force", [ ( - [ + [ # UC1: copy examples to target dir with overwrite + # expected: Existing files are overwritten and new files copied Path("packA/ex1/script1.py"), Path("packA/ex2/solutions/script2.py"), ], True, ), ( - [ + [ # UC2: copy examples to target dir without overwrite + # expected: Existing files are left unchanged; new files copied Path("packA/ex1/path1/script1.py"), Path("packA/ex1/script1.py"), Path("packA/ex2/solutions/script2.py"), From 52a67525e72b81a318200562e7a6680811c0636b Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 31 Oct 2025 15:25:28 -0400 Subject: [PATCH 21/25] use ANSI color code for now --- src/diffpy/cmi/packsmanager.py | 15 +++++---------- tests/test_packsmanager.py | 4 ++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index 91f468e..af73492 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -17,8 +17,6 @@ from pathlib import Path from typing import List, Union -from rich.console import Console - from diffpy.cmi.installer import ( ParsedReq, install_requirements, @@ -81,7 +79,6 @@ class PacksManager: def __init__(self, root_path=None) -> None: self.packs_dir = _installed_packs_dir(root_path) self.examples_dir = self._get_examples_dir() - self.console = Console() def _get_examples_dir(self) -> Path: """Return the absolute path to the installed examples directory. @@ -222,8 +219,8 @@ def _copy_tree_to_target(self, pack_name, example_name, example_origin): if target_dir.exists(): self._copy_missing_files(example_origin, target_dir) print( - f"WARNING: Example '{pack_name}/{example_name}' " - "already exists at the specified target directory. " + f"\033[33mWARNING:\033[0m Example '{pack_name}/{example_name}'" + " already exists at the specified target directory. " "Existing files were left unchanged; " "new or missing files were copied. To overwrite everything, " "rerun with --force." @@ -239,8 +236,8 @@ def _overwrite_example( """Delete target and copy example.""" shutil.rmtree(target) shutil.copytree(example_origin, target) - self.console.print( - f"[green]Overwriting example '{pack_name}/{example_name}'.[/]" + print( + f"\033[32mOverwriting example '{pack_name}/{example_name}'.\033[0m" ) def _copy_missing_files(self, example_origin, target): @@ -259,9 +256,7 @@ def _copy_new_example( self, example_origin, target, pack_name, example_name ): shutil.copytree(example_origin, target) - self.console.print( - f"[green]Copied example '{pack_name}/{example_name}'.[/]" - ) + print(f"\033[32mCopied example '{pack_name}/{example_name}'.\033[0m") def _resolve_pack_file(self, identifier: Union[str, Path]) -> Path: """Resolve a pack identifier to an absolute .txt path. diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index 62d2168..a9204c1 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -278,8 +278,8 @@ def test_copy_examples_location(input, expected_paths, example_cases): # Expected: Raise a warning with the message. ["ex1"], ( - "WARNING: Example 'packA/ex1' already exists at " - "the specified target directory. " + "\033[33mWARNING:\033[0m Example 'packA/ex1' already exists at" + " the specified target directory. " "Existing files were left unchanged; new or missing " "files were copied. " "To overwrite everything, rerun with --force." From 611bdf5e57ff6c81bda322fb83b351a3102e5cc3 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 31 Oct 2025 15:26:18 -0400 Subject: [PATCH 22/25] rm rich from reqs --- requirements/conda.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements/conda.txt b/requirements/conda.txt index 3248c7e..e9bdbc1 100644 --- a/requirements/conda.txt +++ b/requirements/conda.txt @@ -4,4 +4,3 @@ PyYAML diffpy.utils diffpy.srfit diffpy.structure -rich From d6f34e0abf3a70c4e6b455dbedd3e13e4b117fe9 Mon Sep 17 00:00:00 2001 From: Caden Myers <158210249+cadenmyers13@users.noreply.github.com> Date: Fri, 31 Oct 2025 19:58:43 -0400 Subject: [PATCH 23/25] Rm colors from print --- src/diffpy/cmi/packsmanager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index af73492..1f40292 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -219,7 +219,7 @@ def _copy_tree_to_target(self, pack_name, example_name, example_origin): if target_dir.exists(): self._copy_missing_files(example_origin, target_dir) print( - f"\033[33mWARNING:\033[0m Example '{pack_name}/{example_name}'" + f"WARNING: Example '{pack_name}/{example_name}'" " already exists at the specified target directory. " "Existing files were left unchanged; " "new or missing files were copied. To overwrite everything, " @@ -237,7 +237,7 @@ def _overwrite_example( shutil.rmtree(target) shutil.copytree(example_origin, target) print( - f"\033[32mOverwriting example '{pack_name}/{example_name}'.\033[0m" + f"Overwriting example '{pack_name}/{example_name}'." ) def _copy_missing_files(self, example_origin, target): @@ -256,7 +256,7 @@ def _copy_new_example( self, example_origin, target, pack_name, example_name ): shutil.copytree(example_origin, target) - print(f"\033[32mCopied example '{pack_name}/{example_name}'.\033[0m") + print(f"Copied example '{pack_name}/{example_name}'.") def _resolve_pack_file(self, identifier: Union[str, Path]) -> Path: """Resolve a pack identifier to an absolute .txt path. From 087851bf95b2b00e6a555e34889407289719f967 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 23:58:55 +0000 Subject: [PATCH 24/25] [pre-commit.ci] auto fixes from pre-commit hooks --- src/diffpy/cmi/packsmanager.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index 1f40292..8c2f431 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -236,9 +236,7 @@ def _overwrite_example( """Delete target and copy example.""" shutil.rmtree(target) shutil.copytree(example_origin, target) - print( - f"Overwriting example '{pack_name}/{example_name}'." - ) + print(f"Overwriting example '{pack_name}/{example_name}'.") def _copy_missing_files(self, example_origin, target): """Copy only files and directories that are missing in the From 87c1f43e6663174368fd47a36a44fe880263403a Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 31 Oct 2025 20:01:59 -0400 Subject: [PATCH 25/25] rm color from test --- tests/test_packsmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index a9204c1..bc93275 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -278,7 +278,7 @@ def test_copy_examples_location(input, expected_paths, example_cases): # Expected: Raise a warning with the message. ["ex1"], ( - "\033[33mWARNING:\033[0m Example 'packA/ex1' already exists at" + "WARNING: Example 'packA/ex1' already exists at" " the specified target directory. " "Existing files were left unchanged; new or missing " "files were copied. "