nidaqmx 1.2.0.dev1__tar.gz → 1.3.0.dev0__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.
Files changed (109) hide show
  1. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/PKG-INFO +9 -6
  2. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/README.rst +4 -1
  3. nidaqmx-1.3.0.dev0/generated/nidaqmx/__init__.py +31 -0
  4. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/__main__.py +6 -3
  5. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_base_interpreter.py +104 -2
  6. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_bitfield_utils.py +8 -11
  7. nidaqmx-1.3.0.dev0/generated/nidaqmx/_dotenvpath.py +73 -0
  8. nidaqmx-1.3.0.dev0/generated/nidaqmx/_feature_toggles.py +162 -0
  9. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_grpc_interpreter.py +91 -1
  10. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_grpc_time.py +13 -10
  11. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_install_daqmx.py +23 -64
  12. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_installer_metadata.json +6 -6
  13. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_lib.py +72 -62
  14. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_lib_time.py +31 -19
  15. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_library_interpreter.py +766 -5
  16. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_linux_installation_commands.py +5 -6
  17. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_time.py +10 -9
  18. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/constants.py +11 -1
  19. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/errors.py +73 -71
  20. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/grpc_session_options.py +19 -14
  21. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_readers/__init__.py +37 -0
  22. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_readers/_analog_multi_channel_reader.py +235 -0
  23. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_readers/_analog_single_channel_reader.py +193 -0
  24. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_readers/_analog_unscaled_reader.py +352 -0
  25. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_readers/_channel_reader_base.py +128 -0
  26. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_readers/_counter_reader.py +529 -0
  27. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_readers/_digital_multi_channel_reader.py +581 -0
  28. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_readers/_digital_single_channel_reader.py +472 -0
  29. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_readers/_power_readers.py +397 -0
  30. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_writers/__init__.py +34 -0
  31. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_writers/_analog_multi_channel_writer.py +137 -0
  32. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_writers/_analog_single_channel_writer.py +119 -0
  33. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_writers/_analog_unscaled_writer.py +185 -0
  34. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_writers/_channel_writer_base.py +172 -0
  35. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_writers/_counter_writer.py +254 -0
  36. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_writers/_digital_multi_channel_writer.py +365 -0
  37. nidaqmx-1.3.0.dev0/generated/nidaqmx/stream_writers/_digital_single_channel_writer.py +322 -0
  38. nidaqmx-1.3.0.dev0/generated/nidaqmx/system/__init__.py +19 -0
  39. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/_collections/device_collection.py +27 -26
  40. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/_collections/persisted_channel_collection.py +36 -29
  41. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/_collections/persisted_scale_collection.py +30 -26
  42. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/_collections/persisted_task_collection.py +30 -25
  43. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/_collections/physical_channel_collection.py +86 -75
  44. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/_watchdog_modules/expiration_states_collection.py +14 -12
  45. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/storage/__init__.py +3 -1
  46. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/storage/persisted_channel.py +27 -34
  47. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/storage/persisted_scale.py +28 -36
  48. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/storage/persisted_task.py +28 -36
  49. nidaqmx-1.3.0.dev0/generated/nidaqmx/task/__init__.py +15 -0
  50. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/_in_stream.py +18 -2
  51. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/_task.py +718 -331
  52. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/_timing.py +454 -39
  53. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/channels/__init__.py +12 -2
  54. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/collections/__init__.py +12 -2
  55. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/collections/_channel_collection.py +30 -34
  56. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/triggering/__init__.py +11 -2
  57. nidaqmx-1.3.0.dev0/generated/nidaqmx/types.py +71 -0
  58. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/utils.py +41 -39
  59. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/pyproject.toml +70 -31
  60. nidaqmx-1.2.0.dev1/generated/nidaqmx/__init__.py +0 -17
  61. nidaqmx-1.2.0.dev1/generated/nidaqmx/stream_readers.py +0 -2454
  62. nidaqmx-1.2.0.dev1/generated/nidaqmx/stream_writers.py +0 -1401
  63. nidaqmx-1.2.0.dev1/generated/nidaqmx/system/__init__.py +0 -9
  64. nidaqmx-1.2.0.dev1/generated/nidaqmx/task/__init__.py +0 -9
  65. nidaqmx-1.2.0.dev1/generated/nidaqmx/types.py +0 -69
  66. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/LICENSE +0 -0
  67. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/__init__.py +0 -0
  68. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/data_moniker_pb2.py +0 -0
  69. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/data_moniker_pb2.pyi +0 -0
  70. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/data_moniker_pb2_grpc.py +0 -0
  71. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/data_moniker_pb2_grpc.pyi +0 -0
  72. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/nidaqmx_pb2.py +0 -0
  73. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/nidaqmx_pb2.pyi +0 -0
  74. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/nidaqmx_pb2_grpc.py +0 -0
  75. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/nidaqmx_pb2_grpc.pyi +0 -0
  76. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/session_pb2.py +0 -0
  77. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/session_pb2.pyi +0 -0
  78. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/session_pb2_grpc.py +0 -0
  79. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/_stubs/session_pb2_grpc.pyi +0 -0
  80. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/error_codes.py +0 -0
  81. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/scale.py +0 -0
  82. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/_collections/__init__.py +0 -0
  83. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/_watchdog_modules/__init__.py +0 -0
  84. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/_watchdog_modules/expiration_state.py +0 -0
  85. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/device.py +0 -0
  86. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/physical_channel.py +0 -0
  87. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/system.py +0 -0
  88. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/system/watchdog.py +0 -0
  89. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/_export_signals.py +0 -0
  90. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/_out_stream.py +0 -0
  91. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/channels/_ai_channel.py +0 -0
  92. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/channels/_ao_channel.py +0 -0
  93. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/channels/_channel.py +0 -0
  94. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/channels/_ci_channel.py +0 -0
  95. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/channels/_co_channel.py +0 -0
  96. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/channels/_di_channel.py +0 -0
  97. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/channels/_do_channel.py +0 -0
  98. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/collections/_ai_channel_collection.py +0 -0
  99. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/collections/_ao_channel_collection.py +0 -0
  100. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/collections/_ci_channel_collection.py +0 -0
  101. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/collections/_co_channel_collection.py +0 -0
  102. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/collections/_di_channel_collection.py +0 -0
  103. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/collections/_do_channel_collection.py +0 -0
  104. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/triggering/_arm_start_trigger.py +0 -0
  105. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/triggering/_handshake_trigger.py +0 -0
  106. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/triggering/_pause_trigger.py +0 -0
  107. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/triggering/_reference_trigger.py +0 -0
  108. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/triggering/_start_trigger.py +0 -0
  109. {nidaqmx-1.2.0.dev1 → nidaqmx-1.3.0.dev0}/generated/nidaqmx/task/triggering/_triggers.py +0 -0
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: nidaqmx
3
- Version: 1.2.0.dev1
3
+ Version: 1.3.0.dev0
4
4
  Summary: NI-DAQmx Python API
5
- Home-page: https://github.com/ni/nidaqmx-python
6
5
  License: MIT
7
6
  Keywords: nidaqmx,nidaq,daqmx,daq
8
7
  Author: NI
@@ -32,12 +31,13 @@ Requires-Dist: deprecation (>=2.1)
32
31
  Requires-Dist: distro (>=1.9.0) ; sys_platform == "linux"
33
32
  Requires-Dist: grpcio (>=1.49.0,<2.0) ; extra == "grpc"
34
33
  Requires-Dist: hightime (>=0.2.2)
35
- Requires-Dist: numpy (>=1.22) ; python_version >= "3.9" and python_version < "3.12"
36
- Requires-Dist: numpy (>=1.26) ; python_version >= "3.12" and python_version < "3.13"
34
+ Requires-Dist: nitypes (>=0.1.0dev10)
35
+ Requires-Dist: numpy (>=1.22) ; python_version >= "3.9" and python_version < "3.13"
37
36
  Requires-Dist: numpy (>=2.1) ; python_version >= "3.13" and python_version < "4.0"
38
37
  Requires-Dist: protobuf (>=4.21) ; extra == "grpc"
39
38
  Requires-Dist: python-decouple (>=3.8)
40
39
  Requires-Dist: requests (>=2.25.0)
40
+ Requires-Dist: typing_extensions (>=4.0.0)
41
41
  Requires-Dist: tzlocal (>=5.0,<6.0)
42
42
  Project-URL: Documentation, https://nidaqmx-python.readthedocs.io
43
43
  Project-URL: Repository, https://github.com/ni/nidaqmx-python
@@ -100,7 +100,10 @@ system.
100
100
  Python Version Support
101
101
  ----------------------
102
102
 
103
- **nidaqmx** supports CPython 3.9+ and PyPy3.
103
+ **nidaqmx** supports CPython 3.9+.
104
+
105
+ **nidaqmx** supports PyPy3 for non-gRPC use cases. For the status of PyPy support for gRPC,
106
+ see `PyPy support (grpc/grpc#4221) <https://github.com/grpc/grpc/issues/4221>`_.
104
107
 
105
108
  Installation
106
109
  ============
@@ -55,7 +55,10 @@ system.
55
55
  Python Version Support
56
56
  ----------------------
57
57
 
58
- **nidaqmx** supports CPython 3.9+ and PyPy3.
58
+ **nidaqmx** supports CPython 3.9+.
59
+
60
+ **nidaqmx** supports PyPy3 for non-gRPC use cases. For the status of PyPy support for gRPC,
61
+ see `PyPy support (grpc/grpc#4221) <https://github.com/grpc/grpc/issues/4221>`_.
59
62
 
60
63
  Installation
61
64
  ============
@@ -0,0 +1,31 @@
1
+ """The NI-DAQmx API for Python."""
2
+
3
+ from nidaqmx.errors import (
4
+ DaqError,
5
+ DaqReadError,
6
+ DaqResourceWarning,
7
+ DaqWarning,
8
+ DaqWriteError,
9
+ )
10
+ from nidaqmx.grpc_session_options import * # noqa: F403 - 'from nidaqmx.grpc_session_options import *' used; unable to detect undefined names (auto-generated noqa)
11
+ from nidaqmx.scale import Scale
12
+ from nidaqmx.task import Task
13
+ from nidaqmx.types import CtrFreq, CtrTick, CtrTime
14
+
15
+ try:
16
+ from importlib.metadata import version
17
+ except ImportError:
18
+ from importlib_metadata import version # type: ignore[no-redef]
19
+
20
+ __version__ = version(__name__)
21
+
22
+ __all__ = [ # noqa: F405 - 'errors' may be undefined, or defined from star imports: nidaqmx.grpc_session_options (auto-generated noqa)
23
+ "errors",
24
+ "scale",
25
+ "stream_readers",
26
+ "stream_writers",
27
+ "task",
28
+ ]
29
+
30
+ # Do not add a null logging handler. If the application has not configured logging, the
31
+ # default behavior is to log warnings and errors to stderr.
@@ -1,7 +1,8 @@
1
+ """'nidaqmx' command line utility."""
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  import logging
4
- from typing import Optional
5
6
 
6
7
  import click
7
8
 
@@ -16,12 +17,14 @@ from . import _install_daqmx
16
17
  count=True,
17
18
  help="Enable verbose logging. Repeat to increase verbosity.",
18
19
  )
19
- def main(verbosity: int) -> None:
20
+ def main( # noqa: D103 - Missing docstring in public function (auto-generated noqa)
21
+ verbosity: int,
22
+ ) -> None:
20
23
  _configure_logging(verbosity)
21
24
 
22
25
 
23
26
  @main.command()
24
- def installdriver():
27
+ def installdriver(): # noqa: D103 - Missing docstring in public function (auto-generated noqa)
25
28
  _install_daqmx.installdriver()
26
29
 
27
30
 
@@ -1,6 +1,11 @@
1
1
  # Do not edit this file; it was automatically generated.
2
+ from __future__ import annotations
3
+
2
4
  import abc
3
- from typing import Optional
5
+ import numpy
6
+ from nitypes.waveform import AnalogWaveform, DigitalWaveform
7
+ from typing import Any, Sequence
8
+ from nidaqmx.constants import WaveformAttributeMode
4
9
 
5
10
 
6
11
  class BaseEventHandler(abc.ABC):
@@ -1845,4 +1850,101 @@ class BaseInterpreter(abc.ABC):
1845
1850
 
1846
1851
  @abc.abstractmethod
1847
1852
  def hash_task_handle(self, task_handle):
1848
- raise NotImplementedError
1853
+ raise NotImplementedError
1854
+
1855
+ @abc.abstractmethod
1856
+ def read_analog_waveform(
1857
+ self,
1858
+ task_handle: object,
1859
+ number_of_samples_per_channel: int,
1860
+ timeout: float,
1861
+ waveform: AnalogWaveform[numpy.float64],
1862
+ waveform_attribute_mode: WaveformAttributeMode
1863
+ ) -> int:
1864
+ raise NotImplementedError
1865
+
1866
+ @abc.abstractmethod
1867
+ def read_analog_waveforms(
1868
+ self,
1869
+ task_handle: object,
1870
+ number_of_samples_per_channel: int,
1871
+ timeout: float,
1872
+ waveforms: Sequence[AnalogWaveform[numpy.float64]],
1873
+ waveform_attribute_mode: WaveformAttributeMode
1874
+ ) -> int:
1875
+ raise NotImplementedError
1876
+
1877
+ @abc.abstractmethod
1878
+ def read_digital_waveform(
1879
+ self,
1880
+ task_handle: object,
1881
+ number_of_samples_per_channel: int,
1882
+ timeout: float,
1883
+ waveform: DigitalWaveform[Any],
1884
+ waveform_attribute_mode: WaveformAttributeMode
1885
+ ) -> int:
1886
+ raise NotImplementedError
1887
+
1888
+ @abc.abstractmethod
1889
+ def read_digital_waveforms(
1890
+ self,
1891
+ task_handle: object,
1892
+ channel_count: int,
1893
+ number_of_samples_per_channel: int,
1894
+ number_of_signals_per_sample: int,
1895
+ timeout: float,
1896
+ waveforms: Sequence[DigitalWaveform[Any]],
1897
+ waveform_attribute_mode: WaveformAttributeMode,
1898
+ ) -> int:
1899
+ raise NotImplementedError
1900
+
1901
+ @abc.abstractmethod
1902
+ def read_new_digital_waveforms(
1903
+ self,
1904
+ task_handle: object,
1905
+ channel_count: int,
1906
+ number_of_samples_per_channel: int,
1907
+ number_of_signals_per_sample: int,
1908
+ timeout: float,
1909
+ waveform_attribute_mode: WaveformAttributeMode,
1910
+ ) -> Sequence[DigitalWaveform[numpy.uint8]]:
1911
+ raise NotImplementedError
1912
+
1913
+ @abc.abstractmethod
1914
+ def write_analog_waveform(
1915
+ self,
1916
+ task_handle: object,
1917
+ waveform: AnalogWaveform[Any],
1918
+ auto_start: bool,
1919
+ timeout: float
1920
+ ) -> int:
1921
+ raise NotImplementedError
1922
+
1923
+ @abc.abstractmethod
1924
+ def write_analog_waveforms(
1925
+ self,
1926
+ task_handle: object,
1927
+ waveforms: Sequence[AnalogWaveform[Any]],
1928
+ auto_start: bool,
1929
+ timeout: float
1930
+ ) -> int:
1931
+ raise NotImplementedError
1932
+
1933
+ @abc.abstractmethod
1934
+ def write_digital_waveform(
1935
+ self,
1936
+ task_handle: object,
1937
+ waveform: DigitalWaveform[Any],
1938
+ auto_start: bool,
1939
+ timeout: float,
1940
+ ) -> int:
1941
+ raise NotImplementedError
1942
+
1943
+ def write_digital_waveforms(
1944
+ self,
1945
+ task_handle: object,
1946
+ waveform: Sequence[DigitalWaveform[Any]],
1947
+ auto_start: bool,
1948
+ timeout: float,
1949
+ ) -> int:
1950
+ raise NotImplementedError
@@ -1,41 +1,38 @@
1
- def enum_bitfield_to_list(bitfield_value, bitfield_enum_type,
2
- actual_enum_type):
3
- """
4
- Converts a bitfield value to a list of enums.
1
+ def enum_bitfield_to_list(bitfield_value, bitfield_enum_type, actual_enum_type):
2
+ """Converts a bitfield value to a list of enums.
5
3
 
6
4
  Args:
7
5
  bitfield_value (int): Specifies the value of the bitfield.
8
6
  bitfield_enum_type (enum.Enum): Specifies the bitfield enum type
9
7
  from which to mask and extract the enum values.
10
8
  actual_enum_type (enum.Enum): Specifies the actual enum type.
9
+
11
10
  Returns:
12
11
  List[enum.Enum]: Indicates the converted list of enums.
13
12
  """
14
13
  supported_values = []
15
14
  for bitfield_mask in bitfield_enum_type:
16
15
  if bitfield_value & bitfield_mask.value:
17
- enum_value = next(
18
- e for e in actual_enum_type if e.name == bitfield_mask.name)
16
+ enum_value = next(e for e in actual_enum_type if e.name == bitfield_mask.name)
19
17
  supported_values.append(enum_value)
20
18
 
21
19
  return supported_values
22
20
 
23
21
 
24
22
  def enum_list_to_bitfield(enum_list, bitfield_enum_type):
25
- """
26
- Converts a list of enums to a bitfield value.
23
+ """Converts a list of enums to a bitfield value.
27
24
 
28
25
  Args:
29
26
  enum_list (List[enum.Enum]): Specifies the list of enums.
30
27
  bitfield_enum_type (enum.Enum): Specifies the bitfield enum type
31
28
  from which to mask and extract the enum values.
29
+
32
30
  Returns:
33
31
  int: Indicates the value of the bitfield.
34
32
  """
35
33
  bitfield_value = 0
36
34
  for enum_value in enum_list:
37
- bitfield_mask = next(
38
- b for b in bitfield_enum_type if b.name == enum_value.name)
35
+ bitfield_mask = next(b for b in bitfield_enum_type if b.name == enum_value.name)
39
36
  bitfield_value |= bitfield_mask.value
40
37
 
41
- return bitfield_value
38
+ return bitfield_value
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ import sys
5
+ import traceback
6
+ from pathlib import Path
7
+
8
+
9
+ def get_dotenv_search_path() -> Path:
10
+ """Get the search path for loading the `.env` file."""
11
+ # Prefer to load the `.env` file from the current directory or its parents.
12
+ # If the current directory doesn't have a `.env` file, fall back to the
13
+ # script/EXE path or the TestStand code module path.
14
+ cwd = Path.cwd()
15
+ if not _has_dotenv_file(cwd):
16
+ if script_or_exe_path := _get_script_or_exe_path():
17
+ return script_or_exe_path.resolve().parent
18
+ if caller_path := _get_caller_path():
19
+ return caller_path.resolve().parent
20
+ return cwd
21
+
22
+
23
+ def _has_dotenv_file(dir: Path) -> bool:
24
+ """Check whether the dir or its parents contains a `.env` file."""
25
+ return (dir / ".env").exists() or any((p / ".env").exists() for p in dir.parents)
26
+
27
+
28
+ def _get_script_or_exe_path() -> Path | None:
29
+ """Get the path of the top-level script or PyInstaller EXE, if possible."""
30
+ if getattr(sys, "frozen", False):
31
+ return Path(sys.executable)
32
+
33
+ main_module = sys.modules.get("__main__")
34
+ if main_module:
35
+ script_path = getattr(main_module, "__file__", "")
36
+ if script_path:
37
+ return Path(script_path)
38
+
39
+ return None
40
+
41
+
42
+ def _get_caller_path() -> Path | None:
43
+ """Get the path of the module calling into this package, if possible."""
44
+ package_path = _get_package_path()
45
+ for frame, _ in traceback.walk_stack(inspect.currentframe()):
46
+ if frame.f_code.co_filename:
47
+ module_path = Path(frame.f_code.co_filename)
48
+ if _exists(module_path) and not module_path.is_relative_to(package_path):
49
+ return module_path
50
+
51
+ return None
52
+
53
+
54
+ # Path.exists() throws OSError when the path has invalid file characters.
55
+ # https://github.com/python/cpython/issues/79487
56
+ if sys.version_info >= (3, 10):
57
+
58
+ def _exists(path: Path) -> bool:
59
+ return path.exists()
60
+
61
+ else:
62
+
63
+ def _exists(path: Path) -> bool:
64
+ import os
65
+
66
+ return os.path.exists(path)
67
+
68
+
69
+ def _get_package_path() -> Path:
70
+ """Get the path of this package."""
71
+ module = sys.modules[__package__]
72
+ assert module.__file__ and module.__file__.endswith("__init__.py")
73
+ return Path(module.__file__).parent
@@ -0,0 +1,162 @@
1
+ """nidaqmx feature toggles."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import functools
6
+ import sys
7
+ from enum import Enum
8
+ from typing import TYPE_CHECKING, Callable, TypeVar
9
+
10
+ from decouple import AutoConfig, Undefined, undefined
11
+
12
+ from nidaqmx._dotenvpath import get_dotenv_search_path
13
+ from nidaqmx.errors import FeatureNotSupportedError
14
+
15
+ if TYPE_CHECKING:
16
+ if sys.version_info >= (3, 10):
17
+ from typing import ParamSpec
18
+ else:
19
+ from typing_extensions import ParamSpec
20
+
21
+ if sys.version_info >= (3, 11):
22
+ from typing import Self
23
+ else:
24
+ from typing_extensions import Self
25
+
26
+ _P = ParamSpec("_P")
27
+ _T = TypeVar("_T")
28
+
29
+ _PREFIX = "NIDAQMX"
30
+
31
+ _config = AutoConfig(str(get_dotenv_search_path()))
32
+
33
+ if TYPE_CHECKING:
34
+ # Work around decouple's lack of type hints.
35
+ def _config(
36
+ option: str,
37
+ default: _T | Undefined = undefined,
38
+ cast: Callable[[str], _T] | Undefined = undefined,
39
+ ) -> _T: ...
40
+
41
+
42
+ # Based on the recipe at https://docs.python.org/3/howto/enum.html
43
+ class _OrderedEnum(Enum):
44
+ def __ge__(self, other: Self) -> bool:
45
+ if self.__class__ is other.__class__:
46
+ return self.value >= other.value
47
+ return NotImplemented
48
+
49
+ def __gt__(self, other: Self) -> bool:
50
+ if self.__class__ is other.__class__:
51
+ return self.value > other.value
52
+ return NotImplemented
53
+
54
+ def __le__(self, other: Self) -> bool:
55
+ if self.__class__ is other.__class__:
56
+ return self.value <= other.value
57
+ return NotImplemented
58
+
59
+ def __lt__(self, other: Self) -> bool:
60
+ if self.__class__ is other.__class__:
61
+ return self.value < other.value
62
+ return NotImplemented
63
+
64
+
65
+ class CodeReadiness(_OrderedEnum):
66
+ """Indicates whether code is ready to be supported."""
67
+
68
+ RELEASE = 0
69
+ NEXT_RELEASE = 1
70
+ INCOMPLETE = 2
71
+ PROTOTYPE = 3
72
+
73
+
74
+ def _init_code_readiness_level() -> CodeReadiness:
75
+ if _config(f"{_PREFIX}_ALLOW_INCOMPLETE", default=False, cast=bool):
76
+ return CodeReadiness.INCOMPLETE
77
+ elif _config(f"{_PREFIX}_ALLOW_NEXT_RELEASE", default=False, cast=bool):
78
+ return CodeReadiness.NEXT_RELEASE
79
+ else:
80
+ return CodeReadiness.RELEASE
81
+
82
+
83
+ # This is not public because `from _feature_toggles import CODE_READINESS_LEVEL`
84
+ # is incompatible with the patching performed by the use_code_readiness mark.
85
+ _CODE_READINESS_LEVEL = _init_code_readiness_level()
86
+
87
+
88
+ def get_code_readiness_level() -> CodeReadiness:
89
+ """Get the current code readiness level.
90
+
91
+ You can override this in tests by specifying the ``use_code_readiness``
92
+ mark.
93
+ """
94
+ return _CODE_READINESS_LEVEL
95
+
96
+
97
+ class FeatureToggle:
98
+ """A run-time feature toggle."""
99
+
100
+ name: str
101
+ """The name of the feature."""
102
+
103
+ readiness: CodeReadiness
104
+ """The code readiness at which this feature is enabled."""
105
+
106
+ def __init__(self, name: str, readiness: CodeReadiness) -> None:
107
+ """Initialize the feature toggle."""
108
+ assert name == name.upper()
109
+ self.name = name
110
+ self.readiness = readiness
111
+ self._is_enabled_override = None
112
+ # Only read the env var at initialization time.
113
+ if _config(f"{_PREFIX}_ENABLE_{name}", default=False, cast=bool):
114
+ self._is_enabled_override = True
115
+
116
+ @property
117
+ def is_enabled(self) -> bool:
118
+ """Indicates whether the feature is currently enabled.
119
+
120
+ You can enable/disable features in tests by specifying the
121
+ ``enable_feature_toggle`` or ``disable_feature_toggle`` marks.
122
+ """
123
+ if self._is_enabled_override is not None:
124
+ return self._is_enabled_override
125
+ return self.readiness <= get_code_readiness_level()
126
+
127
+ def raise_if_disabled(self) -> None:
128
+ """Raises an error if the feature is disabled."""
129
+ if self.is_enabled:
130
+ return
131
+
132
+ env_vars = f"{_PREFIX}_ENABLE_{self.name}"
133
+ if self.readiness in [CodeReadiness.NEXT_RELEASE, CodeReadiness.INCOMPLETE]:
134
+ env_vars += f" or {_PREFIX}_ALLOW_{self.readiness.name}"
135
+ message = (
136
+ f"The {self.name} feature is not supported at the current code readiness level. "
137
+ f" To enable it, set {env_vars}."
138
+ )
139
+ raise FeatureNotSupportedError(message)
140
+
141
+
142
+ def requires_feature(
143
+ feature_toggle: FeatureToggle,
144
+ ) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]:
145
+ """Decorator specifying that the function requires the specified feature toggle."""
146
+
147
+ def decorator(func: Callable[_P, _T]) -> Callable[_P, _T]:
148
+ @functools.wraps(func)
149
+ def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _T:
150
+ feature_toggle.raise_if_disabled()
151
+ return func(*args, **kwargs)
152
+
153
+ return wrapper
154
+
155
+ return decorator
156
+
157
+
158
+ # --------------------------------------
159
+ # Define feature toggle constants here:
160
+ # --------------------------------------
161
+
162
+ WAVEFORM_SUPPORT = FeatureToggle("WAVEFORM_SUPPORT", CodeReadiness.INCOMPLETE)
@@ -5,7 +5,8 @@ import logging
5
5
  import threading
6
6
  import typing
7
7
  import warnings
8
- from typing import Callable, Generic, TypeVar
8
+ from nitypes.waveform import AnalogWaveform, DigitalWaveform
9
+ from typing import Any, Callable, Generic, Sequence, TypeVar
9
10
 
10
11
  import google.protobuf.message
11
12
  from google.protobuf.timestamp_pb2 import Timestamp as GrpcTimestamp
@@ -17,6 +18,7 @@ from nidaqmx._base_interpreter import BaseEventHandler, BaseInterpreter
17
18
  from nidaqmx._stubs import nidaqmx_pb2 as grpc_types
18
19
  from nidaqmx._stubs import nidaqmx_pb2_grpc as nidaqmx_grpc
19
20
  from nidaqmx._stubs import session_pb2 as session_grpc_types
21
+ from nidaqmx.constants import WaveformAttributeMode
20
22
  from nidaqmx.error_codes import DAQmxErrors
21
23
  from nidaqmx._grpc_time import convert_time_to_timestamp, convert_timestamp_to_time
22
24
 
@@ -3600,6 +3602,94 @@ class GrpcStubInterpreter(BaseInterpreter):
3600
3602
  def internal_get_last_created_chan(self):
3601
3603
  raise NotImplementedError
3602
3604
 
3605
+ def read_analog_waveform(
3606
+ self,
3607
+ task_handle: object,
3608
+ number_of_samples_per_channel: int,
3609
+ timeout: float,
3610
+ waveform: AnalogWaveform[numpy.float64],
3611
+ waveform_attribute_mode: WaveformAttributeMode
3612
+ ) -> int:
3613
+ raise NotImplementedError
3614
+
3615
+ def read_analog_waveforms(
3616
+ self,
3617
+ task_handle: object,
3618
+ number_of_samples_per_channel: int,
3619
+ timeout: float,
3620
+ waveforms: Sequence[AnalogWaveform[numpy.float64]],
3621
+ waveform_attribute_mode: WaveformAttributeMode
3622
+ ) -> int:
3623
+ raise NotImplementedError
3624
+
3625
+ def read_digital_waveform(
3626
+ self,
3627
+ task_handle: object,
3628
+ number_of_samples_per_channel: int,
3629
+ timeout: float,
3630
+ waveform: DigitalWaveform[Any],
3631
+ waveform_attribute_mode: WaveformAttributeMode
3632
+ ) -> int:
3633
+ raise NotImplementedError
3634
+
3635
+ def read_digital_waveforms(
3636
+ self,
3637
+ task_handle: object,
3638
+ channel_count: int,
3639
+ number_of_samples_per_channel: int,
3640
+ number_of_signals_per_sample: int,
3641
+ timeout: float,
3642
+ waveforms: Sequence[DigitalWaveform[Any]],
3643
+ waveform_attribute_mode: WaveformAttributeMode,
3644
+ ) -> int:
3645
+ raise NotImplementedError
3646
+
3647
+ def read_new_digital_waveforms(
3648
+ self,
3649
+ task_handle: object,
3650
+ channel_count: int,
3651
+ number_of_samples_per_channel: int,
3652
+ number_of_signals_per_sample: int,
3653
+ timeout: float,
3654
+ waveform_attribute_mode: WaveformAttributeMode,
3655
+ ) -> Sequence[DigitalWaveform[numpy.uint8]]:
3656
+ raise NotImplementedError
3657
+
3658
+ def write_analog_waveform(
3659
+ self,
3660
+ task_handle: object,
3661
+ waveform: AnalogWaveform[typing.Any],
3662
+ auto_start: bool,
3663
+ timeout: float
3664
+ ) -> int:
3665
+ raise NotImplementedError
3666
+
3667
+ def write_analog_waveforms(
3668
+ self,
3669
+ task_handle: object,
3670
+ waveforms: Sequence[AnalogWaveform[typing.Any]],
3671
+ auto_start: bool,
3672
+ timeout: float
3673
+ ) -> int:
3674
+ raise NotImplementedError
3675
+
3676
+ def write_digital_waveform(
3677
+ self,
3678
+ task_handle: object,
3679
+ waveform: DigitalWaveform[Any],
3680
+ auto_start: bool,
3681
+ timeout: float,
3682
+ ) -> int:
3683
+ raise NotImplementedError
3684
+
3685
+ def write_digital_waveforms(
3686
+ self,
3687
+ task_handle: object,
3688
+ waveform: Sequence[DigitalWaveform[Any]],
3689
+ auto_start: bool,
3690
+ timeout: float,
3691
+ ) -> int:
3692
+ raise NotImplementedError
3603
3693
 
3604
3694
  def _assign_numpy_array(numpy_array, grpc_array):
3605
3695
  """
@@ -1,14 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- from datetime import timezone
4
- from datetime import datetime as std_datetime
5
- from datetime import tzinfo as dt_tzinfo
6
- from hightime import datetime as ht_datetime
7
- from hightime import timedelta as ht_timedelta
8
- from typing import Optional, Union
9
- from nidaqmx._time import _convert_to_desired_timezone
3
+ from datetime import datetime as std_datetime, timezone, tzinfo as dt_tzinfo
10
4
 
11
5
  from google.protobuf.timestamp_pb2 import Timestamp as GrpcTimestamp
6
+ from hightime import datetime as ht_datetime, timedelta as ht_timedelta
7
+
8
+ from nidaqmx._time import _convert_to_desired_timezone
12
9
 
13
10
  # 66 years, 17 leap days = 24107 days = 2082844800 seconds
14
11
  _BIAS_FROM_1970_EPOCH = 2082844800
@@ -22,7 +19,10 @@ _YS_PER_FS = 10**9
22
19
 
23
20
  _EPOCH_1970 = ht_datetime(1970, 1, 1, tzinfo=timezone.utc)
24
21
 
25
- def convert_time_to_timestamp(dt: std_datetime | ht_datetime, ts: GrpcTimestamp | None = None) -> GrpcTimestamp:
22
+
23
+ def convert_time_to_timestamp( # noqa: D103 - Missing docstring in public function (auto-generated noqa)
24
+ dt: std_datetime | ht_datetime, ts: GrpcTimestamp | None = None
25
+ ) -> GrpcTimestamp:
26
26
  seconds_since_1970 = 0
27
27
 
28
28
  if ts is None:
@@ -44,10 +44,13 @@ def convert_time_to_timestamp(dt: std_datetime | ht_datetime, ts: GrpcTimestamp
44
44
  ts.FromNanoseconds(seconds_since_1970 * _NS_PER_S + nanos)
45
45
  return ts
46
46
 
47
- def convert_timestamp_to_time(ts: GrpcTimestamp, tzinfo: dt_tzinfo | None = None) -> ht_datetime:
47
+
48
+ def convert_timestamp_to_time( # noqa: D103 - Missing docstring in public function (auto-generated noqa)
49
+ ts: GrpcTimestamp, tzinfo: dt_tzinfo | None = None
50
+ ) -> ht_datetime:
48
51
  total_nanos = ts.ToNanoseconds()
49
52
  seconds, nanos = divmod(total_nanos, _NS_PER_S)
50
53
  # Convert the nanoseconds to yoctoseconds.
51
54
  total_yoctoseconds = int(round(_YS_PER_NS * nanos))
52
- dt = _EPOCH_1970 + ht_timedelta(seconds = seconds) + ht_timedelta(yoctoseconds=total_yoctoseconds)
55
+ dt = _EPOCH_1970 + ht_timedelta(seconds=seconds) + ht_timedelta(yoctoseconds=total_yoctoseconds)
53
56
  return _convert_to_desired_timezone(dt, tzinfo)