Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions build/helper/metadata_add_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,40 @@ def add_all_config_metadata(config):
'''
config = merge_helper(config, 'config', config, use_re=False)

for repeated_capability in config['repeated_capabilities']:
documentation = repeated_capability.setdefault('documentation', {})
prefix = repeated_capability['prefix']
name = repeated_capability['python_name']

if prefix:
documentation.setdefault(
'description',
(
'If no prefix is added to the items in the parameter, the correct prefix will be added when\n'
'the driver function call is made.\n\n'
".. code:: python\n\n session.{}['0-2'].channel_enabled = True\n\n"
"passes a string of :python:`'{}0, {}1, {}2'` to the set attribute function.\n\n"
'If an invalid repeated capability is passed to the driver, the driver will return an error.\n\n'
'You can also explicitly use the prefix as part of the parameter, but it must be the correct prefix\n'
'for the specific repeated capability.'
).format(name, prefix, prefix, prefix)
)
else:
documentation.setdefault('description', '')

documentation.setdefault(
'examples',
[
{
'code': "session.{}['{}0-{}2'].channel_enabled = True".format(name, prefix, prefix),
'description': (
"passes a string of :python:`'{}0, {}1, {}2'` to the set attribute function."
).format(prefix, prefix, prefix),
}
]
)
documentation.setdefault('valid_indices', [])
Comment thread
ni-jfitzger marked this conversation as resolved.

if 'use_locking' not in config:
config['use_locking'] = True

Expand Down
29 changes: 13 additions & 16 deletions build/templates/rep_caps.rst.mako
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<%
import build.helper as helper
import textwrap

config = template_parameters['metadata'].config
module_name = config['module_name']
Expand Down Expand Up @@ -33,34 +34,30 @@ ${helper.get_rst_header_snippet('Repeated Capabilities', '=')}
% for rep_cap in config['repeated_capabilities']:
<%
name = rep_cap['python_name']
prefix = rep_cap['prefix']
rep_cap_doc = rep_cap['documentation']
%>\
${helper.get_rst_header_snippet(name, '-')}

.. py:attribute:: ${module_name}.Session.${name}[]

% if len(prefix) > 0:
If no prefix is added to the items in the parameter, the correct prefix will be added when
the driver function call is made.
% if rep_cap_doc['description']:
${textwrap.indent(rep_cap_doc['description'], ' ')}

.. code:: python

session.${name}['0-2'].channel_enabled = True

passes a string of :python:`'${prefix}0, ${prefix}1, ${prefix}2'` to the set attribute function.

If an invalid repeated capability is passed to the driver, the driver will return an error.

You can also explicitly use the prefix as part of the parameter, but it must be the correct prefix
for the specific repeated capability.
% endif
% if rep_cap_doc['valid_indices']:
Valid Indices: :python:`'${", ".join(rep_cap_doc["valid_indices"])}'`.

% endif
% for example in rep_cap_doc['examples']:
.. code:: python

session.${name}['${prefix}0-${prefix}2'].channel_enabled = True
${textwrap.indent(example['code'], ' ')}

passes a string of :python:`'${prefix}0, ${prefix}1, ${prefix}2'` to the set attribute function.
% if example['description']:
${textwrap.indent(example['description'], ' ')}

% endif
% endfor

% endfor

37 changes: 29 additions & 8 deletions build/unit_tests/test_metadata_add_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -961,9 +961,7 @@ def _compare_dicts(actual, expected):
},
],
'enum_whitelist_suffix': ['_POINT_FIVE'],
'repeated_capabilities': [
{'python_name': 'channels', 'prefix': '', },
],
'repeated_capabilities': [],
# These are added here strictly for testing.
'functions': {},
'attributes': {},
Expand Down Expand Up @@ -1007,9 +1005,7 @@ def _compare_dicts(actual, expected):
},
],
'enum_whitelist_suffix': ['_POINT_FIVE'],
'repeated_capabilities': [
{'python_name': 'channels', 'prefix': '', },
],
'repeated_capabilities': [],
'use_locking': True,
'functions': functions_expected,
'attributes': attributes_expected,
Expand All @@ -1021,6 +1017,31 @@ def _compare_dicts(actual, expected):
}


config_with_custom_rep_cap_documentation = copy.deepcopy(config_input)
config_with_custom_rep_cap_documentation['repeated_capabilities'] = [
{
'python_name': 'resources',
'prefix': 'res',
'documentation': {
'description': 'Resources use fully-qualified identifiers.',
'examples': [
{
'code': "session.resources['dev0/res0'].channel_enabled = True",
'description': 'Enables the first resource.',
}
],
'valid_indices': ['dev0/res0', 'dev0/res1'],
},
},
]


config_with_custom_rep_cap_documentation_expected = copy.deepcopy(config_expected)
config_with_custom_rep_cap_documentation_expected['repeated_capabilities'] = copy.deepcopy(
config_with_custom_rep_cap_documentation['repeated_capabilities']
)


def _do_the_test_add_functions_metadata(functions, expected):
actual = copy.deepcopy(functions)
actual = add_all_function_metadata(actual, config_input)
Expand Down Expand Up @@ -1073,9 +1094,9 @@ def test_add_all_metadata():
actual_functions = copy.deepcopy(functions_input)
actual_attributes = copy.deepcopy(attributes_input)
actual_enums = copy.deepcopy(enums_input)
actual_config = copy.deepcopy(config_input)
actual_config = copy.deepcopy(config_with_custom_rep_cap_documentation)
actual_config['use_locking'] = False
expected = copy.deepcopy(config_expected)
expected = copy.deepcopy(config_with_custom_rep_cap_documentation_expected)
expected['use_locking'] = False
_do_the_test_add_all_metadata(
functions=actual_functions,
Expand Down
117 changes: 117 additions & 0 deletions build/unit_tests/test_rep_caps_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
from pathlib import Path
from tempfile import TemporaryDirectory
from types import SimpleNamespace

from build.generate_template import generate_template
from build.helper.metadata_add_all import add_all_config_metadata


def _render_rep_caps(config):
repo_root = Path(__file__).resolve().parents[2]
template_path = repo_root / 'build' / 'templates' / 'rep_caps.rst.mako'
metadata = SimpleNamespace(config=add_all_config_metadata(config))
with TemporaryDirectory() as temp_dir:
output_path = Path(temp_dir) / 'rep_caps.rst'
generate_template(str(template_path), {'metadata': metadata}, str(output_path))
return output_path.read_text()


def test_custom_documentation_overwrites_rep_caps_template_defaults():
config = {
'module_name': 'nifake',
'c_function_prefix': 'niFake_',
'repeated_capabilities': [
{
'prefix': 'res',
'python_name': 'resources',
'documentation': {
'description': 'Resource repeated capabilities use fully-qualified identifiers.',
'valid_indices': ['dev0/res0', 'dev0/res1'],
'examples': [
{
'code': (
"session.resources['dev0/res0'].channel_enabled = True\n"
"session.resources['dev0/res1'].channel_enabled = True"
),
'description': (
'The first line enables resource 0.\n'
'The second line enables resource 1.'
),
},
{
'code': "session.resources['dev0/res2'].channel_enabled = True",
'description': '',
},
],
},
}
],
}

rendered = _render_rep_caps(config)

assert 'Resource repeated capabilities use fully-qualified identifiers.' in rendered
assert "Valid Indices: :python:`'dev0/res0, dev0/res1'`." in rendered
assert "session.resources['dev0/res0'].channel_enabled = True" in rendered
assert "session.resources['dev0/res1'].channel_enabled = True" in rendered
assert "session.resources['dev0/res2'].channel_enabled = True" in rendered
assert 'The first line enables resource 0.' in rendered
assert 'The second line enables resource 1.' in rendered

# Custom documentation should override the generic auto-prefix guidance.
assert 'If no prefix is added to the items in the parameter' not in rendered
assert "session.resources['0-2'].channel_enabled = True" not in rendered
assert "'res0, res1, res2'" not in rendered


def test_rep_caps_template_preserves_default_prefixed_behavior():
config = {
'module_name': 'nifake',
'c_function_prefix': 'niFake_',
'repeated_capabilities': [
{
'prefix': 'channel',
'python_name': 'channels',
},
{
'prefix': '',
'python_name': 'instruments',
}
],
}

rendered = _render_rep_caps(config)

assert 'If no prefix is added to the items in the parameter' in rendered
assert "session.channels['0-2'].channel_enabled = True" in rendered
assert "'channel0, channel1, channel2'" in rendered
example_description = (
" passes a string of :python:`'channel0, channel1, channel2'` to the set attribute function."
)
assert rendered.count(example_description) == 2
assert '\n passes a string' not in rendered
assert "set attribute function.\n\n\ninstruments\n" in rendered
assert rendered.endswith('\n\n\n\n')
assert not any(line.isspace() for line in rendered.splitlines())


def test_rep_caps_template_expands_default_documentation_fields():
config = {
'module_name': 'nifake',
'c_function_prefix': 'niFake_',
'repeated_capabilities': [
{
'prefix': 'channel',
'python_name': 'channels',
'documentation': {
'description': 'Custom channel documentation.',
},
}
],
}

rendered = _render_rep_caps(config)

assert 'Custom channel documentation.' in rendered
assert "session.channels['channel0-channel2'].channel_enabled = True" in rendered
assert "'channel0, channel1, channel2'" in rendered
4 changes: 4 additions & 0 deletions generated/nifake/nifake/unit_tests/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,10 @@ def test_chained_repeated_capabilities_list(self):
with nifake.Session('dev1') as session:
assert session.sites[0, 1].channels[2, 3]._repeated_capability_list == ['site0/2', 'site0/3', 'site1/2', 'site1/3']

def test_multi_instrument_chained_repeated_capabilities_list(self):
with nifake.Session('dev1,dev2') as session:
assert session.instruments['dev1', 'dev2'].sites[0, 1]._repeated_capability_list == ['dev1/site0', 'dev1/site1', 'dev2/site0', 'dev2/site1']

def test_chained_repeated_capability_method_on_specific_channel(self):
test_maximum_time_ms = 10 # milliseconds
test_maximum_time = hightime.timedelta(milliseconds=test_maximum_time_ms)
Expand Down
4 changes: 4 additions & 0 deletions src/nifake/unit_tests/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,10 @@ def test_chained_repeated_capabilities_list(self):
with nifake.Session('dev1') as session:
assert session.sites[0, 1].channels[2, 3]._repeated_capability_list == ['site0/2', 'site0/3', 'site1/2', 'site1/3']

def test_multi_instrument_chained_repeated_capabilities_list(self):
with nifake.Session('dev1,dev2') as session:
assert session.instruments['dev1', 'dev2'].sites[0, 1]._repeated_capability_list == ['dev1/site0', 'dev1/site1', 'dev2/site0', 'dev2/site1']

def test_chained_repeated_capability_method_on_specific_channel(self):
test_maximum_time_ms = 10 # milliseconds
test_maximum_time = hightime.timedelta(milliseconds=test_maximum_time_ms)
Expand Down
Loading