Skip to content

Commit 7659ba1

Browse files
authored
Merge pull request #55 from cadenmyers13/dict-build-test
feat: Build dict for copy examples command
2 parents 614335a + 9f4cff9 commit 7659ba1

File tree

4 files changed

+183
-6
lines changed

4 files changed

+183
-6
lines changed

news/dict-build-test.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* Add test for building examples dict.
4+
5+
**Changed:**
6+
7+
* Change example dict build process.
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>

src/diffpy/cmi/__init__.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,23 @@
1818
from importlib.resources import as_file, files
1919

2020

21-
def get_package_dir():
22-
resource = files(__name__)
21+
def get_package_dir(root_path=None):
22+
"""Get the package directory as a context manager.
23+
24+
Parameters
25+
----------
26+
root_path : str, optional
27+
Used for testing, overrides the files(__name__) call.
28+
29+
Returns
30+
-------
31+
context manager
32+
A context manager that yields a pathlib.Path to the package directory.
33+
"""
34+
if root_path is None:
35+
resource = files(__name__)
36+
else:
37+
resource = root_path
2338
return as_file(resource)
2439

2540

src/diffpy/cmi/packsmanager.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
__all__ = ["PacksManager"]
2929

3030

31-
def _installed_packs_dir() -> Path:
31+
def _installed_packs_dir(root_path=None) -> Path:
3232
"""Locate requirements/packs/ for the installed package."""
33-
with get_package_dir() as pkgdir:
33+
with get_package_dir(root_path) as pkgdir:
3434
pkg = Path(pkgdir).resolve()
3535
for c in (
3636
pkg / "requirements" / "packs",
@@ -51,10 +51,24 @@ class PacksManager:
5151
packs_dir : pathlib.Path
5252
Absolute path to the installed packs directory.
5353
Defaults to `requirements/packs` under the installed package.
54+
examples_dir : pathlib.Path
55+
Absolute path to the installed examples directory.
56+
Defaults to `docs/examples` under the installed package.
5457
"""
5558

56-
def __init__(self) -> None:
57-
self.packs_dir = _installed_packs_dir()
59+
def __init__(self, root_path=None) -> None:
60+
self.packs_dir = _installed_packs_dir(root_path)
61+
self.examples_dir = self._get_examples_dir()
62+
63+
def _get_examples_dir(self) -> Path:
64+
"""Return the absolute path to the installed examples directory.
65+
66+
Returns
67+
-------
68+
pathlib.Path
69+
Directory containing shipped examples.
70+
"""
71+
return (self.packs_dir / ".." / ".." / "docs" / "examples").resolve()
5872

5973
def available_packs(self) -> List[str]:
6074
"""List all available packs.
@@ -68,6 +82,37 @@ def available_packs(self) -> List[str]:
6882
p.stem for p in self.packs_dir.glob("*.txt") if p.is_file()
6983
)
7084

85+
def available_examples(self) -> dict[str, List[tuple[str, Path]]]:
86+
"""Finds all examples for each pack and builds a dict.
87+
88+
Parameters
89+
----------
90+
root_path : Path
91+
Root path to the examples directory.
92+
Returns
93+
-------
94+
dict
95+
A dictionary mapping pack names to lists of example names.
96+
97+
Raises
98+
------
99+
FileNotFoundError
100+
If the provided root_path does not exist or is not a directory.
101+
"""
102+
example_dir = self.examples_dir
103+
examples_dict = {}
104+
for pack_path in sorted(example_dir.iterdir()):
105+
if pack_path.is_dir():
106+
pack_name = pack_path.stem
107+
examples_dict[pack_name] = []
108+
for example_path in sorted(pack_path.iterdir()):
109+
if example_path.is_dir():
110+
example_name = example_path.stem
111+
examples_dict[pack_name].append(
112+
(example_name, example_path)
113+
)
114+
return examples_dict
115+
71116
def _resolve_pack_file(self, identifier: Union[str, Path]) -> Path:
72117
"""Resolve a pack identifier to an absolute .txt path.
73118

tests/test_packsmanager.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import pytest
2+
3+
from diffpy.cmi.packsmanager import PacksManager
4+
5+
6+
def paths_and_names_match(expected, actual, root):
7+
"""Compare two tuples (example_name, path), ignoring temp dir
8+
differences."""
9+
if len(expected) != len(actual):
10+
return False
11+
for (exp_name, exp_path), (act_name, act_path) in zip(expected, actual):
12+
if exp_name != act_name:
13+
return False
14+
actual_rel_path = str(act_path.relative_to(root))
15+
if actual_rel_path != exp_path:
16+
return False
17+
return True
18+
19+
20+
example_params = [
21+
# 1) pack with no examples. Expect {'empty_pack': []}
22+
# 2) pack with multiple examples.
23+
# Expect {'full_pack': [('example1`, path_to_1'), 'example2', path_to_2)]
24+
# 3) multiple packs. Expect dict with multiple pack:tuple pairs
25+
# 4) no pack found. Expect {}
26+
# case 1: pack with no examples. Expect {'empty_pack': []}
27+
# 5) multiple packs with the same example names
28+
# Expect dict with multiple pack:tuple pairs
29+
(
30+
"case1",
31+
{"empty_pack": []},
32+
),
33+
# case 2: pack with multiple examples.
34+
# Expect {'full_pack': [('example1', path_to_1),
35+
# ('example2', path_to_2)]}
36+
(
37+
"case2",
38+
{
39+
"full_pack": [
40+
("ex1", "case2/docs/examples/full_pack/ex1"),
41+
("ex2", "case2/docs/examples/full_pack/ex2"),
42+
]
43+
},
44+
),
45+
# case 3: multiple packs. Expect dict with multiple pack:tuple pairs
46+
(
47+
"case3",
48+
{
49+
"packA": [
50+
("ex1", "case3/docs/examples/packA/ex1"),
51+
("ex2", "case3/docs/examples/packA/ex2"),
52+
],
53+
"packB": [("ex3", "case3/docs/examples/packB/ex3")],
54+
},
55+
),
56+
( # case 4: no pack found. Expect {}
57+
"case4",
58+
{},
59+
),
60+
( # case 5: multiple packs with duplicate example names
61+
# Expect dict with multiple pack:tuple pairs
62+
"case5",
63+
{
64+
"packA": [
65+
("ex1", "case5/docs/examples/packA/ex1"),
66+
],
67+
"packB": [
68+
("ex1", "case5/docs/examples/packB/ex1"),
69+
],
70+
},
71+
),
72+
]
73+
74+
75+
@pytest.mark.parametrize("input,expected", example_params)
76+
def test_available_examples(input, expected, example_cases):
77+
case_dir = example_cases / input
78+
pkmg = PacksManager(case_dir)
79+
actual = pkmg.available_examples()
80+
assert actual.keys() == expected.keys()
81+
for pack in expected:
82+
assert paths_and_names_match(
83+
expected[pack], actual[pack], example_cases
84+
)
85+
86+
87+
@pytest.mark.parametrize("input,expected", example_params)
88+
def test_tmp_file_structure(input, expected, example_cases):
89+
example_path = example_cases / input
90+
for path in example_path.rglob("*"):
91+
if path.suffix:
92+
assert path.is_file()
93+
else:
94+
assert path.is_dir()

0 commit comments

Comments
 (0)