Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add annotations to generator #294

Merged
merged 41 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8b901c6
Add service Annotation Support
gpachecoNI Jun 16, 2023
7e3b6b5
Copy discovery_service.proto and run generate grpc stub
gpachecoNI Jun 16, 2023
e324dce
Fix style and running black.
gpachecoNI Jun 22, 2023
f5d92c0
Fix mypy errors.
gpachecoNI Jun 22, 2023
1b205ed
Fix mypy on Tests
gpachecoNI Jun 22, 2023
f527443
Too many characters in single line
gpachecoNI Jun 22, 2023
0907c30
Fix mypy, black
gpachecoNI Jun 23, 2023
c545341
Revert Update to Example
gpachecoNI Jun 23, 2023
3932dea
Reran Black
gpachecoNI Jun 23, 2023
ab61d7f
Fix comments
gpachecoNI Jun 27, 2023
91f1328
Fix comments
gpachecoNI Jun 27, 2023
c079fb4
Fix comments
gpachecoNI Jun 28, 2023
2bbf37d
Revert "Fix style and running black."
gpachecoNI Jun 28, 2023
f990831
Fix comments
gpachecoNI Jun 28, 2023
38bd275
Simplify regex
gpachecoNI Jun 28, 2023
11756b0
Revert changes made by black
gpachecoNI Jun 28, 2023
b91174a
Simplify Regex
gpachecoNI Jun 28, 2023
7e22dec
Revert more black change simplify code
gpachecoNI Jun 28, 2023
3c2ac07
Revert Black
gpachecoNI Jun 28, 2023
a3568e6
revert black changes
gpachecoNI Jun 28, 2023
5e5b921
Change annotation from test to avoid confusion
gpachecoNI Jun 29, 2023
f91e23f
Fix naming of tests examples.
gpachecoNI Jun 29, 2023
1121045
Add annotations to generator
gpachecoNI Jun 22, 2023
dfea8fb
Update
gpachecoNI Jun 22, 2023
aa5fbda
Fix Test
gpachecoNI Jun 22, 2023
bee10fb
Fix comments
gpachecoNI Jun 27, 2023
8b40568
Fix comments
gpachecoNI Jun 29, 2023
62d90ec
Move new test assets to own folder.
gpachecoNI Jun 29, 2023
e7e3db2
Fix comments_Incomplete_Failing Tests
gpachecoNI Jun 29, 2023
3aba584
Seems like this file did not got updated
gpachecoNI Jun 29, 2023
d9cc5fd
Fix tests
gpachecoNI Jun 29, 2023
fc8200e
Run black
gpachecoNI Jun 29, 2023
91c247a
Fix help
gpachecoNI Jun 29, 2023
46ea5b1
Fix comments
gpachecoNI Jun 30, 2023
97b753f
Merge branch 'main' into users/gpacheco/AddServiceAnnotationToGenerator
gpachecoNI Jun 30, 2023
8598bd5
Resolve conflicts, merge main.
gpachecoNI Jun 30, 2023
84be141
Run black
gpachecoNI Jun 30, 2023
072403f
Avoid duplicating files
gpachecoNI Jun 30, 2023
2c5826b
Fix comments, Change ../ for \
gpachecoNI Jun 30, 2023
d3ae482
Revert change to avoid duplicated files, no need to make it more comp…
gpachecoNI Jun 30, 2023
6b52519
Merge branch 'main' into users/gpacheco/AddServiceAnnotationToGenerator
gpachecoNI Jun 30, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import pathlib
import re
from typing import Optional
from typing import List, Optional, Tuple

import click
from mako import exceptions
Expand Down Expand Up @@ -93,6 +93,12 @@ def _resolve_service_class(service_class: str, display_name: str) -> str:
"--service-class",
help="Service Class that the measurement belongs to. Default: '<display_name>_Python'",
)
@click.option(
"-D",
"--description",
default="",
help="Short description of the measurement",
)
@click.option(
"-d",
"--description-url",
Expand All @@ -104,6 +110,22 @@ def _resolve_service_class(service_class: str, display_name: str) -> str:
"--directory-out",
help="Output directory for measurement files. Default: '<current_directory>/<display_name>'",
)
@click.option(
"-c",
"--collection",
gpachecoNI marked this conversation as resolved.
Show resolved Hide resolved
default="",
help="\b\nThe collection that this measurement belongs to. Collection names are specified"
"using a period-delimited namespace hierarchy and are case-insensitive."
"\nExample: 'CurrentTests.Inrush'",
)
@click.option(
"-t",
"--tags",
default=[],
multiple=True,
help="\b\nTags describing the measurement. This option may be repeated to specify multiple tags. Tags are case-insensitive."
"\nExample: '-t test -t Internal'",
)
@click.option(
"-v",
"--verbose",
Expand All @@ -117,6 +139,9 @@ def create_measurement(
service_class: str,
description_url: str,
directory_out: Optional[str],
description: str,
collection: str,
tags: Tuple[str],
verbose: bool,
) -> None:
"""Generate a Python measurement service from a template.
Expand Down Expand Up @@ -166,6 +191,9 @@ def create_measurement(
service_class=service_class,
description_url=description_url,
ui_file_type=ui_file_type,
description=description,
collection=collection,
tags=list(tags),
)
if ui_file_type == "MeasurementUI":
_create_file(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
<%page args="display_name, service_class, description_url"/>\
\
{
"services": [
{
"displayName": "${display_name}",
"serviceClass": "${service_class}",
"descriptionUrl": "${description_url}",
"providedInterfaces": [ "ni.measurementlink.measurement.v1.MeasurementService" ],
"path": "start.bat"
<%page args="display_name, service_class, description_url, description, collection, tags"/>\
<%
import json

service_config = {
"services": [
{
"displayName": display_name,
"serviceClass": service_class,
"descriptionUrl": description_url,
"providedInterfaces": [
"ni.measurementlink.measurement.v1.MeasurementService",
"ni.measurementlink.measurement.v2.MeasurementService",
],
"path": "start.bat",
"annotations": {
"ni/service.description": description,
"ni/service.collection": collection,
"ni/service.tags": tags
},
}
]
}
]
}
%>\
${json.dumps(service_config, indent=2)}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,49 @@ def test___command_line_args___create_measurement___render_without_exception(
]
)

golden_path = test_assets_directory / "example_renders"
golden_path = test_assets_directory / "example_renders" / "measurement"

filenames = ["measurement.py", "SampleMeasurement.serviceconfig", "start.bat"]
for filename in filenames:
_assert_equal(
golden_path / filename,
temp_directory / filename,
)


def test___command_line_args___create_measurement_with_annotations___render_without_exception(
test_assets_directory: pathlib.Path, tmp_path_factory: pytest.TempPathFactory
):
temp_directory = tmp_path_factory.mktemp("measurement_files")

with pytest.raises(SystemExit):
template.create_measurement(
[
"Sample Measurement",
"--measurement-version",
"1.2.3.4",
"--ui-file",
"MeasurementUI.measui",
"--service-class",
"SampleMeasurement_Python",
"-D",
"Measurement description",
"--description-url",
"https://www.example.com/SampleMeasurement.html",
"--directory-out",
temp_directory,
"--collection",
"Measurement.Collection",
"--tags",
"M1",
"--tags",
"M2",
"--tags",
"M3",
]
)

golden_path = test_assets_directory / "example_renders" / "measurement_with_annotations"

filenames = ["measurement.py", "SampleMeasurement.serviceconfig", "start.bat"]
for filename in filenames:
Expand Down
gpachecoNI marked this conversation as resolved.
Outdated
Show resolved Hide resolved

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"services": [
{
"displayName": "Sample Measurement",
"serviceClass": "SampleMeasurement_Python",
"descriptionUrl": "https://www.example.com/SampleMeasurement.html",
"providedInterfaces": [
"ni.measurementlink.measurement.v1.MeasurementService",
"ni.measurementlink.measurement.v2.MeasurementService"
],
"path": "start.bat",
"annotations": {
"ni/service.description": "",
"ni/service.collection": "",
"ni/service.tags": []
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"services": [
{
"displayName": "Sample Measurement",
"serviceClass": "SampleMeasurement_Python",
"descriptionUrl": "https://www.example.com/SampleMeasurement.html",
"providedInterfaces": [
"ni.measurementlink.measurement.v1.MeasurementService",
"ni.measurementlink.measurement.v2.MeasurementService"
],
"path": "start.bat",
"annotations": {
"ni/service.description": "Measurement description",
"ni/service.collection": "Measurement.Collection",
"ni/service.tags": [
"M1",
"M2",
"M3"
]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""A default measurement with an array in and out."""
import logging
import pathlib

import click
import ni_measurementlink_service as nims

service_directory = pathlib.Path(__file__).resolve().parent
measurement_service = nims.MeasurementService(
service_config_path=service_directory / "SampleMeasurement.serviceconfig",
version="1.2.3.4",
ui_file_paths=[service_directory / "MeasurementUI.measui"],
)


@measurement_service.register_measurement
@measurement_service.configuration("Array in", nims.DataType.DoubleArray1D, [0.0])
@measurement_service.output("Array out", nims.DataType.DoubleArray1D)
def measure(array_input):
"""TODO: replace the following line with your own measurement logic."""
array_output = array_input
return (array_output,)


@click.command
@click.option(
"-v",
"--verbose",
count=True,
help="Enable verbose logging. Repeat to increase verbosity.",
)
def main(verbose: int) -> None:
"""Host the Sample Measurement service."""
if verbose > 1:
level = logging.DEBUG
elif verbose == 1:
level = logging.INFO
else:
level = logging.WARNING
logging.basicConfig(format="%(asctime)s %(levelname)s: %(message)s", level=level)

with measurement_service.host_service():
input("Press enter to close the measurement service.\n")


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@echo off
REM The discovery service uses this script to start the measurement service.
REM You can customize this script for your Python setup. The -v option logs
REM messages with level INFO and above.

call python "%~dp0measurement.py" -v
1 change: 1 addition & 0 deletions ni_measurementlink_service/_internal/discovery_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def register_measurement_service(
service_descriptor.service_class = service_info.service_class
service_descriptor.description_url = service_info.description_url
service_descriptor.provided_interfaces.extend(service_info.provided_interfaces)
service_descriptor.annotations.update(service_info.annotations)

# Registration Request Creation
request = discovery_service_pb2.RegisterServiceRequest(
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,27 @@ class ServiceDescriptor(google.protobuf.message.Message):

DESCRIPTOR: google.protobuf.descriptor.Descriptor

@typing_extensions.final
class AnnotationsEntry(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor

KEY_FIELD_NUMBER: builtins.int
VALUE_FIELD_NUMBER: builtins.int
key: builtins.str
value: builtins.str
def __init__(
self,
*,
key: builtins.str = ...,
value: builtins.str = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ...

DISPLAY_NAME_FIELD_NUMBER: builtins.int
DESCRIPTION_URL_FIELD_NUMBER: builtins.int
PROVIDED_INTERFACES_FIELD_NUMBER: builtins.int
SERVICE_CLASS_FIELD_NUMBER: builtins.int
ANNOTATIONS_FIELD_NUMBER: builtins.int
display_name: builtins.str
"""Required. The user visible name of the service."""
description_url: builtins.str
Expand All @@ -43,15 +60,33 @@ class ServiceDescriptor(google.protobuf.message.Message):
"""Required. The "class" of a service. The value of this field should be unique for a given interface in provided_interfaces.
In effect, the .proto service declaration defines the interface, and this field defines a class or concrete type of the interface.
"""
@property
def annotations(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]:
"""Optional. Represents a set of annotations on the service.
Well-known annotations:
- Description
- Key: "ni/service.description"
- Expected format: string
- Example: "Measure inrush current with a shorted load and validate results against configured limits."
- Collection
- Key: "ni/service.collection"
- Expected format: "." delimited namespace/hierarchy case-insensitive string
- Example: "CurrentTests.Inrush"
- Tags
- Key: "ni/service.tags"
- Expected format: serialized JSON string of an array of strings
- Example: "[\\"powerup\\", \\"current\\"]"
"""
def __init__(
self,
*,
display_name: builtins.str = ...,
description_url: builtins.str = ...,
provided_interfaces: collections.abc.Iterable[builtins.str] | None = ...,
service_class: builtins.str = ...,
annotations: collections.abc.Mapping[builtins.str, builtins.str] | None = ...,
) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["description_url", b"description_url", "display_name", b"display_name", "provided_interfaces", b"provided_interfaces", "service_class", b"service_class"]) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["annotations", b"annotations", "description_url", b"description_url", "display_name", b"display_name", "provided_interfaces", b"provided_interfaces", "service_class", b"service_class"]) -> None: ...

global___ServiceDescriptor = ServiceDescriptor

Expand Down
Loading