Skip to content

Commit a38dcd1

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 e008c0c commit a38dcd1

File tree

2 files changed

+82
-12
lines changed

2 files changed

+82
-12
lines changed

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
@@ -53,25 +53,38 @@ def read_manifest(manifest_path: Path) -> dict:
5353
return tomli.load(manifest_file)
5454

5555

56-
def get_manifest_python_symbols(manifest: dict) -> dict[str, dict[str, str]]:
57-
python_manifest = manifest.get("python")
58-
if not isinstance(python_manifest, dict):
59-
raise ValueError("Manifest is missing [python] section")
60-
56+
def _collect_python_symbols(
57+
table: dict[str, object],
58+
*,
59+
prefix: str = "",
60+
) -> dict[str, dict[str, str]]:
6161
symbols: dict[str, dict[str, str]] = {}
62-
for name, entry in python_manifest.items():
63-
if not isinstance(entry, dict):
64-
raise ValueError(f"Entry [python.{name}] must be a table")
65-
kind = entry.get("kind")
66-
signature = entry.get("signature")
62+
kind = table.get("kind")
63+
signature = table.get("signature")
64+
if kind is not None or signature is not None:
6765
if not isinstance(kind, str) or not isinstance(signature, str):
6866
raise ValueError(
69-
f"Entry [python.{name}] must define `kind` and `signature`"
67+
f"Entry [python.{prefix}] must define `kind` and `signature`"
7068
)
71-
symbols[name] = {"kind": kind, "signature": signature}
69+
symbols[prefix] = {"kind": kind, "signature": signature}
70+
71+
for name, entry in table.items():
72+
if name in {"kind", "signature"}:
73+
continue
74+
if not isinstance(entry, dict):
75+
raise ValueError(f"Entry [python.{prefix}] contains invalid child {name}")
76+
child_prefix = f"{prefix}.{name}" if prefix else name
77+
symbols.update(_collect_python_symbols(entry, prefix=child_prefix))
7278
return symbols
7379

7480

81+
def get_manifest_python_symbols(manifest: dict) -> dict[str, dict[str, str]]:
82+
python_manifest = manifest.get("python")
83+
if not isinstance(python_manifest, dict):
84+
raise ValueError("Manifest is missing [python] section")
85+
return _collect_python_symbols(python_manifest)
86+
87+
7588
def get_current_python_symbols(
7689
*,
7790
include_deprecated: bool = False,

backends/arm/test/misc/test_validate_public_api_manifest.py

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

88
import executorch.backends.arm.scripts.public_api_manifest.validate_public_api_manifest as vpam
9+
import tomli
910
from executorch.backends.arm.scripts.public_api_manifest.validate_public_api_manifest import (
1011
format_validation_report,
1112
get_current_python_symbols,
13+
get_manifest_python_symbols,
1214
validate_symbols,
1315
)
1416

@@ -36,6 +38,61 @@ def test_public_api_manifest_exact_comparison_rejects_signature_expansion():
3638
assert issues[0][1] == "signature changed"
3739

3840

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

0 commit comments

Comments
 (0)