Source code for nunavut.dependencies

#
# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# Copyright (C) 2018-2021  UAVCAN Development Team  <uavcan.org>
# This software is distributed under the terms of the MIT License.
#
"""
Objects and utilities for handling DSDL dependencies when generating code for a given type.
"""

import typing

import pydsdl


[docs]class Dependencies: """ Data structure that contains a set of composite types and annotations (bool flags) which constitute a set of dependencies for a set of DSDL objects. """ def __init__(self) -> None: self.composite_types = set() # type: typing.Set[pydsdl.CompositeType] self.uses_integer = False self.uses_float = False self.uses_variable_length_array = False self.uses_array = False self.uses_bool = False self.uses_primitive_static_array = False self.uses_union = False
[docs]class DependencyBuilder: """ Given a list of DSDL types this object builds a set of types that the given types use. .. invisible-code-block: python import pydsdl from unittest.mock import MagicMock from nunavut.dependencies import DependencyBuilder my_dependant_type_l2 = MagicMock(spec=pydsdl.CompositeType) my_dependant_type_l2.parent_service = False my_dependant_type_l2.attributes = [] my_dependant_type_l1 = MagicMock(spec=pydsdl.CompositeType) my_dependant_type_l1.parent_service = False my_dependant_type_l1.attributes = [MagicMock(data_type=my_dependant_type_l2)] my_top_level_type = MagicMock(spec=pydsdl.UnionType) my_top_level_type.parent_service = False my_top_level_type.attributes = [MagicMock(data_type=my_dependant_type_l1)] direct_dependencies = DependencyBuilder(my_top_level_type).direct() assert len(direct_dependencies.composite_types) == 1 assert my_dependant_type_l1 in direct_dependencies.composite_types transitive_dependencies = DependencyBuilder(my_top_level_type).transitive() assert len(transitive_dependencies.composite_types) == 2 assert my_dependant_type_l1 in transitive_dependencies.composite_types assert my_dependant_type_l2 in transitive_dependencies.composite_types assert direct_dependencies.uses_integer assert direct_dependencies.uses_union :param dependant_types: A list of types to build dependencies for. :type dependant_types: typing.Iterable[pydsdl.Any] """ def __init__(self, *dependant_types: pydsdl.Any): self._dependent_types = dependant_types
[docs] def transitive(self) -> Dependencies: """ Build a set of all transitive dependencies for the dependent types set for this builder. """ return self._build_dependency_list(self._dependent_types, True)
[docs] def direct(self) -> Dependencies: """ Build a set of all first-order dependencies for the dependent types set for this builder. """ return self._build_dependency_list(self._dependent_types, False)
# +-----------------------------------------------------------------------+ # | PRIVATE # +-----------------------------------------------------------------------+ @classmethod def _build_dependency_list( cls, dependant_types: typing.Iterable[pydsdl.CompositeType], transitive: bool ) -> Dependencies: results = Dependencies() for dependant in dependant_types: if isinstance(dependant, pydsdl.UnionType): # Unions always require integer for the tag field. results.uses_integer = True results.uses_union = True cls._extract_dependent_types(cls._extract_data_types(dependant), transitive, results) return results @classmethod def _extract_data_types(cls, t: pydsdl.CompositeType) -> typing.List[pydsdl.SerializableType]: # Make a list of all attributes defined by this type if isinstance(t, pydsdl.ServiceType): return [attr.data_type for attr in t.request_type.attributes] + [ attr.data_type for attr in t.response_type.attributes ] else: return [attr.data_type for attr in t.attributes] @classmethod def _extract_dependent_types_handle_array_type( cls, dependant_type: pydsdl.ArrayType, transitive: bool, inout_dependencies: Dependencies ) -> None: if isinstance(dependant_type, pydsdl.VariableLengthArrayType): inout_dependencies.uses_variable_length_array = True else: inout_dependencies.uses_array = True if isinstance(dependant_type.element_type, pydsdl.PrimitiveType): inout_dependencies.uses_primitive_static_array = True @classmethod def _extract_dependent_types( cls, dependant_types: typing.Iterable[pydsdl.Any], transitive: bool, inout_dependencies: Dependencies ) -> None: for dt in dependant_types: if isinstance(dt, pydsdl.CompositeType): if dt not in inout_dependencies.composite_types: inout_dependencies.composite_types.add(dt) if transitive: cls._extract_dependent_types(cls._extract_data_types(dt), transitive, inout_dependencies) elif isinstance(dt, pydsdl.ArrayType): cls._extract_dependent_types_handle_array_type(dt, transitive, inout_dependencies) cls._extract_dependent_types([dt.element_type], transitive, inout_dependencies) elif isinstance(dt, pydsdl.IntegerType): inout_dependencies.uses_integer = True elif isinstance(dt, pydsdl.FloatType): inout_dependencies.uses_float = True elif isinstance(dt, pydsdl.BooleanType): inout_dependencies.uses_bool = True