Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/check_nims.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,14 @@ jobs:
- name: Mypy static analysis (ni-measurementlink-service)
run: poetry run mypy -p ni_measurementlink_service
- name: Mypy static analysis (tests)
run: poetry run mypy tests
run: poetry run mypy tests
- name: Install ni-measurementlink-service with docs
run: poetry install --with docs
- name: Build docs and check for errors/warnings
run: |
rm -rf docs
mkdir -p docs
touch docs/.nojekyll
poetry run sphinx-build _docs_source docs -b html -W --keep-going
- name: Revert docs
run: git clean -dfx docs/ && git restore docs/
24 changes: 23 additions & 1 deletion _docs_source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import autoapi.extension
import toml


# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
Expand Down Expand Up @@ -48,6 +47,29 @@
autoapi_type = "python"
autodoc_typehints = "none"


# WARNING: more than one target found for cross-reference 'MeasurementInfo':
# ni_measurementlink_service.MeasurementInfo,
# ni_measurementlink_service.measurement.info.MeasurementInfo
#
# TODO: figure out how to make :canonical: work with autoapi
def skip_aliases(app, what, name, obj, skip, options):
"""Skip documentation for classes that are exported from multiple modules."""
if name in [
"ni_measurementlink_service.DataType",
"ni_measurementlink_service.MeasurementInfo",
"ni_measurementlink_service.ServiceInfo",
"ni_measurementlink_service.MeasurementService",
]:
skip = True
return skip


def setup(sphinx):
"""Sphinx setup callback."""
sphinx.connect("autoapi-skip-member", skip_aliases)


# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
Expand Down
28 changes: 12 additions & 16 deletions ni_measurementlink_service/_datatypeinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,23 @@


class DataTypeInfo(NamedTuple):
"""Class that represents the information for each of the DataType enum values.

Attributes
----------
grpc_field_type: Field.Kind associated with the DataType

repeated: Whether the DataType is a repeated field

type_specialization: Specific type when value_type
can have more than one use

message_type (str): This is the gRPC full name of the message type.
Required when 'grpc_field_type' is Kind.TypeMessage.
Ignored for any other 'type'.

"""
"""Class that represents the information for each of the :any:`DataType` enum values."""

grpc_field_type: type_pb2.Field.Kind.ValueType
"""`Field.Kind` associated with the :any:`DataType`."""

repeated: bool
"""Whether the :any:`DataType` is a repeated field."""

type_specialization: TypeSpecialization = TypeSpecialization.NoType
"""Specific type when :any:`grpc_field_type` can have more than one use."""

message_type: str = ""
"""The gRPC full name of the message type.

Required when :any:`grpc_field_type` is `Kind.TypeMessage`.
Ignored for any other :any:`grpc_field_type` value.
"""


def get_type_info(data_type: DataType) -> DataTypeInfo:
Expand Down
18 changes: 5 additions & 13 deletions ni_measurementlink_service/_internal/grpc_servicer.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,13 @@ def _get_mapping_by_parameter_name(
) -> Dict[str, Any]:
"""Transform the mapping by id to mapping by parameter names of the measurement function.

Args
----
Args:
mapping_by_id (Dict[int, Any]): Mapping by ID

measure_function (callable): Function from which the parameter names are extracted.

Returns
-------
Dict[str, Any]: Mapping by Parameters names based on the measurement function.

Returns:
Dict[str, Any]: Mapping by Parameters names based on the measurement function
"""
signature = inspect.signature(measure_function)
mapping_by_variable_name = {}
Expand All @@ -132,16 +129,14 @@ def _serialize_outputs(output_metadata: Dict[int, ParameterMetadata], outputs: A
class MeasurementServiceServicerV1(v1_measurement_service_pb2_grpc.MeasurementServiceServicer):
"""Implementation of the Measurement Service's gRPC base class.

Attributes
----------
Attributes:
measurement_info (MeasurementInfo): Measurement info

configuration_parameter_list (List): List of configuration parameters.

output_parameter_list (List): List of output parameters.

measure_function (Callable): Registered measurement function.

"""

def __init__(
Expand Down Expand Up @@ -277,16 +272,14 @@ def _serialize_response(self, outputs: Any) -> v1_measurement_service_pb2.Measur
class MeasurementServiceServicerV2(v2_measurement_service_pb2_grpc.MeasurementServiceServicer):
"""Implementation of the Measurement Service's gRPC base class.

Attributes
----------
Attributes:
measurement_info (MeasurementInfo): Measurement info

configuration_parameter_list (List): List of configuration parameters.

output_parameter_list (List): List of output parameters.

measure_function (Callable): Registered measurement function.

"""

def __init__(
Expand All @@ -306,7 +299,6 @@ def __init__(
output_parameter_list (List): List of output parameters.

measure_function (Callable): Registered measurement function.

"""
super().__init__()

Expand Down
53 changes: 21 additions & 32 deletions ni_measurementlink_service/_internal/parameter/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,48 +10,40 @@


class ParameterMetadata(NamedTuple):
"""Class that represents the metadata of parameters.

Attributes
----------
display_name (str): The display name of the parameter.

type (type_pb2.Field): The datatype of the parameter
represented by the gRPC Field Enum.

repeated (bool): Represent if the parameter is a scalar or 1D array.

True for 1DArray and false for scalar.

default_value (Any): The default value of the parameter.

annotations (Dict[str,str]): Represents a set of annotations on the type.

message_type (str): This is the gRPC full name of the message type.
Required when 'type' is Kind.TypeMessage.
Ignored for any other 'type'.

"""
"""Class that represents the metadata of parameters."""

display_name: str
"""The display name of the parameter."""

type: type_pb2.Field.Kind.ValueType
"""The datatype of the parameter represented by the gRPC field enum."""

repeated: bool
"""Indicates whether the parameter is a scalar or 1D array.

True for 1D array and false for scalar."""

default_value: Any
"""The default value of the parameter."""

annotations: Dict[str, str]
"""Represents a set of annotations on the type."""

message_type: str = ""
"""The gRPC full name of the message type.

Required when 'type' is Kind.TypeMessage. Ignored for any other 'type'.
"""


def validate_default_value_type(parameter_metadata: ParameterMetadata) -> None:
"""Validate and raise exception if the default value does not match the type info.

Args
----
Args:
parameter_metadata (ParameterMetadata): Parameter metadata

Raises
------
Raises:
TypeError: If default value does not match the Datatype.

"""
default_value = parameter_metadata.default_value
if default_value is None:
Expand Down Expand Up @@ -148,14 +140,11 @@ def _validate_default_value_type_for_enum_type(
def get_enum_values_annotation(parameter_metadata: ParameterMetadata) -> str:
"""Gets the value for the "ni/enum.values" annotation if it exists.

Args
----
Args:
parameter_metadata (ParameterMetadata): Parameter metadata

Returns
-------
Returns:
str: The value of "ni/enum.values" annotation

"""
if (
parameter_metadata.annotations.get("ni/type_specialization")
Expand Down
53 changes: 14 additions & 39 deletions ni_measurementlink_service/_internal/parameter/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,13 @@ def deserialize_parameters(
) -> Dict[int, Any]:
"""Deserialize the bytes of the parameter based on the metadata.

Args
----
Args:
parameter_metadata_dict (Dict[int, ParameterMetadata]): Parameter metadata by ID.

parameter_bytes (bytes): Byte string to deserialize.

Returns
-------
Returns:
Dict[int, Any]: Deserialized parameters by ID

"""
# Getting overlapping parameters
overlapping_parameter_by_id = _get_overlapping_parameters(
Expand All @@ -56,16 +53,13 @@ def serialize_parameters(
) -> bytes:
"""Serialize the parameter values in same order based on the metadata_dict.

Args
----
Args:
parameter_metadata_dict (Dict[int, ParameterMetadata]): Parameter metadata by ID.

parameter_value (Sequence[Any]): Parameter values to serialize.

Returns
-------
Returns:
bytes: Serialized byte string containing parameter values.

"""
serialize_buffer = BytesIO() # inner_encoder updates the serialize_buffer
for i, parameter in enumerate(parameter_values):
Expand Down Expand Up @@ -94,14 +88,11 @@ def serialize_parameters(
def serialize_default_values(parameter_metadata_dict: Dict[int, ParameterMetadata]) -> bytes:
"""Serialize the Default values in the Metadata.

Args
-----
Args:
parameter_metadata_dict (Dict[int, ParameterMetadata]): Configuration metadata.

Returns
-------
Returns:
bytes: Serialized byte string containing default values.

"""
default_value_parameter_array = list()
default_value_parameter_array = [
Expand All @@ -115,16 +106,13 @@ def _get_field_index(parameter_bytes: bytes, tag_position: int) -> int:

The tag Position should be the index of the TagValue in the ByteArray for valid field index.

Args
----
Args:
parameter_bytes (bytes): Serialized bytes

tag_position (int): Tag position

Returns
-------
Returns:
int: Filed index of the Tag Position

"""
return parameter_bytes[tag_position] >> _GRPC_WIRE_TYPE_BIT_WIDTH

Expand All @@ -134,20 +122,16 @@ def _get_overlapping_parameters(
) -> Dict[int, Any]:
"""Get the parameters present in both `parameter_metadata_dict` and `parameter_bytes`.

Args
----
Args:
parameter_metadata_dict (Dict[int, ParameterMetadata]): Parameter metadata by ID.

parameter_bytes (bytes): bytes of Parameter that need to be deserialized.

Raises
------
Raises:
Exception: If the protobuf filed index is invalid.

Returns
-------
Returns:
Dict[int, Any]: Overlapping Parameters by ID.

"""
# inner_decoder update the overlapping_parameters
overlapping_parameters_by_id: Dict[int, Any] = {}
Expand Down Expand Up @@ -180,16 +164,13 @@ def _get_missing_parameters(
) -> Dict[int, Any]:
"""Get the Parameters defined in `parameter_metadata_dict` but not in `parameter_by_id`.

Args
----
Args:
parameter_metadata_dict (Dict[int, ParameterMetadata]): Parameter metadata by id.

parameter_by_id (Dict[int, Any]): Parameters by ID to compare the metadata with.

Returns
-------
Returns:
Dict[int, Any]: Missing parameter(as type defaults) by ID.

"""
missing_parameters = {}
for key, value in parameter_metadata_dict.items():
Expand All @@ -210,16 +191,10 @@ def _deserialize_enum_parameters(
) -> None:
"""Converts all enums in `parameter_by_id` to the user defined enum type.

Args
----
Args:
parameter_metadata_dict (Dict[int, ParameterMetadata]): Parameter metadata by id.

parameter_by_id (Dict[int, Any]): Parameters by ID to compare the metadata with.

Returns
-------
None: No return value.

"""
for id, value in parameter_by_id.items():
parameter_metadata = parameter_metadata_dict[id]
Expand Down
Loading