Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated spec_parsing.py to now use the data model directory from python package #36596

Merged
merged 45 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
38f47de
Added modified spec_parsing.py to now use the data model package
vatsalghelani-csa Nov 21, 2024
946f316
Restyled by autopep8
restyled-commits Nov 21, 2024
a51f664
Restyled by isort
restyled-commits Nov 21, 2024
923470f
Fixing the cosmetic changes
vatsalghelani-csa Nov 21, 2024
99fcef5
Use chip.testing as a module to extract, work via PosixPath directory
vatsalghelani-csa Nov 25, 2024
d14f4e0
Restyled by autopep8
restyled-commits Nov 25, 2024
c57a3c1
Restyled by isort
restyled-commits Nov 25, 2024
0d9eb87
Fixed correct directory search
vatsalghelani-csa Nov 25, 2024
296c91f
Solving the wrapping for try-catch according to comments
vatsalghelani-csa Nov 26, 2024
d07b52f
Restyled by autopep8
restyled-commits Nov 26, 2024
c849ea9
Added fixes for both a pre-built location or a full path
vatsalghelani-csa Dec 3, 2024
e3adddd
Fix comment
vatsalghelani-csa Dec 3, 2024
e9ba668
Restyled by autopep8
restyled-commits Dec 3, 2024
f08316e
Adding importlib.resources capability
vatsalghelani-csa Dec 4, 2024
70e3622
Restyled by autopep8
restyled-commits Dec 4, 2024
1b3494e
Restyled by isort
restyled-commits Dec 4, 2024
d28c40e
Fixed _spec_ path error
vatsalghelani-csa Dec 4, 2024
891c278
Fixed to use module as string
vatsalghelani-csa Dec 4, 2024
fd5ab83
Removed unused import
vatsalghelani-csa Dec 4, 2024
68ab20e
Added fixes for path issues
vatsalghelani-csa Dec 5, 2024
0a3d947
Fixing the xml not found error
vatsalghelani-csa Dec 5, 2024
bd4172f
Restyled by autopep8
restyled-commits Dec 5, 2024
5b758e6
Fixed code lint error
vatsalghelani-csa Dec 5, 2024
aa5564e
Fixed code lint error tree
vatsalghelani-csa Dec 5, 2024
79a7db4
Fixed importlib errors
vatsalghelani-csa Dec 6, 2024
fb9f680
Fixed code lint
vatsalghelani-csa Dec 6, 2024
4120e42
Fixed errors
vatsalghelani-csa Dec 6, 2024
4a986d1
Restyled by autopep8
restyled-commits Dec 6, 2024
3c5442e
Some type updates and iteration logic updates to be consistent
andy31415 Dec 9, 2024
0793ba0
Remove unused method
andy31415 Dec 9, 2024
35edf22
Fix logic to match existing usage: we need clusters to be part of the…
andy31415 Dec 9, 2024
ec01ebb
Restyled by autopep8
restyled-commits Dec 9, 2024
1e7a1e2
Restyled by isort
restyled-commits Dec 9, 2024
931a746
remove unused import
andy31415 Dec 9, 2024
222c742
Cleanup some odd comments
andy31415 Dec 9, 2024
100ac23
Another update to avoid using globs
andy31415 Dec 9, 2024
2f2352a
Fix up types and return
andy31415 Dec 9, 2024
cd7a9de
Remove unused import
andy31415 Dec 9, 2024
c1611d2
Another dep cleanup
andy31415 Dec 9, 2024
b8b50fa
Remove one test step: unclear about the value of throwing a specparse…
andy31415 Dec 9, 2024
4bcc9a1
Remove unused import
andy31415 Dec 9, 2024
bb0ace9
update logic to throw specparsing when no XMLs found ... this preserv…
andreilitvin Dec 10, 2024
9853752
Make data model directory consistent with cluster logic
andreilitvin Dec 10, 2024
37f230e
Comments update
andreilitvin Dec 10, 2024
ddde2d9
Added warning levels for checking xml
vatsalghelani-csa Dec 13, 2024
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
5 changes: 1 addition & 4 deletions src/python_testing/TestSpecParsingSupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import jinja2
from chip.testing.global_attribute_ids import GlobalAttributeIds
from chip.testing.matter_testing import MatterBaseTest, ProblemNotice, default_matter_test_main
from chip.testing.spec_parsing import (ClusterParser, DataModelLevel, PrebuiltDataModelDirectory, SpecParsingException, XmlCluster,
from chip.testing.spec_parsing import (ClusterParser, DataModelLevel, PrebuiltDataModelDirectory, XmlCluster,
add_cluster_data_from_xml, build_xml_clusters, check_clusters_for_unknown_commands,
combine_derived_clusters_with_base, get_data_model_directory)
from mobly import asserts
Expand Down Expand Up @@ -276,9 +276,6 @@ def test_build_xml_override(self):

asserts.assert_count_equal(string_override_check.keys(), self.spec_xml_clusters.keys(), "Mismatched cluster generation")

with asserts.assert_raises(SpecParsingException):
build_xml_clusters("baddir")

def test_spec_parsing_access(self):
strs = [None, 'view', 'operate', 'manage', 'admin']
for read in strs:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
# limitations under the License.
#

import glob
import importlib
import importlib.resources as pkg_resources
import logging
import os
import typing
import xml.etree.ElementTree as ElementTree
from copy import deepcopy
from dataclasses import dataclass
from enum import Enum, auto
from typing import Callable, Optional
from importlib.abc import Traversable
from typing import Callable, Optional, Union

import chip.clusters as Clusters
import chip.testing.conformance as conformance_support
Expand Down Expand Up @@ -512,56 +513,74 @@ class PrebuiltDataModelDirectory(Enum):
k1_4 = auto()
kMaster = auto()

@property
cecille marked this conversation as resolved.
Show resolved Hide resolved
def dirname(self):
if self == PrebuiltDataModelDirectory.k1_3:
return "1.3"
if self == PrebuiltDataModelDirectory.k1_4:
return "1.4"
if self == PrebuiltDataModelDirectory.kMaster:
return "master"
raise KeyError("Invalid enum: %r" % self)


class DataModelLevel(Enum):
kCluster = auto()
kDeviceType = auto()

@property
def dirname(self):
if self == DataModelLevel.kCluster:
return "clusters"
if self == DataModelLevel.kDeviceType:
return "device_types"
raise KeyError("Invalid enum: %r" % self)


def get_data_model_directory(data_model_directory: Union[PrebuiltDataModelDirectory, Traversable], data_model_level: DataModelLevel) -> Traversable:
"""
Get the directory of the data model for a specific version and level from the installed package.
"""
# If it's a prebuilt directory, build the path based on the version and data model level
if isinstance(data_model_directory, PrebuiltDataModelDirectory):
top = pkg_resources.files(importlib.import_module('chip.testing')).joinpath(
'data_model').joinpath(data_model_directory.dirname)
else:
top = data_model_directory

class DataModelLevel(str, Enum):
kCluster = 'clusters'
kDeviceType = 'device_types'


def _get_data_model_root() -> str:
"""Attempts to find ${CHIP_ROOT}/data_model or equivalent."""

# Since this class is generally in a module, we have to rely on being bootstrapped or
# we use CWD if we cannot
choices = [os.getcwd()]
return top.joinpath(data_model_level.dirname)

if 'PW_PROJECT_ROOT' in os.environ:
choices.insert(0, os.environ['PW_PROJECT_ROOT'])

for c in choices:
data_model_path = os.path.join(c, 'data_model')
if os.path.exists(os.path.join(data_model_path, 'master', 'scraper_version')):
return data_model_path
raise FileNotFoundError('Cannot find a CHIP_ROOT/data_model path. Tried %r as prefixes.' % choices)
def build_xml_clusters(data_model_directory: Union[PrebuiltDataModelDirectory, Traversable] = PrebuiltDataModelDirectory.k1_4) -> typing.Tuple[dict[int, dict], list]:
"""
Build XML clusters from the specified data model directory.
This function supports both pre-built locations and full paths

if data_model_directory is a Travesable, it is assumed to already contain `clusters` (i.e. be a directory
with all XML files in it)
"""

def get_data_model_directory(data_model_directory: typing.Union[PrebuiltDataModelDirectory, str], data_model_level: DataModelLevel) -> str:
if data_model_directory == PrebuiltDataModelDirectory.k1_3:
return os.path.join(_get_data_model_root(), '1.3', data_model_level)
elif data_model_directory == PrebuiltDataModelDirectory.k1_4:
return os.path.join(_get_data_model_root(), '1.4', data_model_level)
elif data_model_directory == PrebuiltDataModelDirectory.kMaster:
return os.path.join(_get_data_model_root(), 'master', data_model_level)
if isinstance(data_model_directory, PrebuiltDataModelDirectory):
andy31415 marked this conversation as resolved.
Show resolved Hide resolved
top = pkg_resources.files(importlib.import_module("chip.testing")).joinpath(
'data_model').joinpath(data_model_directory.dirname).joinpath(DataModelLevel.kCluster.dirname)
else:
return data_model_directory


def build_xml_clusters(data_model_directory: typing.Union[PrebuiltDataModelDirectory, str] = PrebuiltDataModelDirectory.k1_4) -> tuple[dict[uint, XmlCluster], list[ProblemNotice]]:
dir = get_data_model_directory(data_model_directory, DataModelLevel.kCluster)
top = data_model_directory

clusters: dict[int, XmlCluster] = {}
pure_base_clusters: dict[str, XmlCluster] = {}
ids_by_name: dict[str, int] = {}
problems: list[ProblemNotice] = []
files = glob.glob(f'{dir}/*.xml')
if not files:
raise SpecParsingException(f'No data model files found in specified directory {dir}')

for xml in files:
logging.info(f'Parsing file {xml}')
tree = ElementTree.parse(f'{xml}')
root = tree.getroot()
add_cluster_data_from_xml(root, clusters, pure_base_clusters, ids_by_name, problems)
logging.info("Reading XML clusters from %r", top)
for f in top.iterdir():
if not f.name.endswith('.xml'):
andy31415 marked this conversation as resolved.
Show resolved Hide resolved
logging.info("Ignoring non-XML file %s", f.name)
continue

logging.info('Parsing file %s', f.name)
with f.open("r", encoding="utf8") as file:
root = ElementTree.parse(file).getroot()
add_cluster_data_from_xml(root, clusters, pure_base_clusters, ids_by_name, problems)

# There are a few clusters where the conformance columns are listed as desc. These clusters need specific, targeted tests
# to properly assess conformance. Here, we list them as Optional to allow these for the general test. Targeted tests are described below.
Expand Down Expand Up @@ -721,7 +740,7 @@ def combine_attributes(base: dict[uint, XmlAttribute], derived: dict[uint, XmlAt
xml_clusters[id] = new


def parse_single_device_type(root: ElementTree.Element) -> tuple[list[ProblemNotice], dict[int, XmlDeviceType]]:
def parse_single_device_type(root: ElementTree.Element) -> tuple[dict[int, XmlDeviceType], list[ProblemNotice]]:
problems: list[ProblemNotice] = []
device_types: dict[int, XmlDeviceType] = {}
device = root.iter('deviceType')
Expand Down Expand Up @@ -793,17 +812,19 @@ def parse_single_device_type(root: ElementTree.Element) -> tuple[list[ProblemNot
return device_types, problems


def build_xml_device_types(data_model_directory: typing.Union[PrebuiltDataModelDirectory, str] = PrebuiltDataModelDirectory.k1_4) -> tuple[dict[int, XmlDeviceType], list[ProblemNotice]]:
dir = get_data_model_directory(data_model_directory, DataModelLevel.kDeviceType)
def build_xml_device_types(data_model_directory: typing.Union[PrebuiltDataModelDirectory, Traversable] = PrebuiltDataModelDirectory.k1_4) -> tuple[dict[int, XmlDeviceType], list[ProblemNotice]]:
top = get_data_model_directory(data_model_directory, DataModelLevel.kDeviceType)
device_types: dict[int, XmlDeviceType] = {}
problems = []
for xml in glob.glob(f"{dir}/*.xml"):
logging.info(f'Parsing file {xml}')
tree = ElementTree.parse(f'{xml}')
root = tree.getroot()
tmp_device_types, tmp_problems = parse_single_device_type(root)
problems = problems + tmp_problems
device_types.update(tmp_device_types)
for file in top.iterdir():
if not file.name.endswith('.xml'):
vatsalghelani-csa marked this conversation as resolved.
Show resolved Hide resolved
continue
logging.info('Parsing file %r / %s', top, file.name)
with file.open('r', encoding="utf8") as xml:
root = ElementTree.parse(xml).getroot()
tmp_device_types, tmp_problems = parse_single_device_type(root)
problems = problems + tmp_problems
device_types.update(tmp_device_types)

if -1 not in device_types.keys():
raise ConformanceException("Base device type not found in device type xml data")
Expand Down
Loading