Skip to content

Commit 6052b29

Browse files
julienldclaude
andauthored
fix: include resource files in PyPI package distribution (#230)
The dashboard_guide.md and card_types.json files in ha_mcp/resources/ were not being included in the PyPI package, causing FileNotFoundError when users installed via pip/uv from PyPI. Updated pyproject.toml package-data to include resources/*.md and resources/*.json patterns. Added unit tests to verify resource files are accessible within the package. Closes #225 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9f93cf2 commit 6052b29

2 files changed

Lines changed: 114 additions & 1 deletion

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ package-dir = {"" = "src", "tests" = "tests"}
5050
packages = { find = { where = ["src", "."], include = ["ha_mcp*", "tests"] } }
5151

5252
[tool.setuptools.package-data]
53-
ha_mcp = ["py.typed"]
53+
ha_mcp = ["py.typed", "resources/*.md", "resources/*.json"]
5454

5555
[tool.black]
5656
line-length = 88

tests/src/unit/test_resources.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""Unit tests for package resource files.
2+
3+
These tests verify that resource files (dashboard_guide.md, card_types.json)
4+
are properly accessible within the package - addressing issue #225 where
5+
the files were not included in the PyPI distribution.
6+
"""
7+
8+
import json
9+
from pathlib import Path
10+
11+
import pytest
12+
13+
14+
class TestResourcesAccessibility:
15+
"""Test that package resources are accessible."""
16+
17+
def test_resources_directory_exists(self):
18+
"""The resources directory should exist in the ha_mcp package."""
19+
from ha_mcp.tools.tools_config_dashboards import _get_resources_dir
20+
21+
resources_dir = _get_resources_dir()
22+
assert resources_dir.exists(), f"Resources directory not found: {resources_dir}"
23+
assert resources_dir.is_dir(), f"Resources path is not a directory: {resources_dir}"
24+
25+
def test_dashboard_guide_exists(self):
26+
"""The dashboard_guide.md file should exist and be readable."""
27+
from ha_mcp.tools.tools_config_dashboards import _get_resources_dir
28+
29+
resources_dir = _get_resources_dir()
30+
guide_path = resources_dir / "dashboard_guide.md"
31+
32+
assert guide_path.exists(), f"dashboard_guide.md not found: {guide_path}"
33+
assert guide_path.is_file(), f"dashboard_guide.md is not a file: {guide_path}"
34+
35+
# Verify file is readable and has content
36+
content = guide_path.read_text()
37+
assert len(content) > 0, "dashboard_guide.md is empty"
38+
assert "dashboard" in content.lower(), "dashboard_guide.md doesn't appear to contain dashboard content"
39+
40+
def test_card_types_exists(self):
41+
"""The card_types.json file should exist and be readable."""
42+
from ha_mcp.tools.tools_config_dashboards import _get_resources_dir
43+
44+
resources_dir = _get_resources_dir()
45+
types_path = resources_dir / "card_types.json"
46+
47+
assert types_path.exists(), f"card_types.json not found: {types_path}"
48+
assert types_path.is_file(), f"card_types.json is not a file: {types_path}"
49+
50+
# Verify file is valid JSON with expected structure
51+
content = types_path.read_text()
52+
data = json.loads(content)
53+
54+
assert "card_types" in data, "card_types.json missing 'card_types' key"
55+
assert "total_count" in data, "card_types.json missing 'total_count' key"
56+
assert isinstance(data["card_types"], list), "card_types should be a list"
57+
assert len(data["card_types"]) > 0, "card_types list is empty"
58+
59+
def test_card_types_structure(self):
60+
"""The card_types.json should have valid structure for all entries."""
61+
from ha_mcp.tools.tools_config_dashboards import _get_resources_dir
62+
63+
resources_dir = _get_resources_dir()
64+
types_path = resources_dir / "card_types.json"
65+
data = json.loads(types_path.read_text())
66+
67+
# Verify total_count matches actual list length
68+
assert data["total_count"] == len(data["card_types"]), (
69+
f"total_count ({data['total_count']}) doesn't match "
70+
f"actual card_types length ({len(data['card_types'])})"
71+
)
72+
73+
# Verify all card types are non-empty strings
74+
for card_type in data["card_types"]:
75+
assert isinstance(card_type, str), f"Card type should be string: {card_type}"
76+
assert len(card_type) > 0, "Card type should not be empty string"
77+
78+
79+
class TestPyprojectPackageData:
80+
"""Test that pyproject.toml correctly specifies package data."""
81+
82+
def test_pyproject_includes_resources(self):
83+
"""pyproject.toml should include resource files in package-data."""
84+
# Find pyproject.toml relative to ha_mcp package
85+
import ha_mcp
86+
package_dir = Path(ha_mcp.__file__).parent
87+
project_root = package_dir.parent.parent # src/ha_mcp -> project root
88+
89+
# Try common locations for pyproject.toml
90+
pyproject_paths = [
91+
project_root / "pyproject.toml",
92+
project_root.parent / "pyproject.toml",
93+
]
94+
95+
pyproject_path = None
96+
for path in pyproject_paths:
97+
if path.exists():
98+
pyproject_path = path
99+
break
100+
101+
# Skip test if pyproject.toml not found (installed from wheel)
102+
if pyproject_path is None:
103+
pytest.skip("pyproject.toml not found - likely installed from distribution")
104+
105+
content = pyproject_path.read_text()
106+
107+
# Verify package-data includes resource patterns
108+
assert "resources/*.md" in content, (
109+
"pyproject.toml should include 'resources/*.md' in package-data"
110+
)
111+
assert "resources/*.json" in content, (
112+
"pyproject.toml should include 'resources/*.json' in package-data"
113+
)

0 commit comments

Comments
 (0)