Skip to content
25 changes: 13 additions & 12 deletions build/helper/metadata_filters.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .metadata_find import find_len_size_parameter_names
from .metadata_find import find_size_parameter
from .parameter_usage_options import ParameterUsageOptions

Expand Down Expand Up @@ -348,23 +349,26 @@ def filter_parameters(parameters, parameter_usage_options):
parameters_to_use = []

# Filter based on options
size_parameter = None
ivi_dance_size_parameter = None
len_size_parameter_names = set()
size_twist_parameter = None
# If we are being called looking for the ivi-dance, len or code param, we do not care about the size param so we do
# not call back into ourselves, to avoid infinite recursion
if parameter_usage_options not in [ParameterUsageOptions.IVI_DANCE_PARAMETER, ParameterUsageOptions.LEN_PARAMETER]:
# Find the size parameter - we are assuming there can only be one type, either from ivi-dance or len
Copy link
Collaborator

@ni-jfitzger ni-jfitzger Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

   # Find the size parameter - we are assuming there can only be one type, either from ivi-dance or len

You deleted this comment and the if statement guarding population of len size parameters.
But I don't see any testing for mixed usage.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Jay.
I added two unit tests that verify ivi-dance and len used together, including a case with multiple len size parameters to cover the mixed usage cases.

size_parameter = find_size_parameter(filter_ivi_dance_parameters(parameters), parameters)
if size_parameter is None:
size_parameter = find_size_parameter(filter_len_parameters(parameters), parameters)
# Determine any size parameters that should be skipped based on the presence of ivi-dance or len-sized buffers.
# For ivi-dance, there is a single shared size parameter; for len, there may be multiple independent size parameters.
ivi_dance_size_parameter = find_size_parameter(filter_ivi_dance_parameters(parameters), parameters)
len_size_parameter_names = find_len_size_parameter_names(parameters)
size_twist_parameter = find_size_parameter(filter_ivi_dance_twist_parameters(parameters), parameters, key='value_twist')
for x in parameters:
skip = False
if x['direction'] == 'out' and options_to_use['skip_output_parameters']:
skip = True
if x['direction'] == 'in' and options_to_use['skip_input_parameters']:
skip = True
if x == size_parameter and options_to_use['skip_size_parameter']:
if ivi_dance_size_parameter is not None and x == ivi_dance_size_parameter and options_to_use['skip_size_parameter']:
skip = True
if len_size_parameter_names and x['name'] in len_size_parameter_names and options_to_use['skip_size_parameter']:
skip = True
if size_twist_parameter is not None and x == size_twist_parameter and options_to_use['skip_size_parameter']:
skip = True
Expand Down Expand Up @@ -443,18 +447,15 @@ def filter_ivi_dance_twist_parameters(parameters):
def filter_len_parameters(parameters):
'''Returns the len parameters of a session method if there are any. These are the parameters whose size is determined at runtime using the value of a different parameter.

asserts all parameters that use len reference the same parameter
Note: Multiple len parameters may reference different size parameters.
Args:
parameters: parameters to be checked

Return:
None if no len parameter found
Parameters dict if one is found
Empty list if no len parameter found
List of parameter dicts if any are found
'''
params = filter_parameters(parameters, ParameterUsageOptions.LEN_PARAMETER)
if len(params) > 0:
size_param = params[0]['size']['value']
assert all(x['size']['value'] == size_param for x in params)
return params


Expand Down
16 changes: 16 additions & 0 deletions build/helper/metadata_find.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ def find_size_parameter(parameter_list, parameters, key='value'):
return None


def find_len_size_parameter_names(parameters):
'''Returns names of size parameters referenced by len-sized parameters.

A function may have multiple len-sized parameters, each with a different size parameter.
'''
len_size_parameter_names = set()
len_parameters = [parameter for parameter in parameters if parameter['size']['mechanism'] == 'len']

for parameter in len_parameters:
len_size_parameter = find_size_parameter(parameter, parameters)
if len_size_parameter is not None:
len_size_parameter_names.add(len_size_parameter['name'])

return len_size_parameter_names


def find_custom_type(p, config):
for c in config['custom_types']:
if p['ctypes_type'] == c['ctypes_type']:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
ivi_dance_parameters = helper.filter_ivi_dance_parameters(parameters)
ivi_dance_size_parameter = helper.find_size_parameter(ivi_dance_parameters, parameters)
len_parameters = helper.filter_len_parameters(parameters)
len_size_parameter = helper.find_size_parameter(len_parameters, parameters)
assert ivi_dance_size_parameter is None or len_size_parameter is None
full_func_name = f['interpreter_name'] + method_template['method_python_name_suffix']
c_func_name = config['c_function_prefix'] + f['name']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@

ivi_dance_parameters = helper.filter_ivi_dance_parameters(parameters)
ivi_dance_size_parameter = helper.find_size_parameter(ivi_dance_parameters, parameters)
len_parameters = helper.filter_len_parameters(parameters)
len_size_parameter = helper.find_size_parameter(len_parameters, parameters)
assert ivi_dance_size_parameter is None or len_size_parameter is None

full_func_name = f['interpreter_name'] + method_template['method_python_name_suffix']
c_func_name = config['c_function_prefix'] + f['name']
Expand Down
52 changes: 52 additions & 0 deletions build/unit_tests/test_metadata_filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from build.helper.metadata_filters import filter_parameters
from build.helper.parameter_usage_options import ParameterUsageOptions


def _parameter(name, direction='in', mechanism='fixed', size_value=None):
size = {'mechanism': mechanism}
if size_value is not None:
size['value'] = size_value

return {
'name': name,
'direction': direction,
'size': size,
'is_session_handle': False,
'is_repeated_capability': False,
'enum': None,
'numpy': False,
'use_in_python_api': True,
'complex_array_representation': None,
}


def test_filter_parameters_mixed_usage_ivi_dance_and_len():
parameters = [
_parameter('ivi_size', mechanism='passed-in'),
_parameter('out_waveform', direction='out', mechanism='ivi-dance', size_value='ivi_size'),
_parameter('len_size', mechanism='passed-in'),
_parameter('len_data', mechanism='len', size_value='len_size'),
_parameter('timeout', mechanism='fixed'),
]

filtered = filter_parameters(parameters, ParameterUsageOptions.SESSION_METHOD_DECLARATION)
filtered_names = [parameter['name'] for parameter in filtered]

assert filtered_names == ['len_data', 'timeout']


def test_filter_parameters_mixed_usage_ivi_dance_and_multiple_len_sizes():
parameters = [
_parameter('ivi_size', mechanism='passed-in'),
_parameter('out_waveform', direction='out', mechanism='ivi-dance', size_value='ivi_size'),
_parameter('len_a_size', mechanism='passed-in'),
_parameter('len_b_size', mechanism='passed-in'),
_parameter('len_a_data', mechanism='len', size_value='len_a_size'),
_parameter('len_b_data', mechanism='len', size_value='len_b_size'),
_parameter('timeout', mechanism='fixed'),
]

filtered = filter_parameters(parameters, ParameterUsageOptions.INTERPRETER_METHOD_DECLARATION)
filtered_names = [parameter['name'] for parameter in filtered]

assert filtered_names == ['len_a_data', 'len_b_data', 'timeout']
37 changes: 37 additions & 0 deletions build/unit_tests/test_metadata_find.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from build.helper.metadata_find import find_len_size_parameter_names


def _parameter(name, mechanism='fixed', size_value=None):
size = {'mechanism': mechanism}
if size_value is not None:
size['value'] = size_value

return {
'name': name,
'size': size,
}


def test_find_len_size_parameter_names_multiple_sizes():
parameters = [
_parameter('len_a_size', mechanism='passed-in'),
_parameter('len_b_size', mechanism='passed-in'),
_parameter('len_a_data', mechanism='len', size_value='len_a_size'),
_parameter('len_b_data', mechanism='len', size_value='len_b_size'),
_parameter('timeout', mechanism='fixed'),
]

size_names = find_len_size_parameter_names(parameters)

assert size_names == {'len_a_size', 'len_b_size'}


def test_find_len_size_parameter_names_empty_when_no_len_parameters():
parameters = [
_parameter('value', mechanism='passed-in'),
_parameter('timeout', mechanism='fixed'),
]

size_names = find_len_size_parameter_names(parameters)

assert size_names == set()
6 changes: 6 additions & 0 deletions generated/nifake/nifake/_grpc_stub_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,12 @@ def multiple_array_types(self, output_array_size, input_array_of_floats, input_a
)
return response.output_array, response.output_array_of_fixed_length

def multiple_arrays_different_size(self, values_array, data_array): # noqa: N802
self._invoke(
self._client.MultipleArraysDifferentSize,
grpc_types.MultipleArraysDifferentSizeRequest(vi=self._vi, values_array=values_array, data_array=data_array),
)

def multiple_arrays_same_size(self, values1, values2, values3, values4): # noqa: N802
self._invoke(
self._client.MultipleArraysSameSize,
Expand Down
9 changes: 9 additions & 0 deletions generated/nifake/nifake/_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def __init__(self, ctypes_library):
self.niFake_MethodWithGrpcOnlyParam_cfunc = None
self.niFake_MethodWithProtoOnlyParameter_cfunc = None
self.niFake_MultipleArrayTypes_cfunc = None
self.niFake_MultipleArraysDifferentSize_cfunc = None
self.niFake_MultipleArraysSameSize_cfunc = None
self.niFake_OneInputFunction_cfunc = None
self.niFake_ParametersAreMultipleTypes_cfunc = None
Expand Down Expand Up @@ -477,6 +478,14 @@ def niFake_MultipleArrayTypes(self, vi, output_array_size, output_array, output_
self.niFake_MultipleArrayTypes_cfunc.restype = ViStatus # noqa: F405
return self.niFake_MultipleArrayTypes_cfunc(vi, output_array_size, output_array, output_array_of_fixed_length, input_array_sizes, input_array_of_floats, input_array_of_integers)

def niFake_MultipleArraysDifferentSize(self, vi, values_array, values_array_size, data_array, data_array_size): # noqa: N802
with self._func_lock:
if self.niFake_MultipleArraysDifferentSize_cfunc is None:
self.niFake_MultipleArraysDifferentSize_cfunc = self._get_library_function('niFake_MultipleArraysDifferentSize')
self.niFake_MultipleArraysDifferentSize_cfunc.argtypes = [ViSession, ctypes.POINTER(ViReal64), ViInt32, ctypes.POINTER(ViInt32), ViInt32] # noqa: F405
self.niFake_MultipleArraysDifferentSize_cfunc.restype = ViStatus # noqa: F405
return self.niFake_MultipleArraysDifferentSize_cfunc(vi, values_array, values_array_size, data_array, data_array_size)

def niFake_MultipleArraysSameSize(self, vi, values1, values2, values3, values4, size): # noqa: N802
with self._func_lock:
if self.niFake_MultipleArraysSameSize_cfunc is None:
Expand Down
10 changes: 10 additions & 0 deletions generated/nifake/nifake/_library_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,16 @@ def multiple_array_types(self, output_array_size, input_array_of_floats, input_a
errors.handle_error(self, error_code, ignore_warnings=False, is_error_handling=False)
return [float(output_array_ctype[i]) for i in range(output_array_size_ctype.value)], [float(output_array_of_fixed_length_ctype[i]) for i in range(3)]

def multiple_arrays_different_size(self, values_array, data_array): # noqa: N802
vi_ctype = _visatype.ViSession(self._vi) # case S110
values_array_ctype = _get_ctypes_pointer_for_buffer(value=values_array, library_type=_visatype.ViReal64) # case B550
values_array_size_ctype = _visatype.ViInt32(0 if values_array is None else len(values_array)) # case S160
data_array_ctype = _get_ctypes_pointer_for_buffer(value=data_array, library_type=_visatype.ViInt32) # case B550
data_array_size_ctype = _visatype.ViInt32(0 if data_array is None else len(data_array)) # case S160
error_code = self._library.niFake_MultipleArraysDifferentSize(vi_ctype, values_array_ctype, values_array_size_ctype, data_array_ctype, data_array_size_ctype)
errors.handle_error(self, error_code, ignore_warnings=False, is_error_handling=False)
return

def multiple_arrays_same_size(self, values1, values2, values3, values4): # noqa: N802
vi_ctype = _visatype.ViSession(self._vi) # case S110
values1_ctype = _get_ctypes_pointer_for_buffer(value=values1, library_type=_visatype.ViReal64) # case B550
Expand Down
14 changes: 14 additions & 0 deletions generated/nifake/nifake/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,20 @@ def multiple_array_types(self, output_array_size, input_array_of_floats, input_a
output_array, output_array_of_fixed_length = self._interpreter.multiple_array_types(output_array_size, input_array_of_floats, input_array_of_integers)
return output_array, output_array_of_fixed_length

@ivi_synchronized
def multiple_arrays_different_size(self, values_array, data_array):
r'''multiple_arrays_different_size

Test method with multiple arrays that have different size parameters. This tests the length handling mechanism where different array parameters can reference different size parameters.

Args:
values_array (list of float): Array of double values with its own size parameter.

data_array (list of int): Array of integer values with a different size parameter.

'''
self._interpreter.multiple_arrays_different_size(values_array, data_array)

@ivi_synchronized
def multiple_arrays_same_size(self, values1, values2, values3, values4):
r'''multiple_arrays_same_size
Expand Down
9 changes: 9 additions & 0 deletions generated/nifake/nifake/unit_tests/_mock_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ def __init__(self):
self._defaults['MultipleArrayTypes']['return'] = 0
self._defaults['MultipleArrayTypes']['outputArray'] = None
self._defaults['MultipleArrayTypes']['outputArrayOfFixedLength'] = None
self._defaults['MultipleArraysDifferentSize'] = {}
self._defaults['MultipleArraysDifferentSize']['return'] = 0
self._defaults['MultipleArraysSameSize'] = {}
self._defaults['MultipleArraysSameSize']['return'] = 0
self._defaults['OneInputFunction'] = {}
Expand Down Expand Up @@ -763,6 +765,11 @@ def niFake_MultipleArrayTypes(self, vi, output_array_size, output_array, output_
output_array_of_fixed_length_ref[i] = test_value[i]
return self._defaults['MultipleArrayTypes']['return']

def niFake_MultipleArraysDifferentSize(self, vi, values_array, values_array_size, data_array, data_array_size): # noqa: N802
if self._defaults['MultipleArraysDifferentSize']['return'] != 0:
return self._defaults['MultipleArraysDifferentSize']['return']
return self._defaults['MultipleArraysDifferentSize']['return']

def niFake_MultipleArraysSameSize(self, vi, values1, values2, values3, values4, size): # noqa: N802
if self._defaults['MultipleArraysSameSize']['return'] != 0:
return self._defaults['MultipleArraysSameSize']['return']
Expand Down Expand Up @@ -1122,6 +1129,8 @@ def set_side_effects_and_return_values(self, mock_library):
mock_library.niFake_MethodWithProtoOnlyParameter.return_value = 0
mock_library.niFake_MultipleArrayTypes.side_effect = MockFunctionCallError("niFake_MultipleArrayTypes")
mock_library.niFake_MultipleArrayTypes.return_value = 0
mock_library.niFake_MultipleArraysDifferentSize.side_effect = MockFunctionCallError("niFake_MultipleArraysDifferentSize")
mock_library.niFake_MultipleArraysDifferentSize.return_value = 0
mock_library.niFake_MultipleArraysSameSize.side_effect = MockFunctionCallError("niFake_MultipleArraysSameSize")
mock_library.niFake_MultipleArraysSameSize.return_value = 0
mock_library.niFake_OneInputFunction.side_effect = MockFunctionCallError("niFake_OneInputFunction")
Expand Down
14 changes: 14 additions & 0 deletions generated/nifake/nifake/unit_tests/test_library_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,20 @@ def test_multiple_arrays_same_size_none_input(self):
_matchers.ViInt32Matcher(len(input_array_of_floats1)),
)

def test_multiple_arrays_different_size(self):
self.patched_library.niFake_MultipleArraysDifferentSize.side_effect = self.side_effects_helper.niFake_MultipleArraysDifferentSize
values_array = [1.1, 2.2, 3.3]
data_array = [10, 20, 30, 40, 50]
interpreter = self.get_initialized_library_interpreter()
interpreter.multiple_arrays_different_size(values_array, data_array)
self.patched_library.niFake_MultipleArraysDifferentSize.assert_called_once_with(
_matchers.ViSessionMatcher(SESSION_NUM_FOR_TEST),
_matchers.ViReal64BufferMatcher(values_array),
_matchers.ViInt32Matcher(len(values_array)),
_matchers.ViInt32BufferMatcher(data_array),
_matchers.ViInt32Matcher(len(data_array)),
)

def test_parameters_are_multiple_types(self):
self.patched_library.niFake_ParametersAreMultipleTypes.side_effect = self.side_effects_helper.niFake_ParametersAreMultipleTypes
boolean_val = True
Expand Down
64 changes: 64 additions & 0 deletions src/nifake/metadata/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3120,6 +3120,70 @@
'python_name': 'self_test',
'returns': 'ViStatus'
},
'MultipleArraysDifferentSize': {
'codegen_method': 'public',
'documentation': {
'description': 'Test function with multiple arrays that have different size parameters. This tests the length handling mechanism where different array parameters can reference different size parameters.'
},
'included_in_proto': True,
'parameters': [
{
'direction': 'in',
'documentation': {
'description': 'Identifies a particular instrument session.'
},
'name': 'vi',
'type': 'ViSession'
},
{
'direction': 'in',
'documentation': {
'description': 'Array of double values with its own size parameter.'
},
'name': 'valuesArray',
'numpy': True,
'size': {
'mechanism': 'len',
'value': 'valuesArraySize'
},
'type': 'ViReal64[]',
'use_in_python_api': True
},
{
'direction': 'in',
'documentation': {
'description': 'Specifies the size of the values array.'
},
'name': 'valuesArraySize',
'type': 'ViInt32',
'use_array': False
},
{
'direction': 'in',
'documentation': {
'description': 'Array of integer values with a different size parameter.'
},
'name': 'dataArray',
'numpy': True,
'size': {
'mechanism': 'len',
'value': 'dataArraySize'
},
'type': 'ViInt32[]',
'use_in_python_api': True
},
{
'direction': 'in',
'documentation': {
'description': 'Specifies the size of the data array.'
},
'name': 'dataArraySize',
'type': 'ViInt32',
'use_array': False
}
],
'returns': 'ViStatus'
},
'self_test': {
'codegen_method': 'private',
'documentation': {
Expand Down
Loading