From 53d9cd0461c9e86467276bd065f59e4ca0f09830 Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Fri, 5 Jun 2026 18:00:23 -0500 Subject: [PATCH 1/5] Add explicit service package exports --- nisystemlink/clients/alarm/__init__.py | 4 +- nisystemlink/clients/artifact/__init__.py | 8 ++- .../clients/assetmanagement/__init__.py | 4 +- nisystemlink/clients/dataframe/__init__.py | 4 +- nisystemlink/clients/feeds/__init__.py | 4 +- nisystemlink/clients/file/__init__.py | 4 +- nisystemlink/clients/notebook/__init__.py | 4 +- nisystemlink/clients/notification/__init__.py | 4 +- nisystemlink/clients/product/__init__.py | 4 +- nisystemlink/clients/spec/__init__.py | 4 +- nisystemlink/clients/systems/__init__.py | 4 +- nisystemlink/clients/tag/__init__.py | 42 ++++++++--- nisystemlink/clients/test_plan/__init__.py | 4 +- nisystemlink/clients/testmonitor/__init__.py | 4 +- nisystemlink/clients/work_item/__init__.py | 4 +- tests/test_service_package_exports.py | 72 +++++++++++++++++++ 16 files changed, 147 insertions(+), 27 deletions(-) create mode 100644 tests/test_service_package_exports.py diff --git a/nisystemlink/clients/alarm/__init__.py b/nisystemlink/clients/alarm/__init__.py index daa177e9..bfbfcaa8 100644 --- a/nisystemlink/clients/alarm/__init__.py +++ b/nisystemlink/clients/alarm/__init__.py @@ -1,3 +1,5 @@ +"""Start here with AlarmClient for alarm management.""" + from nisystemlink.clients.alarm._alarm_client import AlarmClient -# flake8: noqa +__all__ = ["AlarmClient"] diff --git a/nisystemlink/clients/artifact/__init__.py b/nisystemlink/clients/artifact/__init__.py index 2f507a2f..d7dcebae 100644 --- a/nisystemlink/clients/artifact/__init__.py +++ b/nisystemlink/clients/artifact/__init__.py @@ -1,3 +1,5 @@ -from ._artifact_client import ArtifactClient - -# flake8: noqa +"""Start here with ArtifactClient for artifact management.""" + +from ._artifact_client import ArtifactClient + +__all__ = ["ArtifactClient"] diff --git a/nisystemlink/clients/assetmanagement/__init__.py b/nisystemlink/clients/assetmanagement/__init__.py index 3b415a1b..ac53c7b7 100644 --- a/nisystemlink/clients/assetmanagement/__init__.py +++ b/nisystemlink/clients/assetmanagement/__init__.py @@ -1,5 +1,7 @@ +"""Start here with AssetManagementClient for asset management.""" + from nisystemlink.clients.assetmanagement._asset_management_client import ( AssetManagementClient, ) -# flake8: noqa +__all__ = ["AssetManagementClient"] diff --git a/nisystemlink/clients/dataframe/__init__.py b/nisystemlink/clients/dataframe/__init__.py index cf553adb..5aef89b9 100644 --- a/nisystemlink/clients/dataframe/__init__.py +++ b/nisystemlink/clients/dataframe/__init__.py @@ -1,3 +1,5 @@ +"""Start here with DataFrameClient for dataframe operations.""" + from ._data_frame_client import DataFrameClient -# flake8: noqa +__all__ = ["DataFrameClient"] diff --git a/nisystemlink/clients/feeds/__init__.py b/nisystemlink/clients/feeds/__init__.py index c5da55bd..cc5561b9 100644 --- a/nisystemlink/clients/feeds/__init__.py +++ b/nisystemlink/clients/feeds/__init__.py @@ -1,3 +1,5 @@ +"""Start here with FeedsClient for feed management.""" + from ._feeds_client import FeedsClient -# flake8: noqa +__all__ = ["FeedsClient"] diff --git a/nisystemlink/clients/file/__init__.py b/nisystemlink/clients/file/__init__.py index 6398a061..7c2f29ba 100644 --- a/nisystemlink/clients/file/__init__.py +++ b/nisystemlink/clients/file/__init__.py @@ -1,3 +1,5 @@ +"""Start here with FileClient for file operations.""" + from ._file_client import FileClient -# flake8: noqa +__all__ = ["FileClient"] diff --git a/nisystemlink/clients/notebook/__init__.py b/nisystemlink/clients/notebook/__init__.py index ee625b42..15e53082 100644 --- a/nisystemlink/clients/notebook/__init__.py +++ b/nisystemlink/clients/notebook/__init__.py @@ -1,3 +1,5 @@ +"""Start here with NotebookClient for notebook management.""" + from ._notebook_client import NotebookClient -# flake8: noqa +__all__ = ["NotebookClient"] diff --git a/nisystemlink/clients/notification/__init__.py b/nisystemlink/clients/notification/__init__.py index 93aa4ea6..62d482bc 100644 --- a/nisystemlink/clients/notification/__init__.py +++ b/nisystemlink/clients/notification/__init__.py @@ -1,3 +1,5 @@ +"""Start here with NotificationClient for notification management.""" + from ._notification_client import NotificationClient -# flake8: noqa +__all__ = ["NotificationClient"] diff --git a/nisystemlink/clients/product/__init__.py b/nisystemlink/clients/product/__init__.py index 7b357f5d..469999f4 100644 --- a/nisystemlink/clients/product/__init__.py +++ b/nisystemlink/clients/product/__init__.py @@ -1,3 +1,5 @@ +"""Start here with ProductClient for product management.""" + from ._product_client import ProductClient -# flake8: noqa +__all__ = ["ProductClient"] diff --git a/nisystemlink/clients/spec/__init__.py b/nisystemlink/clients/spec/__init__.py index 3f1f0074..16a9d4e8 100644 --- a/nisystemlink/clients/spec/__init__.py +++ b/nisystemlink/clients/spec/__init__.py @@ -1,3 +1,5 @@ +"""Start here with SpecClient for specification management.""" + from ._spec_client import SpecClient -# flake8: noqa +__all__ = ["SpecClient"] diff --git a/nisystemlink/clients/systems/__init__.py b/nisystemlink/clients/systems/__init__.py index 0e88f3ca..12e9108a 100644 --- a/nisystemlink/clients/systems/__init__.py +++ b/nisystemlink/clients/systems/__init__.py @@ -1,3 +1,5 @@ +"""Start here with SystemsClient for system management.""" + from nisystemlink.clients.systems._systems_client import SystemsClient -# flake8: noqa +__all__ = ["SystemsClient"] diff --git a/nisystemlink/clients/tag/__init__.py b/nisystemlink/clients/tag/__init__.py index f3b2b112..e77024f1 100644 --- a/nisystemlink/clients/tag/__init__.py +++ b/nisystemlink/clients/tag/__init__.py @@ -1,21 +1,41 @@ # -*- coding: utf-8 -*- -from ._data_type import DataType -from ._retention_type import RetentionType -from ._tag_data import TagData -from ._tag_with_aggregates import TagWithAggregates +"""Start here with TagManager for tag operations and helper types.""" + from ._async_tag_query_result_collection import AsyncTagQueryResultCollection +from ._buffered_tag_writer import BufferedTagWriter +from ._data_type import DataType from ._itag_reader import ITagReader from ._itag_writer import ITagWriter -from ._buffered_tag_writer import BufferedTagWriter -from ._tag_value_reader import TagValueReader -from ._tag_value_writer import TagValueWriter -from ._tag_update_fields import TagUpdateFields +from ._retention_type import RetentionType +from ._tag_data import TagData from ._tag_data_update import TagDataUpdate +from ._tag_manager import TagManager from ._tag_path_utilities import TagPathUtilities from ._tag_query_result_collection import TagQueryResultCollection -from ._tag_subscription import TagSubscription from ._tag_selection import TagSelection -from ._tag_manager import TagManager +from ._tag_subscription import TagSubscription +from ._tag_update_fields import TagUpdateFields +from ._tag_value_reader import TagValueReader +from ._tag_value_writer import TagValueWriter +from ._tag_with_aggregates import TagWithAggregates -# flake8: noqa +__all__ = [ + "DataType", + "RetentionType", + "TagData", + "TagWithAggregates", + "AsyncTagQueryResultCollection", + "ITagReader", + "ITagWriter", + "BufferedTagWriter", + "TagValueReader", + "TagValueWriter", + "TagUpdateFields", + "TagDataUpdate", + "TagPathUtilities", + "TagQueryResultCollection", + "TagSubscription", + "TagSelection", + "TagManager", +] diff --git a/nisystemlink/clients/test_plan/__init__.py b/nisystemlink/clients/test_plan/__init__.py index 013b65cb..f51497c9 100644 --- a/nisystemlink/clients/test_plan/__init__.py +++ b/nisystemlink/clients/test_plan/__init__.py @@ -1,3 +1,5 @@ +"""Start here with TestPlanClient for test plan management.""" + from ._test_plan_client import TestPlanClient -# flake8: noqa +__all__ = ["TestPlanClient"] diff --git a/nisystemlink/clients/testmonitor/__init__.py b/nisystemlink/clients/testmonitor/__init__.py index 827fc6a4..078062a6 100644 --- a/nisystemlink/clients/testmonitor/__init__.py +++ b/nisystemlink/clients/testmonitor/__init__.py @@ -1,3 +1,5 @@ +"""Start here with TestMonitorClient for test monitor operations.""" + from ._test_monitor_client import TestMonitorClient -# flake8: noqa +__all__ = ["TestMonitorClient"] diff --git a/nisystemlink/clients/work_item/__init__.py b/nisystemlink/clients/work_item/__init__.py index d190f619..850d74e0 100644 --- a/nisystemlink/clients/work_item/__init__.py +++ b/nisystemlink/clients/work_item/__init__.py @@ -1,3 +1,5 @@ +"""Start here with WorkItemClient for work item operations.""" + from ._work_item_client import WorkItemClient, WorkItemExecuteApiException -# flake8: noqa +__all__ = ["WorkItemClient", "WorkItemExecuteApiException"] diff --git a/tests/test_service_package_exports.py b/tests/test_service_package_exports.py new file mode 100644 index 00000000..73996390 --- /dev/null +++ b/tests/test_service_package_exports.py @@ -0,0 +1,72 @@ +"""Validate the public surface declared by each service package.""" + +import ast +import unittest +from pathlib import Path + +PACKAGE_EXPORTS = { + "alarm": ["AlarmClient"], + "artifact": ["ArtifactClient"], + "assetmanagement": ["AssetManagementClient"], + "dataframe": ["DataFrameClient"], + "feeds": ["FeedsClient"], + "file": ["FileClient"], + "notebook": ["NotebookClient"], + "notification": ["NotificationClient"], + "product": ["ProductClient"], + "spec": ["SpecClient"], + "systems": ["SystemsClient"], + "tag": [ + "DataType", + "RetentionType", + "TagData", + "TagWithAggregates", + "AsyncTagQueryResultCollection", + "ITagReader", + "ITagWriter", + "BufferedTagWriter", + "TagValueReader", + "TagValueWriter", + "TagUpdateFields", + "TagDataUpdate", + "TagPathUtilities", + "TagQueryResultCollection", + "TagSubscription", + "TagSelection", + "TagManager", + ], + "test_plan": ["TestPlanClient"], + "testmonitor": ["TestMonitorClient"], + "work_item": ["WorkItemClient", "WorkItemExecuteApiException"], +} + + +class TestServicePackageExports(unittest.TestCase): + """Verify service package docstrings and explicit export lists.""" + + def test_service_package_exports_are_explicit(self): + """Each service package should document and declare its public exports.""" + package_root = Path(__file__).resolve().parents[1] / "nisystemlink" / "clients" + + for package_name, exported_names in PACKAGE_EXPORTS.items(): + with self.subTest(package_name=package_name): + module_path = package_root / package_name / "__init__.py" + module = ast.parse(module_path.read_text(encoding="utf-8")) + imported_names = [] + declared_exports = None + module_docstring = ast.get_docstring(module) + + self.assertIsNotNone(module_docstring) + assert module_docstring is not None + self.assertTrue(module_docstring.startswith("Start here with ")) + + for node in module.body: + if isinstance(node, ast.ImportFrom): + imported_names.extend(alias.name for alias in node.names) + elif isinstance(node, ast.Assign): + for target in node.targets: + if isinstance(target, ast.Name) and target.id == "__all__": + declared_exports = ast.literal_eval(node.value) + + self.assertEqual(declared_exports, exported_names) + self.assertCountEqual(imported_names, exported_names) From 71081bdf61675c04e5d563d134b326d62c2e8f43 Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Fri, 5 Jun 2026 18:04:39 -0500 Subject: [PATCH 2/5] Fix tag package import order --- nisystemlink/clients/tag/__init__.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/nisystemlink/clients/tag/__init__.py b/nisystemlink/clients/tag/__init__.py index e77024f1..c3f8afbc 100644 --- a/nisystemlink/clients/tag/__init__.py +++ b/nisystemlink/clients/tag/__init__.py @@ -2,23 +2,23 @@ """Start here with TagManager for tag operations and helper types.""" -from ._async_tag_query_result_collection import AsyncTagQueryResultCollection -from ._buffered_tag_writer import BufferedTagWriter from ._data_type import DataType -from ._itag_reader import ITagReader -from ._itag_writer import ITagWriter from ._retention_type import RetentionType from ._tag_data import TagData -from ._tag_data_update import TagDataUpdate -from ._tag_manager import TagManager +from ._tag_with_aggregates import TagWithAggregates # noqa: I100 +from ._async_tag_query_result_collection import AsyncTagQueryResultCollection # noqa: I100 +from ._itag_reader import ITagReader +from ._itag_writer import ITagWriter +from ._buffered_tag_writer import BufferedTagWriter # noqa: I100 +from ._tag_value_reader import TagValueReader +from ._tag_value_writer import TagValueWriter +from ._tag_update_fields import TagUpdateFields # noqa: I100 +from ._tag_data_update import TagDataUpdate # noqa: I100 from ._tag_path_utilities import TagPathUtilities from ._tag_query_result_collection import TagQueryResultCollection -from ._tag_selection import TagSelection from ._tag_subscription import TagSubscription -from ._tag_update_fields import TagUpdateFields -from ._tag_value_reader import TagValueReader -from ._tag_value_writer import TagValueWriter -from ._tag_with_aggregates import TagWithAggregates +from ._tag_selection import TagSelection # noqa: I100 +from ._tag_manager import TagManager # noqa: I100 __all__ = [ "DataType", From 0934bdc5accd40b48343965334a42e77b9c4e905 Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Fri, 5 Jun 2026 18:06:47 -0500 Subject: [PATCH 3/5] lint fix --- nisystemlink/clients/tag/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nisystemlink/clients/tag/__init__.py b/nisystemlink/clients/tag/__init__.py index c3f8afbc..2ad19618 100644 --- a/nisystemlink/clients/tag/__init__.py +++ b/nisystemlink/clients/tag/__init__.py @@ -6,7 +6,9 @@ from ._retention_type import RetentionType from ._tag_data import TagData from ._tag_with_aggregates import TagWithAggregates # noqa: I100 -from ._async_tag_query_result_collection import AsyncTagQueryResultCollection # noqa: I100 +from ._async_tag_query_result_collection import ( + AsyncTagQueryResultCollection, +) # noqa: I100 from ._itag_reader import ITagReader from ._itag_writer import ITagWriter from ._buffered_tag_writer import BufferedTagWriter # noqa: I100 From 85e6d581a67b0ce46fd6303f99056b6b7d16d4f3 Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Fri, 5 Jun 2026 20:26:18 -0500 Subject: [PATCH 4/5] Fix tag import lint suppression --- nisystemlink/clients/tag/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nisystemlink/clients/tag/__init__.py b/nisystemlink/clients/tag/__init__.py index 2ad19618..56bd4ef8 100644 --- a/nisystemlink/clients/tag/__init__.py +++ b/nisystemlink/clients/tag/__init__.py @@ -6,9 +6,9 @@ from ._retention_type import RetentionType from ._tag_data import TagData from ._tag_with_aggregates import TagWithAggregates # noqa: I100 -from ._async_tag_query_result_collection import ( +from ._async_tag_query_result_collection import ( # noqa: I100 AsyncTagQueryResultCollection, -) # noqa: I100 +) from ._itag_reader import ITagReader from ._itag_writer import ITagWriter from ._buffered_tag_writer import BufferedTagWriter # noqa: I100 From 279ec798bc3e2926ced6cc94c4d8f365547decf7 Mon Sep 17 00:00:00 2001 From: Fred Visser <1458528+fredvisser@users.noreply.github.com> Date: Mon, 8 Jun 2026 08:38:04 -0500 Subject: [PATCH 5/5] Address review feedback in export test --- tests/test_service_package_exports.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_service_package_exports.py b/tests/test_service_package_exports.py index 73996390..7d36a041 100644 --- a/tests/test_service_package_exports.py +++ b/tests/test_service_package_exports.py @@ -57,7 +57,8 @@ def test_service_package_exports_are_explicit(self): module_docstring = ast.get_docstring(module) self.assertIsNotNone(module_docstring) - assert module_docstring is not None + if module_docstring is None: + self.fail(f"Missing module docstring in {module_path}") self.assertTrue(module_docstring.startswith("Start here with ")) for node in module.body: