Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove ct validation when active validation is disabled (#269) #271

Merged
merged 5 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
* The function `enable_qccs_mode` of the HDAWG driver now accept an optional argument to select the QCCS generation.
It's advised to set it to 2 (gen2) when the HDAWG is operated with PQSC together with SHF instruments, and set it
to 1 (gen1) when the HDAWG is operated with PQSC together with UHFQA instruments.
* Improved `CommandTable` performance when `CommandTable.active_validation` is disabled.
* Added `CommandTable.is_valid()` method to check validity of the command table.
* Added `py.typed` type information marker file.

## Version 0.6.1
* Deep gets on nodes with keywords returns an enum like the regular get.
Expand All @@ -19,7 +22,7 @@
This means only `*` symbols are supported. A `*` in the middle of the path matches
everything instead of a `/`. A `*` at the end of the path matches everything.
* Fix problem of garbage collection daq sessions. The problem was caused due to using
lru caches for instance methods. The usage of lru cache has now been bypassed or
lru caches for instance methods. The usage of lru cache has now been bypassed or
replaced with the `functools.cached_property` decorator (which is currently copied
to ensure support for python 3.7).
* `device.factory_reset` now raises an exception if the factory reset was not successful (`#243`).
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
recursive-include src/ *.json
include src/zhinst/toolkit/py.typed
65 changes: 56 additions & 9 deletions src/zhinst/toolkit/command_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
def __init__(
self, schema: dict, path: t.Tuple[str, ...], active_validation: bool = True
):
self._schema = copy.deepcopy(schema)
self._schema = schema
self._path = path
self._childs: t.Dict[t.Union[str, int], t.Any] = {}
self._active_validation = active_validation
Expand Down Expand Up @@ -215,23 +215,25 @@
"""

def __init__(
self, schema: dict, path: t.Tuple[str, ...], active_validation: bool = True
self,
schema: dict,
index_schema: dict,
path: t.Tuple[str, ...],
active_validation: bool = True,
):
super().__init__(schema, path, active_validation)
self._min_length = schema["minItems"]
self._max_length = schema["maxItems"]

self._index_schema = schema["items"]["properties"]["index"]
self._schema["items"]["properties"].pop("index", None)
self._index_schema = index_schema

def __len__(self):
return len(self._childs)

def __getitem__(self, number: int) -> ParentEntry:
self._validate_instance(number, self._index_schema)
try:
return self._childs[number]
except KeyError:
self._validate_instance(number, self._index_schema)
self._childs[number] = ParentEntry(
self._schema["items"],
self._path + (str(number),),
Expand Down Expand Up @@ -338,6 +340,9 @@
No validation happens during command table entry modifications, thus
making the creation of the command table faster.

Method `is_valid()` can be used for command table validation when active
validation is disabled. It is recommended to avoid it in production code.

.. versionadded:: 0.5.0
The ``active_validation`` parameter was added.

Expand Down Expand Up @@ -442,8 +447,13 @@
)

def _table_entry(self) -> ListEntry:
schema = copy.deepcopy(self._ct_schema["definitions"]["table"])
index_schema = schema["items"]["properties"].pop("index")

Check failure

Code scanning / CodeQL

Modification of parameter with default Error

This expression mutates a
default value
.
return ListEntry(
self._ct_schema["definitions"]["table"], ("table",), self._active_validation
schema=schema,
index_schema=index_schema,
path=("table",),
active_validation=self._active_validation,
)

def clear(self) -> None:
Expand All @@ -466,6 +476,11 @@
:class:`~zhinst.toolkit.exceptions.ValidateError`: The command table
does not correspond to the given JSON schema.

.. versionchanged:: 0.6.2

Removed validation when `active_validation` is set to `False`.
This improves performance when validation is not needed.

.. versionchanged:: 0.4.2

Removed `$schema` key from resulting dictionary.
Expand All @@ -474,9 +489,40 @@
"header": self._header.as_dict(),
"table": self._table.as_list(),
}
_validate_instance(result, self._ct_schema)
if self.active_validation:
_validate_instance(result, self._ct_schema)
return result

def is_valid(self, raise_for_invalid: bool = False) -> bool:
"""Checks if the command table is valid.

Args:
raise_for_invalid: Raises exception if the command table is invalid.
The flag can be used for getting feedback on what is wrong in
the command table.

Returns:
True if the command table is valid.

Raises:
ValidationError: If `raise_for_invalid` was set to `True` and the
command table is invalid.

.. versionadded:: 0.6.2
"""
# Due to active_validation having a state, we need to force it to True.
orig_state = self._active_validation
try:
self._active_validation = True if self._active_validation is False else True
self.as_dict()
self._active_validation = orig_state
return True
except ValidationError as e:
self._active_validation = orig_state
if raise_for_invalid:
raise e
return False

def update(self, command_table: t.Union[str, dict]) -> None:
"""Update the existing instance of ``CommandTable`` with command table JSON.

Expand All @@ -493,7 +539,8 @@
return json_ # type: ignore[return-value]

command_table = json_to_dict(copy.deepcopy(command_table))
_validate_instance(command_table, self._ct_schema)
if self._active_validation:
_validate_instance(command_table, self._ct_schema)

def build_nodes(path: t.Optional[ParentEntry], index: int, obj: dict):
for k, v in obj.items():
Expand Down
Empty file added src/zhinst/toolkit/py.typed
Empty file.
20 changes: 19 additions & 1 deletion tests/command_table/test_command_table.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import json
from unittest.mock import patch
from unittest.mock import patch, Mock

import jsonref
import jsonschema
Expand Down Expand Up @@ -161,10 +161,28 @@ def test_command_table_active_validation_table(command_table_schema):
ct.table[999999]
ct = CommandTable(command_table_schema, active_validation=False)
ct.table[999999].amplitude00.value = 1
ct.as_dict()
assert ct.is_valid() is False
ct.active_validation = True
with pytest.raises(ValidationError):
ct.as_dict()


@pytest.mark.parametrize("state", [True, False])
def test_table_is_valid_active_validation_state_persists(command_table, state):
command_table.active_validation = state
command_table.is_valid() is True
assert command_table.active_validation is state

command_table.as_dict = Mock(side_effect=ValidationError)
command_table.is_valid() is False
assert command_table.active_validation is state

with pytest.raises(ValidationError):
command_table.is_valid(raise_for_invalid=True)
assert command_table.active_validation is state


def test_command_table_active_validation_parent_entry(command_table_schema):
ct = CommandTable(command_table_schema, active_validation=True)
ct.table[0]
Expand Down
Loading