"""Contains classes that represents metadata."""
import json
from enum import Enum
from typing import Any, Dict, Iterable, NamedTuple
from google.protobuf import type_pb2
from ni_measurementlink_service._annotations import (
ENUM_VALUES_KEY,
TYPE_SPECIALIZATION_KEY,
)
from ni_measurementlink_service._internal.parameter import serialization_strategy
from ni_measurementlink_service.measurement.info import TypeSpecialization
[docs]
def validate_default_value_type(parameter_metadata: ParameterMetadata) -> None:
"""Validate and raise exception if the default value does not match the type info.
Args:
parameter_metadata (ParameterMetadata): Parameter metadata
Raises:
TypeError: If default value does not match the Datatype.
"""
default_value = parameter_metadata.default_value
if default_value is None:
return None
expected_type = type(
serialization_strategy.get_type_default(
parameter_metadata.type, parameter_metadata.repeated
)
)
display_name = parameter_metadata.display_name
enum_values_annotation = get_enum_values_annotation(parameter_metadata)
if parameter_metadata.repeated:
expected_element_type = type(
serialization_strategy.get_type_default(parameter_metadata.type, False)
)
_validate_default_value_type_for_repeated_type(
default_value,
expected_type,
expected_element_type,
enum_values_annotation,
display_name,
)
else:
_validate_default_value_type_for_scalar_type(
default_value, expected_type, enum_values_annotation, display_name
)
return None
def _validate_default_value_type_for_scalar_type(
default_value: object, expected_type: type, enum_values_annotation: str, display_name: str
) -> None:
"""Validate and raise exception if the default value does not match the type info."""
if enum_values_annotation:
user_enum_dict = json.loads(enum_values_annotation)
_validate_default_value_type_for_enum_type(
default_value, user_enum_dict, enum_values_annotation, display_name
)
else:
_validate_default_value_type_for_basic_type(default_value, expected_type, display_name)
def _validate_default_value_type_for_repeated_type(
default_value: Iterable[object],
expected_type: type,
expected_element_type: type,
enum_values_annotation: str,
display_name: str,
) -> None:
"""Validate and raise exception if the default value does not match the type info."""
if not isinstance(default_value, expected_type):
raise TypeError(
f"Unexpected type {type(default_value)} in the default value for '{display_name}'. Expected type: {expected_type}."
)
if enum_values_annotation:
user_enum_dict = json.loads(enum_values_annotation)
for element in default_value:
_validate_default_value_type_for_enum_type(
element, user_enum_dict, enum_values_annotation, display_name
)
else:
for element in default_value:
_validate_default_value_type_for_basic_type(
element, expected_element_type, display_name
)
def _validate_default_value_type_for_basic_type(
default_value: object,
expected_type: type,
display_name: str,
) -> None:
if not isinstance(default_value, expected_type):
raise TypeError(
f"Unexpected type {type(default_value)} in the default value for '{display_name}'. Expected type: {expected_type}."
)
def _validate_default_value_type_for_enum_type(
default_value: object,
user_enum: Dict[str, int],
enum_values_annotation: str,
display_name: str,
) -> None:
if not _is_valid_enum_value(default_value, user_enum):
raise TypeError(
f"Invalid default value, `{default_value}`, for enum parameter '{display_name}'. Expected values: `{enum_values_annotation}`."
)
[docs]
def get_enum_values_annotation(parameter_metadata: ParameterMetadata) -> str:
"""Gets the value for the "ni/enum.values" annotation if it exists.
Args:
parameter_metadata (ParameterMetadata): Parameter metadata
Returns:
str: The value of "ni/enum.values" annotation
"""
if parameter_metadata.annotations.get(TYPE_SPECIALIZATION_KEY) == TypeSpecialization.Enum.value:
return parameter_metadata.annotations.get(ENUM_VALUES_KEY, "")
else:
return ""
def _is_valid_enum_value(enum_value: object, user_enum: Dict[str, int]) -> bool:
if isinstance(enum_value, Enum):
return enum_value.name in user_enum and user_enum[enum_value.name] == enum_value.value
elif isinstance(enum_value, int):
return enum_value in user_enum.values()
else:
return False