py-teststand 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py_teststand-0.1.0/.gitignore +37 -0
- py_teststand-0.1.0/.python-version +1 -0
- py_teststand-0.1.0/LICENSE +23 -0
- py_teststand-0.1.0/PKG-INFO +160 -0
- py_teststand-0.1.0/README.md +121 -0
- py_teststand-0.1.0/examples/build_sequence.py +87 -0
- py_teststand-0.1.0/examples/create_variables.py +108 -0
- py_teststand-0.1.0/examples/execute_subsequence.py +58 -0
- py_teststand-0.1.0/examples/insert_step.py +125 -0
- py_teststand-0.1.0/examples/update_station_options.py +75 -0
- py_teststand-0.1.0/pyproject.toml +136 -0
- py_teststand-0.1.0/src/py_teststand/__init__.py +289 -0
- py_teststand-0.1.0/src/py_teststand/adapters/__init__.py +37 -0
- py_teststand-0.1.0/src/py_teststand/adapters/activex.py +657 -0
- py_teststand-0.1.0/src/py_teststand/adapters/adapter.py +695 -0
- py_teststand-0.1.0/src/py_teststand/adapters/cvi.py +290 -0
- py_teststand-0.1.0/src/py_teststand/adapters/dll.py +476 -0
- py_teststand-0.1.0/src/py_teststand/adapters/dotnet.py +987 -0
- py_teststand-0.1.0/src/py_teststand/adapters/htbasic.py +142 -0
- py_teststand-0.1.0/src/py_teststand/adapters/labview.py +1553 -0
- py_teststand-0.1.0/src/py_teststand/adapters/labview_nxg.py +835 -0
- py_teststand-0.1.0/src/py_teststand/adapters/python.py +484 -0
- py_teststand-0.1.0/src/py_teststand/adapters/sequence.py +381 -0
- py_teststand-0.1.0/src/py_teststand/analyzer/__init__.py +49 -0
- py_teststand-0.1.0/src/py_teststand/analyzer/analysis_context.py +165 -0
- py_teststand-0.1.0/src/py_teststand/analyzer/analysis_message.py +34 -0
- py_teststand-0.1.0/src/py_teststand/analyzer/analysis_utilities.py +88 -0
- py_teststand-0.1.0/src/py_teststand/analyzer/rule.py +112 -0
- py_teststand-0.1.0/src/py_teststand/analyzer/rule_configuration.py +78 -0
- py_teststand-0.1.0/src/py_teststand/analyzer/rule_setting_values.py +21 -0
- py_teststand-0.1.0/src/py_teststand/analyzer/types.py +0 -0
- py_teststand-0.1.0/src/py_teststand/core/__init__.py +1 -0
- py_teststand-0.1.0/src/py_teststand/core/com_wrapper.py +213 -0
- py_teststand-0.1.0/src/py_teststand/core/engine.py +3787 -0
- py_teststand-0.1.0/src/py_teststand/core/exceptions.py +292 -0
- py_teststand-0.1.0/src/py_teststand/core/external_report_viewers.py +99 -0
- py_teststand-0.1.0/src/py_teststand/core/file_information.py +106 -0
- py_teststand-0.1.0/src/py_teststand/core/images.py +33 -0
- py_teststand-0.1.0/src/py_teststand/core/search.py +169 -0
- py_teststand-0.1.0/src/py_teststand/core/utility.py +17 -0
- py_teststand-0.1.0/src/py_teststand/execution/__init__.py +1 -0
- py_teststand-0.1.0/src/py_teststand/execution/additional_results.py +188 -0
- py_teststand-0.1.0/src/py_teststand/execution/breakpoint.py +36 -0
- py_teststand-0.1.0/src/py_teststand/execution/database_options.py +116 -0
- py_teststand-0.1.0/src/py_teststand/execution/edit_args.py +63 -0
- py_teststand-0.1.0/src/py_teststand/execution/execution.py +926 -0
- py_teststand-0.1.0/src/py_teststand/execution/interactive_args.py +118 -0
- py_teststand-0.1.0/src/py_teststand/execution/output_record_stream.py +170 -0
- py_teststand-0.1.0/src/py_teststand/execution/report.py +302 -0
- py_teststand-0.1.0/src/py_teststand/execution/result_log.py +328 -0
- py_teststand-0.1.0/src/py_teststand/execution/sync_manager.py +713 -0
- py_teststand-0.1.0/src/py_teststand/execution/thread.py +198 -0
- py_teststand-0.1.0/src/py_teststand/execution/watch_expression.py +179 -0
- py_teststand-0.1.0/src/py_teststand/ext/__init__.py +1 -0
- py_teststand-0.1.0/src/py_teststand/ext/events.py +220 -0
- py_teststand-0.1.0/src/py_teststand/ext/step_types.py +32 -0
- py_teststand-0.1.0/src/py_teststand/messaging/__init__.py +1 -0
- py_teststand-0.1.0/src/py_teststand/messaging/output_message.py +122 -0
- py_teststand-0.1.0/src/py_teststand/messaging/output_messages.py +60 -0
- py_teststand-0.1.0/src/py_teststand/messaging/ui_message.py +116 -0
- py_teststand-0.1.0/src/py_teststand/property/__init__.py +1 -0
- py_teststand-0.1.0/src/py_teststand/property/array_dimensions.py +45 -0
- py_teststand-0.1.0/src/py_teststand/property/data_type.py +96 -0
- py_teststand-0.1.0/src/py_teststand/property/property_object.py +1190 -0
- py_teststand-0.1.0/src/py_teststand/property/property_object_file.py +330 -0
- py_teststand-0.1.0/src/py_teststand/sequence/__init__.py +1 -0
- py_teststand-0.1.0/src/py_teststand/sequence/code_template.py +63 -0
- py_teststand-0.1.0/src/py_teststand/sequence/expression.py +230 -0
- py_teststand-0.1.0/src/py_teststand/sequence/location.py +609 -0
- py_teststand-0.1.0/src/py_teststand/sequence/sequence.py +558 -0
- py_teststand-0.1.0/src/py_teststand/sequence/sequence_context.py +438 -0
- py_teststand-0.1.0/src/py_teststand/sequence/sequence_file.py +457 -0
- py_teststand-0.1.0/src/py_teststand/sequence/step.py +1296 -0
- py_teststand-0.1.0/src/py_teststand/sequence/step_group.py +14 -0
- py_teststand-0.1.0/src/py_teststand/sequence/step_type.py +640 -0
- py_teststand-0.1.0/src/py_teststand/sequence/type_palette.py +22 -0
- py_teststand-0.1.0/src/py_teststand/station/__init__.py +1 -0
- py_teststand-0.1.0/src/py_teststand/station/search_directories.py +125 -0
- py_teststand-0.1.0/src/py_teststand/station/station_options.py +592 -0
- py_teststand-0.1.0/src/py_teststand/ui/__init__.py +78 -0
- py_teststand-0.1.0/src/py_teststand/ui/application_manager.py +776 -0
- py_teststand-0.1.0/src/py_teststand/ui/button_ctrl.py +237 -0
- py_teststand-0.1.0/src/py_teststand/ui/checkbox_ctrl.py +153 -0
- py_teststand-0.1.0/src/py_teststand/ui/combobox_ctrl.py +158 -0
- py_teststand-0.1.0/src/py_teststand/ui/command.py +256 -0
- py_teststand-0.1.0/src/py_teststand/ui/connections.py +2411 -0
- py_teststand-0.1.0/src/py_teststand/ui/entry_point.py +81 -0
- py_teststand-0.1.0/src/py_teststand/ui/events.py +483 -0
- py_teststand-0.1.0/src/py_teststand/ui/execution_view_manager.py +415 -0
- py_teststand-0.1.0/src/py_teststand/ui/expression_edit_ctrl.py +477 -0
- py_teststand-0.1.0/src/py_teststand/ui/insertion_palette.py +77 -0
- py_teststand-0.1.0/src/py_teststand/ui/label_ctrl.py +159 -0
- py_teststand-0.1.0/src/py_teststand/ui/list_box_ctrl.py +261 -0
- py_teststand-0.1.0/src/py_teststand/ui/listbar_ctrl.py +200 -0
- py_teststand-0.1.0/src/py_teststand/ui/menu_item.py +244 -0
- py_teststand-0.1.0/src/py_teststand/ui/report_view_ctrl.py +147 -0
- py_teststand-0.1.0/src/py_teststand/ui/sequence_file_view_manager.py +380 -0
- py_teststand-0.1.0/src/py_teststand/ui/sequence_view_ctrl.py +436 -0
- py_teststand-0.1.0/src/py_teststand/ui/status_bar.py +240 -0
- py_teststand-0.1.0/src/py_teststand/ui/styles.py +373 -0
- py_teststand-0.1.0/src/py_teststand/ui/variables_view_ctrl.py +42 -0
- py_teststand-0.1.0/src/py_teststand/undo/__init__.py +1 -0
- py_teststand-0.1.0/src/py_teststand/undo/undo_item.py +118 -0
- py_teststand-0.1.0/src/py_teststand/undo/undo_item_creator.py +57 -0
- py_teststand-0.1.0/src/py_teststand/undo/undo_stack.py +105 -0
- py_teststand-0.1.0/src/py_teststand/users/__init__.py +1 -0
- py_teststand-0.1.0/src/py_teststand/users/user.py +108 -0
- py_teststand-0.1.0/src/py_teststand/users/users_file.py +56 -0
- py_teststand-0.1.0/src/py_teststand/workspace/__init__.py +1 -0
- py_teststand-0.1.0/src/py_teststand/workspace/workspace_file.py +86 -0
- py_teststand-0.1.0/src/py_teststand/workspace/workspace_object.py +190 -0
- py_teststand-0.1.0/tests/__init__.py +0 -0
- py_teststand-0.1.0/tests/conftest.py +115 -0
- py_teststand-0.1.0/tests/integration/test_api_compatibility.py +76 -0
- py_teststand-0.1.0/tests/integration/test_memory_leaks.py +57 -0
- py_teststand-0.1.0/tests/test_core.py +103 -0
- py_teststand-0.1.0/tests/unit/__init__.py +0 -0
- py_teststand-0.1.0/tests/unit/adapters/test_activex_adapter.py +36 -0
- py_teststand-0.1.0/tests/unit/adapters/test_dotnet_runtime_kind.py +27 -0
- py_teststand-0.1.0/tests/unit/adapters/test_python_adapter.py +163 -0
- py_teststand-0.1.0/tests/unit/analyzer/__init__.py +0 -0
- py_teststand-0.1.0/tests/unit/analyzer/test_rule_authoring.py +162 -0
- py_teststand-0.1.0/tests/unit/core/__init__.py +0 -0
- py_teststand-0.1.0/tests/unit/core/test_engine.py +117 -0
- py_teststand-0.1.0/tests/unit/core/test_enums.py +210 -0
- py_teststand-0.1.0/tests/unit/core/test_errors.py +75 -0
- py_teststand-0.1.0/tests/unit/core/test_expression.py +42 -0
- py_teststand-0.1.0/tests/unit/core/test_property_object_file.py +283 -0
- py_teststand-0.1.0/tests/unit/core/test_sequence.py +158 -0
- py_teststand-0.1.0/tests/unit/core/test_sequence_file.py +64 -0
- py_teststand-0.1.0/tests/unit/core/test_step.py +258 -0
- py_teststand-0.1.0/tests/unit/execution/test_database_options.py +136 -0
- py_teststand-0.1.0/tests/unit/execution/test_execution.py +82 -0
- py_teststand-0.1.0/tests/unit/sequence/test_sequence.py +82 -0
- py_teststand-0.1.0/tests/unit/sequence/test_sequence_file.py +84 -0
- py_teststand-0.1.0/tests/unit/test_core_mechanics.py +87 -0
- py_teststand-0.1.0/tests/unit/ui/test_events.py +141 -0
- py_teststand-0.1.0/tests/unit/ui/test_execution_view_manager.py +71 -0
- py_teststand-0.1.0/tests/unit/ui/test_sequence_file_view_manager.py +65 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
Thumbs.db
|
|
3
|
+
.vscode/
|
|
4
|
+
.idea/
|
|
5
|
+
*.swp
|
|
6
|
+
*.swo
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*$py.class
|
|
10
|
+
*.so
|
|
11
|
+
.venv
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
env/
|
|
15
|
+
ENV/
|
|
16
|
+
dist/
|
|
17
|
+
build/
|
|
18
|
+
*.egg-info/
|
|
19
|
+
*.egg
|
|
20
|
+
.eggs/
|
|
21
|
+
.ruff_cache/
|
|
22
|
+
.mypy_cache/
|
|
23
|
+
.pytest_cache/
|
|
24
|
+
.coverage
|
|
25
|
+
.coverage.*
|
|
26
|
+
htmlcov/
|
|
27
|
+
.nox/
|
|
28
|
+
.tox/
|
|
29
|
+
|
|
30
|
+
# Dynamic Generated Documentation (#TODO)
|
|
31
|
+
/site/
|
|
32
|
+
/public/
|
|
33
|
+
docs/
|
|
34
|
+
/tmp/
|
|
35
|
+
/temp/
|
|
36
|
+
scratch/
|
|
37
|
+
*.log
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.8
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2026 Dominik Rajchel
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: py-teststand
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Community object-oriented Python 3 bindings for the National Instruments TestStand™ COM API
|
|
5
|
+
Project-URL: Homepage, https://github.com/TheDomcio/py-teststand
|
|
6
|
+
Project-URL: Repository, https://github.com/TheDomcio/py-teststand
|
|
7
|
+
Project-URL: Issues, https://github.com/TheDomcio/py-teststand/issues
|
|
8
|
+
Project-URL: Bug Reports, https://github.com/TheDomcio/py-teststand/issues
|
|
9
|
+
Project-URL: Source Code, https://github.com/TheDomcio/py-teststand
|
|
10
|
+
Project-URL: TestStand API Reference, https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html
|
|
11
|
+
Author: Dominik Rajchel
|
|
12
|
+
License: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: automation,com,manufacturing,national-instruments,ni,python,pywin32,station-provisioning,test-automation,teststand,windows
|
|
15
|
+
Classifier: Development Status :: 3 - Alpha
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: Manufacturing
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
20
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 7
|
|
21
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
|
|
22
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3
|
|
24
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
32
|
+
Classifier: Topic :: Software Development :: Testing
|
|
33
|
+
Classifier: Topic :: System :: Hardware :: Hardware Drivers
|
|
34
|
+
Classifier: Typing :: Typed
|
|
35
|
+
Requires-Python: >=3.8.20
|
|
36
|
+
Requires-Dist: pywin32>=305; sys_platform == 'win32'
|
|
37
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# py-teststand
|
|
41
|
+
|
|
42
|
+
Community object-oriented Python 3 bindings for the [National Instruments TestStand™ COM API](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html).
|
|
43
|
+
|
|
44
|
+
> ⚠️ **Early Implementation Stage** — Consider as experimental. Interfaces may change between releases without prior notice.
|
|
45
|
+
|
|
46
|
+
## Overview
|
|
47
|
+
|
|
48
|
+
`py-teststand` exposes the [TestStand™ COM API](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html) as an object-oriented Python interface via [pywin32](https://pypi.org/project/pywin32/).
|
|
49
|
+
|
|
50
|
+
### Name
|
|
51
|
+
|
|
52
|
+
The package is named `py-teststand` (with a dash) to avoid naming collision with the [pytest testing framework](https://pytest.org/) and for easier relation to TestStand™ test executive.
|
|
53
|
+
|
|
54
|
+
[](https://pypi.org/project/py-teststand/)
|
|
55
|
+
[](https://pypi.org/project/py-teststand/)
|
|
56
|
+
[](https://opensource.org/licenses/MIT)
|
|
57
|
+
[](https://github.com/TheDomcio/py-teststand/actions)
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Project Status
|
|
62
|
+
|
|
63
|
+
`py-teststand` is a hobby project maintained on a best-effort basis
|
|
64
|
+
and is **not** under active full-time development (for first release).
|
|
65
|
+
There are no guaranteed scheduled release cadences or support (but feel free to contact).
|
|
66
|
+
|
|
67
|
+
That said:
|
|
68
|
+
|
|
69
|
+
- **Bug reports and feature requests** are welcome via [GitHub Issues](https://github.com/TheDomcio/py-teststand/issues).
|
|
70
|
+
- **Pull requests** are welcomed and reviewed. If you are working with the TestStand™ COM API and have improvements, fixes, or additional bindings, contributions are encouraged.
|
|
71
|
+
|
|
72
|
+
If you encounter a missing TestStand™ binding, an incorrect type annotation, or unexpected TestStand™ COM dispatch behavior, opening an issue with a reproducible case is the most effective way to get it addressed, I will try to investigate and find solution as fast as possible.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Implementation Notes
|
|
77
|
+
|
|
78
|
+
### Type Library Generation
|
|
79
|
+
|
|
80
|
+
Python class stubs and interface definitions are generated from the TestStand™ [COM Type Library](https://learn.microsoft.com/en-us/windows/win32/com/type-libraries-and-the-component-object-model) (`.tlb`) using [pywin32's](https://pypi.org/project/pywin32/) [`makepy`](https://github.com/mhammond/pywin32/blob/main/com/win32com/client/makepy.py) utility. The generated output is cached as a [pywin32 dispatch cache](https://mhammond.github.io/pywin32/) module and checked into the repository, meaning a live TestStand™ installation is not required at import time — only at runtime when TestStand™ COM objects are actually instantiated.
|
|
81
|
+
|
|
82
|
+
The generation process follows this pipeline:
|
|
83
|
+
|
|
84
|
+
1. **TLB introspection** — `makepy` reads the registered TestStand™ COM type library via the Windows registry and reflects all exposed interfaces, `CoClass` definitions, enumerations, and dispatch IDs.
|
|
85
|
+
2. **Cache dump** — The reflected TestStand™ metadata is serialized into a Python module stored under `win32com/gen_py/`, keyed by the TestStand™ type library GUID and version. This cache is committed to the repository so users do not need to run `makepy` themselves.
|
|
86
|
+
3. **Wrapper generation** — `py-teststand` classes are authored on top of the cached TestStand™ dispatch definitions, adding Python type annotations and translating raw COM `VARIANT` and `IDispatch` returns into typed Python objects where applicable.
|
|
87
|
+
|
|
88
|
+
This approach means the bindings target a specific version of the TestStand™ type library. Compatibility across TestStand™ engine versions is maintained by keeping the wrapper surface aligned with stable, long-lived TestStand™ COM interfaces present across the supported version range.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Design Goals
|
|
93
|
+
|
|
94
|
+
- **Python 3.8 minimum** — Python 3.8 (uv supports / distributes even 3.8.20) is the last CPython release with official Windows 7 support. Many manufacturing and test environments run long-lifecycle OS images on air-gapped station hardware where upgrading the OS is not feasible on short timescales. Maintaining a 3.8-compatible codebase allows incremental adoption of Python-based automation on existing TestStand™ station hardware without requiring a platform migration first.
|
|
95
|
+
- **TestStand™ station options as code** — The [TestStand™ Station Options](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html) object model is fully exposed, allowing TestStand™ search directories, model paths, station globals, and result processing configuration to be read and written programmatically. This makes TestStand™ station configuration reproducible and suitable for provisioners such as [Ansible](https://www.ansible.com/), [Chef](https://www.chef.io/), or custom deployment scripts — replacing manual point-and-click TestStand™ setup with version-controlled configuration.
|
|
96
|
+
- **No TestStand™ documentation mirroring** — The library does **not** duplicate or paraphrase the [official TestStand™ API reference](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html) in its docstrings, as eproducing NI's documentation would introduce a secondary source that diverges from the official TestStand™ spec as new versions evolve, creating a risk of misinformation, therefore for authoritative descriptions of TestStand™ COM objects, properties, method parameters, and return value semantics etc. refer directly to the [TestStand™ API Reference](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html).
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Technical Stack
|
|
101
|
+
|
|
102
|
+
| Tool | Purpose |
|
|
103
|
+
| :----------------------------------------------- | :---------------------------------------------------------- |
|
|
104
|
+
| [**uv**](https://github.com/astral-sh/uv) | Python package and project manager |
|
|
105
|
+
| [**ty**](https://github.com/astral-sh/ty) | Static type checker for interface validation |
|
|
106
|
+
| [**ruff**](https://github.com/astral-sh/ruff) | Linter and code formatter |
|
|
107
|
+
| [**pytest**](https://pytest.org/) | Unit and integration test runner |
|
|
108
|
+
| [**pywin32**](https://pypi.org/project/pywin32/) | Windows COM dispatch layer and TestStand™ TLB introspection |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Compatibility
|
|
113
|
+
|
|
114
|
+
| Component | Versions |
|
|
115
|
+
| :---------------------------------------------------------------------------------------------------------------------- | :----------- |
|
|
116
|
+
| [**Windows**](https://www.microsoft.com/en-us/windows) | 7 — 11 |
|
|
117
|
+
| [**Python**](https://www.python.org/downloads/) | 3.8 — 3.14 |
|
|
118
|
+
| [**TestStand™**](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html) | 2016 — 2026+ |
|
|
119
|
+
|
|
120
|
+
> Older TestStand™ engine versions may also work if the underlying TestStand™ COM interfaces have not changed, but they were not explicitly tested.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Features
|
|
125
|
+
|
|
126
|
+
- **Pythonic attribute access** — TestStand™ COM properties are accessible via standard Python attribute notation instead of raw `Dispatch` calls.
|
|
127
|
+
- **Type annotations** — All public members carry type hints compatible with [ty](https://github.com/astral-sh/ty).
|
|
128
|
+
- **TestStand™ station options provisioning** — Read and write TestStand™ station-level configuration suitable for use in automated deployment pipelines (like test stations install scripts).
|
|
129
|
+
- **Minimal binding surface** — No behavior is added beyond what the TestStand™ COM layer provides. Edge cases and error conditions follow the TestStand™ COM API contract documented in the [official TestStand™ reference](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html). The binding layer is intentionally lightweight and not overcommented, signatures and object hierarchy map directly to the TestStand™ COM API without adding abstraction or reinterpreting behavior (but modules themselves are sorted under domains for easier management).
|
|
130
|
+
- **No live TestStand™ installation required at import time** — The committed pywin32 dispatch cache allows the library to be imported and partially used (type checking, configuration building) without a TestStand™ installation present.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Installation
|
|
135
|
+
|
|
136
|
+
### [pip](https://pip.pypa.io/en/stable/)
|
|
137
|
+
|
|
138
|
+
```powershell
|
|
139
|
+
pip install py-teststand
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### [uv](https://github.com/astral-sh/uv)
|
|
143
|
+
|
|
144
|
+
```powershell
|
|
145
|
+
uv pip install py-teststand
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Popularity Over Time
|
|
151
|
+
|
|
152
|
+
[](https://star-history.com/#TheDomcio/py-teststand&Date)
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Legal
|
|
157
|
+
|
|
158
|
+
TestStand™ is a registered trademark of [National Instruments Corporation](https://www.ni.com/). Refer to [NI's TestStand™ licensing options](https://www.ni.com/docs/en-US/bundle/teststand/page/teststand-licensing-options.html) for information on required licenses to operate the TestStand™ engine.
|
|
159
|
+
|
|
160
|
+
`py-teststand` is an independent community project and is not affiliated with, endorsed by, or maintained by National Instruments or its parent company [Emerson](https://www.emerson.com/). References to the TestStand™ API are made solely for interoperability purposes.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# py-teststand
|
|
2
|
+
|
|
3
|
+
Community object-oriented Python 3 bindings for the [National Instruments TestStand™ COM API](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html).
|
|
4
|
+
|
|
5
|
+
> ⚠️ **Early Implementation Stage** — Consider as experimental. Interfaces may change between releases without prior notice.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
`py-teststand` exposes the [TestStand™ COM API](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html) as an object-oriented Python interface via [pywin32](https://pypi.org/project/pywin32/).
|
|
10
|
+
|
|
11
|
+
### Name
|
|
12
|
+
|
|
13
|
+
The package is named `py-teststand` (with a dash) to avoid naming collision with the [pytest testing framework](https://pytest.org/) and for easier relation to TestStand™ test executive.
|
|
14
|
+
|
|
15
|
+
[](https://pypi.org/project/py-teststand/)
|
|
16
|
+
[](https://pypi.org/project/py-teststand/)
|
|
17
|
+
[](https://opensource.org/licenses/MIT)
|
|
18
|
+
[](https://github.com/TheDomcio/py-teststand/actions)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Project Status
|
|
23
|
+
|
|
24
|
+
`py-teststand` is a hobby project maintained on a best-effort basis
|
|
25
|
+
and is **not** under active full-time development (for first release).
|
|
26
|
+
There are no guaranteed scheduled release cadences or support (but feel free to contact).
|
|
27
|
+
|
|
28
|
+
That said:
|
|
29
|
+
|
|
30
|
+
- **Bug reports and feature requests** are welcome via [GitHub Issues](https://github.com/TheDomcio/py-teststand/issues).
|
|
31
|
+
- **Pull requests** are welcomed and reviewed. If you are working with the TestStand™ COM API and have improvements, fixes, or additional bindings, contributions are encouraged.
|
|
32
|
+
|
|
33
|
+
If you encounter a missing TestStand™ binding, an incorrect type annotation, or unexpected TestStand™ COM dispatch behavior, opening an issue with a reproducible case is the most effective way to get it addressed, I will try to investigate and find solution as fast as possible.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Implementation Notes
|
|
38
|
+
|
|
39
|
+
### Type Library Generation
|
|
40
|
+
|
|
41
|
+
Python class stubs and interface definitions are generated from the TestStand™ [COM Type Library](https://learn.microsoft.com/en-us/windows/win32/com/type-libraries-and-the-component-object-model) (`.tlb`) using [pywin32's](https://pypi.org/project/pywin32/) [`makepy`](https://github.com/mhammond/pywin32/blob/main/com/win32com/client/makepy.py) utility. The generated output is cached as a [pywin32 dispatch cache](https://mhammond.github.io/pywin32/) module and checked into the repository, meaning a live TestStand™ installation is not required at import time — only at runtime when TestStand™ COM objects are actually instantiated.
|
|
42
|
+
|
|
43
|
+
The generation process follows this pipeline:
|
|
44
|
+
|
|
45
|
+
1. **TLB introspection** — `makepy` reads the registered TestStand™ COM type library via the Windows registry and reflects all exposed interfaces, `CoClass` definitions, enumerations, and dispatch IDs.
|
|
46
|
+
2. **Cache dump** — The reflected TestStand™ metadata is serialized into a Python module stored under `win32com/gen_py/`, keyed by the TestStand™ type library GUID and version. This cache is committed to the repository so users do not need to run `makepy` themselves.
|
|
47
|
+
3. **Wrapper generation** — `py-teststand` classes are authored on top of the cached TestStand™ dispatch definitions, adding Python type annotations and translating raw COM `VARIANT` and `IDispatch` returns into typed Python objects where applicable.
|
|
48
|
+
|
|
49
|
+
This approach means the bindings target a specific version of the TestStand™ type library. Compatibility across TestStand™ engine versions is maintained by keeping the wrapper surface aligned with stable, long-lived TestStand™ COM interfaces present across the supported version range.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Design Goals
|
|
54
|
+
|
|
55
|
+
- **Python 3.8 minimum** — Python 3.8 (uv supports / distributes even 3.8.20) is the last CPython release with official Windows 7 support. Many manufacturing and test environments run long-lifecycle OS images on air-gapped station hardware where upgrading the OS is not feasible on short timescales. Maintaining a 3.8-compatible codebase allows incremental adoption of Python-based automation on existing TestStand™ station hardware without requiring a platform migration first.
|
|
56
|
+
- **TestStand™ station options as code** — The [TestStand™ Station Options](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html) object model is fully exposed, allowing TestStand™ search directories, model paths, station globals, and result processing configuration to be read and written programmatically. This makes TestStand™ station configuration reproducible and suitable for provisioners such as [Ansible](https://www.ansible.com/), [Chef](https://www.chef.io/), or custom deployment scripts — replacing manual point-and-click TestStand™ setup with version-controlled configuration.
|
|
57
|
+
- **No TestStand™ documentation mirroring** — The library does **not** duplicate or paraphrase the [official TestStand™ API reference](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html) in its docstrings, as eproducing NI's documentation would introduce a secondary source that diverges from the official TestStand™ spec as new versions evolve, creating a risk of misinformation, therefore for authoritative descriptions of TestStand™ COM objects, properties, method parameters, and return value semantics etc. refer directly to the [TestStand™ API Reference](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html).
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Technical Stack
|
|
62
|
+
|
|
63
|
+
| Tool | Purpose |
|
|
64
|
+
| :----------------------------------------------- | :---------------------------------------------------------- |
|
|
65
|
+
| [**uv**](https://github.com/astral-sh/uv) | Python package and project manager |
|
|
66
|
+
| [**ty**](https://github.com/astral-sh/ty) | Static type checker for interface validation |
|
|
67
|
+
| [**ruff**](https://github.com/astral-sh/ruff) | Linter and code formatter |
|
|
68
|
+
| [**pytest**](https://pytest.org/) | Unit and integration test runner |
|
|
69
|
+
| [**pywin32**](https://pypi.org/project/pywin32/) | Windows COM dispatch layer and TestStand™ TLB introspection |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Compatibility
|
|
74
|
+
|
|
75
|
+
| Component | Versions |
|
|
76
|
+
| :---------------------------------------------------------------------------------------------------------------------- | :----------- |
|
|
77
|
+
| [**Windows**](https://www.microsoft.com/en-us/windows) | 7 — 11 |
|
|
78
|
+
| [**Python**](https://www.python.org/downloads/) | 3.8 — 3.14 |
|
|
79
|
+
| [**TestStand™**](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html) | 2016 — 2026+ |
|
|
80
|
+
|
|
81
|
+
> Older TestStand™ engine versions may also work if the underlying TestStand™ COM interfaces have not changed, but they were not explicitly tested.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Features
|
|
86
|
+
|
|
87
|
+
- **Pythonic attribute access** — TestStand™ COM properties are accessible via standard Python attribute notation instead of raw `Dispatch` calls.
|
|
88
|
+
- **Type annotations** — All public members carry type hints compatible with [ty](https://github.com/astral-sh/ty).
|
|
89
|
+
- **TestStand™ station options provisioning** — Read and write TestStand™ station-level configuration suitable for use in automated deployment pipelines (like test stations install scripts).
|
|
90
|
+
- **Minimal binding surface** — No behavior is added beyond what the TestStand™ COM layer provides. Edge cases and error conditions follow the TestStand™ COM API contract documented in the [official TestStand™ reference](https://www.ni.com/docs/en-US/bundle/teststand-api-reference/page/tshelp/teststand-api-reference.html). The binding layer is intentionally lightweight and not overcommented, signatures and object hierarchy map directly to the TestStand™ COM API without adding abstraction or reinterpreting behavior (but modules themselves are sorted under domains for easier management).
|
|
91
|
+
- **No live TestStand™ installation required at import time** — The committed pywin32 dispatch cache allows the library to be imported and partially used (type checking, configuration building) without a TestStand™ installation present.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Installation
|
|
96
|
+
|
|
97
|
+
### [pip](https://pip.pypa.io/en/stable/)
|
|
98
|
+
|
|
99
|
+
```powershell
|
|
100
|
+
pip install py-teststand
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### [uv](https://github.com/astral-sh/uv)
|
|
104
|
+
|
|
105
|
+
```powershell
|
|
106
|
+
uv pip install py-teststand
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Popularity Over Time
|
|
112
|
+
|
|
113
|
+
[](https://star-history.com/#TheDomcio/py-teststand&Date)
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Legal
|
|
118
|
+
|
|
119
|
+
TestStand™ is a registered trademark of [National Instruments Corporation](https://www.ni.com/). Refer to [NI's TestStand™ licensing options](https://www.ni.com/docs/en-US/bundle/teststand/page/teststand-licensing-options.html) for information on required licenses to operate the TestStand™ engine.
|
|
120
|
+
|
|
121
|
+
`py-teststand` is an independent community project and is not affiliated with, endorsed by, or maintained by National Instruments or its parent company [Emerson](https://www.emerson.com/). References to the TestStand™ API are made solely for interoperability purposes.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Build a TestStand sequence file from scratch with NumericLimitTest steps.
|
|
2
|
+
|
|
3
|
+
Creates a new sequence file, populates ``MainSequence`` with two
|
|
4
|
+
NumericLimitTest steps (a temperature check and a voltage monitor) whose
|
|
5
|
+
high/low limits are configured through the underlying
|
|
6
|
+
``PropertyObject``, adds a ``CustomSubsequence`` containing an ``Action``
|
|
7
|
+
step so downstream examples (``insert_step.py``) have something to target,
|
|
8
|
+
prints a verification summary of the constructed sequence, and writes
|
|
9
|
+
the file to a temp directory.
|
|
10
|
+
|
|
11
|
+
Demonstrates:
|
|
12
|
+
- Creating sequence files and adding subsequences with ``new_sequence``
|
|
13
|
+
- Building steps with ``Engine.new_step`` (no adapter / None adapter)
|
|
14
|
+
- Setting standard step properties (``name``, ``precondition``, ``record_result``)
|
|
15
|
+
- Reaching into nested TestStand properties (``Limits.High`` / ``Limits.Low``)
|
|
16
|
+
via ``step.as_property_object()``
|
|
17
|
+
- Inserting steps into a specific ``StepGroup`` at a chosen index
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import tempfile
|
|
23
|
+
import uuid
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
from py_teststand import Engine
|
|
27
|
+
from py_teststand.sequence.step_group import StepGroup
|
|
28
|
+
|
|
29
|
+
ROOT_TEMP_DIR = Path(tempfile.gettempdir()) / "py-teststand"
|
|
30
|
+
LATEST_POINTER = ROOT_TEMP_DIR / "latest_sequence.txt"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def main() -> None:
|
|
34
|
+
run_dir = ROOT_TEMP_DIR / uuid.uuid4().hex
|
|
35
|
+
run_dir.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
output_path = run_dir / "test_sequence.seq"
|
|
37
|
+
|
|
38
|
+
with Engine() as engine:
|
|
39
|
+
sequence_file = engine.new_sequence_file()
|
|
40
|
+
main_sequence = sequence_file.get_sequence_by_name("MainSequence")
|
|
41
|
+
|
|
42
|
+
first_step = engine.new_step(adapter_key_name="", step_type_name="NumericLimitTest")
|
|
43
|
+
first_step.name = "Temperature Check"
|
|
44
|
+
first_step.precondition = "Locals.TempSensorPresent == True"
|
|
45
|
+
first_step.record_result = True
|
|
46
|
+
|
|
47
|
+
first_step_property_object = first_step.as_property_object()
|
|
48
|
+
first_step_property_object["Limits.High"] = 85.0
|
|
49
|
+
first_step_property_object["Limits.Low"] = 15.0
|
|
50
|
+
|
|
51
|
+
main_sequence.insert_step(first_step, 0, StepGroup.Main)
|
|
52
|
+
|
|
53
|
+
second_step = engine.new_step(adapter_key_name="", step_type_name="NumericLimitTest")
|
|
54
|
+
second_step.name = "Voltage Monitor"
|
|
55
|
+
second_step.precondition = "Locals.DUTPowered == True"
|
|
56
|
+
|
|
57
|
+
second_step_property_object = second_step.as_property_object()
|
|
58
|
+
second_step_property_object["Limits.High"] = 5.25
|
|
59
|
+
second_step_property_object["Limits.Low"] = 4.75
|
|
60
|
+
|
|
61
|
+
main_sequence.insert_step(second_step, 1, StepGroup.Main)
|
|
62
|
+
|
|
63
|
+
sub_seq = engine.new_sequence()
|
|
64
|
+
sub_seq.name = "CustomSubsequence"
|
|
65
|
+
sequence_file.insert_sequence(sub_seq)
|
|
66
|
+
|
|
67
|
+
init_step = engine.new_step(adapter_key_name="", step_type_name="Action")
|
|
68
|
+
init_step.name = "Initialize Hardware"
|
|
69
|
+
sub_seq.insert_step(init_step, 0, StepGroup.Main)
|
|
70
|
+
|
|
71
|
+
print(f"Created sequence with {main_sequence.get_num_steps()} steps:")
|
|
72
|
+
for i in range(main_sequence.get_num_steps()):
|
|
73
|
+
s = main_sequence.get_step(i)
|
|
74
|
+
po_s = s.as_property_object()
|
|
75
|
+
|
|
76
|
+
print(f" [{i}] {s.name}")
|
|
77
|
+
print(f" Limits: Low={po_s['Limits.Low']}, High={po_s['Limits.High']}")
|
|
78
|
+
print(f" Precond: {s.precondition}")
|
|
79
|
+
|
|
80
|
+
sequence_file.save(str(output_path))
|
|
81
|
+
LATEST_POINTER.write_text(str(output_path), encoding="utf-8")
|
|
82
|
+
print(f"\nSaved sequence file to {output_path}")
|
|
83
|
+
print(f"Pointer written to {LATEST_POINTER}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
main()
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Create string variables across every TestStand variable scope.
|
|
2
|
+
|
|
3
|
+
Opens the sequence file produced by ``build_sequence.py`` and populates it
|
|
4
|
+
with new string variables across the four standard TestStand scopes:
|
|
5
|
+
|
|
6
|
+
- **Sequence Locals** — temporary per-call storage on ``MainSequence``
|
|
7
|
+
(``Locals.OperatorName``).
|
|
8
|
+
- **Sequence Parameters** — caller-supplied inputs on ``MainSequence``
|
|
9
|
+
(``Parameters.DUTSerial``).
|
|
10
|
+
- **File Globals** — values shared across every sequence in the file
|
|
11
|
+
(``FileGlobals.BatchID``).
|
|
12
|
+
- **Station Globals** — values shared across every sequence file run on
|
|
13
|
+
this station, persisted via ``Engine.commit_globals_to_disk``
|
|
14
|
+
(``StationInfo.StationName``).
|
|
15
|
+
|
|
16
|
+
Also adds a second subsequence ``MeasurementRoutine`` with its own
|
|
17
|
+
parameter and local so the example shows how variables are scoped per
|
|
18
|
+
sequence rather than per file.
|
|
19
|
+
|
|
20
|
+
Demonstrates:
|
|
21
|
+
- Creating sub-properties of arbitrary type via
|
|
22
|
+
``PropertyObject.new_sub_property`` + ``PropValType``
|
|
23
|
+
- Reading and writing values with ``PropertyObject.__setitem__`` / ``__getitem__``
|
|
24
|
+
- Reaching ``Sequence.locals`` / ``Sequence.parameters`` /
|
|
25
|
+
``SequenceFile.file_globals`` / ``Engine.globals``
|
|
26
|
+
- Adding subsequences with ``SequenceFile.new_sequence``
|
|
27
|
+
- Committing station-globals changes to disk
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import tempfile
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
|
|
35
|
+
from py_teststand import Engine
|
|
36
|
+
from py_teststand.property.property_object import PropValType
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _ensure_string_var(container, name: str, value: str) -> None:
|
|
40
|
+
"""Create a scalar string sub-property and assign it if it does not exist."""
|
|
41
|
+
if not container.exists(name, 0):
|
|
42
|
+
container.new_sub_property(name, PropValType.String, False, "")
|
|
43
|
+
container[name] = value
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
ROOT_TEMP_DIR = Path(tempfile.gettempdir()) / "py-teststand"
|
|
47
|
+
LATEST_POINTER = ROOT_TEMP_DIR / "latest_sequence.txt"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def main() -> None:
|
|
51
|
+
if not LATEST_POINTER.exists():
|
|
52
|
+
print(f"Error: Pointer file not found at {LATEST_POINTER}")
|
|
53
|
+
print("Run build_sequence.py first.")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
sequence_path = Path(LATEST_POINTER.read_text(encoding="utf-8").strip())
|
|
57
|
+
|
|
58
|
+
if not sequence_path.exists():
|
|
59
|
+
print(f"Error: Sequence file not found at {sequence_path}")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
with Engine() as engine:
|
|
63
|
+
with engine.get_sequence_file(str(sequence_path)) as seq_file:
|
|
64
|
+
main_sequence = seq_file.get_sequence_by_name("MainSequence")
|
|
65
|
+
|
|
66
|
+
with main_sequence.locals as main_locals:
|
|
67
|
+
_ensure_string_var(main_locals, "OperatorName", "Alice")
|
|
68
|
+
|
|
69
|
+
with main_sequence.parameters as main_params:
|
|
70
|
+
_ensure_string_var(main_params, "DUTSerial", "SN-000000")
|
|
71
|
+
|
|
72
|
+
with seq_file.file_globals as file_globals:
|
|
73
|
+
_ensure_string_var(file_globals, "BatchID", "BATCH-2026-Q2-001")
|
|
74
|
+
|
|
75
|
+
measurement_seq = seq_file.new_sequence("MeasurementRoutine")
|
|
76
|
+
with measurement_seq.parameters as measurement_params:
|
|
77
|
+
_ensure_string_var(measurement_params, "ChannelLabel", "CH-A")
|
|
78
|
+
with measurement_seq.locals as measurement_locals:
|
|
79
|
+
_ensure_string_var(measurement_locals, "LastReading", "")
|
|
80
|
+
|
|
81
|
+
seq_file.save()
|
|
82
|
+
|
|
83
|
+
print("Variables created:")
|
|
84
|
+
with main_sequence.locals as ml:
|
|
85
|
+
print(f" MainSequence.Locals.OperatorName = {ml['OperatorName']!r}")
|
|
86
|
+
with main_sequence.parameters as mp:
|
|
87
|
+
print(f" MainSequence.Parameters.DUTSerial = {mp['DUTSerial']!r}")
|
|
88
|
+
with seq_file.file_globals as fg:
|
|
89
|
+
print(f" FileGlobals.BatchID = {fg['BatchID']!r}")
|
|
90
|
+
with measurement_seq.parameters as msp:
|
|
91
|
+
print(f" MeasurementRoutine.Parameters.Channel= {msp['ChannelLabel']!r}")
|
|
92
|
+
with measurement_seq.locals as msl:
|
|
93
|
+
print(f" MeasurementRoutine.Locals.LastReading= {msl['LastReading']!r}")
|
|
94
|
+
|
|
95
|
+
with engine.globals as station_globals:
|
|
96
|
+
if not station_globals.exists("StationInfo", 0):
|
|
97
|
+
station_globals.new_sub_property("StationInfo", PropValType.Container, False, "")
|
|
98
|
+
station_info = station_globals.get_property_object("StationInfo", 0)
|
|
99
|
+
assert station_info is not None
|
|
100
|
+
with station_info as info:
|
|
101
|
+
_ensure_string_var(info, "StationName", "STATION_01")
|
|
102
|
+
print(f" StationGlobals.StationInfo.StationName = {info['StationName']!r}")
|
|
103
|
+
|
|
104
|
+
engine.commit_globals_to_disk(prompt_on_save_conflicts=False)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
main()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Build and run a sequence end-to-end using only built-in TestStand step types.
|
|
2
|
+
|
|
3
|
+
Creates a fresh in-memory sequence file, appends three ``Action`` steps to
|
|
4
|
+
``MainSequence`` using the None adapter (no external code module), starts an
|
|
5
|
+
execution, waits for it to finish, and walks the resulting ``ResultList`` to
|
|
6
|
+
print the recorded step name and status for each step.
|
|
7
|
+
|
|
8
|
+
The example focuses on the execution + result reporting surface — no code
|
|
9
|
+
modules, no LabVIEW/DLL adapters — so it can be exercised on any TestStand
|
|
10
|
+
engine without external dependencies.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from py_teststand import Engine
|
|
16
|
+
from py_teststand.core.engine import AdapterKeyName
|
|
17
|
+
from py_teststand.sequence.step_group import StepGroup
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main() -> None:
|
|
21
|
+
with Engine() as engine:
|
|
22
|
+
sequence_file = engine.new_sequence_file()
|
|
23
|
+
main_sequence = sequence_file.get_sequence_by_name("MainSequence")
|
|
24
|
+
|
|
25
|
+
for name in ("Initialize", "Run Test", "Cleanup"):
|
|
26
|
+
step = engine.new_step(AdapterKeyName.NoneAdapterKeyName, "Action")
|
|
27
|
+
step.name = name
|
|
28
|
+
step.record_result = True
|
|
29
|
+
main_sequence.insert_step(step, main_sequence.get_num_steps(), StepGroup.Main)
|
|
30
|
+
|
|
31
|
+
with engine.new_execution(sequence_file, "MainSequence") as execution:
|
|
32
|
+
execution.wait_for_end_ex(-1)
|
|
33
|
+
|
|
34
|
+
results = execution.result_object
|
|
35
|
+
if results is None:
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
with results as res:
|
|
39
|
+
if not res.exists("ResultList", 0):
|
|
40
|
+
print("No results recorded.")
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
result_list_obj = res.get_property_object("ResultList", 0)
|
|
44
|
+
if result_list_obj is None:
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
with result_list_obj as result_list:
|
|
48
|
+
for step_result in result_list:
|
|
49
|
+
if step_result is None:
|
|
50
|
+
continue
|
|
51
|
+
with step_result as sr:
|
|
52
|
+
name = sr.get_val_string("TS.StepName", 0)
|
|
53
|
+
status = sr.get_val_string("Status", 0)
|
|
54
|
+
print(f"{name}: {status}")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
main()
|