diff --git a/lib/galaxy/tool_util/verify/assertion_models.py b/lib/galaxy/tool_util/verify/assertion_models.py index eb4429170655..142c476ffdb6 100644 --- a/lib/galaxy/tool_util/verify/assertion_models.py +++ b/lib/galaxy/tool_util/verify/assertion_models.py @@ -81,12 +81,8 @@ def check_non_negative_if_int(v: typing.Any): has_line_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_line_model(AssertionModel): - r"""Asserts the specified output contains the line specified by the - argument line. The exact number of occurrences can be optionally - specified by the argument n""" - - that: Literal["has_line"] = "has_line" +class base_has_line_model(AssertionModel): + """base model for has_line describing attributes.""" line: str = Field( ..., @@ -133,6 +129,20 @@ class has_line_model(AssertionModel): ) +class has_line_model(base_has_line_model): + r"""Asserts the specified output contains the line specified by the + argument line. The exact number of occurrences can be optionally + specified by the argument n""" + + that: Literal["has_line"] = "has_line" + + +class has_line_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_line: base_has_line_model + + has_line_matching_expression_description = """The regular expressions to attempt match in the output.""" has_line_matching_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" @@ -148,12 +158,8 @@ class has_line_model(AssertionModel): has_line_matching_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_line_matching_model(AssertionModel): - r"""Asserts the specified output contains a line matching the - regular expression specified by the argument expression. If n is given - the assertion checks for exactly n occurences.""" - - that: Literal["has_line_matching"] = "has_line_matching" +class base_has_line_matching_model(AssertionModel): + """base model for has_line_matching describing attributes.""" expression: str = Field( ..., @@ -200,6 +206,20 @@ class has_line_matching_model(AssertionModel): ) +class has_line_matching_model(base_has_line_matching_model): + r"""Asserts the specified output contains a line matching the + regular expression specified by the argument expression. If n is given + the assertion checks for exactly n occurences.""" + + that: Literal["has_line_matching"] = "has_line_matching" + + +class has_line_matching_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_line_matching: base_has_line_matching_model + + has_n_lines_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" has_n_lines_delta_description = ( @@ -213,12 +233,8 @@ class has_line_matching_model(AssertionModel): has_n_lines_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_n_lines_model(AssertionModel): - r"""Asserts the specified output contains ``n`` lines allowing - for a difference in the number of lines (delta) - or relative differebce in the number of lines""" - - that: Literal["has_n_lines"] = "has_n_lines" +class base_has_n_lines_model(AssertionModel): + """base model for has_n_lines describing attributes.""" n: Annotated[ typing.Optional[typing.Union[str, int]], @@ -260,6 +276,20 @@ class has_n_lines_model(AssertionModel): ) +class has_n_lines_model(base_has_n_lines_model): + r"""Asserts the specified output contains ``n`` lines allowing + for a difference in the number of lines (delta) + or relative differebce in the number of lines""" + + that: Literal["has_n_lines"] = "has_n_lines" + + +class has_n_lines_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_n_lines: base_has_n_lines_model + + has_text_text_description = """The text to search for in the output.""" has_text_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" @@ -275,12 +305,8 @@ class has_n_lines_model(AssertionModel): has_text_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_text_model(AssertionModel): - r"""Asserts specified output contains the substring specified by - the argument text. The exact number of occurrences can be - optionally specified by the argument n""" - - that: Literal["has_text"] = "has_text" +class base_has_text_model(AssertionModel): + """base model for has_text describing attributes.""" text: str = Field( ..., @@ -327,6 +353,20 @@ class has_text_model(AssertionModel): ) +class has_text_model(base_has_text_model): + r"""Asserts specified output contains the substring specified by + the argument text. The exact number of occurrences can be + optionally specified by the argument n""" + + that: Literal["has_text"] = "has_text" + + +class has_text_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_text: base_has_text_model + + has_text_matching_expression_description = """The regular expressions to attempt match in the output.""" has_text_matching_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" @@ -342,13 +382,8 @@ class has_text_model(AssertionModel): has_text_matching_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_text_matching_model(AssertionModel): - r"""Asserts the specified output contains text matching the - regular expression specified by the argument expression. - If n is given the assertion checks for exacly n (nonoverlapping) - occurences.""" - - that: Literal["has_text_matching"] = "has_text_matching" +class base_has_text_matching_model(AssertionModel): + """base model for has_text_matching describing attributes.""" expression: str = Field( ..., @@ -395,14 +430,26 @@ class has_text_matching_model(AssertionModel): ) -not_has_text_text_description = """The text to search for in the output.""" +class has_text_matching_model(base_has_text_matching_model): + r"""Asserts the specified output contains text matching the + regular expression specified by the argument expression. + If n is given the assertion checks for exacly n (nonoverlapping) + occurences.""" + + that: Literal["has_text_matching"] = "has_text_matching" -class not_has_text_model(AssertionModel): - r"""Asserts specified output does not contain the substring - specified by the argument text""" +class has_text_matching_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_text_matching: base_has_text_matching_model + + +not_has_text_text_description = """The text to search for in the output.""" - that: Literal["not_has_text"] = "not_has_text" + +class base_not_has_text_model(AssertionModel): + """base model for not_has_text describing attributes.""" text: str = Field( ..., @@ -410,6 +457,19 @@ class not_has_text_model(AssertionModel): ) +class not_has_text_model(base_not_has_text_model): + r"""Asserts specified output does not contain the substring + specified by the argument text""" + + that: Literal["not_has_text"] = "not_has_text" + + +class not_has_text_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + not_has_text: base_not_has_text_model + + has_n_columns_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" has_n_columns_delta_description = ( @@ -429,19 +489,8 @@ class not_has_text_model(AssertionModel): has_n_columns_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_n_columns_model(AssertionModel): - r"""Asserts tabular output contains the specified - number (``n``) of columns. - - For instance, ````. The assertion tests only the first line. - Number of columns can optionally also be specified with ``delta``. Alternatively the - range of expected occurences can be specified by ``min`` and/or ``max``. - - Optionally a column separator (``sep``, default is `` ``) `and comment character(s) - can be specified (``comment``, default is empty string). The first non-comment - line is used for determining the number of columns.""" - - that: Literal["has_n_columns"] = "has_n_columns" +class base_has_n_columns_model(AssertionModel): + """base model for has_n_columns describing attributes.""" n: Annotated[ typing.Optional[typing.Union[str, int]], @@ -493,30 +542,38 @@ class has_n_columns_model(AssertionModel): ) -attribute_is_path_description = """The Python xpath-like expression to find the target element.""" +class has_n_columns_model(base_has_n_columns_model): + r"""Asserts tabular output contains the specified + number (``n``) of columns. -attribute_is_attribute_description = """The XML attribute name to test against from the target XML element.""" + For instance, ````. The assertion tests only the first line. + Number of columns can optionally also be specified with ``delta``. Alternatively the + range of expected occurences can be specified by ``min`` and/or ``max``. -attribute_is_text_description = """The expected attribute value to test against on the target XML element""" + Optionally a column separator (``sep``, default is `` ``) `and comment character(s) + can be specified (``comment``, default is empty string). The first non-comment + line is used for determining the number of columns.""" -attribute_is_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" + that: Literal["has_n_columns"] = "has_n_columns" -class attribute_is_model(AssertionModel): - r"""Asserts the XML ``attribute`` for the element (or tag) with the specified - XPath-like ``path`` is the specified ``text``. +class has_n_columns_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" - For example: + has_n_columns: base_has_n_columns_model - ```xml - - ``` - The assertion implicitly also asserts that an element matching ``path`` exists. - With ``negate`` the result of the assertion (on the equality) can be inverted (the - implicit assertion on the existence of the path is not affected).""" +attribute_is_path_description = """The Python xpath-like expression to find the target element.""" + +attribute_is_attribute_description = """The XML attribute name to test against from the target XML element.""" + +attribute_is_text_description = """The expected attribute value to test against on the target XML element""" + +attribute_is_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" - that: Literal["attribute_is"] = "attribute_is" + +class base_attribute_is_model(AssertionModel): + """base model for attribute_is describing attributes.""" path: str = Field( ..., @@ -539,6 +596,29 @@ class attribute_is_model(AssertionModel): ) +class attribute_is_model(base_attribute_is_model): + r"""Asserts the XML ``attribute`` for the element (or tag) with the specified + XPath-like ``path`` is the specified ``text``. + + For example: + + ```xml + + ``` + + The assertion implicitly also asserts that an element matching ``path`` exists. + With ``negate`` the result of the assertion (on the equality) can be inverted (the + implicit assertion on the existence of the path is not affected).""" + + that: Literal["attribute_is"] = "attribute_is" + + +class attribute_is_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + attribute_is: base_attribute_is_model + + attribute_matches_path_description = """The Python xpath-like expression to find the target element.""" attribute_matches_attribute_description = """The XML attribute name to test against from the target XML element.""" @@ -550,21 +630,8 @@ class attribute_is_model(AssertionModel): attribute_matches_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class attribute_matches_model(AssertionModel): - r"""Asserts the XML ``attribute`` for the element (or tag) with the specified - XPath-like ``path`` matches the regular expression specified by ``expression``. - - For example: - - ```xml - - ``` - - The assertion implicitly also asserts that an element matching ``path`` exists. - With ``negate`` the result of the assertion (on the matching) can be inverted (the - implicit assertion on the existence of the path is not affected).""" - - that: Literal["attribute_matches"] = "attribute_matches" +class base_attribute_matches_model(AssertionModel): + """base model for attribute_matches describing attributes.""" path: str = Field( ..., @@ -587,12 +654,59 @@ class attribute_matches_model(AssertionModel): ) +class attribute_matches_model(base_attribute_matches_model): + r"""Asserts the XML ``attribute`` for the element (or tag) with the specified + XPath-like ``path`` matches the regular expression specified by ``expression``. + + For example: + + ```xml + + ``` + + The assertion implicitly also asserts that an element matching ``path`` exists. + With ``negate`` the result of the assertion (on the matching) can be inverted (the + implicit assertion on the existence of the path is not affected).""" + + that: Literal["attribute_matches"] = "attribute_matches" + + +class attribute_matches_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + attribute_matches: base_attribute_matches_model + + element_text_path_description = """The Python xpath-like expression to find the target element.""" element_text_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class element_text_model(AssertionModel): +class base_element_text_model(AssertionModel): + """base model for element_text describing attributes.""" + + path: str = Field( + ..., + description=element_text_path_description, + ) + + negate: typing.Union[bool, str] = Field( + False, + description=element_text_negate_description, + ) + + children: typing.Optional["assertion_list"] = None + asserts: typing.Optional["assertion_list"] = None + + @model_validator(mode="before") + @classmethod + def validate_children(self, data: typing.Any): + if isinstance(data, dict) and "children" not in data and "asserts" not in data: + raise ValueError("At least one of 'children' or 'asserts' must be specified for this assertion type.") + return data + + +class element_text_model(base_element_text_model): r"""This tag allows the developer to recurisively specify additional assertions as child elements about just the text contained in the element specified by the XPath-like ``path``, e.g. @@ -610,17 +724,11 @@ class element_text_model(AssertionModel): that: Literal["element_text"] = "element_text" - path: str = Field( - ..., - description=element_text_path_description, - ) - negate: typing.Union[bool, str] = Field( - False, - description=element_text_negate_description, - ) +class element_text_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" - children: "assertion_list" + element_text: base_element_text_model element_text_is_path_description = """The Python xpath-like expression to find the target element.""" @@ -632,7 +740,26 @@ class element_text_model(AssertionModel): element_text_is_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class element_text_is_model(AssertionModel): +class base_element_text_is_model(AssertionModel): + """base model for element_text_is describing attributes.""" + + path: str = Field( + ..., + description=element_text_is_path_description, + ) + + text: str = Field( + ..., + description=element_text_is_text_description, + ) + + negate: typing.Union[bool, str] = Field( + False, + description=element_text_is_negate_description, + ) + + +class element_text_is_model(base_element_text_is_model): r"""Asserts the text of the XML element with the specified XPath-like ``path`` is the specified ``text``. @@ -648,20 +775,11 @@ class element_text_is_model(AssertionModel): that: Literal["element_text_is"] = "element_text_is" - path: str = Field( - ..., - description=element_text_is_path_description, - ) - text: str = Field( - ..., - description=element_text_is_text_description, - ) +class element_text_is_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" - negate: typing.Union[bool, str] = Field( - False, - description=element_text_is_negate_description, - ) + element_text_is: base_element_text_is_model element_text_matches_path_description = """The Python xpath-like expression to find the target element.""" @@ -673,21 +791,8 @@ class element_text_is_model(AssertionModel): ) -class element_text_matches_model(AssertionModel): - r"""Asserts the text of the XML element with the specified XPath-like ``path`` - matches the regular expression defined by ``expression``. - - For example: - - ```xml - - ``` - - The assertion implicitly also asserts that an element matching ``path`` exists. - With ``negate`` the result of the assertion (on the matching) can be inverted (the - implicit assertion on the existence of the path is not affected).""" - - that: Literal["element_text_matches"] = "element_text_matches" +class base_element_text_matches_model(AssertionModel): + """base model for element_text_matches describing attributes.""" path: str = Field( ..., @@ -705,24 +810,38 @@ class element_text_matches_model(AssertionModel): ) -has_element_with_path_path_description = """The Python xpath-like expression to find the target element.""" +class element_text_matches_model(base_element_text_matches_model): + r"""Asserts the text of the XML element with the specified XPath-like ``path`` + matches the regular expression defined by ``expression``. -has_element_with_path_negate_description = ( - """A boolean that can be set to true to negate the outcome of the assertion.""" -) - - -class has_element_with_path_model(AssertionModel): - r"""Asserts the XML output contains at least one element (or tag) with the specified - XPath-like ``path``, e.g. + For example: ```xml - + ``` - With ``negate`` the result of the assertion can be inverted.""" + The assertion implicitly also asserts that an element matching ``path`` exists. + With ``negate`` the result of the assertion (on the matching) can be inverted (the + implicit assertion on the existence of the path is not affected).""" - that: Literal["has_element_with_path"] = "has_element_with_path" + that: Literal["element_text_matches"] = "element_text_matches" + + +class element_text_matches_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + element_text_matches: base_element_text_matches_model + + +has_element_with_path_path_description = """The Python xpath-like expression to find the target element.""" + +has_element_with_path_negate_description = ( + """A boolean that can be set to true to negate the outcome of the assertion.""" +) + + +class base_has_element_with_path_model(AssertionModel): + """base model for has_element_with_path describing attributes.""" path: str = Field( ..., @@ -735,6 +854,25 @@ class has_element_with_path_model(AssertionModel): ) +class has_element_with_path_model(base_has_element_with_path_model): + r"""Asserts the XML output contains at least one element (or tag) with the specified + XPath-like ``path``, e.g. + + ```xml + + ``` + + With ``negate`` the result of the assertion can be inverted.""" + + that: Literal["has_element_with_path"] = "has_element_with_path" + + +class has_element_with_path_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_element_with_path: base_has_element_with_path_model + + has_n_elements_with_path_path_description = """The Python xpath-like expression to find the target element.""" has_n_elements_with_path_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" @@ -756,21 +894,8 @@ class has_element_with_path_model(AssertionModel): ) -class has_n_elements_with_path_model(AssertionModel): - r"""Asserts the XML output contains the specified number (``n``, optionally with ``delta``) of elements (or - tags) with the specified XPath-like ``path``. - - For example: - - ```xml - - ``` - - Alternatively to ``n`` and ``delta`` also the ``min`` and ``max`` attributes - can be used to specify the range of the expected number of occurences. - With ``negate`` the result of the assertion can be inverted.""" - - that: Literal["has_n_elements_with_path"] = "has_n_elements_with_path" +class base_has_n_elements_with_path_model(AssertionModel): + """base model for has_n_elements_with_path describing attributes.""" path: str = Field( ..., @@ -817,12 +942,45 @@ class has_n_elements_with_path_model(AssertionModel): ) -class is_valid_xml_model(AssertionModel): +class has_n_elements_with_path_model(base_has_n_elements_with_path_model): + r"""Asserts the XML output contains the specified number (``n``, optionally with ``delta``) of elements (or + tags) with the specified XPath-like ``path``. + + For example: + + ```xml + + ``` + + Alternatively to ``n`` and ``delta`` also the ``min`` and ``max`` attributes + can be used to specify the range of the expected number of occurences. + With ``negate`` the result of the assertion can be inverted.""" + + that: Literal["has_n_elements_with_path"] = "has_n_elements_with_path" + + +class has_n_elements_with_path_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_n_elements_with_path: base_has_n_elements_with_path_model + + +class base_is_valid_xml_model(AssertionModel): + """base model for is_valid_xml describing attributes.""" + + +class is_valid_xml_model(base_is_valid_xml_model): r"""Asserts the output is a valid XML file (e.g. ````).""" that: Literal["is_valid_xml"] = "is_valid_xml" +class is_valid_xml_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + is_valid_xml: base_is_valid_xml_model + + xml_element_path_description = """The Python xpath-like expression to find the target element.""" xml_element_attribute_description = """The XML attribute name to test against from the target XML element.""" @@ -844,40 +1002,8 @@ class is_valid_xml_model(AssertionModel): xml_element_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class xml_element_model(AssertionModel): - r"""Assert if the XML file contains element(s) or tag(s) with the specified - [XPath-like ``path``](https://lxml.de/xpathxslt.html). If ``n`` and ``delta`` - or ``min`` and ``max`` are given also the number of occurences is checked. - - ```xml - - - - - - ``` - - With ``negate="true"`` the outcome of the assertions wrt the precence and number - of ``path`` can be negated. If there are any sub assertions then check them against - - - the content of the attribute ``attribute`` - - the element's text if no attribute is given - - ```xml - - - - - - ``` - - Sub-assertions are not subject to the ``negate`` attribute of ``xml_element``. - If ``all`` is ``true`` then the sub assertions are checked for all occurences. - - Note that all other XML assertions can be expressed by this assertion (Galaxy - also implements the other assertions by calling this one).""" - - that: Literal["xml_element"] = "xml_element" +class base_xml_element_model(AssertionModel): + """base model for xml_element describing attributes.""" path: str = Field( ..., @@ -934,21 +1060,58 @@ class xml_element_model(AssertionModel): ) children: typing.Optional["assertion_list"] = None + asserts: typing.Optional["assertion_list"] = None -has_json_property_with_text_property_description = """The property name to search the JSON document for.""" +class xml_element_model(base_xml_element_model): + r"""Assert if the XML file contains element(s) or tag(s) with the specified + [XPath-like ``path``](https://lxml.de/xpathxslt.html). If ``n`` and ``delta`` + or ``min`` and ``max`` are given also the number of occurences is checked. -has_json_property_with_text_text_description = """The expected text value of the target JSON attribute.""" + ```xml + + + + + + ``` + With ``negate="true"`` the outcome of the assertions wrt the precence and number + of ``path`` can be negated. If there are any sub assertions then check them against -class has_json_property_with_text_model(AssertionModel): - r"""Asserts the JSON document contains a property or key with the specified text (i.e. string) value. + - the content of the attribute ``attribute`` + - the element's text if no attribute is given ```xml - - ```""" + + + + + + ``` + + Sub-assertions are not subject to the ``negate`` attribute of ``xml_element``. + If ``all`` is ``true`` then the sub assertions are checked for all occurences. + + Note that all other XML assertions can be expressed by this assertion (Galaxy + also implements the other assertions by calling this one).""" + + that: Literal["xml_element"] = "xml_element" + + +class xml_element_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + xml_element: base_xml_element_model - that: Literal["has_json_property_with_text"] = "has_json_property_with_text" + +has_json_property_with_text_property_description = """The property name to search the JSON document for.""" + +has_json_property_with_text_text_description = """The expected text value of the target JSON attribute.""" + + +class base_has_json_property_with_text_model(AssertionModel): + """base model for has_json_property_with_text describing attributes.""" property: str = Field( ..., @@ -961,6 +1124,22 @@ class has_json_property_with_text_model(AssertionModel): ) +class has_json_property_with_text_model(base_has_json_property_with_text_model): + r"""Asserts the JSON document contains a property or key with the specified text (i.e. string) value. + + ```xml + + ```""" + + that: Literal["has_json_property_with_text"] = "has_json_property_with_text" + + +class has_json_property_with_text_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_json_property_with_text: base_has_json_property_with_text_model + + has_json_property_with_value_property_description = """The property name to search the JSON document for.""" has_json_property_with_value_value_description = ( @@ -968,14 +1147,8 @@ class has_json_property_with_text_model(AssertionModel): ) -class has_json_property_with_value_model(AssertionModel): - r"""Asserts the JSON document contains a property or key with the specified JSON value. - - ```xml - - ```""" - - that: Literal["has_json_property_with_value"] = "has_json_property_with_value" +class base_has_json_property_with_value_model(AssertionModel): + """base model for has_json_property_with_value describing attributes.""" property: str = Field( ..., @@ -988,19 +1161,29 @@ class has_json_property_with_value_model(AssertionModel): ) -has_h5_attribute_key_description = """HDF5 attribute to check value of.""" +class has_json_property_with_value_model(base_has_json_property_with_value_model): + r"""Asserts the JSON document contains a property or key with the specified JSON value. -has_h5_attribute_value_description = """Expected value of HDF5 attribute to check.""" + ```xml + + ```""" + that: Literal["has_json_property_with_value"] = "has_json_property_with_value" -class has_h5_attribute_model(AssertionModel): - r"""Asserts HDF5 output contains the specified ``value`` for an attribute (``key``), e.g. - ```xml - - ```""" +class has_json_property_with_value_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_json_property_with_value: base_has_json_property_with_value_model - that: Literal["has_h5_attribute"] = "has_h5_attribute" + +has_h5_attribute_key_description = """HDF5 attribute to check value of.""" + +has_h5_attribute_value_description = """Expected value of HDF5 attribute to check.""" + + +class base_has_h5_attribute_model(AssertionModel): + """base model for has_h5_attribute describing attributes.""" key: str = Field( ..., @@ -1013,13 +1196,27 @@ class has_h5_attribute_model(AssertionModel): ) -has_h5_keys_keys_description = """HDF5 attributes to check value of as a comma-separated string.""" +class has_h5_attribute_model(base_has_h5_attribute_model): + r"""Asserts HDF5 output contains the specified ``value`` for an attribute (``key``), e.g. + + ```xml + + ```""" + + that: Literal["has_h5_attribute"] = "has_h5_attribute" -class has_h5_keys_model(AssertionModel): - r"""Asserts the specified HDF5 output has the given keys.""" +class has_h5_attribute_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_h5_attribute: base_has_h5_attribute_model - that: Literal["has_h5_keys"] = "has_h5_keys" + +has_h5_keys_keys_description = """HDF5 attributes to check value of as a comma-separated string.""" + + +class base_has_h5_keys_model(AssertionModel): + """base model for has_h5_keys describing attributes.""" keys: str = Field( ..., @@ -1027,6 +1224,18 @@ class has_h5_keys_model(AssertionModel): ) +class has_h5_keys_model(base_has_h5_keys_model): + r"""Asserts the specified HDF5 output has the given keys.""" + + that: Literal["has_h5_keys"] = "has_h5_keys" + + +class has_h5_keys_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_h5_keys: base_has_h5_keys_model + + has_archive_member_path_description = """The regular expression specifying the archive member.""" has_archive_member_all_description = ( @@ -1046,53 +1255,8 @@ class has_h5_keys_model(AssertionModel): has_archive_member_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_archive_member_model(AssertionModel): - r"""This tag allows to check if ``path`` is contained in a compressed file. - - The path is a regular expression that is matched against the full paths of the objects in - the compressed file (remember that "matching" means it is checked if a prefix of - the full path of an archive member is described by the regular expression). - Valid archive formats include ``.zip``, ``.tar``, and ``.tar.gz``. Note that - depending on the archive creation method: - - - full paths of the members may be prefixed with ``./`` - - directories may be treated as empty files - - ```xml - - ``` - - With ``n`` and ``delta`` (or ``min`` and ``max``) assertions on the number of - archive members matching ``path`` can be expressed. The following could be used, - e.g., to assert an archive containing n±1 elements out of which at least - 4 need to have a ``txt`` extension. - - ```xml - - - ``` - - In addition the tag can contain additional assertions as child elements about - the first member in the archive matching the regular expression ``path``. For - instance - - ```xml - - - - ``` - - If the ``all`` attribute is set to ``true`` then all archive members are subject - to the assertions. Note that, archive members matching the ``path`` are sorted - alphabetically. - - The ``negate`` attribute of the ``has_archive_member`` assertion only affects - the asserts on the presence and number of matching archive members, but not any - sub-assertions (which can offer the ``negate`` attribute on their own). The - check if the file is an archive at all, which is also done by the function, is - not affected.""" - - that: Literal["has_archive_member"] = "has_archive_member" +class base_has_archive_member_model(AssertionModel): + """base model for has_archive_member describing attributes.""" path: str = Field( ..., @@ -1144,6 +1308,62 @@ class has_archive_member_model(AssertionModel): ) children: typing.Optional["assertion_list"] = None + asserts: typing.Optional["assertion_list"] = None + + +class has_archive_member_model(base_has_archive_member_model): + r"""This tag allows to check if ``path`` is contained in a compressed file. + + The path is a regular expression that is matched against the full paths of the objects in + the compressed file (remember that "matching" means it is checked if a prefix of + the full path of an archive member is described by the regular expression). + Valid archive formats include ``.zip``, ``.tar``, and ``.tar.gz``. Note that + depending on the archive creation method: + + - full paths of the members may be prefixed with ``./`` + - directories may be treated as empty files + + ```xml + + ``` + + With ``n`` and ``delta`` (or ``min`` and ``max``) assertions on the number of + archive members matching ``path`` can be expressed. The following could be used, + e.g., to assert an archive containing n±1 elements out of which at least + 4 need to have a ``txt`` extension. + + ```xml + + + ``` + + In addition the tag can contain additional assertions as child elements about + the first member in the archive matching the regular expression ``path``. For + instance + + ```xml + + + + ``` + + If the ``all`` attribute is set to ``true`` then all archive members are subject + to the assertions. Note that, archive members matching the ``path`` are sorted + alphabetically. + + The ``negate`` attribute of the ``has_archive_member`` assertion only affects + the asserts on the presence and number of matching archive members, but not any + sub-assertions (which can offer the ``negate`` attribute on their own). The + check if the file is an archive at all, which is also done by the function, is + not affected.""" + + that: Literal["has_archive_member"] = "has_archive_member" + + +class has_archive_member_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_archive_member: base_has_archive_member_model has_size_value_description = """Deprecated alias for `size`""" @@ -1161,13 +1381,17 @@ class has_archive_member_model(AssertionModel): has_size_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_size_model(AssertionModel): - r"""Asserts the specified output has a size of the specified value - - Attributes size and value or synonyms though value is considered deprecated. - The size optionally allows for absolute (``delta``) difference.""" +class base_has_size_model(AssertionModel): + """base model for has_size describing attributes.""" - that: Literal["has_size"] = "has_size" + value: Annotated[ + typing.Optional[typing.Union[str, int]], + BeforeValidator(check_bytes), + BeforeValidator(check_non_negative_if_int), + ] = Field( + None, + description=has_size_value_description, + ) size: Annotated[ typing.Optional[typing.Union[str, int]], @@ -1209,6 +1433,21 @@ class has_size_model(AssertionModel): ) +class has_size_model(base_has_size_model): + r"""Asserts the specified output has a size of the specified value + + Attributes size and value or synonyms though value is considered deprecated. + The size optionally allows for absolute (``delta``) difference.""" + + that: Literal["has_size"] = "has_size" + + +class has_size_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_size: base_has_size_model + + has_image_center_of_mass_center_of_mass_description = """The required center of mass of the image intensities (horizontal and vertical coordinate, separated by a comma).""" has_image_center_of_mass_channel_description = """Restricts the assertion to a specific channel of the image (where ``0`` corresponds to the first image channel).""" @@ -1218,14 +1457,8 @@ class has_size_model(AssertionModel): ) -class has_image_center_of_mass_model(AssertionModel): - r"""Asserts the specified output is an image and has the specified center of mass. - - Asserts the output is an image and has a specific center of mass, - or has an Euclidean distance of ``eps`` or less to that point (e.g., - ````).""" - - that: Literal["has_image_center_of_mass"] = "has_image_center_of_mass" +class base_has_image_center_of_mass_model(AssertionModel): + """base model for has_image_center_of_mass describing attributes.""" center_of_mass: Annotated[str, BeforeValidator(check_center_of_mass)] = Field( ..., @@ -1243,6 +1476,22 @@ class has_image_center_of_mass_model(AssertionModel): ) +class has_image_center_of_mass_model(base_has_image_center_of_mass_model): + r"""Asserts the specified output is an image and has the specified center of mass. + + Asserts the output is an image and has a specific center of mass, + or has an Euclidean distance of ``eps`` or less to that point (e.g., + ````).""" + + that: Literal["has_image_center_of_mass"] = "has_image_center_of_mass" + + +class has_image_center_of_mass_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_image_center_of_mass: base_has_image_center_of_mass_model + + has_image_channels_channels_description = """Expected number of channels of the image.""" has_image_channels_delta_description = """Maximum allowed difference of the number of channels (default is 0). The observed number of channels has to be in the range ``value +- delta``.""" @@ -1254,14 +1503,8 @@ class has_image_center_of_mass_model(AssertionModel): has_image_channels_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_image_channels_model(AssertionModel): - r"""Asserts the output is an image and has a specific number of channels. - - The number of channels is plus/minus ``delta`` (e.g., ````). - - Alternatively the range of the expected number of channels can be specified by ``min`` and/or ``max``.""" - - that: Literal["has_image_channels"] = "has_image_channels" +class base_has_image_channels_model(AssertionModel): + """base model for has_image_channels describing attributes.""" channels: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, @@ -1289,6 +1532,22 @@ class has_image_channels_model(AssertionModel): ) +class has_image_channels_model(base_has_image_channels_model): + r"""Asserts the output is an image and has a specific number of channels. + + The number of channels is plus/minus ``delta`` (e.g., ````). + + Alternatively the range of the expected number of channels can be specified by ``min`` and/or ``max``.""" + + that: Literal["has_image_channels"] = "has_image_channels" + + +class has_image_channels_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_image_channels: base_has_image_channels_model + + has_image_height_height_description = """Expected height of the image (in pixels).""" has_image_height_delta_description = """Maximum allowed difference of the image height (in pixels, default is 0). The observed height has to be in the range ``value +- delta``.""" @@ -1300,13 +1559,8 @@ class has_image_channels_model(AssertionModel): has_image_height_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_image_height_model(AssertionModel): - r"""Asserts the output is an image and has a specific height (in pixels). - - The height is plus/minus ``delta`` (e.g., ````). - Alternatively the range of the expected height can be specified by ``min`` and/or ``max``.""" - - that: Literal["has_image_height"] = "has_image_height" +class base_has_image_height_model(AssertionModel): + """base model for has_image_height describing attributes.""" height: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, @@ -1334,6 +1588,21 @@ class has_image_height_model(AssertionModel): ) +class has_image_height_model(base_has_image_height_model): + r"""Asserts the output is an image and has a specific height (in pixels). + + The height is plus/minus ``delta`` (e.g., ````). + Alternatively the range of the expected height can be specified by ``min`` and/or ``max``.""" + + that: Literal["has_image_height"] = "has_image_height" + + +class has_image_height_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_image_height: base_has_image_height_model + + has_image_mean_intensity_channel_description = """Restricts the assertion to a specific channel of the image (where ``0`` corresponds to the first image channel).""" has_image_mean_intensity_mean_intensity_description = """The required mean value of the image intensities.""" @@ -1345,13 +1614,8 @@ class has_image_height_model(AssertionModel): has_image_mean_intensity_max_description = """An upper bound of the required mean value of the image intensities.""" -class has_image_mean_intensity_model(AssertionModel): - r"""Asserts the output is an image and has a specific mean intensity value. - - The mean intensity value is plus/minus ``eps`` (e.g., ````). - Alternatively the range of the expected mean intensity value can be specified by ``min`` and/or ``max``.""" - - that: Literal["has_image_mean_intensity"] = "has_image_mean_intensity" +class base_has_image_mean_intensity_model(AssertionModel): + """base model for has_image_mean_intensity describing attributes.""" channel: typing.Optional[StrictInt] = Field( None, @@ -1379,6 +1643,21 @@ class has_image_mean_intensity_model(AssertionModel): ) +class has_image_mean_intensity_model(base_has_image_mean_intensity_model): + r"""Asserts the output is an image and has a specific mean intensity value. + + The mean intensity value is plus/minus ``eps`` (e.g., ````). + Alternatively the range of the expected mean intensity value can be specified by ``min`` and/or ``max``.""" + + that: Literal["has_image_mean_intensity"] = "has_image_mean_intensity" + + +class has_image_mean_intensity_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_image_mean_intensity: base_has_image_mean_intensity_model + + has_image_mean_object_size_channel_description = """Restricts the assertion to a specific channel of the image (where ``0`` corresponds to the first image channel).""" has_image_mean_object_size_labels_description = """List of labels, separated by a comma. Labels *not* on this list will be excluded from consideration. Cannot be used in combination with ``exclude_labels``.""" @@ -1398,14 +1677,8 @@ class has_image_mean_intensity_model(AssertionModel): ) -class has_image_mean_object_size_model(AssertionModel): - r"""Asserts the output is an image with labeled objects which have the specified mean size (number of pixels), - - The mean size is plus/minus ``eps`` (e.g., ````). - - The labels must be unique.""" - - that: Literal["has_image_mean_object_size"] = "has_image_mean_object_size" +class base_has_image_mean_object_size_model(AssertionModel): + """base model for has_image_mean_object_size describing attributes.""" channel: typing.Optional[StrictInt] = Field( None, @@ -1449,6 +1722,22 @@ class has_image_mean_object_size_model(AssertionModel): ) +class has_image_mean_object_size_model(base_has_image_mean_object_size_model): + r"""Asserts the output is an image with labeled objects which have the specified mean size (number of pixels), + + The mean size is plus/minus ``eps`` (e.g., ````). + + The labels must be unique.""" + + that: Literal["has_image_mean_object_size"] = "has_image_mean_object_size" + + +class has_image_mean_object_size_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_image_mean_object_size: base_has_image_mean_object_size_model + + has_image_n_labels_channel_description = """Restricts the assertion to a specific channel of the image (where ``0`` corresponds to the first image channel).""" has_image_n_labels_labels_description = """List of labels, separated by a comma. Labels *not* on this list will be excluded from consideration. Cannot be used in combination with ``exclude_labels``.""" @@ -1466,15 +1755,8 @@ class has_image_mean_object_size_model(AssertionModel): has_image_n_labels_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_image_n_labels_model(AssertionModel): - r"""Asserts the output is an image and has the specified labels. - - Labels can be a number of labels or unique values (e.g., - ````). - - The primary usage of this assertion is to verify the number of objects in images with uniquely labeled objects.""" - - that: Literal["has_image_n_labels"] = "has_image_n_labels" +class base_has_image_n_labels_model(AssertionModel): + """base model for has_image_n_labels describing attributes.""" channel: typing.Optional[StrictInt] = Field( None, @@ -1517,6 +1799,23 @@ class has_image_n_labels_model(AssertionModel): ) +class has_image_n_labels_model(base_has_image_n_labels_model): + r"""Asserts the output is an image and has the specified labels. + + Labels can be a number of labels or unique values (e.g., + ````). + + The primary usage of this assertion is to verify the number of objects in images with uniquely labeled objects.""" + + that: Literal["has_image_n_labels"] = "has_image_n_labels" + + +class has_image_n_labels_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_image_n_labels: base_has_image_n_labels_model + + has_image_width_width_description = """Expected width of the image (in pixels).""" has_image_width_delta_description = """Maximum allowed difference of the image width (in pixels, default is 0). The observed width has to be in the range ``value +- delta``.""" @@ -1528,13 +1827,8 @@ class has_image_n_labels_model(AssertionModel): has_image_width_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" -class has_image_width_model(AssertionModel): - r"""Asserts the output is an image and has a specific width (in pixels). - - The width is plus/minus ``delta`` (e.g., ````). - Alternatively the range of the expected width can be specified by ``min`` and/or ``max``.""" - - that: Literal["has_image_width"] = "has_image_width" +class base_has_image_width_model(AssertionModel): + """base model for has_image_width describing attributes.""" width: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, @@ -1562,7 +1856,22 @@ class has_image_width_model(AssertionModel): ) -any_assertion_model = Annotated[ +class has_image_width_model(base_has_image_width_model): + r"""Asserts the output is an image and has a specific width (in pixels). + + The width is plus/minus ``delta`` (e.g., ````). + Alternatively the range of the expected width can be specified by ``min`` and/or ``max``.""" + + that: Literal["has_image_width"] = "has_image_width" + + +class has_image_width_model_nested(AssertionModel): + r"""Nested version of this assertion model.""" + + has_image_width: base_has_image_width_model + + +any_assertion_model_flat = Annotated[ typing.Union[ has_line_model, has_line_matching_model, @@ -1597,4 +1906,100 @@ class has_image_width_model(AssertionModel): Field(discriminator="that"), ] -assertion_list = RootModel[typing.List[any_assertion_model]] +any_assertion_model_nested = typing.Union[ + has_line_model_nested, + has_line_matching_model_nested, + has_n_lines_model_nested, + has_text_model_nested, + has_text_matching_model_nested, + not_has_text_model_nested, + has_n_columns_model_nested, + attribute_is_model_nested, + attribute_matches_model_nested, + element_text_model_nested, + element_text_is_model_nested, + element_text_matches_model_nested, + has_element_with_path_model_nested, + has_n_elements_with_path_model_nested, + is_valid_xml_model_nested, + xml_element_model_nested, + has_json_property_with_text_model_nested, + has_json_property_with_value_model_nested, + has_h5_attribute_model_nested, + has_h5_keys_model_nested, + has_archive_member_model_nested, + has_size_model_nested, + has_image_center_of_mass_model_nested, + has_image_channels_model_nested, + has_image_height_model_nested, + has_image_mean_intensity_model_nested, + has_image_mean_object_size_model_nested, + has_image_n_labels_model_nested, + has_image_width_model_nested, +] + +assertion_list = RootModel[typing.List[typing.Union[any_assertion_model_flat, any_assertion_model_nested]]] + + +class assertion_dict(AssertionModel): + + has_line: typing.Optional[base_has_line_model] = None + + has_line_matching: typing.Optional[base_has_line_matching_model] = None + + has_n_lines: typing.Optional[base_has_n_lines_model] = None + + has_text: typing.Optional[base_has_text_model] = None + + has_text_matching: typing.Optional[base_has_text_matching_model] = None + + not_has_text: typing.Optional[base_not_has_text_model] = None + + has_n_columns: typing.Optional[base_has_n_columns_model] = None + + attribute_is: typing.Optional[base_attribute_is_model] = None + + attribute_matches: typing.Optional[base_attribute_matches_model] = None + + element_text: typing.Optional[base_element_text_model] = None + + element_text_is: typing.Optional[base_element_text_is_model] = None + + element_text_matches: typing.Optional[base_element_text_matches_model] = None + + has_element_with_path: typing.Optional[base_has_element_with_path_model] = None + + has_n_elements_with_path: typing.Optional[base_has_n_elements_with_path_model] = None + + is_valid_xml: typing.Optional[base_is_valid_xml_model] = None + + xml_element: typing.Optional[base_xml_element_model] = None + + has_json_property_with_text: typing.Optional[base_has_json_property_with_text_model] = None + + has_json_property_with_value: typing.Optional[base_has_json_property_with_value_model] = None + + has_h5_attribute: typing.Optional[base_has_h5_attribute_model] = None + + has_h5_keys: typing.Optional[base_has_h5_keys_model] = None + + has_archive_member: typing.Optional[base_has_archive_member_model] = None + + has_size: typing.Optional[base_has_size_model] = None + + has_image_center_of_mass: typing.Optional[base_has_image_center_of_mass_model] = None + + has_image_channels: typing.Optional[base_has_image_channels_model] = None + + has_image_height: typing.Optional[base_has_image_height_model] = None + + has_image_mean_intensity: typing.Optional[base_has_image_mean_intensity_model] = None + + has_image_mean_object_size: typing.Optional[base_has_image_mean_object_size_model] = None + + has_image_n_labels: typing.Optional[base_has_image_n_labels_model] = None + + has_image_width: typing.Optional[base_has_image_width_model] = None + + +assertions = typing.Union[assertion_list, assertion_dict]