From 8283045fafc6c37bb4d3da531a05c16849b37148 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 10 Oct 2025 09:57:35 -0400 Subject: [PATCH 1/8] test for building dict from temp dir --- tests/test_packsmanager.py | 93 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 tests/test_packsmanager.py diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py new file mode 100644 index 0000000..d3af5d8 --- /dev/null +++ b/tests/test_packsmanager.py @@ -0,0 +1,93 @@ +import pytest + +from diffpy.cmi.packsmanager import PacksManager + +example_params = [ + # 1) pack with no examples. Expect {'empty_pack': []} + # 2) pack with multiple examples. + # Expect {'full_pack': [('example1`, path_to_1'), 'example2', path_to_2)] + # 3) multiple packs. Expect dict with multiple pack:tuple pairs + # 4) no pack found. Expect {} + # case 1: pack with no examples. Expect {'empty_pack': []} + # 5) multiple packs with the same example names + # Expect dict with multiple pack:tuple pairs + ( + "case1", + {"empty_pack": []}, + ), + # case 2: pack with multiple examples. + # Expect {'full_pack': [('example1', path_to_1), + # ('example2', path_to_2)]} + ( + "case2", + { + "full_pack": [ + ("ex1", "case2/docs/examples/full_pack/ex1"), + ("ex2", "case2/docs/examples/full_pack/ex2"), + ] + }, + ), + # case 3: multiple packs. Expect dict with multiple pack:tuple pairs + ( + "case3", + { + "packA": [ + ("ex1", "case3/docs/examples/packA/ex1"), + ("ex2", "case3/docs/examples/packA/ex2"), + ], + "packB": [("ex3", "case3/docs/examples/packB/ex3")], + }, + ), + ( # case 4: no pack found. Expect {} + "case4", + {}, + ), + ( # case 5: multiple packs with duplicate example names + # Expect dict with multiple pack:tuple pairs + "case5", + { + "packA": [ + ("ex1", "case5/docs/examples/packA/ex1"), + ], + "packB": [ + ("ex1", "case5/docs/examples/packB/ex1"), + ], + }, + ), +] + + +def paths_and_names_match(expected, actual, root): + """Compare two (example_name, path) lists ignoring temp dir + differences.""" + if len(expected) != len(actual): + return False + for (exp_name, exp_path), (act_name, act_path) in zip(expected, actual): + if exp_name != act_name: + return False + actual_rel_path = str(act_path.relative_to(root)) + if actual_rel_path != exp_path: + return False + return True + + +@pytest.mark.parametrize("input,expected", example_params) +def test_available_examples(input, expected, example_cases): + case_dir = example_cases / input + pkmg = PacksManager(case_dir) + actual = pkmg.available_examples() + assert actual.keys() == expected.keys() + for pack in expected: + assert paths_and_names_match( + expected[pack], actual[pack], example_cases + ) + + +@pytest.mark.parametrize("input,expected", example_params) +def test_tmp_file_structure(input, expected, example_cases): + example_path = example_cases / input + for path in example_path.rglob("*"): + if path.suffix: + assert path.is_file() + else: + assert path.is_dir() From ee28cb841f0da0bfe5ee0460422a496a89232645 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 10 Oct 2025 10:00:18 -0400 Subject: [PATCH 2/8] news --- news/dict-build-test.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 news/dict-build-test.rst diff --git a/news/dict-build-test.rst b/news/dict-build-test.rst new file mode 100644 index 0000000..cc1bbb1 --- /dev/null +++ b/news/dict-build-test.rst @@ -0,0 +1,23 @@ +**Added:** + +* Add test for building examples dict. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* From 1a5b8be09a2844a3e582c1b4cedf3f6875c081c8 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 10 Oct 2025 10:07:46 -0400 Subject: [PATCH 3/8] add optional root_path to get_package_dir --- src/diffpy/cmi/__init__.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/diffpy/cmi/__init__.py b/src/diffpy/cmi/__init__.py index 62108fd..6253d84 100644 --- a/src/diffpy/cmi/__init__.py +++ b/src/diffpy/cmi/__init__.py @@ -18,8 +18,23 @@ from importlib.resources import as_file, files -def get_package_dir(): - resource = files(__name__) +def get_package_dir(root_path=None): + """Get the package directory as a context manager. + + Parameters + ---------- + root_path : str, optional + Used for testing, overrides the files(__name__) call. + + Returns + ------- + context manager + A context manager that yields a pathlib.Path to the package directory. + """ + if root_path is None: + resource = files(__name__) + else: + resource = root_path return as_file(resource) From 00663740d7d37195b0f57146e6e0c00f0f0afc06 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 10 Oct 2025 10:08:54 -0400 Subject: [PATCH 4/8] add available_examples which builds dict --- src/diffpy/cmi/packsmanager.py | 58 +++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index bd8b10b..166ded5 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -28,9 +28,9 @@ __all__ = ["PacksManager"] -def _installed_packs_dir() -> Path: +def _installed_packs_dir(root_path=None) -> Path: """Locate requirements/packs/ for the installed package.""" - with get_package_dir() as pkgdir: + with get_package_dir(root_path) as pkgdir: pkg = Path(pkgdir).resolve() for c in ( pkg / "requirements" / "packs", @@ -51,10 +51,29 @@ class PacksManager: packs_dir : pathlib.Path Absolute path to the installed packs directory. Defaults to `requirements/packs` under the installed package. + examples_dir : pathlib.Path + Absolute path to the installed examples directory. + Defaults to `docs/examples` under the installed package. """ - def __init__(self) -> None: - self.packs_dir = _installed_packs_dir() + def __init__(self, root_path=None) -> None: + self.packs_dir = _installed_packs_dir(root_path) + self.examples_dir = self._get_examples_dir() + + def _get_examples_dir(self) -> Path: + """Return the absolute path to the installed examples directory. + + Returns + ------- + pathlib.Path + Directory containing shipped examples. + + Raises + ------ + FileNotFoundError + If the examples directory cannot be located in the installation. + """ + return (self.packs_dir / ".." / ".." / "docs" / "examples").resolve() def available_packs(self) -> List[str]: """List all available packs. @@ -68,6 +87,37 @@ def available_packs(self) -> List[str]: p.stem for p in self.packs_dir.glob("*.txt") if p.is_file() ) + def available_examples(self) -> dict[str, List[tuple[str, Path]]]: + """Finds all examples for each pack and builds a dict. + + Parameters + ---------- + root_path : Path + Root path to the examples directory. + Returns + ------- + dict + A dictionary mapping pack names to lists of example names. + + Raises + ------ + FileNotFoundError + If the provided root_path does not exist or is not a directory. + """ + example_dir = self.examples_dir + examples_dict = {} + for pack_path in sorted(example_dir.iterdir()): + if pack_path.is_dir(): + pack_name = pack_path.stem + examples_dict[pack_name] = [] + for example_path in sorted(pack_path.iterdir()): + if example_path.is_dir(): + example_name = example_path.stem + examples_dict[pack_name].append( + (example_name, example_path) + ) + return examples_dict + def _resolve_pack_file(self, identifier: Union[str, Path]) -> Path: """Resolve a pack identifier to an absolute .txt path. From 37174f11abdbef28aab6b0479ee732a39b87bf1e Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 10 Oct 2025 10:13:12 -0400 Subject: [PATCH 5/8] update news --- news/dict-build-test.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/news/dict-build-test.rst b/news/dict-build-test.rst index cc1bbb1..de4d827 100644 --- a/news/dict-build-test.rst +++ b/news/dict-build-test.rst @@ -4,7 +4,7 @@ **Changed:** -* +* Change example dict build process. **Deprecated:** From 3e3d9e7f54a5caeebf463e2b015409ef1cc5be7c Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 10 Oct 2025 10:42:07 -0400 Subject: [PATCH 6/8] move helper to top of module --- tests/test_packsmanager.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/test_packsmanager.py b/tests/test_packsmanager.py index d3af5d8..2e601c7 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -2,6 +2,21 @@ from diffpy.cmi.packsmanager import PacksManager + +def paths_and_names_match(expected, actual, root): + """Compare two (example_name, path) lists ignoring temp dir + differences.""" + if len(expected) != len(actual): + return False + for (exp_name, exp_path), (act_name, act_path) in zip(expected, actual): + if exp_name != act_name: + return False + actual_rel_path = str(act_path.relative_to(root)) + if actual_rel_path != exp_path: + return False + return True + + example_params = [ # 1) pack with no examples. Expect {'empty_pack': []} # 2) pack with multiple examples. @@ -57,20 +72,6 @@ ] -def paths_and_names_match(expected, actual, root): - """Compare two (example_name, path) lists ignoring temp dir - differences.""" - if len(expected) != len(actual): - return False - for (exp_name, exp_path), (act_name, act_path) in zip(expected, actual): - if exp_name != act_name: - return False - actual_rel_path = str(act_path.relative_to(root)) - if actual_rel_path != exp_path: - return False - return True - - @pytest.mark.parametrize("input,expected", example_params) def test_available_examples(input, expected, example_cases): case_dir = example_cases / input From cd0055873e81fd38f3b2f6ae3eae5db17b429fa3 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 10 Oct 2025 10:43:09 -0400 Subject: [PATCH 7/8] fix docstring --- 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 2e601c7..986b060 100644 --- a/tests/test_packsmanager.py +++ b/tests/test_packsmanager.py @@ -4,7 +4,7 @@ def paths_and_names_match(expected, actual, root): - """Compare two (example_name, path) lists ignoring temp dir + """Compare two tuples (example_name, path), ignoring temp dir differences.""" if len(expected) != len(actual): return False From 9f4cff93c63076107b9b703468f2edb0aa559899 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Fri, 10 Oct 2025 10:44:51 -0400 Subject: [PATCH 8/8] rm Raises in docstring --- src/diffpy/cmi/packsmanager.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/diffpy/cmi/packsmanager.py b/src/diffpy/cmi/packsmanager.py index 166ded5..b33ae83 100644 --- a/src/diffpy/cmi/packsmanager.py +++ b/src/diffpy/cmi/packsmanager.py @@ -67,11 +67,6 @@ def _get_examples_dir(self) -> Path: ------- pathlib.Path Directory containing shipped examples. - - Raises - ------ - FileNotFoundError - If the examples directory cannot be located in the installation. """ return (self.packs_dir / ".." / ".." / "docs" / "examples").resolve()