From 0f51d40933127e21ad702f9e3f122504b17aadd8 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Thu, 12 Sep 2024 15:07:45 +0530 Subject: [PATCH 01/42] Feat: Add interactive mode for client generation --- .../client/__init__.py | 352 +++++++++++++----- .../client/__main__.py | 21 +- .../tests/acceptance/test_client_generator.py | 8 +- .../test_non_streaming_measurement_client.py | 4 +- .../test_streaming_measurement_client.py | 4 +- 5 files changed, 292 insertions(+), 97 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 792e4bc9b..55e9ba370 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -1,7 +1,7 @@ """Utilizes command line args to create a Measurement Plug-In Client using template files.""" import pathlib -from typing import Any, List, Optional +from typing import Any, List, Optional, Tuple import black import click @@ -47,8 +47,212 @@ def _create_file( file.write(formatted_output) -@click.command() +def _resolve_output_directory(directory_out: Optional[str]) -> pathlib.Path: + if directory_out is None: + directory_out_path = pathlib.Path.cwd() + 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.") + + return directory_out_path + + +def _validate_identifier(name: str, name_type: str) -> None: + if not is_python_identifier(name): + raise click.ClickException( + f"The {name_type} name '{name}' is not a valid Python identifier." + ) + + +def _get_interactive_module_and_class_names() -> Tuple[str, str]: + module_name = click.prompt("Enter a module name for the client", type=str) + _validate_identifier(module_name, "module") + class_name = click.prompt("Enter a class name for the client", type=str) + _validate_identifier(class_name, "class") + + return module_name, class_name + + +def _extract_base_service_class(service_class: str) -> str: + 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." + ) + return base_service_class + + +def _create_module_and_class_names(base_service_class: str) -> Tuple[str, str]: + module_name = camel_to_snake_case(base_service_class) + "_client" + class_name = base_service_class.replace("_", "") + "Client" + return module_name, class_name + + +def _get_class_and_module_names( + service_class: str, + is_multiple_client_generation: bool, + module_name: Optional[str], + class_name: Optional[str], + interactive_mode: bool, +) -> Tuple[str, str]: + if interactive_mode: + return _get_interactive_module_and_class_names() + elif is_multiple_client_generation or module_name is None or class_name is None: + base_service_class = _extract_base_service_class(service_class) + if is_multiple_client_generation: + module_name, class_name = _create_module_and_class_names(base_service_class) + 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" + + _validate_identifier(module_name, "module") + _validate_identifier(class_name, "class") + return module_name, class_name + + +def _get_selected_measurement_service_class( + selection: int, measurement_service_classes: List[str] +) -> List[str]: + if not (1 <= selection <= len(measurement_service_classes)): + raise click.ClickException( + f"Input {selection} is out of bounds. Please try again by entering a valid serial number." + ) + return [measurement_service_classes[selection - 1]] + + +def _get_measurement_service_class( + all: bool, + interactive: bool, + measurement_service_class: List[str], + discovery_client: DiscoveryClient, +) -> List[str]: + + if all or interactive: + measurement_service_class = get_all_registered_measurement_service_classes(discovery_client) + if len(measurement_service_class) == 0: + raise click.ClickException("No registered measurements.") + if interactive: + print("\nList of available measurement services:\n") + for index, service_class in enumerate(measurement_service_class, start=1): + print(f"{index}. {service_class}") + + selection = click.prompt( + "\nEnter the serial number for the desired measurement service client creation", + type=int, + ) + measurement_service_class = _get_selected_measurement_service_class( + selection, measurement_service_class + ) + + 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." + ) + return measurement_service_class + + +def _generate_measurement_client( + discovery_client: DiscoveryClient, + channel_pool: GrpcChannelPool, + service_class: str, + built_in_import_modules: List[str], + custom_import_modules: List[str], + module_name: str, + class_name: str, + directory_out_path: pathlib.Path, +) -> None: + 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) + output_metadata = get_output_metadata_by_index(metadata) + + configuration_parameters_with_type_and_default_values, measure_api_parameters = ( + get_configuration_parameters_with_type_and_default_values( + configuration_metadata, built_in_import_modules + ) + ) + output_parameters_with_type = get_output_parameters_with_type( + output_metadata, built_in_import_modules, custom_import_modules + ) + + _create_file( + template_name="measurement_plugin_client.py.mako", + file_name=f"{module_name}.py", + directory_out=directory_out_path, + 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), + ) + + print( + f"The measurement plug-in client for the service class '{service_class}' has been created successfully." + ) + + +def _create_client( + channel_pool: GrpcChannelPool, + discovery_client: DiscoveryClient, + built_in_import_modules: List[str], + custom_import_modules: List[str], + measurement_service_class: List[str] = [], + all: bool = False, + module_name: Optional[str] = "", + class_name: Optional[str] = "", + directory_out: Optional[str] = "", + interactive_mode: bool = False, +) -> None: + + measurement_service_class = _get_measurement_service_class( + all, interactive_mode, measurement_service_class, discovery_client + ) + + directory_out_path = _resolve_output_directory(directory_out) + + is_multiple_client_generation = len(measurement_service_class) > 1 + for service_class in measurement_service_class: + module_name, class_name = _get_class_and_module_names( + service_class, is_multiple_client_generation, module_name, class_name, interactive_mode + ) + _generate_measurement_client( + discovery_client, + channel_pool, + service_class, + built_in_import_modules, + custom_import_modules, + module_name, + class_name, + directory_out_path, + ) + + +@click.command(name="b") @click.argument("measurement_service_class", nargs=-1) +@click.option( + "-a", + "--all", + is_flag=True, + help="Creates Python Measurement Plug-In Client for all the registered measurement services.", +) @click.option( "-m", "--module-name", @@ -59,18 +263,12 @@ def _create_file( "--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( "-o", "--directory-out", help="Output directory for Measurement Plug-In Client files. Default: '/'", ) -def create_client( +def create_client_in_batch_mode( measurement_service_class: List[str], all: bool, module_name: Optional[str], @@ -89,91 +287,71 @@ def create_client( built_in_import_modules: List[str] = [] custom_import_modules: List[str] = [] - 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() - else: - directory_out_path = pathlib.Path(directory_out) + _create_client( + channel_pool=channel_pool, + discovery_client=discovery_client, + built_in_import_modules=built_in_import_modules, + custom_import_modules=custom_import_modules, + measurement_service_class=measurement_service_class, + all=all, + module_name=module_name, + class_name=class_name, + directory_out=directory_out, + interactive_mode=False, + ) - 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: - 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" +@click.command(name="i") +def create_client_in_interactive_mode() -> None: + """Generates a Python Measurement Plug-In Client module for measurement service interactively. - 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." - ) + You can use the generated module to interact with the corresponding measurement service. + """ + channel_pool = GrpcChannelPool() + discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) + built_in_import_modules: List[str] = [] + custom_import_modules: List[str] = [] - measurement_service_stub = get_measurement_service_stub( - discovery_client, channel_pool, service_class + while True: + _create_client( + interactive_mode=True, + channel_pool=channel_pool, + discovery_client=discovery_client, + built_in_import_modules=built_in_import_modules, + custom_import_modules=custom_import_modules, ) - metadata = measurement_service_stub.GetMetadata( - v2_measurement_service_pb2.GetMetadataRequest() - ) - configuration_metadata = get_configuration_metadata_by_index(metadata, service_class) - output_metadata = get_output_metadata_by_index(metadata) - configuration_parameters_with_type_and_default_values, measure_api_parameters = ( - get_configuration_parameters_with_type_and_default_values( - configuration_metadata, built_in_import_modules + selection = ( + click.prompt( + "\nEnter 'Y' to continue, or enter any other keys to exit", + type=str, + default="", + show_default=False, ) + .strip() + .lower() ) - output_parameters_with_type = get_output_parameters_with_type( - output_metadata, built_in_import_modules, custom_import_modules - ) + if selection == "y": + continue + else: + break - _create_file( - template_name="measurement_plugin_client.py.mako", - file_name=f"{module_name}.py", - directory_out=directory_out_path, - 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), - ) - print( - f"The measurement plug-in client for the service class '{service_class}' has been created successfully." - ) +@click.group() +def cli() -> None: + """Generates a Python Measurement Plug-In Client module for the measurement service. + + You can use the generated module to interact with the corresponding measurement service. + + Use command 'b' to generate the client module in batch mode, or 'i' for interactive mode. + """ + pass + + +def create_client() -> None: + """Generates a Python Measurement Plug-In Client module for the measurement service.""" + cli() + + +cli.add_command(create_client_in_batch_mode) +cli.add_command(create_client_in_interactive_mode) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py index 8f6fc91db..1a0583a72 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py @@ -1,5 +1,22 @@ """Creates a measurement client through use of __init__.py.""" -from ni_measurement_plugin_sdk_generator.client import create_client +import click -create_client() +from ni_measurement_plugin_sdk_generator.client import ( + create_client_in_batch_mode, + create_client_in_interactive_mode, +) + + +@click.command() +@click.argument("mode", default="b") +@click.pass_context +def _create_client(context: click.Context, mode: str) -> None: + if mode == "i": + context.invoke(create_client_in_interactive_mode) + else: + context.invoke(create_client_in_batch_mode()) + + +if __name__ == "__main__": + _create_client() diff --git a/packages/generator/tests/acceptance/test_client_generator.py b/packages/generator/tests/acceptance/test_client_generator.py index a19fcab49..cf7dc6d55 100644 --- a/packages/generator/tests/acceptance/test_client_generator.py +++ b/packages/generator/tests/acceptance/test_client_generator.py @@ -7,7 +7,7 @@ import pytest from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService -from ni_measurement_plugin_sdk_generator.client import create_client +from ni_measurement_plugin_sdk_generator.client import create_client_in_batch_mode from tests.utilities.discovery_service_process import DiscoveryServiceProcess from tests.utilities.measurements import non_streaming_data_measurement, streaming_data_measurement @@ -23,7 +23,7 @@ def test___command_line_args___create_client___render_without_error( filename = f"{module_name}.py" with pytest.raises(SystemExit) as exc_info: - create_client( + create_client_in_batch_mode( [ "ni.tests.NonStreamingDataMeasurement_Python", "--module-name", @@ -49,7 +49,7 @@ def test___command_line_args___create_client_for_all_registered_measurements___r temp_directory = tmp_path_factory.mktemp("measurement_plugin_client_files") with pytest.raises(SystemExit) as exc_info: - create_client( + create_client_in_batch_mode( [ "--all", "--directory-out", @@ -80,7 +80,7 @@ def test___command_line_args___create_client___render_with_proper_line_ending( filename = f"{module_name}.py" with pytest.raises(SystemExit) as exc_info: - create_client( + create_client_in_batch_mode( [ "ni.tests.NonStreamingDataMeasurement_Python", "--module-name", diff --git a/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py b/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py index 5d085f366..5d3d213c2 100644 --- a/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py +++ b/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py @@ -6,7 +6,7 @@ import pytest from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService -from ni_measurement_plugin_sdk_generator.client import create_client +from ni_measurement_plugin_sdk_generator.client import create_client_in_batch_mode from tests.utilities.discovery_service_process import DiscoveryServiceProcess from tests.utilities.measurements import non_streaming_data_measurement @@ -73,7 +73,7 @@ def measurement_client_directory( module_name = "test_measurement_client" with pytest.raises(SystemExit): - create_client( + create_client_in_batch_mode( [ "ni.tests.NonStreamingDataMeasurement_Python", "--module-name", diff --git a/packages/generator/tests/acceptance/test_streaming_measurement_client.py b/packages/generator/tests/acceptance/test_streaming_measurement_client.py index ad08b084b..46da38d7f 100644 --- a/packages/generator/tests/acceptance/test_streaming_measurement_client.py +++ b/packages/generator/tests/acceptance/test_streaming_measurement_client.py @@ -6,7 +6,7 @@ import pytest from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService -from ni_measurement_plugin_sdk_generator.client import create_client +from ni_measurement_plugin_sdk_generator.client import create_client_in_batch_mode from tests.utilities.discovery_service_process import DiscoveryServiceProcess from tests.utilities.measurements import streaming_data_measurement @@ -59,7 +59,7 @@ def measurement_client_directory( module_name = "test_measurement_client" with pytest.raises(SystemExit): - create_client( + create_client_in_batch_mode( [ "ni.tests.StreamingDataMeasurement_Python", "--module-name", From 333ef333199dcfb46ca131849f348663733b2b7d Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Thu, 12 Sep 2024 15:43:04 +0530 Subject: [PATCH 02/42] Fix: Lint errors --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 2 +- .../ni_measurement_plugin_sdk_generator/client/__main__.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 55e9ba370..2b4c00f6a 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -114,7 +114,7 @@ def _get_class_and_module_names( module_name = camel_to_snake_case(base_service_class) + "_client" if class_name is None: class_name = base_service_class.replace("_", "") + "Client" - + _validate_identifier(module_name, "module") _validate_identifier(class_name, "class") return module_name, class_name diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py index 1a0583a72..70ef2437c 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py @@ -18,5 +18,4 @@ def _create_client(context: click.Context, mode: str) -> None: context.invoke(create_client_in_batch_mode()) -if __name__ == "__main__": - _create_client() +_create_client() From e96824afd96db4520e38401873f889285304e514 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Thu, 12 Sep 2024 15:48:55 +0530 Subject: [PATCH 03/42] Fix: Minor .message improvements --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 2b4c00f6a..c5e975b5e 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -67,9 +67,9 @@ def _validate_identifier(name: str, name_type: str) -> None: def _get_interactive_module_and_class_names() -> Tuple[str, str]: - module_name = click.prompt("Enter a module name for the client", type=str) + module_name = click.prompt("Enter a name for the python client module", type=str) _validate_identifier(module_name, "module") - class_name = click.prompt("Enter a class name for the client", type=str) + class_name = click.prompt("Enter a name for the python client class", type=str) _validate_identifier(class_name, "class") return module_name, class_name @@ -142,7 +142,7 @@ def _get_measurement_service_class( if len(measurement_service_class) == 0: raise click.ClickException("No registered measurements.") if interactive: - print("\nList of available measurement services:\n") + print("\nList of registered measurements:") for index, service_class in enumerate(measurement_service_class, start=1): print(f"{index}. {service_class}") From e15a116e1bb2604c3d54d78861e0a992d4487601 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Thu, 12 Sep 2024 16:20:39 +0530 Subject: [PATCH 04/42] Refractor: craeting class and module names methods --- .../client/__init__.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index c5e975b5e..1a9819612 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -90,10 +90,12 @@ def _extract_base_service_class(service_class: str) -> str: return base_service_class -def _create_module_and_class_names(base_service_class: str) -> Tuple[str, str]: - module_name = camel_to_snake_case(base_service_class) + "_client" - class_name = base_service_class.replace("_", "") + "Client" - return module_name, class_name +def _create_module_name(base_service_class: str) -> str: + return camel_to_snake_case(base_service_class) + "_client" + + +def _create_class_name(base_service_class: str) -> str: + return base_service_class.replace("_", "") + "Client" def _get_class_and_module_names( @@ -108,12 +110,13 @@ def _get_class_and_module_names( elif is_multiple_client_generation or module_name is None or class_name is None: base_service_class = _extract_base_service_class(service_class) if is_multiple_client_generation: - module_name, class_name = _create_module_and_class_names(base_service_class) + module_name = _create_module_name(base_service_class) + class_name = _create_class_name(base_service_class) else: if module_name is None: - module_name = camel_to_snake_case(base_service_class) + "_client" + module_name = _create_module_name(base_service_class) if class_name is None: - class_name = base_service_class.replace("_", "") + "Client" + class_name = _create_class_name(base_service_class) _validate_identifier(module_name, "module") _validate_identifier(class_name, "class") From 13fe908adfb01150d8f0865a776fdd7ccf29f0fe Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Thu, 12 Sep 2024 18:45:31 +0530 Subject: [PATCH 05/42] Client: Use click group as script command --- .../client/__init__.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 1a9819612..29163dccf 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -341,7 +341,7 @@ def create_client_in_interactive_mode() -> None: @click.group() -def cli() -> None: +def create_client() -> None: """Generates a Python Measurement Plug-In Client module for the measurement service. You can use the generated module to interact with the corresponding measurement service. @@ -351,10 +351,5 @@ def cli() -> None: pass -def create_client() -> None: - """Generates a Python Measurement Plug-In Client module for the measurement service.""" - cli() - - -cli.add_command(create_client_in_batch_mode) -cli.add_command(create_client_in_interactive_mode) +create_client.add_command(create_client_in_batch_mode) +create_client.add_command(create_client_in_interactive_mode) From 9173aee21d1d14fe2bd9b6f9726910cb8e2b06ef Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Sat, 14 Sep 2024 00:14:34 +0530 Subject: [PATCH 06/42] Fix: Add interactive mode in existing command --- .../client/__init__.py | 103 ++++++++---------- .../client/__main__.py | 20 +--- .../tests/acceptance/test_client_generator.py | 8 +- .../test_non_streaming_measurement_client.py | 4 +- .../test_streaming_measurement_client.py | 4 +- 5 files changed, 58 insertions(+), 81 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 29163dccf..db1c4a66a 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -271,12 +271,23 @@ def _create_client( "--directory-out", help="Output directory for Measurement Plug-In Client files. Default: '/'", ) -def create_client_in_batch_mode( +@click.option( + "-i", + "--interactive", + is_flag=True, + help=( + "Enables interactive mode for creating Measurement Plug-In Client files. " + "If no parameters are provided, this mode will be activated by default." + "When using this mode, no other parameters should be specified. " + ), +) +def create_client( measurement_service_class: List[str], all: bool, module_name: Optional[str], class_name: Optional[str], directory_out: Optional[str], + interactive: bool, ) -> None: """Generates a Python Measurement Plug-In Client module for the measurement service. @@ -290,66 +301,48 @@ def create_client_in_batch_mode( built_in_import_modules: List[str] = [] custom_import_modules: List[str] = [] - _create_client( - channel_pool=channel_pool, - discovery_client=discovery_client, - built_in_import_modules=built_in_import_modules, - custom_import_modules=custom_import_modules, - measurement_service_class=measurement_service_class, - all=all, - module_name=module_name, - class_name=class_name, - directory_out=directory_out, - interactive_mode=False, + has_batch_parameters = ( + measurement_service_class or all or module_name or class_name or directory_out ) - - -@click.command(name="i") -def create_client_in_interactive_mode() -> None: - """Generates a Python Measurement Plug-In Client module for measurement service interactively. - - You can use the generated module to interact with the corresponding measurement service. - """ - channel_pool = GrpcChannelPool() - discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) - built_in_import_modules: List[str] = [] - custom_import_modules: List[str] = [] - - while True: + if has_batch_parameters and not interactive: _create_client( - interactive_mode=True, channel_pool=channel_pool, discovery_client=discovery_client, built_in_import_modules=built_in_import_modules, custom_import_modules=custom_import_modules, + measurement_service_class=measurement_service_class, + all=all, + module_name=module_name, + class_name=class_name, + directory_out=directory_out, + interactive_mode=False, ) - - selection = ( - click.prompt( - "\nEnter 'Y' to continue, or enter any other keys to exit", - type=str, - default="", - show_default=False, + else: + if has_batch_parameters and interactive: + raise click.ClickException( + "Interactive mode does not support additional parameters. Please remove any extra parameters and try again." + ) + print("Creating the Python Measurement Plug-In Client in interactive mode...") + while True: + _create_client( + channel_pool=channel_pool, + discovery_client=discovery_client, + built_in_import_modules=built_in_import_modules, + custom_import_modules=custom_import_modules, + interactive_mode=True, ) - .strip() - .lower() - ) - if selection == "y": - continue - else: - break - - -@click.group() -def create_client() -> None: - """Generates a Python Measurement Plug-In Client module for the measurement service. - - You can use the generated module to interact with the corresponding measurement service. - - Use command 'b' to generate the client module in batch mode, or 'i' for interactive mode. - """ - pass - -create_client.add_command(create_client_in_batch_mode) -create_client.add_command(create_client_in_interactive_mode) + selection = ( + click.prompt( + "\nEnter 'yes' or 'y' to continue, or enter any other keys to exit", + type=str, + default="", + show_default=False, + ) + .strip() + .lower() + ) + if selection in ["yes", "y"]: + continue + else: + break diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py index 70ef2437c..8f6fc91db 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__main__.py @@ -1,21 +1,5 @@ """Creates a measurement client through use of __init__.py.""" -import click +from ni_measurement_plugin_sdk_generator.client import create_client -from ni_measurement_plugin_sdk_generator.client import ( - create_client_in_batch_mode, - create_client_in_interactive_mode, -) - - -@click.command() -@click.argument("mode", default="b") -@click.pass_context -def _create_client(context: click.Context, mode: str) -> None: - if mode == "i": - context.invoke(create_client_in_interactive_mode) - else: - context.invoke(create_client_in_batch_mode()) - - -_create_client() +create_client() diff --git a/packages/generator/tests/acceptance/test_client_generator.py b/packages/generator/tests/acceptance/test_client_generator.py index cf7dc6d55..a19fcab49 100644 --- a/packages/generator/tests/acceptance/test_client_generator.py +++ b/packages/generator/tests/acceptance/test_client_generator.py @@ -7,7 +7,7 @@ import pytest from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService -from ni_measurement_plugin_sdk_generator.client import create_client_in_batch_mode +from ni_measurement_plugin_sdk_generator.client import create_client from tests.utilities.discovery_service_process import DiscoveryServiceProcess from tests.utilities.measurements import non_streaming_data_measurement, streaming_data_measurement @@ -23,7 +23,7 @@ def test___command_line_args___create_client___render_without_error( filename = f"{module_name}.py" with pytest.raises(SystemExit) as exc_info: - create_client_in_batch_mode( + create_client( [ "ni.tests.NonStreamingDataMeasurement_Python", "--module-name", @@ -49,7 +49,7 @@ def test___command_line_args___create_client_for_all_registered_measurements___r temp_directory = tmp_path_factory.mktemp("measurement_plugin_client_files") with pytest.raises(SystemExit) as exc_info: - create_client_in_batch_mode( + create_client( [ "--all", "--directory-out", @@ -80,7 +80,7 @@ def test___command_line_args___create_client___render_with_proper_line_ending( filename = f"{module_name}.py" with pytest.raises(SystemExit) as exc_info: - create_client_in_batch_mode( + create_client( [ "ni.tests.NonStreamingDataMeasurement_Python", "--module-name", diff --git a/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py b/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py index 5d3d213c2..5d085f366 100644 --- a/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py +++ b/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py @@ -6,7 +6,7 @@ import pytest from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService -from ni_measurement_plugin_sdk_generator.client import create_client_in_batch_mode +from ni_measurement_plugin_sdk_generator.client import create_client from tests.utilities.discovery_service_process import DiscoveryServiceProcess from tests.utilities.measurements import non_streaming_data_measurement @@ -73,7 +73,7 @@ def measurement_client_directory( module_name = "test_measurement_client" with pytest.raises(SystemExit): - create_client_in_batch_mode( + create_client( [ "ni.tests.NonStreamingDataMeasurement_Python", "--module-name", diff --git a/packages/generator/tests/acceptance/test_streaming_measurement_client.py b/packages/generator/tests/acceptance/test_streaming_measurement_client.py index 46da38d7f..ad08b084b 100644 --- a/packages/generator/tests/acceptance/test_streaming_measurement_client.py +++ b/packages/generator/tests/acceptance/test_streaming_measurement_client.py @@ -6,7 +6,7 @@ import pytest from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService -from ni_measurement_plugin_sdk_generator.client import create_client_in_batch_mode +from ni_measurement_plugin_sdk_generator.client import create_client from tests.utilities.discovery_service_process import DiscoveryServiceProcess from tests.utilities.measurements import streaming_data_measurement @@ -59,7 +59,7 @@ def measurement_client_directory( module_name = "test_measurement_client" with pytest.raises(SystemExit): - create_client_in_batch_mode( + create_client( [ "ni.tests.StreamingDataMeasurement_Python", "--module-name", From 0ed470da4b88315af3849000cd04e8e8c660ea1b Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Sat, 14 Sep 2024 00:19:26 +0530 Subject: [PATCH 07/42] Fix: Remove command name --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index db1c4a66a..57db631e6 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -248,7 +248,7 @@ def _create_client( ) -@click.command(name="b") +@click.command() @click.argument("measurement_service_class", nargs=-1) @click.option( "-a", From 915181e63125accdc6d92fdc1af3cf955b0ffce2 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 16:09:03 +0530 Subject: [PATCH 08/42] Client: List display name instead of service class --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 6 +++--- .../ni_measurement_plugin_sdk_generator/client/_support.py | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 57db631e6..68fb80219 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -19,7 +19,7 @@ get_measurement_service_stub, get_output_metadata_by_index, get_output_parameters_with_type, - get_all_registered_measurement_service_classes, + get_all_registered_measurement_info, is_python_identifier, remove_suffix, to_ordered_set, @@ -141,12 +141,12 @@ def _get_measurement_service_class( ) -> List[str]: if all or interactive: - measurement_service_class = get_all_registered_measurement_service_classes(discovery_client) + measurement_service_class, measurement_display_name = get_all_registered_measurement_info(discovery_client) if len(measurement_service_class) == 0: raise click.ClickException("No registered measurements.") if interactive: print("\nList of registered measurements:") - for index, service_class in enumerate(measurement_service_class, start=1): + for index, service_class in enumerate(measurement_display_name, start=1): print(f"{index}. {service_class}") selection = click.prompt( diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py index d7aec02c9..a81da167b 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py @@ -82,7 +82,7 @@ def get_measurement_service_stub( return v2_measurement_service_pb2_grpc.MeasurementServiceStub(channel) -def get_all_registered_measurement_service_classes(discovery_client: DiscoveryClient) -> List[str]: +def get_all_registered_measurement_info(discovery_client: DiscoveryClient) -> Tuple[List[str], List[str]]: """Returns the service classes of all the registered measurement services.""" registered_measurement_services = discovery_client.enumerate_services( _V2_MEASUREMENT_SERVICE_INTERFACE @@ -90,7 +90,10 @@ def get_all_registered_measurement_service_classes(discovery_client: DiscoveryCl measurement_service_classes = [ measurement_service.service_class for measurement_service in registered_measurement_services ] - return measurement_service_classes + measurement_display_names = [ + measurement_service.display_name for measurement_service in registered_measurement_services + ] + return measurement_service_classes, measurement_display_names def get_configuration_metadata_by_index( From c27f72e24cc9ae186994d2942e4d4eca94b8bfa3 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 16:10:43 +0530 Subject: [PATCH 09/42] Client: Generate default class and module name for interactive mode --- .../client/__init__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 68fb80219..e612632f4 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -66,10 +66,13 @@ def _validate_identifier(name: str, name_type: str) -> None: ) -def _get_interactive_module_and_class_names() -> Tuple[str, str]: - module_name = click.prompt("Enter a name for the python client module", type=str) +def _get_interactive_module_and_class_names(base_service_class: str) -> Tuple[str, str]: + 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 choose the default name", type=str, default=default_module_name) _validate_identifier(module_name, "module") - class_name = click.prompt("Enter a name for the python client class", type=str) + + 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 choose the default name", type=str, default=default_class_name) _validate_identifier(class_name, "class") return module_name, class_name @@ -105,10 +108,10 @@ def _get_class_and_module_names( class_name: Optional[str], interactive_mode: bool, ) -> Tuple[str, str]: + base_service_class = _extract_base_service_class(service_class) if interactive_mode: - return _get_interactive_module_and_class_names() + return _get_interactive_module_and_class_names(base_service_class) elif is_multiple_client_generation or module_name is None or class_name is None: - base_service_class = _extract_base_service_class(service_class) if is_multiple_client_generation: module_name = _create_module_name(base_service_class) class_name = _create_class_name(base_service_class) From a5fd96955e0ac2c84242cfa9aa1580b911fbe11f Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 16:17:38 +0530 Subject: [PATCH 10/42] Client: Refractor display messages --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index e612632f4..0ab2f4f94 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -131,7 +131,7 @@ def _get_selected_measurement_service_class( ) -> List[str]: if not (1 <= selection <= len(measurement_service_classes)): raise click.ClickException( - f"Input {selection} is out of bounds. Please try again by entering a valid serial number." + f"Input {selection} is not invalid. Please try again by selecting a valid measurement from the list." ) return [measurement_service_classes[selection - 1]] @@ -153,7 +153,7 @@ def _get_measurement_service_class( print(f"{index}. {service_class}") selection = click.prompt( - "\nEnter the serial number for the desired measurement service client creation", + "\nSelect a measurement to generate a client", type=int, ) measurement_service_class = _get_selected_measurement_service_class( From bf15afaaa902439359849119e8b928333a91ee0a Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 16:20:24 +0530 Subject: [PATCH 11/42] Client: Capiltialize the noun python --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 0ab2f4f94..6657ab9f6 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -68,11 +68,11 @@ def _validate_identifier(name: str, name_type: str) -> None: def _get_interactive_module_and_class_names(base_service_class: str) -> Tuple[str, str]: 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 choose the default name", type=str, default=default_module_name) + module_name = click.prompt("Enter a name for the Python client module (or) press enter to choose 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 choose the default name", type=str, default=default_class_name) + class_name = click.prompt("Enter a name for the Python client class (or) press enter to choose the default name", type=str, default=default_class_name) _validate_identifier(class_name, "class") return module_name, class_name From 8c0840b56b85716481fe118bbe5b4785d1d163a1 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 16:32:59 +0530 Subject: [PATCH 12/42] Client: Spacing in options help context --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 6657ab9f6..6fd0896e3 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -280,7 +280,7 @@ def _create_client( is_flag=True, help=( "Enables interactive mode for creating Measurement Plug-In Client files. " - "If no parameters are provided, this mode will be activated by default." + "If no parameters are provided, this mode will be activated by default. " "When using this mode, no other parameters should be specified. " ), ) From 0a96c9773724885bd765ef6c8a67ad5120b7647f Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 17:58:57 +0530 Subject: [PATCH 13/42] Client: Fix method signatures --- .../client/__init__.py | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 6fd0896e3..14a1656e6 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -172,12 +172,13 @@ def _generate_measurement_client( discovery_client: DiscoveryClient, channel_pool: GrpcChannelPool, service_class: str, - built_in_import_modules: List[str], - custom_import_modules: List[str], module_name: str, class_name: str, directory_out_path: pathlib.Path, ) -> None: + built_in_import_modules: List[str] = [] + custom_import_modules: List[str] = [] + measurement_service_stub = get_measurement_service_stub( discovery_client, channel_pool, service_class ) @@ -218,12 +219,10 @@ def _generate_measurement_client( def _create_client( channel_pool: GrpcChannelPool, discovery_client: DiscoveryClient, - built_in_import_modules: List[str], - custom_import_modules: List[str], measurement_service_class: List[str] = [], all: bool = False, - module_name: Optional[str] = "", - class_name: Optional[str] = "", + module_name: Optional[str] = None, + class_name: Optional[str] = None, directory_out: Optional[str] = "", interactive_mode: bool = False, ) -> None: @@ -243,8 +242,6 @@ def _create_client( discovery_client, channel_pool, service_class, - built_in_import_modules, - custom_import_modules, module_name, class_name, directory_out_path, @@ -301,8 +298,6 @@ def create_client( """ channel_pool = GrpcChannelPool() discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) - built_in_import_modules: List[str] = [] - custom_import_modules: List[str] = [] has_batch_parameters = ( measurement_service_class or all or module_name or class_name or directory_out @@ -311,14 +306,11 @@ def create_client( _create_client( channel_pool=channel_pool, discovery_client=discovery_client, - built_in_import_modules=built_in_import_modules, - custom_import_modules=custom_import_modules, measurement_service_class=measurement_service_class, all=all, module_name=module_name, class_name=class_name, directory_out=directory_out, - interactive_mode=False, ) else: if has_batch_parameters and interactive: @@ -330,8 +322,6 @@ def create_client( _create_client( channel_pool=channel_pool, discovery_client=discovery_client, - built_in_import_modules=built_in_import_modules, - custom_import_modules=custom_import_modules, interactive_mode=True, ) From 9522d8c4a2013be13caabdb5577a8add34c5b761 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 18:01:45 +0530 Subject: [PATCH 14/42] Client; Fix lint errors --- .../client/__init__.py | 16 +++++++++++++--- .../client/_support.py | 4 +++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 14a1656e6..b640304fe 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -68,11 +68,19 @@ def _validate_identifier(name: str, name_type: str) -> None: def _get_interactive_module_and_class_names(base_service_class: str) -> Tuple[str, str]: 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 choose the default name", type=str, default=default_module_name) + module_name = click.prompt( + "Enter a name for the Python client module (or) press enter to choose 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 choose the default name", type=str, default=default_class_name) + class_name = click.prompt( + "Enter a name for the Python client class (or) press enter to choose the default name", + type=str, + default=default_class_name, + ) _validate_identifier(class_name, "class") return module_name, class_name @@ -144,7 +152,9 @@ def _get_measurement_service_class( ) -> List[str]: if all or interactive: - measurement_service_class, measurement_display_name = get_all_registered_measurement_info(discovery_client) + measurement_service_class, measurement_display_name = get_all_registered_measurement_info( + discovery_client + ) if len(measurement_service_class) == 0: raise click.ClickException("No registered measurements.") if interactive: diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py index a81da167b..45cd48a8c 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py @@ -82,7 +82,9 @@ def get_measurement_service_stub( return v2_measurement_service_pb2_grpc.MeasurementServiceStub(channel) -def get_all_registered_measurement_info(discovery_client: DiscoveryClient) -> Tuple[List[str], List[str]]: +def get_all_registered_measurement_info( + discovery_client: DiscoveryClient, +) -> Tuple[List[str], List[str]]: """Returns the service classes of all the registered measurement services.""" registered_measurement_services = discovery_client.enumerate_services( _V2_MEASUREMENT_SERVICE_INTERFACE From 63140d4b65dfc974ea76dfdf706492633b5c1b96 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 18:29:21 +0530 Subject: [PATCH 15/42] Client: Update directory_out default value to None --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index b640304fe..a3ca35ccc 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -233,7 +233,7 @@ def _create_client( all: bool = False, module_name: Optional[str] = None, class_name: Optional[str] = None, - directory_out: Optional[str] = "", + directory_out: Optional[str] = None, interactive_mode: bool = False, ) -> None: From 3fe0238c7d0fc83ae4934f41b37c0d1ffe11087e Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 19:58:02 +0530 Subject: [PATCH 16/42] Client: Update exit message --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index a3ca35ccc..be9f28038 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -337,7 +337,7 @@ def create_client( selection = ( click.prompt( - "\nEnter 'yes' or 'y' to continue, or enter any other keys to exit", + "\nEnter 'x' to exit or enter any other keys to select another measurement", type=str, default="", show_default=False, @@ -345,7 +345,7 @@ def create_client( .strip() .lower() ) - if selection in ["yes", "y"]: + if selection == 'x': continue else: break From 72d16c4a53ff18796cb8f45b6f16906135df3364 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 19:59:29 +0530 Subject: [PATCH 17/42] Client: Update parameter names --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index be9f28038..208f49930 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -159,8 +159,8 @@ def _get_measurement_service_class( raise click.ClickException("No registered measurements.") if interactive: print("\nList of registered measurements:") - for index, service_class in enumerate(measurement_display_name, start=1): - print(f"{index}. {service_class}") + for index, display_name in enumerate(measurement_display_name, start=1): + print(f"{index}. {display_name}") selection = click.prompt( "\nSelect a measurement to generate a client", From b5ea612c0b0cba771599c29e8aeea73d448aeb4c Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 20:22:12 +0530 Subject: [PATCH 18/42] Client: Use optgroup to group different modes --- .../client/__init__.py | 75 ++++++++++--------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 208f49930..021bcc210 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -5,6 +5,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, @@ -259,38 +260,51 @@ def _create_client( @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 Client for all the registered measurement services.", ) -@click.option( +@optgroup.option( + "-i", + "--interactive", + is_flag=True, + help=( + "Creates Python Measurement Plug-In Client for any registered measurement services interactively. " + "If no modes are provided, this mode will be activated by default. " + ), +) +@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( +@optgroup.option( "-o", "--directory-out", help="Output directory for Measurement Plug-In Client files. Default: '/'", ) -@click.option( - "-i", - "--interactive", - is_flag=True, - help=( - "Enables interactive mode for creating Measurement Plug-In Client files. " - "If no parameters are provided, this mode will be activated by default. " - "When using this mode, no other parameters should be specified. " - ), -) def create_client( measurement_service_class: List[str], all: bool, @@ -309,24 +323,7 @@ def create_client( channel_pool = GrpcChannelPool() discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) - has_batch_parameters = ( - measurement_service_class or all or module_name or class_name or directory_out - ) - if has_batch_parameters and not interactive: - _create_client( - channel_pool=channel_pool, - discovery_client=discovery_client, - measurement_service_class=measurement_service_class, - all=all, - module_name=module_name, - class_name=class_name, - directory_out=directory_out, - ) - else: - if has_batch_parameters and interactive: - raise click.ClickException( - "Interactive mode does not support additional parameters. Please remove any extra parameters and try again." - ) + if interactive: print("Creating the Python Measurement Plug-In Client in interactive mode...") while True: _create_client( @@ -345,7 +342,17 @@ def create_client( .strip() .lower() ) - if selection == 'x': + if selection == "x": continue else: break + else: + _create_client( + channel_pool=channel_pool, + discovery_client=discovery_client, + measurement_service_class=measurement_service_class, + all=all, + module_name=module_name, + class_name=class_name, + directory_out=directory_out, + ) From 84b3108558ab02e99916491180cb12fa0a2ea762 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 21:56:40 +0530 Subject: [PATCH 19/42] Client: Refractor create client method --- .../client/__init__.py | 236 ++++++++---------- 1 file changed, 100 insertions(+), 136 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 021bcc210..302cb37af 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -1,7 +1,7 @@ """Utilizes command line args to create a Measurement Plug-In Client using template files.""" import pathlib -from typing import Any, List, Optional, Tuple +from typing import Any, List, Optional import black import click @@ -48,7 +48,7 @@ def _create_file( file.write(formatted_output) -def _resolve_output_directory(directory_out: Optional[str]) -> pathlib.Path: +def _resolve_output_directory(directory_out: Optional[str] = None) -> pathlib.Path: if directory_out is None: directory_out_path = pathlib.Path.cwd() else: @@ -67,26 +67,6 @@ def _validate_identifier(name: str, name_type: str) -> None: ) -def _get_interactive_module_and_class_names(base_service_class: str) -> Tuple[str, str]: - 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 choose 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 choose the default name", - type=str, - default=default_class_name, - ) - _validate_identifier(class_name, "class") - - return module_name, class_name - - def _extract_base_service_class(service_class: str) -> str: base_service_class = service_class.split(".")[-1] base_service_class = remove_suffix(base_service_class) @@ -110,31 +90,6 @@ def _create_class_name(base_service_class: str) -> str: return base_service_class.replace("_", "") + "Client" -def _get_class_and_module_names( - service_class: str, - is_multiple_client_generation: bool, - module_name: Optional[str], - class_name: Optional[str], - interactive_mode: bool, -) -> Tuple[str, str]: - base_service_class = _extract_base_service_class(service_class) - if interactive_mode: - return _get_interactive_module_and_class_names(base_service_class) - elif is_multiple_client_generation or module_name is None or class_name is None: - if is_multiple_client_generation: - module_name = _create_module_name(base_service_class) - class_name = _create_class_name(base_service_class) - else: - if module_name is None: - module_name = _create_module_name(base_service_class) - if class_name is None: - class_name = _create_class_name(base_service_class) - - _validate_identifier(module_name, "module") - _validate_identifier(class_name, "class") - return module_name, class_name - - def _get_selected_measurement_service_class( selection: int, measurement_service_classes: List[str] ) -> List[str]: @@ -142,59 +97,27 @@ def _get_selected_measurement_service_class( raise click.ClickException( f"Input {selection} is not invalid. Please try again by selecting a valid measurement from the list." ) - return [measurement_service_classes[selection - 1]] - + return measurement_service_classes[selection - 1] -def _get_measurement_service_class( - all: bool, - interactive: bool, - measurement_service_class: List[str], - discovery_client: DiscoveryClient, -) -> List[str]: - if all or interactive: - measurement_service_class, measurement_display_name = get_all_registered_measurement_info( - discovery_client - ) - if len(measurement_service_class) == 0: - raise click.ClickException("No registered measurements.") - if interactive: - print("\nList of registered measurements:") - for index, display_name in enumerate(measurement_display_name, start=1): - print(f"{index}. {display_name}") - - selection = click.prompt( - "\nSelect a measurement to generate a client", - type=int, - ) - measurement_service_class = _get_selected_measurement_service_class( - selection, measurement_service_class - ) - - 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." - ) - return measurement_service_class - - -def _generate_measurement_client( +def _create_client( discovery_client: DiscoveryClient, channel_pool: GrpcChannelPool, - service_class: str, + measurement_service_class: str, module_name: str, class_name: str, - directory_out_path: pathlib.Path, + directory_out: pathlib.Path, ) -> None: built_in_import_modules: List[str] = [] custom_import_modules: List[str] = [] measurement_service_stub = get_measurement_service_stub( - discovery_client, channel_pool, service_class + 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, service_class) + configuration_metadata = get_configuration_metadata_by_index( + metadata, measurement_service_class + ) output_metadata = get_output_metadata_by_index(metadata) configuration_parameters_with_type_and_default_values, measure_api_parameters = ( @@ -209,12 +132,12 @@ def _generate_measurement_client( _create_file( template_name="measurement_plugin_client.py.mako", file_name=f"{module_name}.py", - directory_out=directory_out_path, + 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=service_class, + 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, @@ -223,41 +146,9 @@ def _generate_measurement_client( ) print( - f"The measurement plug-in client for the service class '{service_class}' has been created successfully." - ) - - -def _create_client( - channel_pool: GrpcChannelPool, - discovery_client: DiscoveryClient, - measurement_service_class: List[str] = [], - all: bool = False, - module_name: Optional[str] = None, - class_name: Optional[str] = None, - directory_out: Optional[str] = None, - interactive_mode: bool = False, -) -> None: - - measurement_service_class = _get_measurement_service_class( - all, interactive_mode, measurement_service_class, discovery_client + f"The measurement plug-in client for the service class '{measurement_service_class}' has been created successfully." ) - directory_out_path = _resolve_output_directory(directory_out) - - is_multiple_client_generation = len(measurement_service_class) > 1 - for service_class in measurement_service_class: - module_name, class_name = _get_class_and_module_names( - service_class, is_multiple_client_generation, module_name, class_name, interactive_mode - ) - _generate_measurement_client( - discovery_client, - channel_pool, - service_class, - module_name, - class_name, - directory_out_path, - ) - @click.command() @optgroup.group( @@ -282,8 +173,7 @@ def _create_client( "--interactive", is_flag=True, help=( - "Creates Python Measurement Plug-In Client for any registered measurement services interactively. " - "If no modes are provided, this mode will be activated by default. " + "Creates Python Measurement Plug-In Client for any registered measurement services interactively." ), ) @optgroup.group( @@ -323,20 +213,81 @@ def create_client( channel_pool = GrpcChannelPool() discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) - if interactive: + directory_out = _resolve_output_directory() + + if all: + measurement_service_class, _ = get_all_registered_measurement_info(discovery_client) + if len(measurement_service_class) == 0: + raise click.ClickException("No registered measurements.") + + for service_class in measurement_service_class: + 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, + ) + + elif interactive: print("Creating the Python Measurement Plug-In Client in interactive mode...") + while True: + measurement_service_class, measurement_display_names = ( + get_all_registered_measurement_info(discovery_client) + ) + if len(measurement_service_class) == 0: + raise click.ClickException("No registered measurements.") + + 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", + type=int, + ) + service_class = _get_selected_measurement_service_class( + selection, measurement_service_class + ) + + 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 choose 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 choose the default name", + type=str, + default=default_class_name, + ) + _validate_identifier(class_name, "class") + _create_client( channel_pool=channel_pool, discovery_client=discovery_client, - interactive_mode=True, + measurement_service_class=service_class, + module_name=module_name, + class_name=class_name, + directory_out=directory_out, ) selection = ( click.prompt( "\nEnter 'x' to exit or enter any other keys to select another measurement", type=str, - default="", + default="x", show_default=False, ) .strip() @@ -347,12 +298,25 @@ def create_client( else: break else: - _create_client( - channel_pool=channel_pool, - discovery_client=discovery_client, - measurement_service_class=measurement_service_class, - all=all, - module_name=module_name, - class_name=class_name, - directory_out=directory_out, - ) + if not measurement_service_class: + raise click.ClickException( + "The measurement service class cannot be empty. Please provide a measurement service class or use the 'all' flag to generate clients for all registered measurements or 'interactive' flag to generate client for any registered measurements." + ) + + for service_class in measurement_service_class: + base_service_class = _extract_base_service_class(service_class) + if module_name is None: + module_name = _create_module_name(base_service_class) + if 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, + ) From ec125a81132de4dcfadc12ae16a536908ae2e18b Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 21:58:33 +0530 Subject: [PATCH 20/42] Client: Update directory out creation --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 302cb37af..0df27cd26 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -213,13 +213,12 @@ def create_client( channel_pool = GrpcChannelPool() discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) - directory_out = _resolve_output_directory() - if all: + directory_out = _resolve_output_directory() measurement_service_class, _ = get_all_registered_measurement_info(discovery_client) if len(measurement_service_class) == 0: raise click.ClickException("No registered measurements.") - + for service_class in measurement_service_class: base_service_class = _extract_base_service_class(service_class) module_name = _create_module_name(base_service_class) @@ -238,6 +237,7 @@ def create_client( elif interactive: print("Creating the Python Measurement Plug-In Client in interactive mode...") + directory_out = _resolve_output_directory() while True: measurement_service_class, measurement_display_names = ( @@ -302,6 +302,7 @@ def create_client( raise click.ClickException( "The measurement service class cannot be empty. Please provide a measurement service class or use the 'all' flag to generate clients for all registered measurements or 'interactive' flag to generate client for any registered measurements." ) + directory_out = _resolve_output_directory(directory_out) for service_class in measurement_service_class: base_service_class = _extract_base_service_class(service_class) From 2b3dc514c1a28e049dbb4560febccea1f9446a1b Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 21:59:16 +0530 Subject: [PATCH 21/42] Client: Update directory out in all mode --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 0df27cd26..36a035ece 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -214,7 +214,7 @@ def create_client( discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) if all: - directory_out = _resolve_output_directory() + directory_out = _resolve_output_directory(directory_out) measurement_service_class, _ = get_all_registered_measurement_info(discovery_client) if len(measurement_service_class) == 0: raise click.ClickException("No registered measurements.") From bff6625ec2d37e83936fab3cb29251df2cbf7a4f Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 22:03:35 +0530 Subject: [PATCH 22/42] Client: Update interactive exit message --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 36a035ece..1f1dd0c68 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -285,7 +285,7 @@ def create_client( selection = ( click.prompt( - "\nEnter 'x' to exit or enter any other keys to select another measurement", + "\nEnter 'x' to exit or enter any other keys to continue client creation for another measurement", type=str, default="x", show_default=False, @@ -294,8 +294,6 @@ def create_client( .lower() ) if selection == "x": - continue - else: break else: if not measurement_service_class: From b93899bf94f6079a634fafe5045bf1b89fb81d32 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 22:24:17 +0530 Subject: [PATCH 23/42] Client: Create class & module name for multiple clients --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 1f1dd0c68..37b208d88 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -302,11 +302,12 @@ def create_client( ) directory_out = _resolve_output_directory(directory_out) + is_multiple_measurement_client_creation = len(measurement_service_class) > 0 for service_class in measurement_service_class: base_service_class = _extract_base_service_class(service_class) - if module_name is None: + if is_multiple_measurement_client_creation or module_name is None: module_name = _create_module_name(base_service_class) - if class_name is None: + if is_multiple_measurement_client_creation or class_name is None: class_name = _create_class_name(base_service_class) _validate_identifier(module_name, "module") _validate_identifier(class_name, "class") From 0448027ef6f36f840eb527963468f2c5c679129d Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 22:27:05 +0530 Subject: [PATCH 24/42] Client: Remove exception for no service class --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 37b208d88..c7cc9ee51 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -296,14 +296,10 @@ def create_client( if selection == "x": break else: - if not measurement_service_class: - raise click.ClickException( - "The measurement service class cannot be empty. Please provide a measurement service class or use the 'all' flag to generate clients for all registered measurements or 'interactive' flag to generate client for any registered measurements." - ) directory_out = _resolve_output_directory(directory_out) is_multiple_measurement_client_creation = len(measurement_service_class) > 0 - for service_class in measurement_service_class: + for service_class in measurement_service_class: base_service_class = _extract_base_service_class(service_class) if is_multiple_measurement_client_creation or module_name is None: module_name = _create_module_name(base_service_class) From 707386bd58c6e806b3a0aeb23162faf4c6954c1c Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 23:38:09 +0530 Subject: [PATCH 25/42] Packages: Add click-option-group --- packages/generator/poetry.lock | 23 +++++++++++++++++++++-- packages/generator/pyproject.toml | 1 + 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/generator/poetry.lock b/packages/generator/poetry.lock index 821d50f43..5e688e9a2 100644 --- a/packages/generator/poetry.lock +++ b/packages/generator/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "bandit" @@ -85,6 +85,25 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "click-option-group" +version = "0.5.6" +description = "Option groups missing in Click" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "click-option-group-0.5.6.tar.gz", hash = "sha256:97d06703873518cc5038509443742b25069a3c7562d1ea72ff08bfadde1ce777"}, + {file = "click_option_group-0.5.6-py3-none-any.whl", hash = "sha256:38a26d963ee3ad93332ddf782f9259c5bdfe405e73408d943ef5e7d0c3767ec7"}, +] + +[package.dependencies] +Click = ">=7.0,<9" + +[package.extras] +docs = ["Pallets-Sphinx-Themes", "m2r2", "sphinx"] +tests = ["pytest"] +tests-cov = ["coverage", "coveralls", "pytest", "pytest-cov"] + [[package]] name = "colorama" version = "0.4.6" @@ -1087,4 +1106,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "63f83af369e27a6a82cbe0535c26bb0bdbff66262a2d5b4c644ee7d6a81d8ad5" +content-hash = "9124bff8bc949530cbe537972a3b447f167bc39866ee5bfdedff4de9fa53a2c1" diff --git a/packages/generator/pyproject.toml b/packages/generator/pyproject.toml index b1a48df17..04c37af8f 100644 --- a/packages/generator/pyproject.toml +++ b/packages/generator/pyproject.toml @@ -25,6 +25,7 @@ click = ">=8.1.3" grpcio = "^1.49.1" protobuf = "^4.21" black = ">=24.8.0" +click-option-group = "^0.5.6" # ni-measurement-plugin-sdk-service = {version = "^2.0.0"} [tool.poetry.group.dev.dependencies] From 94084b544144f9f801f64e1cbbd4a81166d0f56a Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Mon, 16 Sep 2024 23:38:45 +0530 Subject: [PATCH 26/42] Client: Update context help --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index c7cc9ee51..063861d1e 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -166,14 +166,14 @@ def _create_client( "-a", "--all", is_flag=True, - help="Creates Python Measurement Plug-In Client for all the registered measurement services.", + 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 Client for any registered measurement services interactively." + "Creates Python Measurement Plug-In Clients interactively." ), ) @optgroup.group( From 16a4aa643f4f96f9d76dd9b9f7186556000a58d7 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 00:06:25 +0530 Subject: [PATCH 27/42] Client: extract create_client into helper functions --- .../client/__init__.py | 233 ++++++++++-------- 1 file changed, 127 insertions(+), 106 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 063861d1e..152e2de5b 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -150,6 +150,124 @@ def _create_client( ) +def _create_all_clients(directory_out: str) -> None: + channel_pool = GrpcChannelPool() + discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) + + if all: + directory_out = _resolve_output_directory(directory_out) + measurement_service_class, _ = get_all_registered_measurement_info(discovery_client) + if len(measurement_service_class) == 0: + raise click.ClickException("No registered measurements.") + + for service_class in measurement_service_class: + 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, + ) + + +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 = _resolve_output_directory() + + while True: + measurement_service_class, measurement_display_names = get_all_registered_measurement_info( + discovery_client + ) + if len(measurement_service_class) == 0: + raise click.ClickException("No registered measurements.") + + 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", + type=int, + ) + service_class = _get_selected_measurement_service_class( + selection, measurement_service_class + ) + + 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 choose 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 choose 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, + ) + + selection = ( + click.prompt( + "\nEnter 'x' to exit or enter any other keys to continue client creation for another measurement", + type=str, + default="", + show_default=False, + ) + .strip() + .lower() + ) + if selection == "x": + break + + +def _create_clients( + measurement_service_class: List[str], module_name: str, class_name: str, directory_out: str +) -> None: + channel_pool = GrpcChannelPool() + discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) + directory_out = _resolve_output_directory(directory_out) + + is_multiple_measurement_client_creation = len(measurement_service_class) > 1 + for service_class in measurement_service_class: + base_service_class = _extract_base_service_class(service_class) + if is_multiple_measurement_client_creation or module_name is None: + module_name = _create_module_name(base_service_class) + if is_multiple_measurement_client_creation 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, + ) + + @click.command() @optgroup.group( "all-modes", @@ -172,9 +290,7 @@ def _create_client( "-i", "--interactive", is_flag=True, - help=( - "Creates Python Measurement Plug-In Clients interactively." - ), + help=("Creates Python Measurement Plug-In Clients interactively."), ) @optgroup.group( "optional parameters", @@ -210,109 +326,14 @@ def create_client( MEASUREMENT_SERVICE_CLASS: Accepts one or more measurement service classes. Separate each service class with a space. """ - channel_pool = GrpcChannelPool() - discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) - if all: - directory_out = _resolve_output_directory(directory_out) - measurement_service_class, _ = get_all_registered_measurement_info(discovery_client) - if len(measurement_service_class) == 0: - raise click.ClickException("No registered measurements.") - - for service_class in measurement_service_class: - 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, - ) - + _create_all_clients(directory_out) elif interactive: - print("Creating the Python Measurement Plug-In Client in interactive mode...") - directory_out = _resolve_output_directory() - - while True: - measurement_service_class, measurement_display_names = ( - get_all_registered_measurement_info(discovery_client) - ) - if len(measurement_service_class) == 0: - raise click.ClickException("No registered measurements.") - - 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", - type=int, - ) - service_class = _get_selected_measurement_service_class( - selection, measurement_service_class - ) - - 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 choose 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 choose 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, - ) - - selection = ( - click.prompt( - "\nEnter 'x' to exit or enter any other keys to continue client creation for another measurement", - type=str, - default="x", - show_default=False, - ) - .strip() - .lower() - ) - if selection == "x": - break + _create_clients_interactively() else: - directory_out = _resolve_output_directory(directory_out) - - is_multiple_measurement_client_creation = len(measurement_service_class) > 0 - for service_class in measurement_service_class: - base_service_class = _extract_base_service_class(service_class) - if is_multiple_measurement_client_creation or module_name is None: - module_name = _create_module_name(base_service_class) - if is_multiple_measurement_client_creation 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, - ) + _create_clients( + measurement_service_class=measurement_service_class, + module_name=module_name, + class_name=class_name, + directory_out=directory_out, + ) From 1a7fc5728799d33df0d36ce70d2fd5ad7c1e1bb6 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 00:23:01 +0530 Subject: [PATCH 28/42] Client: Fix myPy errors --- .../client/__init__.py | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 152e2de5b..5f2a352c0 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -92,7 +92,7 @@ def _create_class_name(base_service_class: str) -> str: def _get_selected_measurement_service_class( selection: int, measurement_service_classes: List[str] -) -> List[str]: +) -> str: if not (1 <= selection <= len(measurement_service_classes)): raise click.ClickException( f"Input {selection} is not invalid. Please try again by selecting a valid measurement from the list." @@ -150,38 +150,37 @@ def _create_client( ) -def _create_all_clients(directory_out: str) -> None: +def _create_all_clients(directory_out: Optional[str]) -> None: channel_pool = GrpcChannelPool() discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) - if all: - directory_out = _resolve_output_directory(directory_out) - measurement_service_class, _ = get_all_registered_measurement_info(discovery_client) - if len(measurement_service_class) == 0: - raise click.ClickException("No registered measurements.") + directory_out_path = _resolve_output_directory(directory_out) + measurement_service_class, _ = get_all_registered_measurement_info(discovery_client) + if len(measurement_service_class) == 0: + raise click.ClickException("No registered measurements.") - for service_class in measurement_service_class: - 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, - ) + for service_class in measurement_service_class: + 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 = _resolve_output_directory() + directory_out_path = _resolve_output_directory() while True: measurement_service_class, measurement_display_names = get_all_registered_measurement_info( @@ -224,7 +223,7 @@ def _create_clients_interactively() -> None: measurement_service_class=service_class, module_name=module_name, class_name=class_name, - directory_out=directory_out, + directory_out=directory_out_path, ) selection = ( @@ -242,11 +241,14 @@ def _create_clients_interactively() -> None: def _create_clients( - measurement_service_class: List[str], module_name: str, class_name: str, directory_out: str + measurement_service_class: 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 = _resolve_output_directory(directory_out) + directory_out_path = _resolve_output_directory(directory_out) is_multiple_measurement_client_creation = len(measurement_service_class) > 1 for service_class in measurement_service_class: @@ -264,7 +266,7 @@ def _create_clients( measurement_service_class=service_class, module_name=module_name, class_name=class_name, - directory_out=directory_out, + directory_out=directory_out_path, ) From 88a2280e7cc59a9da40b1653dfadfb1d173fd520 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 00:34:48 +0530 Subject: [PATCH 29/42] Packages: Update click-option-group version --- packages/generator/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generator/pyproject.toml b/packages/generator/pyproject.toml index 04c37af8f..8a20313b2 100644 --- a/packages/generator/pyproject.toml +++ b/packages/generator/pyproject.toml @@ -25,7 +25,7 @@ click = ">=8.1.3" grpcio = "^1.49.1" protobuf = "^4.21" black = ">=24.8.0" -click-option-group = "^0.5.6" +click-option-group = ">=0.5.6" # ni-measurement-plugin-sdk-service = {version = "^2.0.0"} [tool.poetry.group.dev.dependencies] From 19bc97d21c467ee6977e468e5d6002da771ababe Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 00:40:17 +0530 Subject: [PATCH 30/42] Client: Update exit prompt message --- .../client/__init__.py | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 5f2a352c0..a49cbd17e 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -194,11 +194,13 @@ def _create_clients_interactively() -> None: print(f"{index}. {display_name}") selection = click.prompt( - "\nSelect a measurement to generate a client", - type=int, + "\nSelect a measurement to generate a client (x to exit)", + type=str, ) + if selection.lower() == "x": + break service_class = _get_selected_measurement_service_class( - selection, measurement_service_class + int(selection), measurement_service_class ) base_service_class = _extract_base_service_class(service_class) @@ -226,19 +228,6 @@ def _create_clients_interactively() -> None: directory_out=directory_out_path, ) - selection = ( - click.prompt( - "\nEnter 'x' to exit or enter any other keys to continue client creation for another measurement", - type=str, - default="", - show_default=False, - ) - .strip() - .lower() - ) - if selection == "x": - break - def _create_clients( measurement_service_class: List[str], From edb8f910f51c93e3767dea5af19b5c135c999bbe Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 00:42:52 +0530 Subject: [PATCH 31/42] Packages: Update lock file --- packages/generator/poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generator/poetry.lock b/packages/generator/poetry.lock index 5e688e9a2..c6dacb904 100644 --- a/packages/generator/poetry.lock +++ b/packages/generator/poetry.lock @@ -1106,4 +1106,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "9124bff8bc949530cbe537972a3b447f167bc39866ee5bfdedff4de9fa53a2c1" +content-hash = "735ab9d7c16ec2994ec1a170d7e36cf817cbae62e5fa6ac7785bb835af058148" From 68ea922ea7c75ae241304e5ff0259a8a05edddf2 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 17:44:20 +0530 Subject: [PATCH 32/42] Client: Update -s option case to kebab case --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index a49cbd17e..075189ae9 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -267,7 +267,7 @@ def _create_clients( ) @optgroup.option( "-s", - "--measurement_service_class", + "--measurement-service-class", help="Creates Python Measurement Plug-In Client for the given measurement services.", multiple=True, ) From c8ea8cc8f9f0bd1b52b6ce5cc609b58272c83d21 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 17:46:23 +0530 Subject: [PATCH 33/42] Client: Rearrange command signature --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 075189ae9..052756e6e 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -305,10 +305,10 @@ def _create_clients( def create_client( measurement_service_class: List[str], all: bool, + interactive: bool, module_name: Optional[str], class_name: Optional[str], directory_out: Optional[str], - interactive: bool, ) -> None: """Generates a Python Measurement Plug-In Client module for the measurement service. From 1c9c5b0fcd44dd5514ea636d33ce4956537a8d02 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 18:13:23 +0530 Subject: [PATCH 34/42] Client: Update create client command docstring --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 052756e6e..073edb91e 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -315,7 +315,7 @@ 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. """ if all: _create_all_clients(directory_out) From 7c848ed6f12d2f943eb3f80ccce2a18bb379722b Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 18:21:35 +0530 Subject: [PATCH 35/42] Client: Update prompt messages --- .../client/__init__.py | 8 ++++---- .../client/_support.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 073edb91e..0c1a8ee07 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -146,7 +146,7 @@ def _create_client( ) print( - f"The measurement plug-in client for the service class '{measurement_service_class}' has been created successfully." + f"The measurement plug-in client for the service class '{measurement_service_class}' is created successfully." ) @@ -206,14 +206,14 @@ def _create_clients_interactively() -> None: 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 choose the default name", + "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 choose the default name", + "Enter a name for the Python client class, or press Enter to use the default name.", type=str, default=default_class_name, ) @@ -281,7 +281,7 @@ def _create_clients( "-i", "--interactive", is_flag=True, - help=("Creates Python Measurement Plug-In Clients interactively."), + help="Creates Python Measurement Plug-In Clients interactively.", ) @optgroup.group( "optional parameters", diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py index 45cd48a8c..fdcc864f8 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py @@ -85,7 +85,7 @@ def get_measurement_service_stub( def get_all_registered_measurement_info( discovery_client: DiscoveryClient, ) -> Tuple[List[str], List[str]]: - """Returns the service classes of all the registered measurement services.""" + """Returns the service classes and display names of all the registered measurement services.""" registered_measurement_services = discovery_client.enumerate_services( _V2_MEASUREMENT_SERVICE_INTERFACE ) From 16dbeaca8321e76b95029161232a52282674e153 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 18:25:09 +0530 Subject: [PATCH 36/42] Refractor; precise variable names --- .../client/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 0c1a8ee07..3f69a18d3 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -230,7 +230,7 @@ def _create_clients_interactively() -> None: def _create_clients( - measurement_service_class: List[str], + measurement_service_classes: List[str], module_name: Optional[str], class_name: Optional[str], directory_out: Optional[str], @@ -239,12 +239,12 @@ def _create_clients( discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) directory_out_path = _resolve_output_directory(directory_out) - is_multiple_measurement_client_creation = len(measurement_service_class) > 1 - for service_class in measurement_service_class: + 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 is_multiple_measurement_client_creation or module_name is None: + if has_multiple_service_classes or module_name is None: module_name = _create_module_name(base_service_class) - if is_multiple_measurement_client_creation or class_name is None: + 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") From 34f4204bcb3a13c0310ed09f50915be5e945fa63 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 18:40:23 +0530 Subject: [PATCH 37/42] Client: Update plural varaible names --- .../client/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 3f69a18d3..01b6541f0 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -155,11 +155,11 @@ def _create_all_clients(directory_out: Optional[str]) -> None: discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) directory_out_path = _resolve_output_directory(directory_out) - measurement_service_class, _ = get_all_registered_measurement_info(discovery_client) - if len(measurement_service_class) == 0: + measurement_service_classes, _ = get_all_registered_measurement_info(discovery_client) + if len(measurement_service_classes) == 0: raise click.ClickException("No registered measurements.") - for service_class in measurement_service_class: + 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) @@ -183,10 +183,10 @@ def _create_clients_interactively() -> None: directory_out_path = _resolve_output_directory() while True: - measurement_service_class, measurement_display_names = get_all_registered_measurement_info( + measurement_service_classes, measurement_display_names = get_all_registered_measurement_info( discovery_client ) - if len(measurement_service_class) == 0: + if len(measurement_service_classes) == 0: raise click.ClickException("No registered measurements.") print("\nList of registered measurements:") @@ -200,7 +200,7 @@ def _create_clients_interactively() -> None: if selection.lower() == "x": break service_class = _get_selected_measurement_service_class( - int(selection), measurement_service_class + int(selection), measurement_service_classes ) base_service_class = _extract_base_service_class(service_class) From dc3b4f0d8965b7bdda953baed208853f7c2583d8 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 18:43:38 +0530 Subject: [PATCH 38/42] Client: Add sub helper methods --- .../client/__init__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 01b6541f0..c7a4faa38 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -100,6 +100,11 @@ def _get_selected_measurement_service_class( return measurement_service_classes[selection - 1] +def _validate_measurement_service_classes(measurement_service_classes: List[str]) -> None: + if len(measurement_service_classes) == 0: + raise click.ClickException("No registered measurements.") + + def _create_client( discovery_client: DiscoveryClient, channel_pool: GrpcChannelPool, @@ -156,8 +161,7 @@ def _create_all_clients(directory_out: Optional[str]) -> None: directory_out_path = _resolve_output_directory(directory_out) measurement_service_classes, _ = get_all_registered_measurement_info(discovery_client) - if len(measurement_service_classes) == 0: - raise click.ClickException("No registered measurements.") + _validate_measurement_service_classes(measurement_service_classes) for service_class in measurement_service_classes: base_service_class = _extract_base_service_class(service_class) @@ -186,8 +190,7 @@ def _create_clients_interactively() -> None: measurement_service_classes, measurement_display_names = get_all_registered_measurement_info( discovery_client ) - if len(measurement_service_classes) == 0: - raise click.ClickException("No registered measurements.") + _validate_measurement_service_classes(measurement_service_classes) print("\nList of registered measurements:") for index, display_name in enumerate(measurement_display_names, start=1): From 42636505c91f74dd7af35bd17e2ad6a7381bac42 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 19:11:40 +0530 Subject: [PATCH 39/42] Client: Moved helper methods to support.py --- .../client/__init__.py | 115 +++++------------- .../client/_support.py | 106 ++++++++++++---- 2 files changed, 115 insertions(+), 106 deletions(-) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index c7a4faa38..5480ea1e4 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -14,16 +14,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_info, - is_python_identifier, - remove_suffix, + get_selected_measurement_service_class, to_ordered_set, + resolve_output_directory, + validate_identifier, + validate_measurement_service_classes, ) @@ -48,63 +52,6 @@ def _create_file( file.write(formatted_output) -def _resolve_output_directory(directory_out: Optional[str] = None) -> pathlib.Path: - if directory_out is None: - directory_out_path = pathlib.Path.cwd() - 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.") - - return directory_out_path - - -def _validate_identifier(name: str, name_type: str) -> None: - if not is_python_identifier(name): - raise click.ClickException( - f"The {name_type} name '{name}' is not a valid Python identifier." - ) - - -def _extract_base_service_class(service_class: str) -> str: - 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." - ) - return base_service_class - - -def _create_module_name(base_service_class: str) -> str: - return camel_to_snake_case(base_service_class) + "_client" - - -def _create_class_name(base_service_class: str) -> str: - return base_service_class.replace("_", "") + "Client" - - -def _get_selected_measurement_service_class( - selection: int, measurement_service_classes: List[str] -) -> str: - if not (1 <= selection <= len(measurement_service_classes)): - raise click.ClickException( - f"Input {selection} is not invalid. Please try again by selecting a valid measurement from the list." - ) - return measurement_service_classes[selection - 1] - - -def _validate_measurement_service_classes(measurement_service_classes: List[str]) -> None: - if len(measurement_service_classes) == 0: - raise click.ClickException("No registered measurements.") - - def _create_client( discovery_client: DiscoveryClient, channel_pool: GrpcChannelPool, @@ -159,16 +106,16 @@ 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) + 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) + 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") + 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, @@ -184,13 +131,13 @@ 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() + directory_out_path = resolve_output_directory() while True: - measurement_service_classes, measurement_display_names = get_all_registered_measurement_info( - discovery_client + measurement_service_classes, measurement_display_names = ( + get_all_registered_measurement_info(discovery_client) ) - _validate_measurement_service_classes(measurement_service_classes) + validate_measurement_service_classes(measurement_service_classes) print("\nList of registered measurements:") for index, display_name in enumerate(measurement_display_names, start=1): @@ -202,25 +149,25 @@ def _create_clients_interactively() -> None: ) if selection.lower() == "x": break - service_class = _get_selected_measurement_service_class( + 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) + 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) + 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") + validate_identifier(class_name, "class") _create_client( channel_pool=channel_pool, @@ -240,17 +187,17 @@ def _create_clients( ) -> None: channel_pool = GrpcChannelPool() discovery_client = DiscoveryClient(grpc_channel_pool=channel_pool) - directory_out_path = _resolve_output_directory(directory_out) + 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) + 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) + 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") + class_name = create_class_name(base_service_class) + validate_identifier(module_name, "module") + validate_identifier(class_name, "class") _create_client( channel_pool=channel_pool, @@ -326,7 +273,7 @@ def create_client( _create_clients_interactively() else: _create_clients( - measurement_service_class=measurement_service_class, + measurement_service_classes=measurement_service_class, module_name=module_name, class_name=class_name, directory_out=directory_out, diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py index fdcc864f8..9370240fc 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/_support.py @@ -2,6 +2,7 @@ import keyword import os +import pathlib import re import sys from typing import AbstractSet, Dict, Iterable, List, Optional, Tuple, TypeVar @@ -240,33 +241,68 @@ def to_ordered_set(values: Iterable[_T]) -> AbstractSet[_T]: return dict.fromkeys(values).keys() -def camel_to_snake_case(camel_case_string: str) -> str: - """Converts a camelCase string to a snake_case string.""" - partial = camel_case_string - for regex in _CAMEL_TO_SNAKE_CASE_REGEXES: - partial = regex.sub(r"\1_\2", partial) +def resolve_output_directory(directory_out: Optional[str] = None) -> pathlib.Path: + """Returns the validated directory output path.""" + if directory_out is None: + directory_out_path = pathlib.Path.cwd() + else: + directory_out_path = pathlib.Path(directory_out) - return partial.lower() + if not directory_out_path.exists(): + raise click.ClickException(f"The specified directory '{directory_out}' was not found.") - -def remove_suffix(string: str) -> str: - """Removes the suffix from the given string.""" - suffixes = ["_Python", "_LabVIEW"] - for suffix in suffixes: - if string.endswith(suffix): - if sys.version_info >= (3, 9): - return string.removesuffix(suffix) - else: - return string[0 : len(string) - len(suffix)] - return string + return directory_out_path -def is_python_identifier(input_string: Optional[str]) -> bool: +def validate_identifier(name: str, name_type: str) -> None: """Validates whether the given string is a valid Python identifier.""" - if input_string is None: - return False - pattern = r"^[a-zA-Z_][a-zA-Z0-9_]*$" - return re.fullmatch(pattern, input_string) is not None + if not _is_python_identifier(name): + raise click.ClickException( + f"The {name_type} name '{name}' is not a valid Python identifier." + ) + + +def extract_base_service_class(service_class: str) -> str: + """Creates a base service class from the measurement service class.""" + 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." + ) + return base_service_class + + +def create_module_name(base_service_class: str) -> str: + """Creates a module name using base service class.""" + return _camel_to_snake_case(base_service_class) + "_client" + + +def create_class_name(base_service_class: str) -> str: + """Creates a class name using base service class.""" + return base_service_class.replace("_", "") + "Client" + + +def get_selected_measurement_service_class( + selection: int, measurement_service_classes: List[str] +) -> str: + """Returns the selected measurement service class.""" + if not (1 <= selection <= len(measurement_service_classes)): + raise click.ClickException( + f"Input {selection} is not invalid. Please try again by selecting a valid measurement from the list." + ) + return measurement_service_classes[selection - 1] + + +def validate_measurement_service_classes(measurement_service_classes: List[str]) -> None: + """Validates whether the given measurement service classes list is empty.""" + if len(measurement_service_classes) == 0: + raise click.ClickException("No registered measurements.") def _get_python_identifier(input_string: str) -> str: @@ -289,3 +325,29 @@ def _get_python_type_as_str(type: Field.Kind.ValueType, is_array: bool) -> str: if is_array: return f"List[{python_type.__name__}]" return python_type.__name__ + + +def _camel_to_snake_case(camel_case_string: str) -> str: + partial = camel_case_string + for regex in _CAMEL_TO_SNAKE_CASE_REGEXES: + partial = regex.sub(r"\1_\2", partial) + + return partial.lower() + + +def _remove_suffix(string: str) -> str: + suffixes = ["_Python", "_LabVIEW"] + for suffix in suffixes: + if string.endswith(suffix): + if sys.version_info >= (3, 9): + return string.removesuffix(suffix) + else: + return string[0 : len(string) - len(suffix)] + return string + + +def _is_python_identifier(input_string: Optional[str]) -> bool: + if input_string is None: + return False + pattern = r"^[a-zA-Z_][a-zA-Z0-9_]*$" + return re.fullmatch(pattern, input_string) is not None From 84b06c02a167887b369a0c4ed9250e5ca760b8cb Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 19:17:30 +0530 Subject: [PATCH 40/42] Tests: update create client command in tests --- packages/generator/tests/acceptance/test_client_generator.py | 2 ++ .../tests/acceptance/test_non_streaming_measurement_client.py | 1 + .../tests/acceptance/test_streaming_measurement_client.py | 1 + 3 files changed, 4 insertions(+) diff --git a/packages/generator/tests/acceptance/test_client_generator.py b/packages/generator/tests/acceptance/test_client_generator.py index a19fcab49..c17a376e3 100644 --- a/packages/generator/tests/acceptance/test_client_generator.py +++ b/packages/generator/tests/acceptance/test_client_generator.py @@ -25,6 +25,7 @@ def test___command_line_args___create_client___render_without_error( with pytest.raises(SystemExit) as exc_info: create_client( [ + "--measurement-service-class", "ni.tests.NonStreamingDataMeasurement_Python", "--module-name", module_name, @@ -82,6 +83,7 @@ def test___command_line_args___create_client___render_with_proper_line_ending( with pytest.raises(SystemExit) as exc_info: create_client( [ + "--measurement-service-class", "ni.tests.NonStreamingDataMeasurement_Python", "--module-name", module_name, diff --git a/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py b/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py index 5d085f366..f2bff1079 100644 --- a/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py +++ b/packages/generator/tests/acceptance/test_non_streaming_measurement_client.py @@ -75,6 +75,7 @@ def measurement_client_directory( with pytest.raises(SystemExit): create_client( [ + "--measurement-service-class", "ni.tests.NonStreamingDataMeasurement_Python", "--module-name", module_name, diff --git a/packages/generator/tests/acceptance/test_streaming_measurement_client.py b/packages/generator/tests/acceptance/test_streaming_measurement_client.py index ad08b084b..059c2ac9b 100644 --- a/packages/generator/tests/acceptance/test_streaming_measurement_client.py +++ b/packages/generator/tests/acceptance/test_streaming_measurement_client.py @@ -61,6 +61,7 @@ def measurement_client_directory( with pytest.raises(SystemExit): create_client( [ + "--measurement-service-class", "ni.tests.StreamingDataMeasurement_Python", "--module-name", module_name, From 310e80632ec267d6bd77e4f9863840d9aa6989e9 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 19:24:18 +0530 Subject: [PATCH 41/42] Test: Update tests --- .../tests/acceptance/test_pin_aware_measurement_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/generator/tests/acceptance/test_pin_aware_measurement_client.py b/packages/generator/tests/acceptance/test_pin_aware_measurement_client.py index 91183a1c6..374fdbd3e 100644 --- a/packages/generator/tests/acceptance/test_pin_aware_measurement_client.py +++ b/packages/generator/tests/acceptance/test_pin_aware_measurement_client.py @@ -159,6 +159,7 @@ def measurement_client_directory( with pytest.raises(SystemExit): create_client( [ + "--measurement-service-class", "ni.tests.PinAwareMeasurement_Python", "--module-name", module_name, From 67dda3c14d7dfb3d40066c242925c98f0f4806b2 Mon Sep 17 00:00:00 2001 From: Mounika Battu Date: Tue, 17 Sep 2024 19:36:44 +0530 Subject: [PATCH 42/42] Client: Add missing required argument --- .../ni_measurement_plugin_sdk_generator/client/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py index 53613d59e..01fc181f0 100644 --- a/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py +++ b/packages/generator/ni_measurement_plugin_sdk_generator/client/__init__.py @@ -104,6 +104,7 @@ def _create_client( 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, ) print(