From c51a0d336965714a17a50d9d4bd77535273864fa Mon Sep 17 00:00:00 2001 From: Daniel Thaler Date: Sun, 27 Aug 2023 22:49:34 +0200 Subject: [PATCH] add type annotations --- autosar_data.pyi | 162 +++++++++++++++++++++++++++++++++++++++++ test/arxmlfile_test.py | 4 +- test/element_test.py | 33 +++++---- test/model_test.py | 8 +- test/test.py | 2 +- test/version_test.py | 2 +- 6 files changed, 187 insertions(+), 24 deletions(-) create mode 100644 autosar_data.pyi diff --git a/autosar_data.pyi b/autosar_data.pyi new file mode 100644 index 0000000..853ec4f --- /dev/null +++ b/autosar_data.pyi @@ -0,0 +1,162 @@ +from .autosar_data import * +from typing import Dict, List, FrozenSet, Literal, TypeAlias, Tuple, Union + +IncompatibleItemError: TypeAlias = Union[IncompatibleAttributeError, IncompatibleAttributeValueError, IncompatibleElementError] +ElementName: TypeAlias = str # ~5900 variants is too many to list here +AttributeName: TypeAlias = Literal["ACCESSKEY", "ALIGN", "ALLOW-BREAK", "ALT", "BASE", "BGCOLOR", "BINDING-TIME", "BLUEPRINT-VALUE", "BREAK", "CLASS", "COLNAME", "COLNUM", "COLOR", "COLS", "COLSEP", "COLWIDTH", "COORDS", "DEST", "EDIT-HEIGHT", "EDIT-WIDTH", "EDITFIT", "EDITSCALE", "ENUM-TABLE", "FILENAME", "FIT", "FLOAT", "FONT", "FRAME", "GENERATOR", "GID", "HEIGHT", "HELP-ENTRY", "HREF", "HTML-FIT", "HTML-HEIGHT", "HTML-SCALE", "HTML-WIDTH", "INDEX", "INTERVAL-TYPE", "ITEM-LABEL-POS", "KEEP-WITH-PREVIOUS", "L", "LEVEL", "MIME-TYPE", "MOREROWS", "NAME", "NAME-PATTERN", "NAMEEND", "NAMEST", "NOHREF", "NOTATION", "NOTE-TYPE", "ONBLUR", "ONCLICK", "ONDBLCLICK", "ONFOCUS", "ONKEYDOWN", "ONKEYPRESS", "ONKEYUP", "ONMOUSEDOWN", "ONMOUSEMOVE", "ONMOUSEOUT", "ONMOUSEOVER", "ONMOUSEUP", "ORIENT", "PGWIDE", "RESOLUTION-POLICY", "ROTATE", "ROWSEP", "S", "SCALE", "SD", "SHAPE", "SHORT-LABEL", "SHOW-CONTENT", "SHOW-RESOURCE-ALIAS-NAME", "SHOW-RESOURCE-CATEGORY", "SHOW-RESOURCE-LONG-NAME", "SHOW-RESOURCE-NUMBER", "SHOW-RESOURCE-PAGE", "SHOW-RESOURCE-SHORT-NAME", "SHOW-RESOURCE-TYPE", "SHOW-SEE", "SI", "SPANNAME", "STYLE", "T", "TABINDEX", "TABSTYLE", "TEX-RENDER", "TITLE", "TYPE", "UUID", "VALIDITY", "VALIGN", "VIEW", "WIDTH", "xml:space", "xmlns", "xmlns:xsi", "xsi:schemaLocation"] +EnumItem: TypeAlias = str # ~2500 variants is too many to list here +CharacterData: TypeAlias = Union[EnumItem, str, int, float] +ElementContent: TypeAlias = Union[Element, CharacterData] + +class ArxmlFile: + filename: str + version: AutosarVersion + def check_version_compatibility(self, version: AutosarVersion) -> List[IncompatibleItemError]: ... + model: AutosarModel + elements_dfs: ArxmlFileElementsDfsIterator + def serialize(self) -> str: ... + xml_standalone: bool + +class ArxmlFileElementsDfsIterator: + def __iter__(self) -> ArxmlFileElementsDfsIterator: ... + def __next__(self) -> Tuple[int, Element]: ... + +class Attribute: + attrname: AttributeName + content: CharacterData + +class AttributeIterator: + def __iter__(self) -> AttributeIterator: ... + def __next__(self) -> Attribute : ... + +class AutosarDataError(Exception): + pass + +class AutosarModel: + def create_file(self, filename: str, version: AutosarVersion) -> ArxmlFile: ... + def load_buffer(self, buffer: str, filename: str, strict: bool) -> Tuple[ArxmlFile, List[str]]: ... + def load_file(self, filename: str, strict: bool) -> Tuple[ArxmlFile, List[str]]: ... + def remove_file(self, arxmlfile: ArxmlFile) -> None: ... + def serialize_files(self) -> Dict[str, str]: ... + def write(self) -> None: ... + files: List[ArxmlFile] + root_element: Element + def get_element_by_path(self, autosar_path: str) -> Element: ... + elements_dfs: ElementsDfsIterator + def sort(self) -> None: ... + identifiable_elements: List[str] + def get_references_to(self, target_path: str) -> List[Element]: ... + def check_references(self) -> List[Element]: ... + +class AutosarVersion: + def __new__(cls, verstring: str) -> AutosarVersion: ... + # this is the stupid result of method used by PyO3 to translate Rust enums + Autosar_4_0_1: AutosarVersion + Autosar_4_0_2: AutosarVersion + Autosar_4_0_3: AutosarVersion + Autosar_4_1_1: AutosarVersion + Autosar_4_1_2: AutosarVersion + Autosar_4_1_3: AutosarVersion + Autosar_4_2_1: AutosarVersion + Autosar_4_2_2: AutosarVersion + Autosar_4_3_0: AutosarVersion + Autosar_00042: AutosarVersion + Autosar_00043: AutosarVersion + Autosar_00044: AutosarVersion + Autosar_00045: AutosarVersion + Autosar_00046: AutosarVersion + Autosar_00047: AutosarVersion + Autosar_00048: AutosarVersion + Autosar_00049: AutosarVersion + Autosar_00050: AutosarVersion + Autosar_00051: AutosarVersion + +class ContentType: + # this is the stupid result of method used by PyO3 to translate Rust enums + Elements: ContentType + CharacterData: ContentType + Mixed: ContentType + +class Element: + def serialize(self) -> str: ... + parent: Element + element_name: ElementName + element_type: ElementType + item_name: str + is_identifiable: bool + is_reference: bool + path: str + model: AutosarModel + content_type: ContentType + def create_sub_element(self, element_name: ElementName) -> Element: ... + def create_sub_element_at(self, element_name: ElementName, position: int) -> Element: ... + def create_named_sub_element(self, element_name: ElementName, item_name: str) -> Element: ... + def create_named_sub_element_at(self, element_name: ElementName, item_name: str, position: int) -> Element: ... + def create_copied_sub_element(self, other: Element) -> Element: ... + def create_copied_sub_element_at(self, other: Element, position: int) -> Element: ... + def move_element_here(self, move_element: Element) -> Element: ... + def move_element_here_at(self, move_element: Element, position: int) -> Element: ... + def remove_sub_element(self, element: Element) -> None: ... + reference_target: Element + def get_sub_element(self, name_str: str) -> Element: ... + def get_sub_element_at(self, position: int) -> Element: ... + position: int + sub_elements: ElementsIterator + elements_dfs: ElementsDfsIterator + character_data: CharacterData + def remove_character_data(self) -> None: ... + def insert_character_content_item(self, chardata: str, position: int) -> None: ... + def remove_character_content_item(self, position: int) -> None: ... + content_item_count: int + content: ElementContentIterator + attributes: AttributeIterator + def attribute_value(self, attrname: AttributeName) -> CharacterData: ... + def set_attribute(self, attrname: AttributeName, chardata: CharacterData) -> None: ... + def set_attribute_string(self, attrname: AttributeName, value: str) -> None: ... + def remove_attribute(self, attrname: AttributeName) -> None: ... + def sort(self) -> None: ... + def list_valid_sub_elements(self) -> List[ValidSubElementInfo]: ... + file_membership: Tuple[bool, FrozenSet[ArxmlFile]] + def add_to_file(self, file: ArxmlFile) -> None: ... + def remove_from_file(self, file: ArxmlFile) -> None: ... + xml_path: str + +class ElementContentIterator: + def __iter__(self) -> ElementContentIterator: ... + def __next__(self) -> ElementContent: ... + +class ElementType: + is_named: bool + is_ref: bool + is_ordered: bool + splittable: int + def splittable_in(self, version: AutosarVersion) -> bool: ... + def reference_dest_value(self, target: ElementType) -> EnumItem: ... + def find_sub_element(self, target_name: ElementName, version: int) -> ElementType: ... + +class ElementsDfsIterator: + def __iter__(self) -> ElementsDfsIterator: ... + def __next__(self) -> Tuple[int, Element]: ... + +class ElementsIterator: + def __iter__(self) -> ElementsIterator: ... + def __next__(self) -> Element: ... + +class IncompatibleAttributeError: + element: Element + attribute: AttributeName + +class IncompatibleAttributeValueError: + element: Element + attribute: AttributeName + attribute_value: str + +class IncompatibleElementError: + element: Element + +class ValidSubElementInfo: + element_name: str + is_named: bool + is_allowed: bool + +version: str diff --git a/test/arxmlfile_test.py b/test/arxmlfile_test.py index 2927284..d64727c 100644 --- a/test/arxmlfile_test.py +++ b/test/arxmlfile_test.py @@ -1,7 +1,7 @@ from autosar_data import * import pytest -def test_arxlfile_basic(): +def test_arxlfile_basic() -> None: model = AutosarModel() file1 = model.create_file("filename1.arxml", AutosarVersion.Autosar_00051) @@ -56,7 +56,7 @@ def test_arxlfile_basic(): print(file1.model) -def test_check_version_compatibility(): +def test_check_version_compatibility() -> None: model = AutosarModel() file1 = model.create_file("filename", AutosarVersion.Autosar_00050) diff --git a/test/element_test.py b/test/element_test.py index aea02e2..8a5776e 100644 --- a/test/element_test.py +++ b/test/element_test.py @@ -1,7 +1,7 @@ from autosar_data import * import pytest -def test_element_basic(): +def test_element_basic() -> None: model = AutosarModel() # create some elements @@ -37,13 +37,13 @@ def test_element_basic(): assert el_ar_packages.item_name is None with pytest.raises(AutosarDataError): print(el_ar_packages.path) - + # the behavior of the element is determined by its element_type assert el_ar_packages.is_identifiable == el_ar_packages.element_type.is_named assert el_ar_packages.is_reference == el_ar_packages.element_type.is_ref assert not el_ar_packages.element_type.is_ordered assert el_ar_packages.element_type.splittable != 0 - + # Element has __str__ and __repr__ el_ar_packages_repr = el_ar_packages.__repr__() assert not el_ar_packages_repr is None @@ -79,7 +79,7 @@ def test_element_basic(): assert el_ar_package.xml_path == "///NewName" -def test_element_content(): +def test_element_content() -> None: model = AutosarModel() # create some elements for the test @@ -89,7 +89,7 @@ def test_element_content(): el_l2 = el_pkg1 \ .create_sub_element("DESC") \ .create_sub_element("L-2") - + # different elements have different content types assert el_pkg1.content_type == ContentType.Elements assert el_short_name.content_type == ContentType.CharacterData @@ -100,7 +100,7 @@ def test_element_content(): assert len([c for c in el_l2.content]) == 1 el_l2.create_sub_element("BR") assert len([c for c in el_l2.content]) == 2 - + assert el_l2.content_item_count == 2 # check the content @@ -129,7 +129,7 @@ def test_element_content(): .create_named_sub_element("AR-PACKAGE", "CanPkg") \ .create_sub_element("ELEMENTS") \ .create_named_sub_element("CAN-CLUSTER", "CanCluster") - + # various character data elements have constraints, e.g the reference element can only contain an autosar path # integers, or strings that do not look like paths cause an exception with pytest.raises(AutosarDataError): @@ -146,7 +146,7 @@ def test_element_content(): el_fibex_element_ref.set_attribute("DEST", "bla") with pytest.raises(AutosarDataError): el_fibex_element_ref.set_attribute("DEST", "default") - + # in cases where the element name of the target is NOT a valid value in the "DEST" attribute # the function reference_dest_value() can be used instead destval = el_fibex_element_ref.element_type.reference_dest_value(el_can_cluster.element_type) @@ -196,7 +196,7 @@ def test_element_content(): el_cse_code.character_data = "text" -def test_element_creation(): +def test_element_creation() -> None: model = AutosarModel() # create an unnamed element @@ -259,7 +259,7 @@ def test_element_creation(): # the element Autosar is not a valid sub element of ArPackage with pytest.raises(AutosarDataError): el_pkg1.create_sub_element_at("AUTOSAR", 1) - + # it is possible to check which sub elements would be valid # returns a list of tuples: (ElementName, is_named, currently_allowed) vsi_list = el_pkg1.list_valid_sub_elements() @@ -268,14 +268,14 @@ def test_element_creation(): allowed_elements = [vsi.element_name if vsi.is_allowed else None for vsi in vsi_list] assert not "AUTOSAR" in allowed_elements assert "CATEGORY" in allowed_elements - + # remove an element el_ar_packages.remove_sub_element(el_pkg3) with pytest.raises(AutosarDataError): invalid = el_pkg3.path with pytest.raises(AutosarDataError): - invalid = el_pkg3.model - + invalid2 = el_pkg3.model + # validate the resulting model element_info = [x for x in model.root_element.elements_dfs] # element info is a list of tuple(depth, element) @@ -297,8 +297,9 @@ def test_element_creation(): assert element_info[11][1].item_name == "System" assert element_info[12][1].element_name == "SHORT-NAME" assert len(element_info) == 13 - -def test_element_attributes(): + + +def test_element_attributes() -> None: model = AutosarModel() el_autosar = model.root_element @@ -328,7 +329,7 @@ def test_element_attributes(): assert len([attr for attr in el_autosar.attributes]) == 4 -def test_file_membership(): +def test_file_membership() -> None: model = AutosarModel() file1 = model.create_file("file1", AutosarVersion.Autosar_00050) file2 = model.create_file("file2", AutosarVersion.Autosar_00050) diff --git a/test/model_test.py b/test/model_test.py index adad2cb..ff43ffb 100644 --- a/test/model_test.py +++ b/test/model_test.py @@ -2,7 +2,7 @@ import pytest import os -def test_model_basic(): +def test_model_basic() -> None: model = AutosarModel() # check that the object was created - model is not None assert isinstance(model, AutosarModel) @@ -12,7 +12,7 @@ def test_model_basic(): assert len(model.identifiable_elements) == 0 -def test_model_files(tmp_path): +def test_model_files(tmp_path: str) -> None: model = AutosarModel() # create a file @@ -68,7 +68,7 @@ def test_model_files(tmp_path): -def test_model_identifiables(): +def test_model_identifiables() -> None: model = AutosarModel() # create some elements el_elements = model.root_element \ @@ -108,7 +108,7 @@ def test_model_identifiables(): assert el_can_cluster_referrers[0] == el_fibex_element_ref -def test_model_misc(): +def test_model_misc() -> None: model = AutosarModel() model2 = AutosarModel() diff --git a/test/test.py b/test/test.py index a4a1915..7625420 100644 --- a/test/test.py +++ b/test/test.py @@ -1,7 +1,7 @@ from autosar_data import * import pytest -def test_others(): +def test_others() -> None: model = AutosarModel() # content type - __str__ / __repr__ diff --git a/test/version_test.py b/test/version_test.py index a3d7337..3ebd3d8 100644 --- a/test/version_test.py +++ b/test/version_test.py @@ -1,7 +1,7 @@ from autosar_data import * import pytest -def test_version(): +def test_version() -> None: model = AutosarModel() ver = AutosarVersion("AUTOSAR_4-0-1.xsd")