All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sila_interoperability.communication-tests.add-features.py Maven / Gradle / Ivy

The newest version!
#!/usr/bin/env python
"""Tool to manage Python/proto code generation"""
import subprocess
import sys
from argparse import ArgumentParser
from os.path import dirname, join

from lxml import etree

MODULE_DIR = join(dirname(__file__), "sila2_interop_communication_tester")
RESOURCE_DIR = join(MODULE_DIR, "resources")


def add_feature(fdl_path):
    # fdl validation, fdl -> proto
    feature_xml_schema = etree.XMLSchema(etree.parse(join(RESOURCE_DIR, "xsd", "FeatureDefinition.xsd")))

    # strip BOM
    with open(fdl_path, "rb") as fdl_file:
        fdl_file_content = fdl_file.read()
    if b"<" not in fdl_file_content[:10]:
        raise RuntimeError("No opening tag found in XML file")
    fdl_file_content = fdl_file_content[fdl_file_content.find(b"<") :]
    with open(fdl_path, "wb") as fdl_file:
        fdl_file.write(fdl_file_content)

    feature_xml_parser = etree.XMLParser(schema=feature_xml_schema, encoding="utf-8")
    feature_xml = etree.parse(fdl_path, parser=feature_xml_parser).getroot()
    xsl_transform = etree.XSLT(etree.parse(join(RESOURCE_DIR, "xslt", "fdl2proto.xsl")))
    proto_content = xsl_transform(feature_xml)
    feature_id = feature_xml.xpath(
        "/sila:Feature/sila:Identifier/text()", namespaces=dict(sila="http://www.sila-standard.org")
    )[0]
    proto_dir = join(RESOURCE_DIR, "proto")
    proto_file = join(proto_dir, f"{feature_id}.proto")
    with open(proto_file, "w", encoding="utf-8") as proto_fp:
        proto_fp.write(str(proto_content))
    with open(join(RESOURCE_DIR, "fdl", f"{feature_id}.sila.xml"), "wb") as fdl_out_fp:
        fdl_out_fp.write(fdl_file_content)

    # proto -> py, pyi
    stub_dir = join(MODULE_DIR, "grpc_stubs")
    process = subprocess.run(
        [
            f"{sys.executable}",
            "-m",
            "grpc_tools.protoc",
            f"--proto_path={proto_dir}",
            f"--python_out={stub_dir}",
            f"--grpc_python_out={stub_dir}",
            f"--mypy_out={stub_dir}",
            f"--mypy_grpc_out={stub_dir}",
            f"{proto_file}",
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    if process.returncode != 0:
        raise RuntimeError(f"protoc failed: {process.stderr}")

    convert_to_relative_import(join(stub_dir, f"{feature_id}_pb2.py"), ["SiLAFramework"])
    convert_to_relative_import(join(stub_dir, f"{feature_id}_pb2.pyi"), ["SiLAFramework"])
    convert_to_relative_import(join(stub_dir, f"{feature_id}_pb2_grpc.py"), ["SiLAFramework", feature_id])
    convert_to_relative_import(join(stub_dir, f"{feature_id}_pb2_grpc.pyi"), ["SiLAFramework", feature_id])


def convert_to_relative_import(filepath, proto_names):
    with open(filepath, encoding="utf-8") as fp:
        content = fp.read()
    for proto_name in proto_names:
        content = content.replace(
            f"import {proto_name}_pb2",
            f"from . import {proto_name}_pb2",
        )
    with open(filepath, "w", encoding="utf-8") as fp:
        fp.write(content)


def main():
    parser = ArgumentParser("add-features", description="Add features to this tool (.sila.xml, .proto, .py, .pyi)")
    parser.add_argument("FDLs", nargs="+", help="Feature definition files (.sila.xml)")
    args = parser.parse_args()

    for fdl_path in args.FDLs:
        add_feature(fdl_path)


if __name__ == "__main__":
    main()




© 2015 - 2024 Weber Informatics LLC | Privacy Policy