Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ Submodules
.. toctree::
:maxdepth: 1

/autoapi/ni/measurementlink/discovery/v1/annotations/index
/autoapi/ni/measurementlink/discovery/v1/discovery_service_pb2/index
/autoapi/ni/measurementlink/discovery/v1/discovery_service_pb2_grpc/index
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Constants for discovery service annotations."""

SERVICE_COLLECTION_KEY = "ni/service.collection"
"""Key for an annotation indicating the hierarchical collection the service belongs to."""

SERVICE_DESCRIPTION_KEY = "ni/service.description"
"""Key for an annotation containing a description of the service."""

SERVICE_PROGRAMMINGLANGUAGE_KEY = "ni/service.programminglanguage"
"""Key for an annotation indicating the programming language in which the service is implemented."""

SERVICE_TAGS_KEY = "ni/service.tags"
"""Key for an annotation containing the tags for the service."""
17 changes: 16 additions & 1 deletion tools/grpc_generator/src/grpc_generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,15 @@ def reset_python_package(generation_spec: GenerationSpec) -> None:

match generation_spec.output_format:
case OutputFormat.SUBPACKAGE:
shutil.rmtree(generation_spec.package_folder)
# Only remove generated subpackage dirs.
# This allows for non-generated "mixin" subpackages.
dirs_to_remove = []
for subpackage_dir in generation_spec.package_folder.iterdir():
if is_generated_subpackage_dir(subpackage_dir):
dirs_to_remove.append(subpackage_dir)

for dir_to_remove in dirs_to_remove:
shutil.rmtree(dir_to_remove)
case OutputFormat.SUBMODULE:
grpc_files = sorted(generation_spec.package_folder.glob("*_pb2.py*"))
grpc_files.extend(generation_spec.package_folder.glob("*_pb2_grpc.py*"))
Expand Down Expand Up @@ -232,3 +240,10 @@ def invoke_protoc(protoc_arguments: list[str]) -> None:
raise click.ClickException(
click.style(f"protoc exited with error code {exit_code}", "bright_magenta")
)


def is_generated_subpackage_dir(candidate: pathlib.Path) -> bool:
"""Determine if the input path is named like a generated subpackage dir."""
if not candidate.is_dir():
return False
return candidate.name.endswith("_pb2") or candidate.name.endswith("_pb2_grpc")
71 changes: 68 additions & 3 deletions tools/grpc_generator/tests/unit/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ def assert_is_subpackage(output_path: pathlib.Path) -> None:
assert not output_path.joinpath("py.typed").exists()
assert all(entry.is_dir() for entry in output_path.iterdir())
for entry in output_path.iterdir():
assert entry.joinpath("__init__.py").exists()
assert entry.joinpath("__init__.pyi").exists()
assert entry.joinpath("py.typed").exists()
# Assumption: Any subpackage dir that does not end in _pb2
# contains handwritten mixin files we want to keep and won't
# have the same format as a generated subpackage dir.
if generator.is_generated_subpackage_dir(entry):
assert entry.joinpath("__init__.py").exists()
assert entry.joinpath("__init__.pyi").exists()
assert entry.joinpath("py.typed").exists()


def test___generator___call_generator_help___succeeds() -> None:
Expand Down Expand Up @@ -134,3 +138,64 @@ def test___existing_package___generate_submodules___updates_submodules(
assert not previous_api_file.exists()
for support_file in support_files:
assert support_file.exists(), "Support file incorrectly deleted!"


def test___existing_package___generate_subpackages___updates_subpackages(
tmp_path: pathlib.Path,
) -> None:
# Add files to a separate subpackage that aren't generated files.
output_folder = tmp_path.joinpath("ni/protobuf/types")
mixin_folder = output_folder.joinpath("mixin")
mixin_folder.mkdir(parents=True, exist_ok=True)
support_files = [
mixin_folder.joinpath("helper.py"),
mixin_folder.joinpath("converter.py"),
]
for support_file in support_files:
support_file.touch()

result = call_generate(
[
"--output-basepath",
f"{tmp_path!s}",
"--output-format",
generator.OutputFormat.SUBPACKAGE.value,
"--proto-subpath",
"ni/protobuf/types",
]
)
assert result.exit_code == 0
assert_is_subpackage(output_folder)
for support_file in support_files:
assert support_file.exists(), "Support file incorrectly deleted!"

# Mimic an API change by adding "previous" generated subpackage dirs
previous_pb2_dir = output_folder.joinpath("prev_dir_pb2")
previous_pb2_dir.mkdir(parents=True, exist_ok=True)
previous_pb2_grpc_dir = output_folder.joinpath("prev_dir_pb2_grpc")
previous_pb2_grpc_dir.mkdir(parents=True, exist_ok=True)
previous_api_files = [
previous_pb2_dir.joinpath("__init__.py"),
previous_pb2_dir.joinpath("__init__.pyi"),
previous_pb2_dir.joinpath("py.typed"),
previous_pb2_grpc_dir.joinpath("__init__.py"),
previous_pb2_grpc_dir.joinpath("__init__.pyi"),
previous_pb2_grpc_dir.joinpath("py.typed"),
]
for previous_api_file in previous_api_files:
previous_api_file.touch()

result = call_generate(
[
"--output-basepath",
f"{tmp_path!s}",
"--output-format",
generator.OutputFormat.SUBPACKAGE.value,
"--proto-subpath",
"ni/protobuf/types",
]
)
assert result.exit_code == 0
assert_is_subpackage(output_folder)
assert not previous_pb2_dir.exists(), "Previous subpackage dir not deleted correctly!"
assert not previous_pb2_grpc_dir.exists(), "Previous subpackage dir not deleted correctly!"
Loading