Skip to content

Commit 3c5c6b2

Browse files
Arm backend: Flatten nested Python manifest tables
Flatten nested TOML tables when reading Python public API manifest entries so validation also checks nested symbols. Add regression coverage that shows nested entries are parsed and that missing nested symbols are reported by validation. Signed-off-by: Sebastian Larsson <sebastian.larsson@arm.com> Change-Id: Ic04883d07647b3b6f4ed9b86c68d969dcbd90ade
1 parent c75c293 commit 3c5c6b2

2 files changed

Lines changed: 87 additions & 12 deletions

File tree

backends/arm/scripts/public_api_manifest/validate_public_api_manifest.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,38 @@ def read_manifest(manifest_path: Path) -> dict:
5656
return tomllib.load(manifest_file)
5757

5858

59-
def get_manifest_python_symbols(manifest: dict) -> dict[str, dict[str, str]]:
60-
python_manifest = manifest.get("python")
61-
if not isinstance(python_manifest, dict):
62-
raise ValueError("Manifest is missing [python] section")
63-
59+
def _collect_python_symbols(
60+
table: dict[str, object],
61+
*,
62+
prefix: str = "",
63+
) -> dict[str, dict[str, str]]:
6464
symbols: dict[str, dict[str, str]] = {}
65-
for name, entry in python_manifest.items():
66-
if not isinstance(entry, dict):
67-
raise ValueError(f"Entry [python.{name}] must be a table")
68-
kind = entry.get("kind")
69-
signature = entry.get("signature")
65+
kind = table.get("kind")
66+
signature = table.get("signature")
67+
if kind is not None or signature is not None:
7068
if not isinstance(kind, str) or not isinstance(signature, str):
7169
raise ValueError(
72-
f"Entry [python.{name}] must define `kind` and `signature`"
70+
f"Entry [python.{prefix}] must define `kind` and `signature`"
7371
)
74-
symbols[name] = {"kind": kind, "signature": signature}
72+
symbols[prefix] = {"kind": kind, "signature": signature}
73+
74+
for name, entry in table.items():
75+
if name in {"kind", "signature"}:
76+
continue
77+
if not isinstance(entry, dict):
78+
raise ValueError(f"Entry [python.{prefix}] contains invalid child {name}")
79+
child_prefix = f"{prefix}.{name}" if prefix else name
80+
symbols.update(_collect_python_symbols(entry, prefix=child_prefix))
7581
return symbols
7682

7783

84+
def get_manifest_python_symbols(manifest: dict) -> dict[str, dict[str, str]]:
85+
python_manifest = manifest.get("python")
86+
if not isinstance(python_manifest, dict):
87+
raise ValueError("Manifest is missing [python] section")
88+
return _collect_python_symbols(python_manifest)
89+
90+
7891
def get_current_python_symbols(
7992
*,
8093
include_deprecated: bool = False,

backends/arm/test/misc/test_validate_public_api_manifest.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@
66
from pathlib import Path
77

88
import executorch.backends.arm.scripts.public_api_manifest.validate_public_api_manifest as vpam
9+
10+
try:
11+
import tomllib
12+
except ModuleNotFoundError:
13+
import tomli as tomllib
14+
915
from executorch.backends.arm.scripts.public_api_manifest.validate_public_api_manifest import (
1016
format_validation_report,
1117
get_current_python_symbols,
18+
get_manifest_python_symbols,
1219
validate_symbols,
1320
)
1421

@@ -36,6 +43,61 @@ def test_public_api_manifest_exact_comparison_rejects_signature_expansion():
3643
assert issues[0][1] == "signature changed"
3744

3845

46+
def test_get_manifest_python_symbols_flattens_nested_tables():
47+
manifest = tomllib.loads(
48+
"""
49+
[python]
50+
51+
[python.Foo]
52+
kind = "class"
53+
signature = "Foo()"
54+
55+
[python.Foo.bar]
56+
kind = "function"
57+
signature = "Foo.bar() -> None"
58+
"""
59+
)
60+
61+
assert get_manifest_python_symbols(manifest) == {
62+
"Foo": {"kind": "class", "signature": "Foo()"},
63+
"Foo.bar": {"kind": "function", "signature": "Foo.bar() -> None"},
64+
}
65+
66+
67+
def test_nested_python_manifest_entries_are_validated():
68+
manifest_symbols = get_manifest_python_symbols(
69+
tomllib.loads(
70+
"""
71+
[python]
72+
73+
[python.Foo]
74+
kind = "class"
75+
signature = "Foo()"
76+
77+
[python.Foo.bar]
78+
kind = "function"
79+
signature = "Foo.bar(x: int) -> int"
80+
"""
81+
)
82+
)
83+
84+
issues = validate_symbols(
85+
manifest_symbols,
86+
{
87+
"Foo": {"kind": "class", "signature": "Foo()"},
88+
},
89+
)
90+
91+
assert issues == [
92+
(
93+
"Foo.bar",
94+
"entry is present in the manifest but missing from the current API",
95+
"Foo.bar(x: int) -> int",
96+
None,
97+
)
98+
]
99+
100+
39101
def test_public_api_manifest_static_accepts_backward_compatible_signature_expansion():
40102
manifest_entries = {
41103
"foo": {"kind": "function", "signature": "foo(x: int, y: int = 0) -> int"}

0 commit comments

Comments
 (0)