Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion custom_components/pyscript/stubs/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ async def _build_services(self):
def process_fields(fields: dict[str, Any]) -> list[_ServiceField]:
result: list[_ServiceField] = []
for field_name, field in (fields.get("fields") or {}).items():
if field_name == "advanced_fields":
if field_name in ("additional_fields", "advanced_fields"):
result.extend(process_fields(field))
continue
definition = self._describe_service_field(service_id, field_name, field)
Expand Down
66 changes: 66 additions & 0 deletions tests/test_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,72 @@ async def fake_service_descriptions(_hass: HomeAssistant) -> dict[str, dict[str,
pass


@pytest.mark.asyncio
async def test_stubs_flatten_additional_fields(pyscript, monkeypatch):
"""Fields nested under "additional_fields" (e.g. light.turn_on) must be flattened, not dropped."""

hass = pyscript.hass

async def fake_service_descriptions(_hass: HomeAssistant) -> dict[str, dict[str, dict[str, Any]]]:
return {
"light": {
"turn_on": {
"description": "Turn on a light.",
"target": {"entity": {"domain": "light"}},
"fields": {
"transition": {
"required": False,
"selector": {"number": {}},
"description": "Transition.",
},
"additional_fields": {
"collapsed": True,
"fields": {
"brightness": {
"required": False,
"selector": {"number": {"min": 0, "max": 255}},
"description": "Brightness.",
},
"xy_color": {
"required": False,
"selector": {"object": None},
"description": "XY color.",
},
},
},
},
}
}
}

monkeypatch.setattr(
"custom_components.pyscript.stubs.generator.async_get_all_descriptions", fake_service_descriptions
)

await pyscript.start()

stubs_dir = Path(hass.config.path(FOLDER)) / "modules" / "stubs"
generated_target = stubs_dir / "pyscript_generated.py"
stubs_dir.mkdir(parents=True, exist_ok=True)

await hass.services.async_call(DOMAIN, SERVICE_GENERATE_STUBS, {}, blocking=True, return_response=True)

generated_content = generated_target.read_text(encoding="utf-8")

# Sibling top-level field survives alongside the flattened ones.
assert "transition" in generated_content
# Fields nested under "additional_fields" must be flattened into the signature...
assert "brightness" in generated_content
assert "xy_color" in generated_content
# ...and "additional_fields" itself must not show up as a bogus parameter.
assert "additional_fields" not in generated_content

# Cleanup
for child in stubs_dir.iterdir():
child.unlink()
stubs_dir.rmdir()


@pytest.mark.asyncio
async def test_stub_imports_are_ignored(hass, caplog):
"""Verify importing from stubs.* does not raise even when the module is missing."""
Expand Down
Loading