Skip to content

Commit

Permalink
Merge branch 'main' into users/jothees/fix_c_sharp_issue
Browse files Browse the repository at this point in the history
  • Loading branch information
Jotheeswaran-Nandagopal committed Sep 17, 2024
2 parents b967e5f + e3ca88b commit aebae4b
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 141 deletions.
324 changes: 210 additions & 114 deletions packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import black
import click
from click_option_group import optgroup, RequiredMutuallyExclusiveOptionGroup
from mako.template import Template
from ni_measurement_plugin_sdk_service._internal.stubs.ni.measurementlink.measurement.v2 import (
measurement_service_pb2 as v2_measurement_service_pb2,
Expand All @@ -15,16 +16,20 @@
from ni_measurement_plugin_sdk_service.grpc.channelpool import GrpcChannelPool

from ni_measurement_plugin_sdk_generator.client._support import (
camel_to_snake_case,
create_class_name,
create_module_name,
extract_base_service_class,
get_configuration_metadata_by_index,
get_configuration_parameters_with_type_and_default_values,
get_measurement_service_stub,
get_output_metadata_by_index,
get_output_parameters_with_type,
get_all_registered_measurement_service_classes,
is_python_identifier,
remove_suffix,
get_all_registered_measurement_info,
get_selected_measurement_service_class,
to_ordered_set,
resolve_output_directory,
validate_identifier,
validate_measurement_service_classes,
)


Expand Down Expand Up @@ -55,32 +60,215 @@ def _create_file(
file.write(formatted_output)


def _create_client(
discovery_client: DiscoveryClient,
channel_pool: GrpcChannelPool,
measurement_service_class: str,
module_name: str,
class_name: str,
directory_out: pathlib.Path,
) -> None:
built_in_import_modules: List[str] = []
custom_import_modules: List[str] = []
enum_values_by_type: Dict[Type[Enum], Dict[str, int]] = {}
type_url_prefix = "type.googleapis.com/"

measurement_service_stub = get_measurement_service_stub(
discovery_client, channel_pool, measurement_service_class
)
metadata = measurement_service_stub.GetMetadata(v2_measurement_service_pb2.GetMetadataRequest())
configuration_metadata = get_configuration_metadata_by_index(
metadata, measurement_service_class, enum_values_by_type
)
output_metadata = get_output_metadata_by_index(metadata, enum_values_by_type)

configuration_parameters_with_type_and_default_values, measure_api_parameters = (
get_configuration_parameters_with_type_and_default_values(
configuration_metadata, built_in_import_modules, enum_values_by_type
)
)
output_parameters_with_type = get_output_parameters_with_type(
output_metadata, built_in_import_modules, custom_import_modules, enum_values_by_type
)

_create_file(
template_name="measurement_plugin_client.py.mako",
file_name=f"{module_name}.py",
directory_out=directory_out,
class_name=class_name,
display_name=metadata.measurement_details.display_name,
configuration_metadata=configuration_metadata,
output_metadata=output_metadata,
service_class=measurement_service_class,
configuration_parameters_with_type_and_default_values=configuration_parameters_with_type_and_default_values,
measure_api_parameters=measure_api_parameters,
output_parameters_with_type=output_parameters_with_type,
built_in_import_modules=to_ordered_set(built_in_import_modules),
custom_import_modules=to_ordered_set(custom_import_modules),
enum_by_class_name=enum_values_by_type,
configuration_parameters_type_url=type_url_prefix
+ metadata.measurement_signature.configuration_parameters_message_type,
)

print(
f"The measurement plug-in client for the service class '{measurement_service_class}' is created successfully."
)


def _create_all_clients(directory_out: Optional[str]) -> None:
channel_pool = GrpcChannelPool()
discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool)

directory_out_path = resolve_output_directory(directory_out)
measurement_service_classes, _ = get_all_registered_measurement_info(discovery_client)
validate_measurement_service_classes(measurement_service_classes)

for service_class in measurement_service_classes:
base_service_class = extract_base_service_class(service_class)
module_name = create_module_name(base_service_class)
class_name = create_class_name(base_service_class)
validate_identifier(module_name, "module")
validate_identifier(class_name, "class")

_create_client(
channel_pool=channel_pool,
discovery_client=discovery_client,
measurement_service_class=service_class,
module_name=module_name,
class_name=class_name,
directory_out=directory_out_path,
)


def _create_clients_interactively() -> None:
print("Creating the Python Measurement Plug-In Client in interactive mode...")
channel_pool = GrpcChannelPool()
discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool)
directory_out_path = resolve_output_directory()

while True:
measurement_service_classes, measurement_display_names = (
get_all_registered_measurement_info(discovery_client)
)
validate_measurement_service_classes(measurement_service_classes)

print("\nList of registered measurements:")
for index, display_name in enumerate(measurement_display_names, start=1):
print(f"{index}. {display_name}")

selection = click.prompt(
"\nSelect a measurement to generate a client (x to exit)",
type=str,
)
if selection.lower() == "x":
break
service_class = get_selected_measurement_service_class(
int(selection), measurement_service_classes
)

base_service_class = extract_base_service_class(service_class)
default_module_name = create_module_name(base_service_class)
module_name = click.prompt(
"Enter a name for the Python client module, or press Enter to use the default name.",
type=str,
default=default_module_name,
)
validate_identifier(module_name, "module")
default_class_name = create_class_name(base_service_class)
class_name = click.prompt(
"Enter a name for the Python client class, or press Enter to use the default name.",
type=str,
default=default_class_name,
)
validate_identifier(class_name, "class")

_create_client(
channel_pool=channel_pool,
discovery_client=discovery_client,
measurement_service_class=service_class,
module_name=module_name,
class_name=class_name,
directory_out=directory_out_path,
)


def _create_clients(
measurement_service_classes: List[str],
module_name: Optional[str],
class_name: Optional[str],
directory_out: Optional[str],
) -> None:
channel_pool = GrpcChannelPool()
discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool)
directory_out_path = resolve_output_directory(directory_out)

has_multiple_service_classes = len(measurement_service_classes) > 1
for service_class in measurement_service_classes:
base_service_class = extract_base_service_class(service_class)
if has_multiple_service_classes or module_name is None:
module_name = create_module_name(base_service_class)
if has_multiple_service_classes or class_name is None:
class_name = create_class_name(base_service_class)
validate_identifier(module_name, "module")
validate_identifier(class_name, "class")

_create_client(
channel_pool=channel_pool,
discovery_client=discovery_client,
measurement_service_class=service_class,
module_name=module_name,
class_name=class_name,
directory_out=directory_out_path,
)


@click.command()
@click.argument("measurement_service_class", nargs=-1)
@click.option(
@optgroup.group(
"all-modes",
cls=RequiredMutuallyExclusiveOptionGroup,
help="The different modes to create Python measurement client.",
)
@optgroup.option(
"-s",
"--measurement-service-class",
help="Creates Python Measurement Plug-In Client for the given measurement services.",
multiple=True,
)
@optgroup.option(
"-a",
"--all",
is_flag=True,
help="Creates Python Measurement Plug-In Clients for all registered measurement services.",
)
@optgroup.option(
"-i",
"--interactive",
is_flag=True,
help="Creates Python Measurement Plug-In Clients interactively.",
)
@optgroup.group(
"optional parameters",
help="Recommended parameters when using measurement service class mode.",
)
@optgroup.option(
"-m",
"--module-name",
help="Name for the Python Measurement Plug-In Client module.",
)
@click.option(
@optgroup.option(
"-c",
"--class-name",
help="Name for the Python Measurement Plug-In Client Class in the generated module.",
)
@click.option(
"-a",
"--all",
is_flag=True,
help="Creates Python Measurement Plug-In Client for all the registered measurement services.",
)
@click.option(
@optgroup.option(
"-o",
"--directory-out",
help="Output directory for Measurement Plug-In Client files. Default: '<current_directory>/<module_name>'",
)
def create_client(
measurement_service_class: List[str],
all: bool,
interactive: bool,
module_name: Optional[str],
class_name: Optional[str],
directory_out: Optional[str],
Expand All @@ -90,108 +278,16 @@ def create_client(
You can use the generated module to interact with the corresponding measurement service.
MEASUREMENT_SERVICE_CLASS: Accepts one or more measurement service classes.
Separate each service class with a space.
Provide each service class separately.
"""
channel_pool = GrpcChannelPool()
discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool)
built_in_import_modules: List[str] = []
custom_import_modules: List[str] = []
type_url_prefix = "type.googleapis.com/"

if all:
measurement_service_class = get_all_registered_measurement_service_classes(discovery_client)
if len(measurement_service_class) == 0:
raise click.ClickException("No registered measurements.")
else:
if not measurement_service_class:
raise click.ClickException(
"The measurement service class cannot be empty. Either provide a measurement service class or use the 'all' flag to generate clients for all registered measurements."
)

if directory_out is None:
directory_out_path = pathlib.Path.cwd()
_create_all_clients(directory_out)
elif interactive:
_create_clients_interactively()
else:
directory_out_path = pathlib.Path(directory_out)

if not directory_out_path.exists():
raise click.ClickException(f"The specified directory '{directory_out}' was not found.")

is_multiple_client_generation = len(measurement_service_class) > 1
for service_class in measurement_service_class:
enum_values_by_type: Dict[Type[Enum], Dict[str, int]] = {}
if is_multiple_client_generation or module_name is None or class_name is None:
base_service_class = service_class.split(".")[-1]
base_service_class = remove_suffix(base_service_class)

if not base_service_class.isidentifier():
raise click.ClickException(
"Client creation failed.\nEither provide a module name or update the measurement with a valid service class."
)
if not any(ch.isupper() for ch in base_service_class):
print(
f"Warning: The service class '{service_class}' does not adhere to the recommended format."
)

if is_multiple_client_generation:
module_name = camel_to_snake_case(base_service_class) + "_client"
class_name = base_service_class.replace("_", "") + "Client"
else:
if module_name is None:
module_name = camel_to_snake_case(base_service_class) + "_client"
if class_name is None:
class_name = base_service_class.replace("_", "") + "Client"

if not is_python_identifier(module_name):
raise click.ClickException(
f"The module name '{module_name}' is not a valid Python identifier."
)
if not is_python_identifier(class_name):
raise click.ClickException(
f"The class name '{class_name}' is not a valid Python identifier."
)

measurement_service_stub = get_measurement_service_stub(
discovery_client, channel_pool, service_class
)
metadata = measurement_service_stub.GetMetadata(
v2_measurement_service_pb2.GetMetadataRequest()
)
configuration_metadata = get_configuration_metadata_by_index(
metadata, service_class, enum_values_by_type
)
output_metadata = get_output_metadata_by_index(metadata, enum_values_by_type)

configuration_parameters_with_type_and_default_values, measure_api_parameters = (
get_configuration_parameters_with_type_and_default_values(
configuration_metadata, built_in_import_modules, enum_values_by_type
)
)
output_parameters_with_type = get_output_parameters_with_type(
output_metadata,
built_in_import_modules,
custom_import_modules,
enum_values_by_type,
)

_create_file(
template_name="measurement_plugin_client.py.mako",
file_name=f"{module_name}.py",
directory_out=directory_out_path,
_create_clients(
measurement_service_classes=measurement_service_class,
module_name=module_name,
class_name=class_name,
display_name=metadata.measurement_details.display_name,
configuration_metadata=configuration_metadata,
output_metadata=output_metadata,
service_class=service_class,
configuration_parameters_with_type_and_default_values=configuration_parameters_with_type_and_default_values,
measure_api_parameters=measure_api_parameters,
output_parameters_with_type=output_parameters_with_type,
built_in_import_modules=to_ordered_set(built_in_import_modules),
custom_import_modules=to_ordered_set(custom_import_modules),
enum_by_class_name=enum_values_by_type,
configuration_parameters_type_url=type_url_prefix
+ metadata.measurement_signature.configuration_parameters_message_type,
)

print(
f"The measurement plug-in client for the service class '{service_class}' has been created successfully."
directory_out=directory_out,
)
Loading

0 comments on commit aebae4b

Please sign in to comment.