diff --git a/platformio/datamodel.py b/platformio/datamodel.py index 5390484c..63281858 100644 --- a/platformio/datamodel.py +++ b/platformio/datamodel.py @@ -39,12 +39,20 @@ class DataFieldException(DataModelException): self.field.name, ) + def __repr__(self): + return str(self) + class ListOfType(object): def __init__(self, type): self.type = type +class DictOfType(object): + def __init__(self, type): + self.type = type + + class DataField(object): def __init__( self, @@ -92,6 +100,8 @@ class DataField(object): return self.type(**value).as_dict() if isinstance(self.type, ListOfType): return self._validate_list_of_type(self.type.type, value) + if isinstance(self.type, DictOfType): + return self._validate_dict_of_type(self.type.type, value) if issubclass(self.type, (str, bool)): return getattr(self, "_validate_%s_value" % self.type.__name__)(value) except ValueError as e: @@ -106,6 +116,12 @@ class DataField(object): assert issubclass(list_of_type, DataModel) return [list_of_type(**v).as_dict() for v in value] + def _validate_dict_of_type(self, dict_of_type, value): + if not isinstance(value, dict): + raise ValueError("Value should be a dict") + assert issubclass(dict_of_type, DataModel) + return {k: dict_of_type(**v).as_dict() for k, v in value.items()} + def _validate_str_value(self, value): if not isinstance(value, string_types): value = str(value) diff --git a/platformio/package/manifest/model.py b/platformio/package/manifest/model.py index c87f3417..bfbbfa24 100644 --- a/platformio/package/manifest/model.py +++ b/platformio/package/manifest/model.py @@ -14,7 +14,13 @@ import semantic_version -from platformio.datamodel import DataField, DataModel, ListOfType, StrictDataModel +from platformio.datamodel import ( + DataField, + DataModel, + DictOfType, + ListOfType, + StrictDataModel, +) def validate_semver_field(_, value): @@ -41,6 +47,11 @@ class ExportModel(DataModel): exclude = DataField(type=ListOfType(DataField())) +class ExampleModel(DataModel): + base = DataField(required=True) + files = DataField(type=ListOfType(DataField())) + + class ManifestModel(DataModel): # Required fields @@ -66,6 +77,7 @@ class ManifestModel(DataModel): repository = DataField(type=RepositoryModel) export = DataField(type=ExportModel) + examples = DataField(type=DictOfType(ExampleModel)) class StrictManifestModel(ManifestModel, StrictDataModel): diff --git a/tests/test_pkgmanifest.py b/tests/test_pkgmanifest.py index 6bfe93e0..0262b26a 100644 --- a/tests/test_pkgmanifest.py +++ b/tests/test_pkgmanifest.py @@ -169,7 +169,7 @@ sentence=This is Arduino library } -def test_library_json_valid_model(): +def test_library_json_model(): contents = """ { "name": "ArduinoJson", @@ -193,7 +193,17 @@ def test_library_json_valid_model(): ], "frameworks": "arduino", "platforms": "*", - "license": "MIT" + "license": "MIT", + "examples": { + "JsonConfigFile": { + "base": "examples/JsonConfigFile", + "files": ["JsonConfigFile.ino"] + }, + "JsonHttpClient": { + "base": "examples/JsonHttpClient", + "files": ["JsonHttpClient.ino"] + } + } } """ data = parser.ManifestParserFactory.new( @@ -227,11 +237,21 @@ def test_library_json_valid_model(): "frameworks": ["arduino"], "platforms": ["*"], "license": "MIT", + "examples": { + "JsonConfigFile": { + "base": "examples/JsonConfigFile", + "files": ["JsonConfigFile.ino"], + }, + "JsonHttpClient": { + "base": "examples/JsonHttpClient", + "files": ["JsonHttpClient.ino"], + }, + }, } ) -def test_library_properties_valid_model(): +def library_properties_model(): contents = """ name=U8glib version=1.19.1 @@ -282,7 +302,7 @@ architectures=avr,sam ) -def test_broken_model(): +def test_broken_models(): # non-strict mode assert len(ManifestModel(name="MyPackage").get_exceptions()) == 4 assert ManifestModel(name="MyPackage", version="broken_version").version is None