Repository navigation
Add high-level Python wrapper for the MDS V2 Data Store API - #196
Open
sudharshana-ni wants to merge 11 commits into
Open
sudharshana-ni wants to merge 11 commits into
sudharshana-ni wants to merge 11 commits into
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The promised metadata unlink safeguard is missing, and inline UUT updates supported by the pinned API are rejected.
4 open issues
What changed in this PR
Adds ni.datastore.data.v2, a high-level Python interface to the V2 Data Store service alongside the existing V1 API.
Changes:
- Introduces resource models, value conversion, and client methods for creation, queries, updates, metadata links, and deletion.
- Adds unit tests, API documentation, and V2 script and notebook examples.
- Adds V2 dependencies and includes the overview example in CI.
| File | Description |
|---|---|
| tests/unit/data/test_v2_types.py | Tests model conversion and API coverage. |
| tests/unit/data/test_v2_data_store_client.py | Tests requests, validation, and client lifecycle. |
| src/ni/datastore/data/v2/_types/_test_result.py | Adds the test-result model. |
| src/ni/datastore/data/v2/_types/_step.py | Adds steps with nested resources. |
| src/ni/datastore/data/v2/_types/_status.py | Defines lifecycle statuses. |
| src/ni/datastore/data/v2/_types/_query_results.py | Adds query results with continuation links. |
| src/ni/datastore/data/v2/_types/_outcome.py | Defines terminal outcomes. |
| src/ni/datastore/data/v2/_types/_measurement.py | Adds measurements, values, and limits. |
| src/ni/datastore/data/v2/_types/_json_value.py | Adds structured JSON values and attributes. |
| src/ni/datastore/data/v2/_types/_error_information.py | Adds error-detail conversion. |
| src/ni/datastore/data/v2/_types/_condition.py | Adds step-owned conditions. |
| src/ni/datastore/data/v2/_types/__init__.py | Initializes the model package. |
| src/ni/datastore/data/v2/_grpc_conversion.py | Converts Python values to and from protobuf. |
| src/ni/datastore/data/v2/_data_store_client.py | Implements the high-level V2 client. |
| src/ni/datastore/data/v2/__init__.py | Exports the public V2 API. |
| pyproject.toml | Adds Git-sourced V2 dependencies. |
| poetry.lock | Locks V2 dependencies and updates protobuf types. |
| examples/overview-v2/src/overview.py | Demonstrates publishing and querying waveforms. |
| examples/overview-v2/src/__init__.py | Initializes the example package. |
| examples/overview-v2/README.md | Documents setup and execution. |
| examples/overview-v2/pyproject.toml | Configures example dependencies and entry point. |
| examples/overview-v2/poetry.toml | Configures an in-project virtual environment. |
| examples/notebooks/v2/update-delete/update_delete.ipynb | Demonstrates updates, metadata links, and deletion. |
| examples/notebooks/v2/structured-extensions/structured_extensions.ipynb | Demonstrates structured extensions. |
| examples/notebooks/v2/README.md | Documents notebook setup and contents. |
| examples/notebooks/v2/query/query_measurements.ipynb | Demonstrates OData queries. |
| examples/notebooks/v2/query/publish_sample_data.ipynb | Creates sample query data. |
| examples/notebooks/v2/publish/batch_create.ipynb | Demonstrates atomic and nested creation. |
| examples/notebooks/v2/overview/publish_measurement.ipynb | Introduces the V2 publishing workflow. |
| examples/notebooks/v2/measurement-values/measurement_values.ipynb | Demonstrates supported values and limits. |
| docs/reference/ni-data-store-v2.md | Documents V2 usage and V1 differences. |
| docs/reference/index.rst | Links the V2 reference page. |
| .github/workflows/check_examples.yml | Adds the V2 overview to example checks. |
Review effort: Balanced
mjohanse-emr
requested changes
Oct 27, 2026
mjohanse-emr
requested changes
Oct 27, 2026
mjohanse-emr
left a comment
Contributor
There was a problem hiding this comment.
I still need to review the documentation and workflow changes, but I'm done with the v2 directory and tests.
mjohanse-emr
requested changes
Oct 27, 2026
mjohanse-emr
left a comment
Contributor
There was a problem hiding this comment.
Ok, I think I'm done with the first round. This is a big PR and I expect some back and forth. Feel free to reply with specific questions, even before resetting my vote.
mjohanse-emr
approved these changes
Oct 29, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What does this Pull Request accomplish?
Adds
ni.datastore.data.v2, a high-level Python wrapper around the V2 low-level client (ni.measurements.data.v2.client), plus V2 examples. Callers use Python objects and values instead of building protobuf requests and field masks.DataStoreClientwraps every method of the V2 low-level client:create_test_result,create_step,create_measurement(with limits, extension, and inline software/hardware/test adapter items, which must already be linked to the test result).batch_create_test_results,batch_create_steps,batch_create_measurements. Test results can contain nested steps, and steps can contain child steps, conditions, and measurements. Each batch is atomic.get_*,query_*(OData, optionalread_fields) andread_measurement_values(value plus upper and lower limits).update_*andbatch_update_*. Update masks are built from keyword arguments;Noneclears a field. Unknown or read-only fields raiseValueErrorbefore any RPC.add_condition(adds or replaces by name) andremove_condition.add/remove_test_result_metadataandadd/remove_measurement_metadata. Following DEC-017,remove_test_result_metadatafirst checks the test result's measurements. If any still references an item being unlinked, it raisesValueErrorand nothing is removed; callers unlink the item from those measurements first.delete_*andbatch_delete_*, with the service'sforceoption for test results and steps.Models (V1 style):
TestResult,Step,Measurement,Condition,ErrorInformation,Outcome,Status,JsonValue, andQueryResults.Values:
bool,int,float,str,Scalar,Vector, lists of scalars, analog/complex/digital waveforms,Spectrum,XYData, and JSON dictionaries.Scalar(with or without units), and a list as aVector.Scalaris given withoutunits, the measurement'sunitsare taken from theScalar, asConditionalready does.hightime.datetime, durations arehightime.timedelta, and extensions are dictionaries.Forward-compatible fields:
keywords,duration,uut_id,comparison_type, JSON values, andschema_idare included in the API. The service does not store them yet (New Resource Fields and Structured JSON features).Documentation:
docs/reference/ni-data-store-v2.md, linked from the reference index. It covers V1 differences, the DEC-017 unlink order, the status/outcome rule, the gRPC 4 MB receive limit for user-supplied channels, and waveform timestamp comparison.Examples (V1 examples are unchanged):
examples/overview-v2/: a standalone script project, the V2 companion ofexamples/overview/. Added to thecheck_examplesCI matrix.examples/notebooks/v2/: notebooks for overview, sample data and queries, batch create, measurement values and limits, update and delete (including the DEC-017 unlink order), and structured extensions.Dependency note: The V2 client and proto packages currently come from the
users/sudharshana/add-v2-proto-and-client-packagebranch ofni-apis-python, locked to commit28aee05in bothpoetry.lockandexamples/overview-v2/poetry.lock. They will be switched to the published packages once that PR is merged and released. No generated protobuf code is copied into this repository. Relocking also updatedni-protobuf-typesfrom1.3.0.dev0to1.3.0.Why should this Pull Request be merged?
nitypesvalues.What testing has been done?
Unit tests: 331 passed, 114 of them for V2. They cover model/protobuf round trips, request and update-mask construction, value conversions (including Scalar reads and units), validation, the DEC-017 unlink check, batch helpers, and client lifecycle.
Static checks:
ni-python-styleguide lint, strictmypy,pyright, andblackpass for the package and forexamples/overview-v2. The Read the Docs build passes.Validation against a local datastore-service in V2 mode: an independent validation script, written separately from the unit tests, ran 199 checks against a locally started service with a fresh SQLite store. 177 passed. All 22 failures are service features not built yet, rules working as designed, or the service behavior listed under known gaps; none is a wrapper defect.
Revisions tested:
ni-measurements-data-v2-client/-proto1.0.0.dev0,ni-apis-pythoncommit28aee05.NI.Measurements.Data.V1.DataStoreServicebuilt from commitd5a85122(product version2027 Q1+d5a8512), started withNIDataStore_ApiMode=v2and SQLite storage.ni-measurements-metadata-v1-client1.0.0 (V2 reuses the V1 Metadata API).How it was run:
NIDataStore_ApiMode=v2, a local HTTP/2 port, andV2DataStoreSettings__*paths pointing to a fresh folder.DataStoreClientandMetadataStoreClientwere connected withgrpc.insecure_channel("[::1]:<port>").poetry run python validate_v2.py. Checks that must fail locally ran against an unused port, to prove they fail before any RPC.Scenarios covered:
Vectorlimits, andread_fieldsmasks.NOT_FOUND.$filteron Id, Name (eq/contains/startswith), Outcome, Status, TestResultId, StepId, OperatorId, andConditions/any(...).$orderby,$top,$skip, andread_fieldswork. Invalid OData,$select, and unknown properties returnINVALID_ARGUMENT.None; atomic batch updates. LocalValueErrorfor empty or non-updatable fields.NOT_FOUNDfor a missing condition or step.force. Withoutforce,FAILED_PRECONDITIONis returned and nothing is deleted. A step with child steps can't be deleted. A force list with the wrong length raisesValueErrorlocally.NOT_FOUNDfor unknown IDs andINVALID_ARGUMENTfor malformed IDs. Unsupported values, ints over 32 bits, and non-UTC datetimes fail locally. Use afterclose()raisesRuntimeError. A stopped service returnsUNAVAILABLE.After the review fixes, all 7 V2 notebooks (run with
nbconvert) and theoverview-v2script were re-run successfully against the same service. Additional live checks:RESOURCE_EXHAUSTEDon a plain channel.remove_test_result_metadataraisesValueErrorand leaves both links in place while a measurement references the item. It removes an unused item normally, and succeeds after the item is unlinked from the measurements first.Known service gaps (the wrapper already sends and reads these fields):
keywords,duration,uut_id, andcomparison_typeare not stored yet, and are rejected on update.created_at/updated_at/created_by/updated_by,Step.path/path_ids,Measurement.value_type.next_linkwill be replaced bynext_page_tokenwithpage_size/page_token, and the wrapper'squery_*methods will be updated when it lands.schema_idis rejected with a generic "invalid fields" error (DEC-033 not decided).