././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775774795.8653326 galaxy_tool_util_models-26.0.0/0000755000175100017510000000000015166026114016171 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/HISTORY.rst0000644000175100017510000000777315166026066020110 0ustar00runnerrunnerHistory ------- .. to_doc ------------------- 26.0.0 (2026-04-08) ------------------- ========= Bug fixes ========= * Fix Pydantic UnsupportedFieldAttributeWarning for Field defaults in Annotated by `@mvdbeek `_ in `#21923 `_ * Backport paired or unpaired mapping fix by `@mvdbeek `_ in `#21936 `_ * Fix validation of certain classes of text validators in tools. by `@jmchilton `_ in `#22280 `_ ============ Enhancements ============ * Tool Request API by `@jmchilton `_ in `#20935 `_ * Update fastapi to 0.123.4 and ``get_openapi()`` fork by `@nsoranzo `_ in `#21384 `_ * Enable attaching sample sheet to landing requests by `@mvdbeek `_ in `#21489 `_ * Apply 2026 black style by `@galaxybot `_ in `#21618 `_ ============= Other changes ============= * Merge 25.1 into dev, fix openapi schema generation for TypedDict by `@mvdbeek `_ in `#21168 `_ * Fix tool request API for multiple select parameters with defaults. by `@jmchilton `_ in `#21416 `_ ------------------- 25.1.2 (2026-03-09) ------------------- No recorded changes since last release ------------------- 25.1.1 (2026-02-03) ------------------- No recorded changes since last release ------------------- 25.1.0 (2025-12-12) ------------------- ============ Enhancements ============ * Implement Sample Sheets by `@jmchilton `_ in `#19305 `_ * Prepare ``ToolBox.dynamic_tool_to_tool()`` for CWL formats by `@nsoranzo `_ in `#20364 `_ * Type annotation fixes for mypy 1.16.0 by `@nsoranzo `_ in `#20424 `_ * Add configfiles support and various enhancements for user defined tools by `@mvdbeek `_ in `#20761 `_ * Add support for picking ``from_work_dir`` directory by `@mvdbeek `_ in `#20916 `_ * Include format in internal json model by `@mvdbeek `_ in `#20917 `_ * Add resource docs and tweak tool source schema title generation by `@mvdbeek `_ in `#20951 `_ * Use workflow-style payload in data landing request by `@mvdbeek `_ in `#21107 `_ ------------------- 25.0.4 (2025-11-18) ------------------- No recorded changes since last release ------------------- 25.0.3 (2025-09-23) ------------------- ========= Bug fixes ========= * Fix collection element sorting in extended_metadata by `@mvdbeek `_ in `#20928 `_ ------------------- 25.0.2 (2025-08-13) ------------------- ========= Bug fixes ========= * Fix parameter models for optional color params. by `@jmchilton `_ in `#20705 `_ ------------------- 25.0.1 (2025-06-20) ------------------- No recorded changes since last release ------------------- 25.0.0 (2025-06-18) ------------------- First release ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/LICENSE0000644000175100017510000000306615166026066017211 0ustar00runnerrunnerCopyright (c) 2005-2026 Galaxy Contributors (see CONTRIBUTORS.md) Galaxy is provided from 2026-02-25 onwards entirely under the MIT License. Some icons found in Galaxy are from the Silk Icons set, available under the Creative Commons Attribution 2.5 License, from: http://www.famfamfam.com/lab/icons/silk/ Other images and documentation are licensed under the Creative Commons Attribution 3.0 (CC BY 3.0) License. See: http://creativecommons.org/licenses/by/3.0/ -------------------------------------------------------------------------------- MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/MANIFEST.in0000644000175100017510000000004715166026066017736 0ustar00runnerrunnerinclude *.rst *.txt LICENSE */py.typed ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775774795.8653004 galaxy_tool_util_models-26.0.0/PKG-INFO0000644000175100017510000001145615166026114017275 0ustar00runnerrunnerMetadata-Version: 2.4 Name: galaxy-tool-util-models Version: 26.0.0 Summary: Pydantic models for Galaxy tools Home-page: https://github.com/galaxyproject/galaxy Author: Galaxy Project and Community Author-email: galaxy-committers@lists.galaxyproject.org License: MIT Requires-Python: >=3.8 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: pydantic>=2.7.4 Requires-Dist: typing-extensions Provides-Extra: test Requires-Dist: pytest; extra == "test" Dynamic: license-file .. image:: https://badge.fury.io/py/galaxy-tool-util-models.svg :target: https://pypi.org/project/galaxy-tool-util-models/ Overview -------- Pydantic models to support Galaxy_ tool utilities. * Code: https://github.com/galaxyproject/galaxy/tree/dev/packages/tool_util_models .. _Galaxy: http://galaxyproject.org/ History ------- .. to_doc ------------------- 26.0.0 (2026-04-08) ------------------- ========= Bug fixes ========= * Fix Pydantic UnsupportedFieldAttributeWarning for Field defaults in Annotated by `@mvdbeek `_ in `#21923 `_ * Backport paired or unpaired mapping fix by `@mvdbeek `_ in `#21936 `_ * Fix validation of certain classes of text validators in tools. by `@jmchilton `_ in `#22280 `_ ============ Enhancements ============ * Tool Request API by `@jmchilton `_ in `#20935 `_ * Update fastapi to 0.123.4 and ``get_openapi()`` fork by `@nsoranzo `_ in `#21384 `_ * Enable attaching sample sheet to landing requests by `@mvdbeek `_ in `#21489 `_ * Apply 2026 black style by `@galaxybot `_ in `#21618 `_ ============= Other changes ============= * Merge 25.1 into dev, fix openapi schema generation for TypedDict by `@mvdbeek `_ in `#21168 `_ * Fix tool request API for multiple select parameters with defaults. by `@jmchilton `_ in `#21416 `_ ------------------- 25.1.2 (2026-03-09) ------------------- No recorded changes since last release ------------------- 25.1.1 (2026-02-03) ------------------- No recorded changes since last release ------------------- 25.1.0 (2025-12-12) ------------------- ============ Enhancements ============ * Implement Sample Sheets by `@jmchilton `_ in `#19305 `_ * Prepare ``ToolBox.dynamic_tool_to_tool()`` for CWL formats by `@nsoranzo `_ in `#20364 `_ * Type annotation fixes for mypy 1.16.0 by `@nsoranzo `_ in `#20424 `_ * Add configfiles support and various enhancements for user defined tools by `@mvdbeek `_ in `#20761 `_ * Add support for picking ``from_work_dir`` directory by `@mvdbeek `_ in `#20916 `_ * Include format in internal json model by `@mvdbeek `_ in `#20917 `_ * Add resource docs and tweak tool source schema title generation by `@mvdbeek `_ in `#20951 `_ * Use workflow-style payload in data landing request by `@mvdbeek `_ in `#21107 `_ ------------------- 25.0.4 (2025-11-18) ------------------- No recorded changes since last release ------------------- 25.0.3 (2025-09-23) ------------------- ========= Bug fixes ========= * Fix collection element sorting in extended_metadata by `@mvdbeek `_ in `#20928 `_ ------------------- 25.0.2 (2025-08-13) ------------------- ========= Bug fixes ========= * Fix parameter models for optional color params. by `@jmchilton `_ in `#20705 `_ ------------------- 25.0.1 (2025-06-20) ------------------- No recorded changes since last release ------------------- 25.0.0 (2025-06-18) ------------------- First release ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/README.rst0000644000175100017510000000050215166026066017663 0ustar00runnerrunner .. image:: https://badge.fury.io/py/galaxy-tool-util-models.svg :target: https://pypi.org/project/galaxy-tool-util-models/ Overview -------- Pydantic models to support Galaxy_ tool utilities. * Code: https://github.com/galaxyproject/galaxy/tree/dev/packages/tool_util_models .. _Galaxy: http://galaxyproject.org/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/dev-requirements.txt0000644000175100017510000000006115166026066022234 0ustar00runnerrunner# For dev mypy # For release build twine==6.2.0 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775774795.861448 galaxy_tool_util_models-26.0.0/galaxy/0000755000175100017510000000000015166026114017456 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/__init__.py0000644000175100017510000000013215166026066021571 0ustar00runnerrunnerfrom pkgutil import extend_path __path__ = extend_path(__path__, __name__) # noqa: F821 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/py.typed0000644000175100017510000000000015166026066021151 0ustar00runnerrunner././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775774795.8635619 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/0000755000175100017510000000000015166026114023033 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/__init__.py0000644000175100017510000002112415166026066025152 0ustar00runnerrunner"""Define the ParsedTool model representing metadata extracted from a tool's source. This is abstraction exported by newer tool shed APIS (circa 2024) and should be sufficient for reasoning about tool state externally from Galaxy. """ from typing import ( Any, Dict, List, Optional, Union, ) from pydantic import ( AfterValidator, AnyUrl, BaseModel, ConfigDict, Field, model_validator, RootModel, ) from typing_extensions import ( Annotated, Literal, NotRequired, TypedDict, ) from galaxy.tool_util_models.parameters import GalaxyToolParameterModel from ._base import ToolSourceBaseModel from .assertions import assertions from .parameters import ToolParameterT from .tool_outputs import ( IncomingToolOutput, ToolOutput, ) from .tool_source import ( Citation, ContainerRequirement, HelpContent, JavascriptRequirement, OutputCompareType, ResourceRequirement, XrefDict, YamlTemplateConfigFile, ) def normalize_dict(values, keys: List[str]): for key in keys: items = values.get(key) if isinstance(items, dict): # dict-of-dicts format # Transform dict-of-dicts to list-of-dicts values[key] = [{"name": k, **v} for k, v in items.items()] class ToolSourceBase(ToolSourceBaseModel): id: Optional[str] = None name: Optional[str] = None version: Optional[str] = "1.0" profile: Optional[float] = None description: Optional[str] = None container: Optional[str] = None requirements: Optional[List[Union[JavascriptRequirement, ResourceRequirement, ContainerRequirement]]] = [] inputs: List[GalaxyToolParameterModel] = [] outputs: List[IncomingToolOutput] = [] citations: Optional[List[Citation]] = None license: Optional[str] = None edam_operations: Optional[List[str]] = None edam_topics: Optional[List[str]] = None xrefs: Optional[List[XrefDict]] = None help: Optional[HelpContent] = None @model_validator(mode="before") @classmethod def normalize_items(cls, values): if isinstance(values, dict): normalize_dict(values, ["inputs", "outputs"]) return values # repeated fields to get consistent order, ugh, FIXME obviously class UserToolSource(ToolSourceBaseModel): class_: Annotated[Literal["GalaxyUserTool"], Field(alias="class")] id: Annotated[ str, Field( description="Unique identifier for the tool. Should be all lower-case and should not include whitespace.", examples=["my-cool-tool"], min_length=3, max_length=255, ), ] version: Annotated[str, Field(description="Version for the tool.", examples=["0.1.0"])] name: Annotated[ str, Field( description="The name of the tool, displayed in the tool menu. This is not the same as the tool id, which is a unique identifier for the tool." ), ] description: Annotated[ Optional[str], Field( description="The description is displayed in the tool menu immediately following the hyperlink for the tool." ), ] = None configfiles: Annotated[ Optional[List[YamlTemplateConfigFile]], Field(description="A list of config files for this tool.") ] = None container: Annotated[ str, Field(description="Container image to use for this tool.", examples=["quay.io/biocontainers/python:3.13"]) ] requirements: Annotated[ Optional[List[Union[JavascriptRequirement, ResourceRequirement, ContainerRequirement]]], Field( description="A list of requirements needed to execute this tool. These can be javascript expressions, resource requirements or container images." ), ] = [] shell_command: Annotated[ str, Field( title="shell_command", description="A string that contains the command to be executed. Parameters can be referenced inside $().", examples=["head -n '$(inputs.n_lines)' '$(inputs.data_input.path)'"], ), ] inputs: List[GalaxyToolParameterModel] = [] outputs: List[IncomingToolOutput] = [] citations: Optional[List[Citation]] = None license: Annotated[ Optional[str], Field( description="A full URI or a a short [SPDX](https://spdx.org/licenses/) identifier for a license for this tool wrapper. The tool wrapper license can be independent of the underlying tool license. This license covers the tool yaml and associated scripts shipped with the tool.", examples=["MIT"], ), ] = None edam_operations: Optional[List[str]] = None edam_topics: Optional[List[str]] = None xrefs: Optional[List[XrefDict]] = None help: Annotated[Optional[HelpContent], Field(description="Help text shown below the tool interface.")] = None @model_validator(mode="before") @classmethod def normalize_items(cls, values): if isinstance(values, dict): normalize_dict(values, ["inputs", "outputs"]) return values class AdminToolSource(ToolSourceBase): class_: Annotated[Literal["GalaxyTool"], Field(alias="class")] command: str DynamicToolSources = Annotated[Union[UserToolSource, AdminToolSource], Field(discriminator="class_")] class ParsedTool(ToolSourceBaseModel): id: str version: Optional[str] name: str description: Optional[str] inputs: List[ToolParameterT] outputs: List[ToolOutput] citations: List[Citation] license: Optional[str] profile: Optional[str] edam_operations: List[str] edam_topics: List[str] xrefs: List[XrefDict] help: Optional[HelpContent] class StrictModel(BaseModel): model_config = ConfigDict(extra="forbid", field_title_generator=lambda field_name, field_info: field_name.lower()) class BaseTestOutputModel(StrictModel): file: Optional[str] = None path: Optional[str] = None location: Optional[AnyUrl] = None ftype: Optional[str] = None sort: Optional[bool] = None compare: Optional[OutputCompareType] = None checksum: Optional[str] = None metadata: Optional[Dict[str, Any]] = None asserts: Optional[assertions] = None delta: Optional[int] = None delta_frac: Optional[float] = None lines_diff: Optional[int] = None decompress: Optional[bool] = None class TestDataOutputAssertions(BaseTestOutputModel): class_: Optional[Literal["File"]] = Field("File", alias="class") class TestCollectionCollectionElementAssertions(StrictModel): elements: Optional[Dict[str, "TestCollectionElementAssertion"]] = None element_tests: Optional[Dict[str, "TestCollectionElementAssertion"]] = None class TestCollectionDatasetElementAssertions(BaseTestOutputModel): pass TestCollectionElementAssertion = Union[ TestCollectionDatasetElementAssertions, TestCollectionCollectionElementAssertions ] TestCollectionCollectionElementAssertions.model_rebuild() def _check_collection_type(v: str) -> str: if len(v) == 0: raise ValueError("Invalid empty collection_type specified.") collection_levels = v.split(":") for collection_level in collection_levels: if collection_level not in ["list", "paired", "paired_or_unpaired", "record", "sample_sheet"]: raise ValueError(f"Invalid collection_type specified [{v}]") return v CollectionType = Annotated[Optional[str], AfterValidator(_check_collection_type)] class CollectionAttributes(StrictModel): collection_type: CollectionType = None class TestCollectionOutputAssertions(StrictModel): class_: Optional[Literal["Collection"]] = Field("Collection", alias="class") elements: Optional[Dict[str, TestCollectionElementAssertion]] = None element_tests: Optional[Dict[str, "TestCollectionElementAssertion"]] = None element_count: Optional[int] = None attributes: Optional[CollectionAttributes] = None collection_type: CollectionType = None TestOutputLiteral = Union[bool, int, float, str] TestOutputAssertions = Union[TestCollectionOutputAssertions, TestDataOutputAssertions, TestOutputLiteral] JobDict = Dict[str, Any] class TestJob(StrictModel): doc: Optional[str] job: JobDict outputs: Dict[str, TestOutputAssertions] expect_failure: Optional[bool] = False Tests = RootModel[List[TestJob]] # TODO: typed dict versions of all thee above for verify code - make this Dict[str, Any] here more # specific. OutputChecks = Union[TestOutputLiteral, Dict[str, Any]] OutputsDict = Dict[str, OutputChecks] class TestJobDict(TypedDict): doc: NotRequired[str] job: NotRequired[JobDict] expect_failure: NotRequired[bool] outputs: OutputsDict TestDicts = List[TestJobDict] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/_base.py0000644000175100017510000000036515166026066024470 0ustar00runnerrunner"""Base model classes for tool utilities.""" from pydantic import ( BaseModel, ConfigDict, ) class ToolSourceBaseModel(BaseModel): model_config = ConfigDict(field_title_generator=lambda field_name, field_info: field_name.lower()) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/_types.py0000644000175100017510000000360515166026066024722 0ustar00runnerrunner"""Type utilities for building pydantic models for tool parameters. Lots of mypy exceptions in here - this code is all well tested and the exceptions are fine otherwise because we're using the typing system to interact with pydantic and build runtime models not to use mypy to type check static code. """ from typing import ( Any, cast, List, Optional, Type, Union, ) # https://stackoverflow.com/questions/56832881/check-if-a-field-is-typing-optional from typing_extensions import ( Annotated, get_args, get_origin, ) def optional(type: Type) -> Type: return_type: Type = Optional[type] # type: ignore[assignment] return return_type def optional_if_needed(type: Type, is_optional: bool) -> Type: return_type: Type = type if is_optional: return_type = optional(type) return return_type def union_type(args: List[Type]) -> Type: return Union[tuple(args)] # type: ignore[return-value] def list_type(arg: Type) -> Type: return List[arg] # type: ignore[valid-type] def cast_as_type(arg) -> Type: return cast(Type, arg) def is_optional(field) -> bool: f = _strip_annotation(field) if f == type(None): # noqa: E721 return True origin = get_origin(f) if origin is Union: return any(is_optional(f) for f in get_args(f)) return False def _strip_annotation(field): is_annotation = get_origin(field) is Annotated if is_annotation: args = get_args(field) return args[0] else: return field def expand_annotation(field: Type, new_annotations: List[Any]) -> Type: is_annotation = get_origin(field) is Annotated if is_annotation: args = get_args(field) # noqa: F841 return Annotated[(args[0], *args[1:], *new_annotations)] # type: ignore[return-value] else: return Annotated[(field, *new_annotations)] # type: ignore[return-value] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/assertions.py0000644000175100017510000035223415166026066025616 0ustar00runnerrunner"""This module is auto-generated, please do not modify.""" import re import typing from pydantic import ( BaseModel, BeforeValidator, ConfigDict, Field, model_validator, RootModel, StrictFloat, StrictInt, ) from typing_extensions import ( Annotated, Literal, ) BYTES_PATTERN = re.compile(r"^(0|[1-9][0-9]*)([kKMGTPE]i?)?$") class AssertionModel(BaseModel): model_config = ConfigDict( extra="forbid", ) def check_bytes(v: typing.Any) -> typing.Any: if isinstance(v, str): assert BYTES_PATTERN.match(v), "Not valid bytes string" return v def check_center_of_mass(v: typing.Any): assert isinstance(v, str) split_parts = v.split(",") assert len(split_parts) == 2 for part in split_parts: assert float(part.strip()) return v def check_regex(v: typing.Any): assert isinstance(v, str) try: re.compile(v) except re.error: raise AssertionError(f"Invalid regular expression {v}") return v def check_non_negative_if_set(v: typing.Any): if v is not None: try: assert float(v) >= 0 except TypeError: raise AssertionError(f"Invalid type found {v}") return v def check_non_negative_if_int(v: typing.Any): if v is not None and isinstance(v, int): assert v >= 0 return v has_line_line_description = """The full line of text to search for in the output.""" has_line_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" has_line_delta_description = ( """Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_line_min_description = """Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_line_max_description = """Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_line_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_line_model(AssertionModel): """base model for has_line describing attributes.""" line: str = Field( ..., description=has_line_line_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_line_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_line_negate_description, ) class base_has_line_model_relaxed(AssertionModel): """base model for has_line describing attributes.""" line: str = Field( ..., description=has_line_line_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_line_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_line_negate_description, ) 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 class has_line_model_relaxed(base_has_line_model_relaxed): 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" 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?``""" has_line_matching_delta_description = ( """Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_line_matching_min_description = """Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_line_matching_max_description = """Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_line_matching_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_line_matching_model(AssertionModel): """base model for has_line_matching describing attributes.""" expression: str = Field( ..., description=has_line_matching_expression_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_matching_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_line_matching_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_matching_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_matching_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_line_matching_negate_description, ) class base_has_line_matching_model_relaxed(AssertionModel): """base model for has_line_matching describing attributes.""" expression: str = Field( ..., description=has_line_matching_expression_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_matching_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_line_matching_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_matching_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_line_matching_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_line_matching_negate_description, ) 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 class has_line_matching_model_relaxed(base_has_line_matching_model_relaxed): 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" has_n_lines_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" has_n_lines_delta_description = ( """Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_n_lines_min_description = """Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_n_lines_max_description = """Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_n_lines_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_n_lines_model(AssertionModel): """base model for has_n_lines describing attributes.""" n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_lines_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_n_lines_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_lines_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_lines_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_n_lines_negate_description, ) class base_has_n_lines_model_relaxed(AssertionModel): """base model for has_n_lines describing attributes.""" n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_lines_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_n_lines_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_lines_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_lines_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_n_lines_negate_description, ) 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 class has_n_lines_model_relaxed(base_has_n_lines_model_relaxed): 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" 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?``""" has_text_delta_description = ( """Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_text_min_description = """Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_text_max_description = """Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_text_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_text_model(AssertionModel): """base model for has_text describing attributes.""" text: str = Field( ..., description=has_text_text_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_text_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_text_negate_description, ) class base_has_text_model_relaxed(AssertionModel): """base model for has_text describing attributes.""" text: str = Field( ..., description=has_text_text_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_text_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_text_negate_description, ) 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 class has_text_model_relaxed(base_has_text_model_relaxed): 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" 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?``""" has_text_matching_delta_description = ( """Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_text_matching_min_description = """Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_text_matching_max_description = """Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_text_matching_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_text_matching_model(AssertionModel): """base model for has_text_matching describing attributes.""" expression: str = Field( ..., description=has_text_matching_expression_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_matching_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_text_matching_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_matching_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_matching_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_text_matching_negate_description, ) class base_has_text_matching_model_relaxed(AssertionModel): """base model for has_text_matching describing attributes.""" expression: str = Field( ..., description=has_text_matching_expression_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_matching_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_text_matching_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_matching_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_text_matching_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_text_matching_negate_description, ) 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 has_text_matching_model_nested(AssertionModel): r"""Nested version of this assertion model.""" has_text_matching: base_has_text_matching_model class has_text_matching_model_relaxed(base_has_text_matching_model_relaxed): 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" not_has_text_text_description = """The text to search for in the output.""" class base_not_has_text_model(AssertionModel): """base model for not_has_text describing attributes.""" text: str = Field( ..., description=not_has_text_text_description, ) class base_not_has_text_model_relaxed(AssertionModel): """base model for not_has_text describing attributes.""" text: str = Field( ..., description=not_has_text_text_description, ) 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 class not_has_text_model_relaxed(base_not_has_text_model_relaxed): r"""Asserts specified output does not contain the substring specified by the argument text""" that: Literal["not_has_text"] = "not_has_text" has_n_columns_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" has_n_columns_delta_description = ( """Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_n_columns_min_description = """Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_n_columns_max_description = """Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_n_columns_sep_description = """Separator defining columns, default: tab""" has_n_columns_comment_description = ( """Comment character(s) used to skip comment lines (which should not be used for counting columns)""" ) has_n_columns_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_n_columns_model(AssertionModel): """base model for has_n_columns describing attributes.""" n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_columns_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_n_columns_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_columns_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_columns_max_description, ) sep: str = Field( " ", description=has_n_columns_sep_description, ) comment: str = Field( "", description=has_n_columns_comment_description, ) negate: typing.Union[bool, str] = Field( False, description=has_n_columns_negate_description, ) class base_has_n_columns_model_relaxed(AssertionModel): """base model for has_n_columns describing attributes.""" n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_columns_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_n_columns_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_columns_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_columns_max_description, ) sep: str = Field( " ", description=has_n_columns_sep_description, ) comment: str = Field( "", description=has_n_columns_comment_description, ) negate: typing.Union[bool, str] = Field( False, description=has_n_columns_negate_description, ) class has_n_columns_model(base_has_n_columns_model): 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 has_n_columns_model_nested(AssertionModel): r"""Nested version of this assertion model.""" has_n_columns: base_has_n_columns_model class has_n_columns_model_relaxed(base_has_n_columns_model_relaxed): 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" 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.""" class base_attribute_is_model(AssertionModel): """base model for attribute_is describing attributes.""" path: str = Field( ..., description=attribute_is_path_description, ) attribute: str = Field( ..., description=attribute_is_attribute_description, ) text: str = Field( ..., description=attribute_is_text_description, ) negate: typing.Union[bool, str] = Field( False, description=attribute_is_negate_description, ) class base_attribute_is_model_relaxed(AssertionModel): """base model for attribute_is describing attributes.""" path: str = Field( ..., description=attribute_is_path_description, ) attribute: str = Field( ..., description=attribute_is_attribute_description, ) text: str = Field( ..., description=attribute_is_text_description, ) negate: typing.Union[bool, str] = Field( False, description=attribute_is_negate_description, ) 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 class attribute_is_model_relaxed(base_attribute_is_model_relaxed): 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" 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.""" attribute_matches_expression_description = ( """The regular expressions to apply against the named attribute on the target XML element.""" ) attribute_matches_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_attribute_matches_model(AssertionModel): """base model for attribute_matches describing attributes.""" path: str = Field( ..., description=attribute_matches_path_description, ) attribute: str = Field( ..., description=attribute_matches_attribute_description, ) expression: Annotated[str, BeforeValidator(check_regex)] = Field( ..., description=attribute_matches_expression_description, ) negate: typing.Union[bool, str] = Field( False, description=attribute_matches_negate_description, ) class base_attribute_matches_model_relaxed(AssertionModel): """base model for attribute_matches describing attributes.""" path: str = Field( ..., description=attribute_matches_path_description, ) attribute: str = Field( ..., description=attribute_matches_attribute_description, ) expression: Annotated[str, BeforeValidator(check_regex)] = Field( ..., description=attribute_matches_expression_description, ) negate: typing.Union[bool, str] = Field( False, description=attribute_matches_negate_description, ) 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 class attribute_matches_model_relaxed(base_attribute_matches_model_relaxed): 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" 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 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 base_element_text_model_relaxed(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. ```xml ``` The assertion implicitly also asserts that an element matching ``path`` exists. With ``negate`` the result of the implicit assertions can be inverted. The sub-assertions, which have their own ``negate`` attribute, are not affected by ``negate``.""" that: Literal["element_text"] = "element_text" class element_text_model_nested(AssertionModel): r"""Nested version of this assertion model.""" element_text: base_element_text_model class element_text_model_relaxed(base_element_text_model_relaxed): 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. ```xml ``` The assertion implicitly also asserts that an element matching ``path`` exists. With ``negate`` the result of the implicit assertions can be inverted. The sub-assertions, which have their own ``negate`` attribute, are not affected by ``negate``.""" that: Literal["element_text"] = "element_text" element_text_is_path_description = """The Python xpath-like expression to find the target element.""" element_text_is_text_description = ( """The expected element text (body of the XML tag) to test against on the target XML element""" ) element_text_is_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" 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 base_element_text_is_model_relaxed(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``. 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["element_text_is"] = "element_text_is" class element_text_is_model_nested(AssertionModel): r"""Nested version of this assertion model.""" element_text_is: base_element_text_is_model class element_text_is_model_relaxed(base_element_text_is_model_relaxed): r"""Asserts the text of the XML element 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["element_text_is"] = "element_text_is" element_text_matches_path_description = """The Python xpath-like expression to find the target element.""" element_text_matches_expression_description = """The regular expressions to apply against the target element.""" element_text_matches_negate_description = ( """A boolean that can be set to true to negate the outcome of the assertion.""" ) class base_element_text_matches_model(AssertionModel): """base model for element_text_matches describing attributes.""" path: str = Field( ..., description=element_text_matches_path_description, ) expression: Annotated[str, BeforeValidator(check_regex)] = Field( ..., description=element_text_matches_expression_description, ) negate: typing.Union[bool, str] = Field( False, description=element_text_matches_negate_description, ) class base_element_text_matches_model_relaxed(AssertionModel): """base model for element_text_matches describing attributes.""" path: str = Field( ..., description=element_text_matches_path_description, ) expression: Annotated[str, BeforeValidator(check_regex)] = Field( ..., description=element_text_matches_expression_description, ) negate: typing.Union[bool, str] = Field( False, description=element_text_matches_negate_description, ) 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``. 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 element_text_matches_model_nested(AssertionModel): r"""Nested version of this assertion model.""" element_text_matches: base_element_text_matches_model class element_text_matches_model_relaxed(base_element_text_matches_model_relaxed): 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" 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( ..., description=has_element_with_path_path_description, ) negate: typing.Union[bool, str] = Field( False, description=has_element_with_path_negate_description, ) class base_has_element_with_path_model_relaxed(AssertionModel): """base model for has_element_with_path describing attributes.""" path: str = Field( ..., description=has_element_with_path_path_description, ) negate: typing.Union[bool, str] = Field( False, description=has_element_with_path_negate_description, ) 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 class has_element_with_path_model_relaxed(base_has_element_with_path_model_relaxed): 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" 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?``""" has_n_elements_with_path_delta_description = ( """Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_n_elements_with_path_min_description = ( """Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_n_elements_with_path_max_description = ( """Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_n_elements_with_path_negate_description = ( """A boolean that can be set to true to negate the outcome of the assertion.""" ) class base_has_n_elements_with_path_model(AssertionModel): """base model for has_n_elements_with_path describing attributes.""" path: str = Field( ..., description=has_n_elements_with_path_path_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_elements_with_path_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_n_elements_with_path_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_elements_with_path_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_elements_with_path_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_n_elements_with_path_negate_description, ) class base_has_n_elements_with_path_model_relaxed(AssertionModel): """base model for has_n_elements_with_path describing attributes.""" path: str = Field( ..., description=has_n_elements_with_path_path_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_elements_with_path_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_n_elements_with_path_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_elements_with_path_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_n_elements_with_path_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_n_elements_with_path_negate_description, ) 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 has_n_elements_with_path_model_relaxed(base_has_n_elements_with_path_model_relaxed): 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_is_valid_xml_model(AssertionModel): """base model for is_valid_xml describing attributes.""" class base_is_valid_xml_model_relaxed(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 class is_valid_xml_model_relaxed(base_is_valid_xml_model_relaxed): r"""Asserts the output is a valid XML file (e.g. ````).""" that: Literal["is_valid_xml"] = "is_valid_xml" 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.""" xml_element_all_description = ( """Check the sub-assertions for all paths matching the path. Default: false, i.e. only the first """ ) xml_element_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" xml_element_delta_description = ( """Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) xml_element_min_description = """Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" xml_element_max_description = """Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" xml_element_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_xml_element_model(AssertionModel): """base model for xml_element describing attributes.""" path: str = Field( ..., description=xml_element_path_description, ) attribute: typing.Optional[typing.Union[str]] = Field( None, description=xml_element_attribute_description, ) all: typing.Union[bool, str] = Field( False, description=xml_element_all_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=xml_element_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=xml_element_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=xml_element_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=xml_element_max_description, ) negate: typing.Union[bool, str] = Field( False, description=xml_element_negate_description, ) children: typing.Optional["assertion_list"] = None asserts: typing.Optional["assertion_list"] = None class base_xml_element_model_relaxed(AssertionModel): """base model for xml_element describing attributes.""" path: str = Field( ..., description=xml_element_path_description, ) attribute: typing.Optional[typing.Union[str]] = Field( None, description=xml_element_attribute_description, ) all: typing.Union[bool, str] = Field( False, description=xml_element_all_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=xml_element_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=xml_element_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=xml_element_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=xml_element_max_description, ) negate: typing.Union[bool, str] = Field( False, description=xml_element_negate_description, ) children: typing.Optional["assertion_list"] = None asserts: typing.Optional["assertion_list"] = None 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. ```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 xml_element_model_nested(AssertionModel): r"""Nested version of this assertion model.""" xml_element: base_xml_element_model class xml_element_model_relaxed(base_xml_element_model_relaxed): 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" 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( ..., description=has_json_property_with_text_property_description, ) text: str = Field( ..., description=has_json_property_with_text_text_description, ) class base_has_json_property_with_text_model_relaxed(AssertionModel): """base model for has_json_property_with_text describing attributes.""" property: str = Field( ..., description=has_json_property_with_text_property_description, ) text: str = Field( ..., description=has_json_property_with_text_text_description, ) 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 class has_json_property_with_text_model_relaxed(base_has_json_property_with_text_model_relaxed): 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" has_json_property_with_value_property_description = """The property name to search the JSON document for.""" has_json_property_with_value_value_description = ( """The expected JSON value of the target JSON attribute (as a JSON encoded string).""" ) class base_has_json_property_with_value_model(AssertionModel): """base model for has_json_property_with_value describing attributes.""" property: str = Field( ..., description=has_json_property_with_value_property_description, ) value: str = Field( ..., description=has_json_property_with_value_value_description, ) class base_has_json_property_with_value_model_relaxed(AssertionModel): """base model for has_json_property_with_value describing attributes.""" property: str = Field( ..., description=has_json_property_with_value_property_description, ) value: str = Field( ..., description=has_json_property_with_value_value_description, ) 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. ```xml ```""" that: Literal["has_json_property_with_value"] = "has_json_property_with_value" 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 class has_json_property_with_value_model_relaxed(base_has_json_property_with_value_model_relaxed): 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" 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( ..., description=has_h5_attribute_key_description, ) value: str = Field( ..., description=has_h5_attribute_value_description, ) class base_has_h5_attribute_model_relaxed(AssertionModel): """base model for has_h5_attribute describing attributes.""" key: str = Field( ..., description=has_h5_attribute_key_description, ) value: str = Field( ..., description=has_h5_attribute_value_description, ) 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_attribute_model_nested(AssertionModel): r"""Nested version of this assertion model.""" has_h5_attribute: base_has_h5_attribute_model class has_h5_attribute_model_relaxed(base_has_h5_attribute_model_relaxed): r"""Asserts HDF5 output contains the specified ``value`` for an attribute (``key``), e.g. ```xml ```""" that: Literal["has_h5_attribute"] = "has_h5_attribute" 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( ..., description=has_h5_keys_keys_description, ) class base_has_h5_keys_model_relaxed(AssertionModel): """base model for has_h5_keys describing attributes.""" keys: str = Field( ..., description=has_h5_keys_keys_description, ) 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 class has_h5_keys_model_relaxed(base_has_h5_keys_model_relaxed): r"""Asserts the specified HDF5 output has the given keys.""" that: Literal["has_h5_keys"] = "has_h5_keys" has_archive_member_path_description = """The regular expression specifying the archive member.""" has_archive_member_all_description = ( """Check the sub-assertions for all paths matching the path. Default: false, i.e. only the first""" ) has_archive_member_n_description = """Desired number, can be suffixed by ``(k|M|G|T|P|E)i?``""" has_archive_member_delta_description = ( """Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_archive_member_min_description = """Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_archive_member_max_description = """Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_archive_member_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_archive_member_model(AssertionModel): """base model for has_archive_member describing attributes.""" path: str = Field( ..., description=has_archive_member_path_description, ) all: typing.Union[bool, str] = Field( False, description=has_archive_member_all_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_archive_member_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_archive_member_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_archive_member_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_archive_member_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_archive_member_negate_description, ) children: typing.Optional["assertion_list"] = None asserts: typing.Optional["assertion_list"] = None class base_has_archive_member_model_relaxed(AssertionModel): """base model for has_archive_member describing attributes.""" path: str = Field( ..., description=has_archive_member_path_description, ) all: typing.Union[bool, str] = Field( False, description=has_archive_member_all_description, ) n: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_archive_member_n_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_archive_member_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_archive_member_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_archive_member_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_archive_member_negate_description, ) 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 class has_archive_member_model_relaxed(base_has_archive_member_model_relaxed): 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" has_size_value_description = """Deprecated alias for `size`""" has_size_size_description = """Desired size of the output (in bytes), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_size_delta_description = ( """Allowed difference with respect to n (default: 0), can be suffixed by ``(k|M|G|T|P|E)i?``""" ) has_size_min_description = """Minimum number (default: -infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_size_max_description = """Maximum number (default: infinity), can be suffixed by ``(k|M|G|T|P|E)i?``""" has_size_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_size_model(AssertionModel): """base model for has_size describing attributes.""" 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]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_size_size_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_size_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_size_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_size_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_size_negate_description, ) class base_has_size_model_relaxed(AssertionModel): """base model for has_size describing attributes.""" 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]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_size_size_description, ) delta: Annotated[ typing.Union[int, str], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int) ] = Field( 0, description=has_size_delta_description, ) min: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_size_min_description, ) max: Annotated[ typing.Optional[typing.Union[str, int]], BeforeValidator(check_bytes), BeforeValidator(check_non_negative_if_int), ] = Field( None, description=has_size_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_size_negate_description, ) 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 class has_size_model_relaxed(base_has_size_model_relaxed): 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" 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).""" has_image_center_of_mass_slice_description = ( """Restricts the assertion to a specific slice of the image (where ``0`` corresponds to the first image slice).""" ) has_image_center_of_mass_frame_description = """Restricts the assertion to a specific frame of the image sequence (where ``0`` corresponds to the first image frame).""" has_image_center_of_mass_eps_description = ( """The maximum allowed Euclidean distance to the required center of mass (defaults to ``0.01``).""" ) 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( ..., description=has_image_center_of_mass_center_of_mass_description, ) channel: typing.Optional[StrictInt] = Field( None, description=has_image_center_of_mass_channel_description, ) slice: typing.Optional[StrictInt] = Field( None, description=has_image_center_of_mass_slice_description, ) frame: typing.Optional[StrictInt] = Field( None, description=has_image_center_of_mass_frame_description, ) eps: Annotated[typing.Union[StrictInt, StrictFloat], BeforeValidator(check_non_negative_if_set)] = Field( 0.01, description=has_image_center_of_mass_eps_description, ) class base_has_image_center_of_mass_model_relaxed(AssertionModel): """base model for has_image_center_of_mass describing attributes.""" center_of_mass: Annotated[str, BeforeValidator(check_center_of_mass)] = Field( ..., description=has_image_center_of_mass_center_of_mass_description, ) channel: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_center_of_mass_channel_description, ) slice: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_center_of_mass_slice_description, ) frame: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_center_of_mass_frame_description, ) eps: Annotated[typing.Union[float, str], BeforeValidator(check_non_negative_if_set)] = Field( 0.01, description=has_image_center_of_mass_eps_description, ) 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 class has_image_center_of_mass_model_relaxed(base_has_image_center_of_mass_model_relaxed): 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" 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``.""" has_image_channels_min_description = """Minimum allowed number of channels.""" has_image_channels_max_description = """Maximum allowed number of channels.""" has_image_channels_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" 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, description=has_image_channels_channels_description, ) delta: Annotated[StrictInt, BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_channels_delta_description, ) min: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_channels_min_description, ) max: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_channels_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_channels_negate_description, ) class base_has_image_channels_model_relaxed(AssertionModel): """base model for has_image_channels describing attributes.""" channels: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_channels_channels_description, ) delta: Annotated[typing.Union[int, str], BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_channels_delta_description, ) min: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_channels_min_description, ) max: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_channels_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_channels_negate_description, ) 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 class has_image_channels_model_relaxed(base_has_image_channels_model_relaxed): 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" has_image_depth_depth_description = """Expected depth of the image (number of slices).""" has_image_depth_delta_description = """Maximum allowed difference of the image depth (number of slices, default is 0). The observed depth has to be in the range ``value +- delta``.""" has_image_depth_min_description = """Minimum allowed depth of the image (number of slices).""" has_image_depth_max_description = """Maximum allowed depth of the image (number of slices).""" has_image_depth_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_image_depth_model(AssertionModel): """base model for has_image_depth describing attributes.""" depth: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_depth_depth_description, ) delta: Annotated[StrictInt, BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_depth_delta_description, ) min: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_depth_min_description, ) max: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_depth_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_depth_negate_description, ) class base_has_image_depth_model_relaxed(AssertionModel): """base model for has_image_depth describing attributes.""" depth: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_depth_depth_description, ) delta: Annotated[typing.Union[int, str], BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_depth_delta_description, ) min: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_depth_min_description, ) max: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_depth_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_depth_negate_description, ) class has_image_depth_model(base_has_image_depth_model): r"""Asserts the output is an image and has a specific depth (number of slices). The depth is plus/minus ``delta`` (e.g., ````). Alternatively the range of the expected depth can be specified by ``min`` and/or ``max``.""" that: Literal["has_image_depth"] = "has_image_depth" class has_image_depth_model_nested(AssertionModel): r"""Nested version of this assertion model.""" has_image_depth: base_has_image_depth_model class has_image_depth_model_relaxed(base_has_image_depth_model_relaxed): r"""Asserts the output is an image and has a specific depth (number of slices). The depth is plus/minus ``delta`` (e.g., ````). Alternatively the range of the expected depth can be specified by ``min`` and/or ``max``.""" that: Literal["has_image_depth"] = "has_image_depth" has_image_frames_frames_description = """Expected number of frames in the image sequence (number of time steps).""" has_image_frames_delta_description = """Maximum allowed difference of the number of frames in the image sequence (number of time steps, default is 0). The observed number of frames has to be in the range ``value +- delta``.""" has_image_frames_min_description = """Minimum allowed number of frames in the image sequence (number of time steps).""" has_image_frames_max_description = """Maximum allowed number of frames in the image sequence (number of time steps).""" has_image_frames_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_image_frames_model(AssertionModel): """base model for has_image_frames describing attributes.""" frames: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_frames_frames_description, ) delta: Annotated[StrictInt, BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_frames_delta_description, ) min: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_frames_min_description, ) max: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_frames_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_frames_negate_description, ) class base_has_image_frames_model_relaxed(AssertionModel): """base model for has_image_frames describing attributes.""" frames: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_frames_frames_description, ) delta: Annotated[typing.Union[int, str], BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_frames_delta_description, ) min: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_frames_min_description, ) max: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_frames_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_frames_negate_description, ) class has_image_frames_model(base_has_image_frames_model): r"""Asserts the output is an image and has a specific number of frames (number of time steps). The number of frames is plus/minus ``delta`` (e.g., ````). Alternatively the range of the expected number of frames can be specified by ``min`` and/or ``max``.""" that: Literal["has_image_frames"] = "has_image_frames" class has_image_frames_model_nested(AssertionModel): r"""Nested version of this assertion model.""" has_image_frames: base_has_image_frames_model class has_image_frames_model_relaxed(base_has_image_frames_model_relaxed): r"""Asserts the output is an image and has a specific number of frames (number of time steps). The number of frames is plus/minus ``delta`` (e.g., ````). Alternatively the range of the expected number of frames can be specified by ``min`` and/or ``max``.""" that: Literal["has_image_frames"] = "has_image_frames" 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``.""" has_image_height_min_description = """Minimum allowed height of the image (in pixels).""" has_image_height_max_description = """Maximum allowed height of the image (in pixels).""" has_image_height_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" 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, description=has_image_height_height_description, ) delta: Annotated[StrictInt, BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_height_delta_description, ) min: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_height_min_description, ) max: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_height_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_height_negate_description, ) class base_has_image_height_model_relaxed(AssertionModel): """base model for has_image_height describing attributes.""" height: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_height_height_description, ) delta: Annotated[typing.Union[int, str], BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_height_delta_description, ) min: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_height_min_description, ) max: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_height_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_height_negate_description, ) 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 class has_image_height_model_relaxed(base_has_image_height_model_relaxed): 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" 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_slice_description = ( """Restricts the assertion to a specific slice of the image (where ``0`` corresponds to the first image slice).""" ) has_image_mean_intensity_frame_description = """Restricts the assertion to a specific frame of the image sequence (where ``0`` corresponds to the first image frame).""" has_image_mean_intensity_mean_intensity_description = """The required mean value of the image intensities.""" has_image_mean_intensity_eps_description = """The absolute tolerance to be used for ``value`` (defaults to ``0.01``). The observed mean value of the image intensities has to be in the range ``value +- eps``.""" has_image_mean_intensity_min_description = """A lower bound of the required mean value of the image intensities.""" has_image_mean_intensity_max_description = """An upper bound of the required mean value of the image intensities.""" class base_has_image_mean_intensity_model(AssertionModel): """base model for has_image_mean_intensity describing attributes.""" channel: typing.Optional[StrictInt] = Field( None, description=has_image_mean_intensity_channel_description, ) slice: typing.Optional[StrictInt] = Field( None, description=has_image_mean_intensity_slice_description, ) frame: typing.Optional[StrictInt] = Field( None, description=has_image_mean_intensity_frame_description, ) mean_intensity: typing.Optional[typing.Union[StrictInt, StrictFloat]] = Field( None, description=has_image_mean_intensity_mean_intensity_description, ) eps: Annotated[typing.Union[StrictInt, StrictFloat], BeforeValidator(check_non_negative_if_set)] = Field( 0.01, description=has_image_mean_intensity_eps_description, ) min: typing.Optional[typing.Union[StrictInt, StrictFloat]] = Field( None, description=has_image_mean_intensity_min_description, ) max: typing.Optional[typing.Union[StrictInt, StrictFloat]] = Field( None, description=has_image_mean_intensity_max_description, ) class base_has_image_mean_intensity_model_relaxed(AssertionModel): """base model for has_image_mean_intensity describing attributes.""" channel: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_mean_intensity_channel_description, ) slice: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_mean_intensity_slice_description, ) frame: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_mean_intensity_frame_description, ) mean_intensity: typing.Optional[typing.Union[float, str]] = Field( None, description=has_image_mean_intensity_mean_intensity_description, ) eps: Annotated[typing.Union[float, str], BeforeValidator(check_non_negative_if_set)] = Field( 0.01, description=has_image_mean_intensity_eps_description, ) min: typing.Optional[typing.Union[float, str]] = Field( None, description=has_image_mean_intensity_min_description, ) max: typing.Optional[typing.Union[float, str]] = Field( None, description=has_image_mean_intensity_max_description, ) 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 class has_image_mean_intensity_model_relaxed(base_has_image_mean_intensity_model_relaxed): 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" 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_slice_description = ( """Restricts the assertion to a specific slice of the image (where ``0`` corresponds to the first image slice).""" ) has_image_mean_object_size_frame_description = """Restricts the assertion to a specific frame of the image sequence (where ``0`` corresponds to the first image frame).""" 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``.""" has_image_mean_object_size_exclude_labels_description = """List of labels to be excluded from consideration, separated by a comma. The primary usage of this attribute is to exclude the background of a label image. Cannot be used in combination with ``labels``.""" has_image_mean_object_size_mean_object_size_description = """The required mean size of the uniquely labeled objects.""" has_image_mean_object_size_eps_description = """The absolute tolerance to be used for ``value`` (defaults to ``0.01``). The observed mean size of the uniquely labeled objects has to be in the range ``value +- eps``.""" has_image_mean_object_size_min_description = ( """A lower bound of the required mean size of the uniquely labeled objects.""" ) has_image_mean_object_size_max_description = ( """An upper bound of the required mean size of the uniquely labeled objects.""" ) 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, description=has_image_mean_object_size_channel_description, ) slice: typing.Optional[StrictInt] = Field( None, description=has_image_mean_object_size_slice_description, ) frame: typing.Optional[StrictInt] = Field( None, description=has_image_mean_object_size_frame_description, ) labels: typing.Optional[typing.List[int]] = Field( None, description=has_image_mean_object_size_labels_description, ) exclude_labels: typing.Optional[typing.List[int]] = Field( None, description=has_image_mean_object_size_exclude_labels_description, ) mean_object_size: Annotated[ typing.Optional[typing.Union[StrictInt, StrictFloat]], BeforeValidator(check_non_negative_if_set) ] = Field( None, description=has_image_mean_object_size_mean_object_size_description, ) eps: Annotated[typing.Union[StrictInt, StrictFloat], BeforeValidator(check_non_negative_if_set)] = Field( 0.01, description=has_image_mean_object_size_eps_description, ) min: Annotated[ typing.Optional[typing.Union[StrictInt, StrictFloat]], BeforeValidator(check_non_negative_if_set) ] = Field( None, description=has_image_mean_object_size_min_description, ) max: Annotated[ typing.Optional[typing.Union[StrictInt, StrictFloat]], BeforeValidator(check_non_negative_if_set) ] = Field( None, description=has_image_mean_object_size_max_description, ) class base_has_image_mean_object_size_model_relaxed(AssertionModel): """base model for has_image_mean_object_size describing attributes.""" channel: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_mean_object_size_channel_description, ) slice: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_mean_object_size_slice_description, ) frame: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_mean_object_size_frame_description, ) labels: typing.Optional[typing.Union[str, typing.List[int]]] = Field( None, description=has_image_mean_object_size_labels_description, ) exclude_labels: typing.Optional[typing.Union[str, typing.List[int]]] = Field( None, description=has_image_mean_object_size_exclude_labels_description, ) mean_object_size: Annotated[ typing.Optional[typing.Union[float, str]], BeforeValidator(check_non_negative_if_set) ] = Field( None, description=has_image_mean_object_size_mean_object_size_description, ) eps: Annotated[typing.Union[float, str], BeforeValidator(check_non_negative_if_set)] = Field( 0.01, description=has_image_mean_object_size_eps_description, ) min: Annotated[typing.Optional[typing.Union[float, str]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_mean_object_size_min_description, ) max: Annotated[typing.Optional[typing.Union[float, str]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_mean_object_size_max_description, ) 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 class has_image_mean_object_size_model_relaxed(base_has_image_mean_object_size_model_relaxed): 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" 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_slice_description = ( """Restricts the assertion to a specific slice of the image (where ``0`` corresponds to the first image slice).""" ) has_image_n_labels_frame_description = """Restricts the assertion to a specific frame of the image sequence (where ``0`` corresponds to the first image frame).""" 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``.""" has_image_n_labels_exclude_labels_description = """List of labels to be excluded from consideration, separated by a comma. The primary usage of this attribute is to exclude the background of a label image. Cannot be used in combination with ``labels``.""" has_image_n_labels_n_description = """Expected number of labels.""" has_image_n_labels_delta_description = """Maximum allowed difference of the number of labels (default is 0). The observed number of labels has to be in the range ``value +- delta``.""" has_image_n_labels_min_description = """Minimum allowed number of labels.""" has_image_n_labels_max_description = """Maximum allowed number of labels.""" has_image_n_labels_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" class base_has_image_n_labels_model(AssertionModel): """base model for has_image_n_labels describing attributes.""" channel: typing.Optional[StrictInt] = Field( None, description=has_image_n_labels_channel_description, ) slice: typing.Optional[StrictInt] = Field( None, description=has_image_n_labels_slice_description, ) frame: typing.Optional[StrictInt] = Field( None, description=has_image_n_labels_frame_description, ) labels: typing.Optional[typing.List[int]] = Field( None, description=has_image_n_labels_labels_description, ) exclude_labels: typing.Optional[typing.List[int]] = Field( None, description=has_image_n_labels_exclude_labels_description, ) n: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_n_labels_n_description, ) delta: Annotated[StrictInt, BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_n_labels_delta_description, ) min: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_n_labels_min_description, ) max: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_n_labels_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_n_labels_negate_description, ) class base_has_image_n_labels_model_relaxed(AssertionModel): """base model for has_image_n_labels describing attributes.""" channel: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_n_labels_channel_description, ) slice: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_n_labels_slice_description, ) frame: typing.Optional[typing.Union[str, int]] = Field( None, description=has_image_n_labels_frame_description, ) labels: typing.Optional[typing.Union[str, typing.List[int]]] = Field( None, description=has_image_n_labels_labels_description, ) exclude_labels: typing.Optional[typing.Union[str, typing.List[int]]] = Field( None, description=has_image_n_labels_exclude_labels_description, ) n: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_n_labels_n_description, ) delta: Annotated[typing.Union[int, str], BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_n_labels_delta_description, ) min: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_n_labels_min_description, ) max: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_n_labels_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_n_labels_negate_description, ) 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 class has_image_n_labels_model_relaxed(base_has_image_n_labels_model_relaxed): 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" 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``.""" has_image_width_min_description = """Minimum allowed width of the image (in pixels).""" has_image_width_max_description = """Maximum allowed width of the image (in pixels).""" has_image_width_negate_description = """A boolean that can be set to true to negate the outcome of the assertion.""" 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, description=has_image_width_width_description, ) delta: Annotated[StrictInt, BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_width_delta_description, ) min: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_width_min_description, ) max: Annotated[typing.Optional[StrictInt], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_width_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_width_negate_description, ) class base_has_image_width_model_relaxed(AssertionModel): """base model for has_image_width describing attributes.""" width: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_width_width_description, ) delta: Annotated[typing.Union[int, str], BeforeValidator(check_non_negative_if_set)] = Field( 0, description=has_image_width_delta_description, ) min: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_width_min_description, ) max: Annotated[typing.Optional[typing.Union[str, int]], BeforeValidator(check_non_negative_if_set)] = Field( None, description=has_image_width_max_description, ) negate: typing.Union[bool, str] = Field( False, description=has_image_width_negate_description, ) 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 class has_image_width_model_relaxed(base_has_image_width_model_relaxed): 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" any_assertion_model_flat = Annotated[ typing.Union[ has_line_model, has_line_matching_model, has_n_lines_model, has_text_model, has_text_matching_model, not_has_text_model, has_n_columns_model, attribute_is_model, attribute_matches_model, element_text_model, element_text_is_model, element_text_matches_model, has_element_with_path_model, has_n_elements_with_path_model, is_valid_xml_model, xml_element_model, has_json_property_with_text_model, has_json_property_with_value_model, has_h5_attribute_model, has_h5_keys_model, has_archive_member_model, has_size_model, has_image_center_of_mass_model, has_image_channels_model, has_image_depth_model, has_image_frames_model, has_image_height_model, has_image_mean_intensity_model, has_image_mean_object_size_model, has_image_n_labels_model, has_image_width_model, ], Field(discriminator="that"), ] 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_depth_model_nested, has_image_frames_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, ] any_assertion_model_flat_relaxed = Annotated[ typing.Union[ has_line_model_relaxed, has_line_matching_model_relaxed, has_n_lines_model_relaxed, has_text_model_relaxed, has_text_matching_model_relaxed, not_has_text_model_relaxed, has_n_columns_model_relaxed, attribute_is_model_relaxed, attribute_matches_model_relaxed, element_text_model_relaxed, element_text_is_model_relaxed, element_text_matches_model_relaxed, has_element_with_path_model_relaxed, has_n_elements_with_path_model_relaxed, is_valid_xml_model_relaxed, xml_element_model_relaxed, has_json_property_with_text_model_relaxed, has_json_property_with_value_model_relaxed, has_h5_attribute_model_relaxed, has_h5_keys_model_relaxed, has_archive_member_model_relaxed, has_size_model_relaxed, has_image_center_of_mass_model_relaxed, has_image_channels_model_relaxed, has_image_depth_model_relaxed, has_image_frames_model_relaxed, has_image_height_model_relaxed, has_image_mean_intensity_model_relaxed, has_image_mean_object_size_model_relaxed, has_image_n_labels_model_relaxed, has_image_width_model_relaxed, ], Field(discriminator="that"), ] assertion_list = RootModel[typing.List[typing.Union[any_assertion_model_flat, any_assertion_model_nested]]] # used to model what the XML conversion should look like - not meant to be consumed outside of # of Galaxy internals / linting. relaxed_assertion_list = RootModel[typing.List[any_assertion_model_flat_relaxed]] 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_depth: typing.Optional[base_has_image_depth_model] = None has_image_frames: typing.Optional[base_has_image_frames_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] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/dynamic_tool_models.py0000644000175100017510000000160515166026066027441 0ustar00runnerrunnerfrom typing import ( Optional, Union, ) from pydantic import BaseModel from typing_extensions import Literal from galaxy.tool_util_models import ( DynamicToolSources, UserToolSource, ) class BaseDynamicToolCreatePayload(BaseModel): active: Optional[bool] = None hidden: Optional[bool] = None class DynamicToolCreatePayload(BaseDynamicToolCreatePayload): src: Literal["representation"] = "representation" representation: DynamicToolSources active: Optional[bool] = True hidden: Optional[bool] = False class DynamicUnprivilegedToolCreatePayload(DynamicToolCreatePayload): representation: UserToolSource class PathBasedDynamicToolCreatePayload(BaseDynamicToolCreatePayload): src: Literal["from_path"] path: str tool_directory: Optional[str] = None DynamicToolPayload = Union[DynamicToolCreatePayload, PathBasedDynamicToolCreatePayload] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/parameter_validators.py0000644000175100017510000004410615166026066027630 0ustar00runnerrunnerfrom typing import ( Any, List, Optional, Union, ) from pydantic import ( BaseModel, ConfigDict, Field, model_validator, PrivateAttr, TypeAdapter, ) from typing_extensions import ( Annotated, Literal, Protocol, Self, ) try: import regex except ImportError: import re as regex class ValidationArgument: doc: Optional[str] xml_body: bool xml_allow_json_load: bool def __init__( self, doc: Optional[str], xml_body: bool = False, xml_allow_json_load: bool = False, ): self.doc = doc self.xml_body = xml_body self.xml_allow_json_load = xml_allow_json_load Negate = Annotated[ bool, ValidationArgument("Negates the result of the validator."), ] NEGATE_DEFAULT = False SPLIT_DEFAULT = "\t" DEFAULT_VALIDATOR_MESSAGE = "Parameter validation error." ValidatorType = Literal[ "expression", "regex", "in_range", "length", "metadata", "dataset_metadata_equal", "unspecified_build", "no_options", "empty_field", "empty_dataset", "empty_extra_files_path", "dataset_metadata_in_data_table", "dataset_metadata_not_in_data_table", "dataset_metadata_in_range", "value_in_data_table", "value_not_in_data_table", "dataset_ok_validator", "dataset_metadata_in_file", ] class ValidatorDescription(Protocol): @property def negate(self) -> bool: ... @property def message(self) -> Optional[str]: ... class StrictModel(BaseModel): model_config = ConfigDict(extra="forbid") class ParameterValidatorModel(StrictModel): type: ValidatorType message: Annotated[ Optional[str], ValidationArgument( """The error message displayed on the tool form if validation fails. A placeholder string ``%s`` will be repaced by the ``value``""" ), ] = None # track validators setup by other input parameters and not validation explicitly implicit: bool = False _static: bool = PrivateAttr(False) _deprecated: bool = PrivateAttr(False) # validators must be explicitly set as 'safe' to operate as user-defined workflow parameters or to be used # within future user-defined tool parameters _safe: bool = PrivateAttr(False) @model_validator(mode="after") def set_default_message(self) -> Self: if self.message is None: self.message = self.default_message return self @property def default_message(self) -> str: return DEFAULT_VALIDATOR_MESSAGE class StaticValidatorModel(ParameterValidatorModel): _static: bool = PrivateAttr(True) def statically_validate(self, v: Any) -> None: ... class ExpressionParameterValidatorModel(StaticValidatorModel): """Check if a one line python expression given expression evaluates to True. The expression is given is the content of the validator tag.""" type: Literal["expression"] = "expression" negate: Negate = NEGATE_DEFAULT expression: Annotated[str, ValidationArgument("Python expression to validate.", xml_body=True)] def statically_validate(self, value: Any) -> None: ExpressionParameterValidatorModel.expression_validation(self.expression, value, self) @staticmethod def ensure_compiled(expression: Union[str, Any]) -> Any: if isinstance(expression, str): return compile(expression, "", "eval") else: return expression @staticmethod def expression_validation( expression: str, value: Any, validator: "ValidatorDescription", compiled_expression: Optional[Any] = None ): if compiled_expression is None: compiled_expression = ExpressionParameterValidatorModel.ensure_compiled(expression) message = None try: evalresult = eval(compiled_expression, dict(value=value)) except Exception: message = f"Validator '{expression}' could not be evaluated on '{value}'" evalresult = False raise_error_if_validation_fails(bool(evalresult), validator, message=message, value_to_show=value) @property def default_message(self) -> str: return f"Value '%s' does not evaluate to {'True' if not self.negate else 'False'} for '{self.expression}'" class RegexParameterValidatorModel(StaticValidatorModel): """Check if a regular expression **matches** the value, i.e. appears at the beginning of the value. To enforce a match of the complete value use ``$`` at the end of the expression. The expression is given is the content of the validator tag. Note that for ``selects`` each option is checked separately.""" type: Literal["regex"] = "regex" negate: Negate = NEGATE_DEFAULT expression: Annotated[str, ValidationArgument("Regular expression to validate against.", xml_body=True)] _safe: bool = PrivateAttr(True) @property def default_message(self) -> str: return f"Value '%s' does {'not ' if not self.negate else ''}match regular expression '{self.expression.replace('%', '%%')}'" def statically_validate(self, value: Any) -> None: if value and not isinstance(value, str): raise ValueError(f"Wrong type found value {value}") RegexParameterValidatorModel.regex_validation(self.expression, value, self) @staticmethod def regex_validation(expression: str, value: Any, validator: "ValidatorDescription"): if not isinstance(value, list): value = [value] for val in value: match = regex.match(expression, val or "") raise_error_if_validation_fails(match is not None, validator, value_to_show=val) class InRangeParameterValidatorModel(StaticValidatorModel): type: Literal["in_range"] = "in_range" min: Optional[Union[float, int]] = None max: Optional[Union[float, int]] = None exclude_min: bool = False exclude_max: bool = False negate: Negate = NEGATE_DEFAULT _safe: bool = PrivateAttr(True) def statically_validate(self, value: Any): if isinstance(value, (int, float)): validates = True if self.min is not None and value == self.min and self.exclude_min: validates = False elif self.min is not None and value < self.min: validates = False elif self.max is not None and value == self.max and self.exclude_max: validates = False if self.max is not None and value > self.max: validates = False raise_error_if_validation_fails(validates, self) @property def default_message(self) -> str: op1 = "<=" op2 = "<=" if self.exclude_min: op1 = "<" if self.exclude_max: op2 = "<" min_str = str(self.min) if self.min is not None else "-infinity" max_str = str(self.max) if self.max is not None else "+infinity" range_description_str = f"({min_str} {op1} value {op2} {max_str})" return f"Value ('%s') must {'not ' if self.negate else ''}fulfill {range_description_str}" class LengthParameterValidatorModel(StaticValidatorModel): type: Literal["length"] = "length" min: Optional[int] = None max: Optional[int] = None negate: Negate = NEGATE_DEFAULT _safe: bool = PrivateAttr(True) def statically_validate(self, value: Any): if isinstance(value, str): length = len(value) validates = True if self.min is not None and length < self.min: validates = False if self.max is not None and length > self.max: validates = False raise_error_if_validation_fails(validates, self) @property def default_message(self) -> str: return f"Must {'not ' if self.negate else ''}have length of at least {self.min} and at most {self.max}" class MetadataParameterValidatorModel(ParameterValidatorModel): type: Literal["metadata"] = "metadata" check: Optional[List[str]] = None skip: Optional[List[str]] = None negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: check = self.check skip = self.skip message = DEFAULT_VALIDATOR_MESSAGE if not self.negate: message = "Metadata '%s' missing, click the pencil icon in the history item to edit / save the metadata attributes" else: if check: message = f"""At least one of the checked metadata '{",".join(check)}' is set, click the pencil icon in the history item to edit / save the metadata attributes""" elif skip: message = f"""At least one of the non skipped metadata '{",".join(skip)}' is set, click the pencil icon in the history item to edit / save the metadata attributes""" return message class DatasetMetadataEqualParameterValidatorModel(ParameterValidatorModel): type: Literal["dataset_metadata_equal"] = "dataset_metadata_equal" metadata_name: str value: Annotated[Any, ValidationArgument("Value to test against", xml_allow_json_load=True)] negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: if not self.negate: message = f"Metadata value for '{self.metadata_name}' must be '{self.value}', but it is '%s'." else: message = f"Metadata value for '{self.metadata_name}' must not be '{self.value}' but it is." return message class UnspecifiedBuildParameterValidatorModel(ParameterValidatorModel): type: Literal["unspecified_build"] = "unspecified_build" negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: return f"{'Unspecified' if not self.negate else 'Specified'} genome build, click the pencil icon in the history item to {'set' if not self.negate else 'remove'} the genome build" class NoOptionsParameterValidatorModel(StaticValidatorModel): type: Literal["no_options"] = "no_options" negate: Negate = NEGATE_DEFAULT @staticmethod def no_options_validate(value: Any, validator: "ValidatorDescription"): raise_error_if_validation_fails(value is not None, validator) def statically_validate(self, value: Any) -> None: NoOptionsParameterValidatorModel.no_options_validate(value, self) @property def default_message(self) -> str: return f"{'No options' if not self.negate else 'Options'} available for selection" class EmptyFieldParameterValidatorModel(StaticValidatorModel): type: Literal["empty_field"] = "empty_field" negate: Negate = NEGATE_DEFAULT @staticmethod def empty_validate(value: Any, validator: "ValidatorDescription"): raise_error_if_validation_fails((value not in ("", None)), validator) def statically_validate(self, value: Any) -> None: EmptyFieldParameterValidatorModel.empty_validate(value, self) @property def default_message(self) -> str: if not self.negate: message = "Field requires a value" else: message = "Field must not set a value" return message class EmptyDatasetParameterValidatorModel(ParameterValidatorModel): type: Literal["empty_dataset"] = "empty_dataset" negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: return f"The selected dataset is {'non-' if self.negate else ''}empty, this tool expects {'non-' if not self.negate else ''}empty files." class EmptyExtraFilesPathParameterValidatorModel(ParameterValidatorModel): type: Literal["empty_extra_files_path"] = "empty_extra_files_path" negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: negate = self.negate return f"The selected dataset's extra_files_path directory is {'non-' if negate else ''}empty or does {'not ' if not negate else ''}exist, this tool expects {'non-' if not negate else ''}empty extra_files_path directories associated with the selected input." class DatasetMetadataInDataTableParameterValidatorModel(ParameterValidatorModel): type: Literal["dataset_metadata_in_data_table"] = "dataset_metadata_in_data_table" table_name: str metadata_name: str metadata_column: Union[int, str] negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: return f"Value for metadata {self.metadata_name} was not found in {self.table_name}." class DatasetMetadataNotInDataTableParameterValidatorModel(ParameterValidatorModel): type: Literal["dataset_metadata_not_in_data_table"] = "dataset_metadata_not_in_data_table" table_name: str metadata_name: str metadata_column: Union[int, str] negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: return f"Value for metadata {self.metadata_name} was not found in {self.table_name}." class DatasetMetadataInRangeParameterValidatorModel(ParameterValidatorModel): type: Literal["dataset_metadata_in_range"] = "dataset_metadata_in_range" metadata_name: str min: Optional[Union[float, int]] = None max: Optional[Union[float, int]] = None exclude_min: bool = False exclude_max: bool = False negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: op1 = "<=" op2 = "<=" if self.exclude_min: op1 = "<" if self.exclude_max: op2 = "<" range_description_str = f"({self.min} {op1} value {op2} {self.max})" return f"Value ('%s') must {'not ' if self.negate else ''}fulfill {range_description_str}" class ValueInDataTableParameterValidatorModel(ParameterValidatorModel): type: Literal["value_in_data_table"] = "value_in_data_table" table_name: str metadata_column: Union[int, str] negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: return "Value for metadata not found." class ValueNotInDataTableParameterValidatorModel(ParameterValidatorModel): type: Literal["value_not_in_data_table"] = "value_not_in_data_table" table_name: str metadata_column: Union[int, str] negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: return f"Value was not found in {self.table_name}." class DatasetOkValidatorParameterValidatorModel(ParameterValidatorModel): type: Literal["dataset_ok_validator"] = "dataset_ok_validator" negate: Negate = NEGATE_DEFAULT @property def default_message(self) -> str: if not self.negate: message = ( "The selected dataset is still being generated, select another dataset or wait until it is completed" ) else: message = "The selected dataset must not be in state OK" return message class DatasetMetadataInFileParameterValidatorModel(ParameterValidatorModel): type: Literal["dataset_metadata_in_file"] = "dataset_metadata_in_file" filename: str metadata_name: str metadata_column: Union[int, str] line_startswith: Optional[str] = None split: str = SPLIT_DEFAULT negate: Negate = NEGATE_DEFAULT _deprecated: bool = PrivateAttr(True) @property def default_message(self) -> str: return f"Value for metadata {self.metadata_name} was not found in {self.filename}." AnyValidatorModel = Annotated[ Union[ ExpressionParameterValidatorModel, RegexParameterValidatorModel, InRangeParameterValidatorModel, LengthParameterValidatorModel, MetadataParameterValidatorModel, DatasetMetadataEqualParameterValidatorModel, UnspecifiedBuildParameterValidatorModel, NoOptionsParameterValidatorModel, EmptyFieldParameterValidatorModel, EmptyDatasetParameterValidatorModel, EmptyExtraFilesPathParameterValidatorModel, DatasetMetadataInDataTableParameterValidatorModel, DatasetMetadataNotInDataTableParameterValidatorModel, DatasetMetadataInRangeParameterValidatorModel, ValueInDataTableParameterValidatorModel, ValueNotInDataTableParameterValidatorModel, DatasetOkValidatorParameterValidatorModel, DatasetMetadataInFileParameterValidatorModel, ], Field(discriminator="type"), ] AnySafeValidatorModel = Annotated[ Union[ RegexParameterValidatorModel, InRangeParameterValidatorModel, LengthParameterValidatorModel, ], Field(discriminator="type"), ] DiscriminatedAnyValidatorModel = TypeAdapter(AnyValidatorModel) # type: ignore[var-annotated] DiscriminatedAnySafeValidatorModel = TypeAdapter(AnySafeValidatorModel) # type: ignore[var-annotated] def raise_error_if_validation_fails( value: bool, validator: ValidatorDescription, message: Optional[str] = None, value_to_show: Optional[str] = None ): if not isinstance(value, bool): raise AssertionError("Validator logic problem - computed validation value must be boolean") if message is None: message = validator.message if not message: message = DEFAULT_VALIDATOR_MESSAGE assert message is not None if value_to_show and "%s" in message: message = message % value_to_show negate = validator.negate if (not negate and value) or (negate and not value): return else: raise ValueError(message) __all__ = ( "AnyValidatorModel", "DiscriminatedAnyValidatorModel", "ValidatorType", "ParameterValidatorModel", "SPLIT_DEFAULT", "StaticValidatorModel", "ExpressionParameterValidatorModel", "RegexParameterValidatorModel", "InRangeParameterValidatorModel", "LengthParameterValidatorModel", "MetadataParameterValidatorModel", "DatasetMetadataEqualParameterValidatorModel", "UnspecifiedBuildParameterValidatorModel", "NoOptionsParameterValidatorModel", "EmptyFieldParameterValidatorModel", "EmptyDatasetParameterValidatorModel", "EmptyExtraFilesPathParameterValidatorModel", "DatasetMetadataInDataTableParameterValidatorModel", "DatasetMetadataNotInDataTableParameterValidatorModel", "DatasetMetadataInRangeParameterValidatorModel", "ValueInDataTableParameterValidatorModel", "ValueNotInDataTableParameterValidatorModel", "DatasetOkValidatorParameterValidatorModel", "DatasetMetadataInFileParameterValidatorModel", ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/parameters.py0000644000175100017510000020616315166026066025566 0ustar00runnerrunner# attempt to model requires_value... # conditional can descend... from abc import abstractmethod from typing import ( Any, Callable, cast, Dict, Iterable, List, Mapping, NamedTuple, Optional, Sequence, Type, TypeVar, Union, ) from pydantic import ( AfterValidator, AliasChoices, AnyUrl, BaseModel, ConfigDict, create_model, Discriminator, Field, field_validator, HttpUrl, model_validator, RootModel, StrictBool, StrictFloat, StrictInt, StrictStr, Tag, TypeAdapter, ) from pydantic.json_schema import SkipJsonSchema from typing_extensions import ( Annotated, Literal, Protocol, ) from ._base import ToolSourceBaseModel from ._types import ( cast_as_type, expand_annotation, is_optional, list_type, optional, optional_if_needed, union_type, ) from .parameter_validators import ( EmptyFieldParameterValidatorModel, ExpressionParameterValidatorModel, InRangeParameterValidatorModel, LengthParameterValidatorModel, NoOptionsParameterValidatorModel, RegexParameterValidatorModel, StaticValidatorModel, ) from .sample_sheet import ( SampleSheetColumnDefinitions, SampleSheetRow, ) from .tool_source import ( DrillDownOptionsDict, JsonTestCollectionDefDict, JsonTestDatasetDefDict, ) # TODO: # - implement data_ref on rules and implement some cross model validation # + request: Return info needed to build request pydantic model at runtime. # + request_internal: This is a pydantic model to validate what Galaxy expects to find in the database, # in particular dataset and collection references should be decoded integers. StateRepresentationT = Literal[ "relaxed_request", "request", "request_internal", "request_internal_dereferenced", "landing_request", "landing_request_internal", "job_runtime", "job_internal", "test_case_xml", "workflow_step", "workflow_step_linked", ] DEFAULT_MODEL_NAME = "DynamicModelForTool" RawStateDict = Dict[str, Any] # could be made more specific - validators need to be classmethod ValidatorDictT = Dict[str, Callable] class DynamicModelInformation(NamedTuple): name: str definition: tuple validators: ValidatorDictT class StrictModel(BaseModel): model_config = ConfigDict(extra="forbid") class ConnectedValue(BaseModel): discriminator: Literal["ConnectedValue"] = Field(alias="__class__") def allow_connected_value(type: Type): return union_type([type, ConnectedValue]) def allow_batching(job_template: DynamicModelInformation, batch_type: Optional[Type] = None) -> DynamicModelInformation: job_py_type: Type = job_template.definition[0] default_value = job_template.definition[1] batch_type = batch_type or job_py_type class BatchRequest(StrictModel): meta_class: Literal["Batch"] = Field(..., alias="__class__") values: List[batch_type] # type: ignore[valid-type] linked: Optional[bool] = None # maybe True instead? request_type = union_type([job_py_type, BatchRequest]) return DynamicModelInformation( job_template.name, (request_type, default_value), {}, # should we modify these somehow? ) class Validators: def validate_not_none(cls, v): assert v is not None, "null is an invalid value for attribute" return v class ParamModel(Protocol): @property def name(self) -> str: ... @property def request_requires_value(self) -> bool: # if this is a non-optional type and no default is defined - an # input value MUST be specified. ... def safe_field_name(name: str) -> str: if name.startswith("_"): return f"X{name}" return name def dynamic_model_information_from_py_type( param_model: ParamModel, py_type: Type, requires_value: Optional[bool] = None, validators=None ): name = safe_field_name(param_model.name) if requires_value is None: requires_value = param_model.request_requires_value initialize = ... if requires_value else None py_type_is_optional = is_optional(py_type) validators = validators or {} if not py_type_is_optional and not requires_value: validators["not_null"] = field_validator(name)(Validators.validate_not_none) return DynamicModelInformation( name, (py_type, Field(initialize, alias=param_model.name if param_model.name != name else None)), validators, ) # We probably need incoming (parameter def) and outgoing (parameter value as transmitted) models, # where value in the incoming model means "default value" and value in the outgoing model is the actual # value a user has set. (incoming/outgoing from the client perspective). class BaseToolParameterModelDefinition(ToolSourceBaseModel): name: Annotated[ str, Field(description="Parameter name. Used when referencing parameter in workflows or inside command templating."), ] parameter_type: str @abstractmethod def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: """Return info needed to build Pydantic model at runtime for validation.""" class BaseGalaxyToolParameterModelDefinition(BaseToolParameterModelDefinition): hidden: bool = False label: Annotated[ Optional[str], Field(description="Will be displayed on the tool page as the label of the parameter.") ] = None help: Annotated[ Optional[str], Field( description="Short bit of text, rendered on the tool form just below the associated field to provide information about the field." ), ] = None argument: Annotated[ Optional[str], Field( description="""If the parameter reflects just one command line argument of a certain tool, this tag should be set to that particular argument. It is rendered in parenthesis after the help section, and it will create the name attribute (if not given explicitly) from the argument attribute by stripping leading dashes and replacing all remaining dashes by underscores (e.g. if argument="--long-parameter" then name="long_parameter" is implicit).""" ), ] = None is_dynamic: bool = False optional: Annotated[bool, Field(description="If `false`, parameter must have a value.")] = False class LabelValue(BaseModel): label: str value: str selected: bool TextCompatiableValidators = Union[ LengthParameterValidatorModel, RegexParameterValidatorModel, ExpressionParameterValidatorModel, EmptyFieldParameterValidatorModel, ] def pydantic_to_galaxy_type(value: Any) -> Any: """We use advanced Pydantic types like URL but the Galaxy validators only expect strings for these.""" if isinstance(value, AnyUrl): return str(value) return value VT = TypeVar("VT", bound=StaticValidatorModel) def decorate_type_with_validators_if_needed( py_type: Type, static_validator_models: Sequence[VT], optional: bool = False ) -> Type: pydantic_validator = pydantic_validator_for(static_validator_models, optional=optional) if pydantic_validator: return expand_annotation(py_type, [pydantic_validator]) else: return py_type # Looks like Annotated only work with one PlainValidator so condensing all static validators # into a single PlainValidator for pydantic. def pydantic_validator_for(static_validator_models: Sequence[VT], optional: bool = False) -> Optional[AfterValidator]: if static_validator_models: def validator(v: Any) -> Any: if optional and (v is None or v == ""): return v gx_val = pydantic_to_galaxy_type(v) for static_validator_model in static_validator_models: static_validator_model.statically_validate(gx_val) return v return AfterValidator(validator) else: return None class TextParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_text"] = "gx_text" type: Literal["text"] area: bool = False default_value: Optional[str] = Field(default=None, alias="value") default_options: List[LabelValue] = [] validators: List[TextCompatiableValidators] = [] @property def py_type(self) -> Type: return optional_if_needed(StrictStr, self.optional) @property def py_type_relaxed_request(self) -> Type: # such a hack but explicit nulls are always allowed in the API even for non-optional # parameters - it becomes "" in the internal state. return optional(StrictStr) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: py_type = self.py_type if state_representation == "relaxed_request": py_type = self.py_type_relaxed_request py_type = decorate_type_with_validators_if_needed(py_type, self.validators, optional=self.optional) if state_representation == "workflow_step_linked": py_type = allow_connected_value(py_type) requires_value = self.request_requires_value if state_representation in ("job_internal", "job_runtime"): requires_value = True return dynamic_model_information_from_py_type(self, py_type, requires_value=requires_value) @property def request_requires_value(self) -> bool: return False NumberCompatiableValidators = Union[InRangeParameterValidatorModel,] class IntegerParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_integer"] = "gx_integer" type: Literal["integer"] optional: bool = False value: Optional[int] = None min: Optional[int] = None max: Optional[int] = None validators: List[NumberCompatiableValidators] = [] @property def py_type(self) -> Type: return optional_if_needed(StrictInt, self.optional) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: py_type = self.py_type validators = self.validators[:] if self.min is not None or self.max is not None: validators.append(InRangeParameterValidatorModel(min=self.min, max=self.max, implicit=True)) py_type = decorate_type_with_validators_if_needed(py_type, validators) if state_representation == "workflow_step_linked": py_type = allow_connected_value(py_type) requires_value = self.request_requires_value if state_representation in ("job_internal", "job_runtime"): requires_value = True elif _is_landing_request(state_representation): requires_value = False return dynamic_model_information_from_py_type(self, py_type, requires_value=requires_value) @property def request_requires_value(self) -> bool: return not self.optional and self.value is None class FloatParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_float"] = "gx_float" type: Literal["float"] value: Optional[float] = None min: Optional[float] = None max: Optional[float] = None validators: List[NumberCompatiableValidators] = [] @property def py_type(self) -> Type: return optional_if_needed(union_type([StrictInt, StrictFloat]), self.optional) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: py_type = self.py_type if state_representation == "workflow_step_linked": py_type = allow_connected_value(py_type) requires_value = self.request_requires_value if state_representation in ("job_internal", "job_runtime"): requires_value = True elif _is_landing_request(state_representation): requires_value = False validators = self.validators[:] if self.min is not None or self.max is not None: validators.append(InRangeParameterValidatorModel(min=self.min, max=self.max, implicit=True)) py_type = decorate_type_with_validators_if_needed(py_type, validators) return dynamic_model_information_from_py_type(self, py_type, requires_value=requires_value) @property def request_requires_value(self) -> bool: return False DataSrcT = Literal["hda", "ldda"] MultiDataSrcT = Literal["hda", "ldda", "hdca"] # @jmchilton you meant CollectionSrcT - fix that at some point please. CollectionStrT = Literal["hdca"] TestCaseDataSrcT = Literal["File"] class LegacyRequestModelAttributes(StrictModel): # Here for bioblend's sake, should be stripped map_over_type: SkipJsonSchema[Optional[str]] = Field(None, exclude=True) hid: SkipJsonSchema[Optional[int]] = Field(None, exclude=True) workflow_step_id: SkipJsonSchema[Optional[str]] = Field(None, exclude=True) label: SkipJsonSchema[Optional[str]] = Field(None, exclude=True) class DataRequestHda(LegacyRequestModelAttributes): src: Literal["hda"] = "hda" id: StrictStr class DataRequestLdda(LegacyRequestModelAttributes): src: Literal["ldda"] = "ldda" id: StrictStr class DataRequestLd(LegacyRequestModelAttributes): src: Literal["ld"] = Field(deprecated=True) id: StrictStr class DataRequestHdca(LegacyRequestModelAttributes): src: Literal["hdca"] = "hdca" id: StrictStr class FileHash(StrictModel): hash_function: Literal["MD5", "SHA-1", "SHA-256", "SHA-512"] hash_value: StrictStr class BaseDataRequest(StrictModel): url: StrictStr = Field(..., alias="location") name: Optional[StrictStr] = None ext: StrictStr dbkey: StrictStr = "?" deferred: StrictBool = False created_from_basename: Optional[StrictStr] = None info: Optional[StrictStr] = None tags: Optional[List[str]] = None hashes: Optional[List[FileHash]] = None space_to_tab: bool = False to_posix_lines: bool = False # to implement: # tags model_config = ConfigDict(extra="forbid", populate_by_name=True) @model_validator(mode="before") @classmethod def allow_filetype_and_extension(cls, data: Any): if isinstance(data, dict): extension = data.get("filetype") if extension: data = data.copy() data.pop("filetype") data["ext"] = extension extension = data.get("extension") if extension: data = data.copy() data.pop("extension") data["ext"] = extension return data class DataRequestUri(BaseDataRequest): # calling it url instead of uri to match data fetch schema... src: Literal["url"] = "url" class FileRequestUri(BaseDataRequest): class_: Literal["File"] = Field(..., alias="class") src: None = Field(None, exclude=True) class CollectionElementDataRequestUri(FileRequestUri): class_: Literal["File"] = Field(..., alias="class") identifier: StrictStr = Field( ..., description="A unique identifier for this element within the collection.", validation_alias=AliasChoices("identifier", "name"), ) class CollectionElementCollectionRequestUri(StrictModel): class_: Literal["Collection"] = Field(..., alias="class") identifier: StrictStr = Field( ..., description="A unique identifier for this element within the collection.", validation_alias=AliasChoices("identifier", "name"), ) collection_type: StrictStr elements: List[ Annotated[ Union["CollectionElementCollectionRequestUri", CollectionElementDataRequestUri], Field(discriminator="class_"), ] ] @model_validator(mode="before") @classmethod def allow_collection_type_by_type(cls, data: Any): if isinstance(data, dict): collection_type = data.get("type") if collection_type: data = data.copy() data.pop("type") data["collection_type"] = collection_type return data class DataRequestCollectionUri(StrictModel): class_: Literal["Collection"] = Field(..., alias="class") collection_type: str elements: List[ Annotated[ Union[CollectionElementCollectionRequestUri, CollectionElementDataRequestUri], Field(discriminator="class_") ] ] deferred: StrictBool = False name: Optional[StrictStr] = None src: None = Field(None, exclude=True) # Sample sheet metadata column_definitions: Optional[SampleSheetColumnDefinitions] = None rows: Optional[Dict[str, SampleSheetRow]] = None _DataRequest = Annotated[ Union[DataRequestHda, DataRequestLdda, DataRequestLd, DataRequestUri], Field(discriminator="src") ] DataRequest: Type = cast(Type, _DataRequest) DataOrCollectionRequest = Union[_DataRequest, FileRequestUri, DataRequestCollectionUri, DataRequestHdca] FileOrCollectionRequest = Annotated[Union[FileRequestUri, DataRequestCollectionUri], Field(discriminator="class_")] DataRequestHda.model_rebuild() DataRequestLd.model_rebuild() DataRequestLdda.model_rebuild() DataRequestUri.model_rebuild() DataRequestHdca.model_rebuild() DataRequestCollectionUri.model_rebuild() DataOrCollectionRequestAdapter: TypeAdapter[DataOrCollectionRequest] = TypeAdapter(DataOrCollectionRequest) class BatchDataInstance(StrictModel): src: MultiDataSrcT id: StrictStr def multi_data_discriminator(v: Any) -> str: if isinstance(v, dict): src = v.get("src", None) clazz = v.get("class", None) if clazz == "Collection": return "data_request_collection_uri" elif src == "hda": return "data_request_hda" elif src == "ldda": return "data_request_ldda" elif src == "hdca": return "data_request_hdca" elif src == "url": return "data_request_uri" return "" def tag(field: Type, tag: str) -> Type: return Annotated[field, Tag(tag)] # type: ignore[return-value] MultiDataInstanceDiscriminator = Discriminator(multi_data_discriminator) MultiDataInstance: Type = cast( Type, Annotated[ union_type( [ tag(DataRequestHda, "data_request_hda"), tag(DataRequestLdda, "data_request_ldda"), tag(DataRequestHdca, "data_request_hdca"), tag(DataRequestUri, "data_request_uri"), tag(DataRequestCollectionUri, "data_request_collection_uri"), ] ), Field(discriminator=MultiDataInstanceDiscriminator), ], ) MultiDataRequest: Type = union_type([MultiDataInstance, list_type(MultiDataInstance)]) class DataRequestInternalHda(StrictModel): src: Literal["hda"] id: StrictInt class DataRequestInternalLdda(StrictModel): src: Literal["ldda"] id: StrictInt class DataRequestInternalHdca(StrictModel): src: Literal["hdca"] id: StrictInt class DataInternalJson(StrictModel): class_: Annotated[Literal["File"], Field(alias="class")] basename: Annotated[ str, Field( description="The base name of the file, that is, the name of the file without any leading directory path" ), ] location: str path: Annotated[str, Field(description="The absolute path to the file on disk.")] listing: Optional[List[str]] # Should be recursive nameroot: Annotated[Optional[str], Field(description="The basename root such that nameroot + nameext == basename")] nameext: Annotated[ Optional[str], Field(description="The basename extension such that nameroot + nameext == basename") ] format: Annotated[str, Field(description="The datatype extension of the file, e.g. 'txt', 'bam', 'fastq.gz'.")] # "secondaryFiles": List[Any], checksum: Optional[str] size: int class DataCollectionInternalJson(RootModel): root: Dict[str, DataInternalJson] class RecursiveDataCollectionInternalJson(RootModel): root: Dict[str, Union[DataInternalJson, "RecursiveDataCollectionInternalJson"]] RecursiveDataCollectionInternalJson.model_rebuild() class DataCollectionPaired(StrictModel): forward: DataInternalJson reverse: DataInternalJson DataRequestInternal: Type = cast( Type, Annotated[ union_type( [ tag(DataRequestInternalHda, "data_request_hda"), tag(DataRequestInternalLdda, "data_request_ldda"), tag(DataRequestInternalHdca, "data_request_hdca"), tag(DataRequestUri, "data_request_uri"), tag(DataRequestCollectionUri, "data_request_collection_uri"), ] ), Field(discriminator=MultiDataInstanceDiscriminator), ], ) DataRequestInternalDereferenced: Type = cast( Type, Annotated[Union[DataRequestInternalHda, DataRequestInternalLdda], Field(discriminator="src")], ) DataJobInternal = DataRequestInternalDereferenced class BatchDataInstanceInternal(StrictModel): src: MultiDataSrcT id: StrictInt MultiDataInstanceInternal: Type = cast( Type, Annotated[ Union[DataRequestInternalHda, DataRequestInternalLdda, DataRequestInternalHdca, DataRequestUri], Field(discriminator="src"), ], ) MultiDataInstanceInternalDereferenced: Type = cast( Type, Annotated[ Union[DataRequestInternalHda, DataRequestInternalLdda, DataRequestInternalHdca], Field(discriminator="src") ], ) MultiDataRequestInternal: Type = union_type([MultiDataInstanceInternal, list_type(MultiDataInstanceInternal)]) MultiDataRequestInternalDereferenced: Type = union_type( [MultiDataInstanceInternalDereferenced, list_type(MultiDataInstanceInternalDereferenced)] ) class DataParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_data"] = "gx_data" type: Literal["data"] extensions: Annotated[ List[str], Field( description="Limit inputs to datasets with these extensions. Use 'data' to allow all input datasets.", examples=["txt", "tabular", "tiff"], ), ] = ["data"] multiple: Annotated[bool, Field(description="Allow multiple values to be selected.")] = False min: Optional[int] = None max: Optional[int] = None @property def py_type(self) -> Type: base_model: Type if self.multiple: base_model = MultiDataRequest else: base_model = DataRequest return optional_if_needed(base_model, self.optional) @property def py_type_internal_json(self) -> Type: base_model: Type if self.multiple: base_model = list_type(DataInternalJson) else: base_model = DataInternalJson return optional_if_needed(base_model, self.optional) @property def py_type_internal(self) -> Type: base_model: Type if self.multiple: base_model = MultiDataRequestInternal else: base_model = DataRequestInternal return optional_if_needed(base_model, self.optional) @property def py_type_internal_dereferenced(self) -> Type: base_model: Type if self.multiple: base_model = MultiDataRequestInternalDereferenced else: base_model = DataRequestInternalDereferenced return optional_if_needed(base_model, self.optional) @property def py_type_test_case(self) -> Type: base_model: Type if self.multiple: base_model = list_type(JsonTestDatasetDefDict) else: base_model = JsonTestDatasetDefDict return optional_if_needed(base_model, self.optional) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: if state_representation in ["request", "relaxed_request"]: return allow_batching(dynamic_model_information_from_py_type(self, self.py_type), BatchDataInstance) elif state_representation == "landing_request": return allow_batching( dynamic_model_information_from_py_type(self, self.py_type, requires_value=False), BatchDataInstance ) elif state_representation == "request_internal": return allow_batching( dynamic_model_information_from_py_type(self, self.py_type_internal), BatchDataInstanceInternal ) elif state_representation == "landing_request_internal": return allow_batching( dynamic_model_information_from_py_type(self, self.py_type_internal, requires_value=False), BatchDataInstanceInternal, ) elif state_representation == "request_internal_dereferenced": return allow_batching( dynamic_model_information_from_py_type(self, self.py_type_internal_dereferenced), BatchDataInstanceInternal, ) elif state_representation == "job_internal": return dynamic_model_information_from_py_type(self, self.py_type_internal_dereferenced, requires_value=True) elif state_representation == "job_runtime": return dynamic_model_information_from_py_type(self, self.py_type_internal_json, requires_value=True) elif state_representation == "test_case_xml": return dynamic_model_information_from_py_type(self, self.py_type_test_case) elif state_representation == "workflow_step": return dynamic_model_information_from_py_type(self, type(None), requires_value=False) elif state_representation == "workflow_step_linked": return dynamic_model_information_from_py_type(self, ConnectedValue) else: raise NotImplementedError( f"Have not implemented data collection parameter models for state representation {state_representation}" ) @property def request_requires_value(self) -> bool: return not self.optional class DataCollectionRequest(StrictModel): src: CollectionStrT id: StrictStr DataCollectionRequestOrCollectionUri: Type = union_type([DataCollectionRequest, DataRequestCollectionUri]) class DataCollectionRequestInternal(StrictModel): src: CollectionStrT id: StrictInt DataCollectionRequestInternalOrCollectionUri: Type = union_type( [DataCollectionRequestInternal, DataRequestCollectionUri] ) CollectionAdapterSrcT = Literal["CollectionAdapter"] class AdaptedDataCollectionRequestBase(StrictModel): src: CollectionAdapterSrcT class AdaptedDataCollectionPromoteDatasetToCollectionRequest(AdaptedDataCollectionRequestBase): adapter_type: Literal["PromoteDatasetToCollection"] collection_type: Literal["list", "paired_or_unpaired"] adapting: DataRequestHda # calling this name and element_identifier to align with fetch API, etc... class AdapterElementRequest(DataRequestHda): name: str # element_identifier class AdaptedDataCollectionPromoteDatasetsToCollectionRequest(AdaptedDataCollectionRequestBase): adapter_type: Literal["PromoteDatasetsToCollection"] # could allow list in here without changing much else I think but I'm trying to keep these tight in scope collection_type: Literal["paired", "paired_or_unpaired"] adapting: List[AdapterElementRequest] AdaptedDataCollectionRequest = Annotated[ Union[ AdaptedDataCollectionPromoteDatasetToCollectionRequest, AdaptedDataCollectionPromoteDatasetsToCollectionRequest ], Field(discriminator="adapter_type"), ] AdaptedDataCollectionRequestTypeAdapter = TypeAdapter(AdaptedDataCollectionRequest) # type: ignore[var-annotated] class DatasetCollectionElementReference(StrictModel): src: Literal["dce"] id: StrictInt class AdaptedDataCollectionPromoteCollectionElementToCollectionRequestInternal(AdaptedDataCollectionRequestBase): adapter_type: Literal["PromoteCollectionElementToCollection"] adapting: DatasetCollectionElementReference class AdaptedDataCollectionPromoteDatasetToCollectionRequestInternal(AdaptedDataCollectionRequestBase): adapter_type: Literal["PromoteDatasetToCollection"] collection_type: Literal["list", "paired_or_unpaired"] adapting: DataRequestInternalHda class AdapterElementRequestInternal(DataRequestInternalHda): name: str # element_identifier class AdaptedDataCollectionPromoteDatasetsToCollectionRequestInternal(AdaptedDataCollectionRequestBase): adapter_type: Literal["PromoteDatasetsToCollection"] # could allow list in here without changing much else I think but I'm trying to keep these tight in scope collection_type: Literal["paired", "paired_or_unpaired"] adapting: List[AdapterElementRequestInternal] AdaptedDataCollectionRequestInternal = Annotated[ Union[ AdaptedDataCollectionPromoteCollectionElementToCollectionRequestInternal, AdaptedDataCollectionPromoteDatasetToCollectionRequestInternal, AdaptedDataCollectionPromoteDatasetsToCollectionRequestInternal, ], Field(discriminator="adapter_type"), ] AdaptedDataCollectionRequestInternalTypeAdapter = TypeAdapter( AdaptedDataCollectionRequestInternal ) # type: ignore[var-annotated] class DataCollectionParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_data_collection"] = "gx_data_collection" type: Literal["data_collection"] collection_type: Optional[str] = None extensions: List[str] = ["data"] value: Optional[Dict[str, Any]] @property def py_type(self) -> Type: return optional_if_needed(DataCollectionRequestOrCollectionUri, self.optional) @property def py_type_internal(self) -> Type: return optional_if_needed(DataCollectionRequestInternalOrCollectionUri, self.optional) @property def py_type_internal_dereferenced(self) -> Type: return optional_if_needed(DataCollectionRequestInternal, self.optional) @property def py_type_internal_json(self) -> Type: if self.collection_type == "list": return optional_if_needed(list_type(DataInternalJson), self.optional) elif self.collection_type: base_type: Optional[Type] = None for subtype in reversed(self.collection_type.split(":")): if subtype == "paired": base_type = DataCollectionPaired elif subtype == "list": if base_type is None: base_type = Dict[str, DataInternalJson] else: base_type = Dict[str, base_type] # type: ignore[valid-type] # we use this at runtime to build pydantic model else: raise Exception(f"unknown subtype '{subtype}' in collection_type '{self.collection_type}'") else: base_type = union_type( [list_type(DataInternalJson), DataCollectionPaired, RecursiveDataCollectionInternalJson] ) assert base_type return optional_if_needed(base_type, self.optional) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: if state_representation in ["request", "relaxed_request"]: return allow_batching(dynamic_model_information_from_py_type(self, self.py_type)) elif state_representation == "landing_request": return allow_batching(dynamic_model_information_from_py_type(self, self.py_type, requires_value=False)) elif state_representation == "landing_request_internal": return allow_batching( dynamic_model_information_from_py_type(self, self.py_type_internal, requires_value=False) ) elif state_representation == "request_internal": return allow_batching(dynamic_model_information_from_py_type(self, self.py_type_internal)) elif state_representation == "request_internal_dereferenced": return allow_batching(dynamic_model_information_from_py_type(self, self.py_type_internal_dereferenced)) elif state_representation == "job_internal": return dynamic_model_information_from_py_type(self, self.py_type_internal, requires_value=True) elif state_representation == "job_runtime": return dynamic_model_information_from_py_type(self, self.py_type_internal_json, requires_value=True) elif state_representation == "workflow_step": return dynamic_model_information_from_py_type(self, type(None), requires_value=False) elif state_representation == "workflow_step_linked": return dynamic_model_information_from_py_type(self, ConnectedValue) elif state_representation == "test_case_xml": return dynamic_model_information_from_py_type(self, JsonTestCollectionDefDict) else: raise NotImplementedError( f"Have not implemented data collection parameter models for state representation {state_representation}" ) @property def request_requires_value(self) -> bool: return not self.optional and self.value is None class HiddenParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_hidden"] = "gx_hidden" type: Literal["hidden"] value: Optional[str] validators: List[TextCompatiableValidators] = [] @property def py_type(self) -> Type: return optional_if_needed(StrictStr, self.optional) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: py_type = self.py_type requires_value = not self.optional and self.value is None py_type = decorate_type_with_validators_if_needed(py_type, self.validators) if state_representation == "workflow_step_linked": py_type = allow_connected_value(py_type) elif state_representation == "workflow_step" and not self.optional: # allow it to be linked in so force allow optional... py_type = optional(py_type) requires_value = False if state_representation in ("job_internal", "job_runtime"): requires_value = True return dynamic_model_information_from_py_type(self, py_type, requires_value=requires_value) @property def request_requires_value(self) -> bool: return not self.optional and self.value is None def ensure_color_valid(value: Optional[Any]): if value is None: return if not isinstance(value, str): raise ValueError(f"Invalid color value type {value.__class__} encountered.") value_str: str = value message = f"Invalid color value string format {value_str} encountered." if len(value_str) != 7: raise ValueError(message + "0") if value_str[0] != "#": raise ValueError(message + "1") for byte_str in value_str[1:]: if byte_str not in "0123456789abcdef": raise ValueError(message + "2") class ColorParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_color"] = "gx_color" type: Literal["color"] value: Optional[str] = None @property def py_type(self) -> Type: return optional_if_needed(StrictStr, self.optional) @staticmethod def validate_color_str(value) -> str: ensure_color_valid(value) return value @staticmethod def validate_color_str_if_value(value) -> str: if value: ensure_color_valid(value) return value @staticmethod def validate_color_str_or_connected_value(value) -> str: if not isinstance(value, ConnectedValue): ensure_color_valid(value) return value def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: py_type = self.py_type requires_value = self.request_requires_value initialize = ... if requires_value else None if state_representation == "workflow_step_linked": py_type = allow_connected_value(py_type) validators = { "color_format": field_validator(self.name)(ColorParameterModel.validate_color_str_or_connected_value) } elif state_representation == "workflow_step": validators = {"color_format": field_validator(self.name)(ColorParameterModel.validate_color_str_if_value)} else: validators = {"color_format": field_validator(self.name)(ColorParameterModel.validate_color_str)} return DynamicModelInformation( self.name, (py_type, initialize), validators, ) @property def request_requires_value(self) -> bool: return False class BooleanParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_boolean"] = "gx_boolean" type: Literal["boolean"] value: Optional[bool] = False truevalue: Optional[str] = None falsevalue: Optional[str] = None @property def py_type(self) -> Type: return optional_if_needed(StrictBool, self.optional) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: py_type = self.py_type if state_representation == "workflow_step_linked": py_type = allow_connected_value(py_type) requires_value = self.request_requires_value if state_representation in ("job_internal", "job_runtime"): requires_value = True return dynamic_model_information_from_py_type(self, py_type, requires_value=requires_value) @property def request_requires_value(self) -> bool: # these parameters always have an implicit default - either None if # if it is optional or 'checked' if not (itself defaulting to False). return False class DirectoryUriParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_directory_uri"] = "gx_directory_uri" type: Literal["directory"] validators: List[TextCompatiableValidators] = [] @property def py_type(self) -> Type: return AnyUrl def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: py_type = self.py_type py_type = decorate_type_with_validators_if_needed(py_type, self.validators) if state_representation == "workflow_step_linked": py_type = allow_connected_value(py_type) requires_value = self.request_requires_value if _is_landing_request(state_representation): requires_value = False return dynamic_model_information_from_py_type(self, py_type, requires_value=requires_value) @property def request_requires_value(self) -> bool: return True class RulesMapping(StrictModel): type: str columns: List[StrictInt] class RulesModel(StrictModel): rules: List[Dict[str, Any]] mappings: List[RulesMapping] class RulesParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_rules"] = "gx_rules" type: Literal["rules"] @property def py_type(self) -> Type: return RulesModel def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: return dynamic_model_information_from_py_type(self, self.py_type) @property def request_requires_value(self) -> bool: return True SelectCompatiableValidators = Union[NoOptionsParameterValidatorModel,] class SelectParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_select"] = "gx_select" type: Literal["select"] options: Optional[List[LabelValue]] = None multiple: bool = False validators: List[SelectCompatiableValidators] = [] @staticmethod def split_str(cls, data: Any) -> Any: if isinstance(data, str): return [x.strip() for x in data.split(",")] return data def py_type_if_required(self, allow_connections: bool = False) -> Type: if self.options is not None: if len(self.options) > 0: literal_options: List[Type] = [cast_as_type(Literal[o.value]) for o in self.options] py_type = union_type(literal_options) else: py_type = type(None) else: py_type = StrictStr if self.multiple: if allow_connections: py_type = list_type(allow_connected_value(py_type)) else: py_type = list_type(py_type) elif allow_connections: py_type = allow_connected_value(py_type) return py_type @property def py_type(self) -> Type: return optional_if_needed(self.py_type_if_required(), self.optional or self.multiple) @property def py_type_workflow_step(self) -> Type: # this is always optional in this context return optional(self.py_type_if_required()) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: validators = {} requires_value = self.request_requires_value py_type = None if state_representation == "workflow_step": py_type = self.py_type_workflow_step elif state_representation == "workflow_step_linked": py_type = self.py_type_if_required(allow_connections=True) py_type = optional_if_needed(py_type, self.optional or self.multiple) elif state_representation == "test_case_xml": # in a YAML test case representation this can be string, in XML we are still expecting a comma separated string py_type = self.py_type_if_required(allow_connections=False) if self.multiple: validators = {"from_string": field_validator(self.name, mode="before")(SelectParameterModel.split_str)} py_type = optional_if_needed(py_type, self.optional) elif state_representation in ("job_internal", "job_runtime"): requires_value = True py_type = self.py_type else: py_type = self.py_type py_type = decorate_type_with_validators_if_needed(py_type, self.validators) return dynamic_model_information_from_py_type( self, py_type, validators=validators, requires_value=requires_value ) @property def has_selected_static_option(self): return self.options is not None and any(o.selected for o in self.options) @property def default_value(self) -> Optional[str]: assert not self.multiple if self.options: for option in self.options: if option.selected: return option.value # single value pick up first value if not self.optional: return self.options[0].value return None @property def default_values(self) -> Optional[List[str]]: assert self.multiple if self.options: return [option.value for option in self.options if option.selected] return None @property def request_requires_value(self) -> bool: # API will allow an empty value and just grab the first static option # see API Tests -> test_tools.py -> test_select_first_by_default # If it is multiple - it will also always just allow null regardless of # optional - see test_select_multiple_null_handling return False @property def dynamic_options(self) -> bool: return self.options is None class GenomeBuildParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_genomebuild"] = "gx_genomebuild" type: Literal["genomebuild"] multiple: bool @property def py_type(self) -> Type: py_type: Type = StrictStr if self.multiple: py_type = list_type(py_type) return optional_if_needed(py_type, self.optional or self.multiple) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: requires_value = self.request_requires_value if state_representation in ("job_internal", "job_runtime"): requires_value = True return dynamic_model_information_from_py_type(self, self.py_type, requires_value=requires_value) @property def request_requires_value(self) -> bool: # it seems to always just pick values currently - an empty multiple or optional comes through as null # and empty single non-optional input comes through as "?"". See gx_genomebuild*.xml tools. return False DrillDownHierarchyT = Literal["recurse", "exact"] def drill_down_possible_values( options: List[DrillDownOptionsDict], multiple: bool, hierarchy: DrillDownHierarchyT ) -> List[str]: possible_values = [] def add_value(option: str, is_leaf: bool): if not multiple and not is_leaf and hierarchy == "recurse": return possible_values.append(option) def walk_selection(option: DrillDownOptionsDict): child_options = option["options"] is_leaf = not child_options add_value(option["value"], is_leaf) if not is_leaf: for child_option in child_options: walk_selection(child_option) for option in options: walk_selection(option) return possible_values class DrillDownParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_drill_down"] = "gx_drill_down" type: Literal["drill_down"] options: Optional[List[DrillDownOptionsDict]] = None multiple: bool hierarchy: DrillDownHierarchyT @property def py_type(self) -> Type: if self.options is not None: literal_options: List[Type] = [ cast_as_type(Literal[o]) for o in drill_down_possible_values(self.options, self.multiple, self.hierarchy) ] py_type = union_type(literal_options) else: py_type = StrictStr if self.multiple: py_type = list_type(py_type) return py_type @property def py_type_test_case_xml(self) -> Type: base_model = str return optional_if_needed(base_model, not self.request_requires_value) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: py_type = self.py_type_test_case_xml if state_representation == "test_case_xml" else self.py_type requires_value = self.request_requires_value if state_representation in ("job_internal", "job_runtime"): requires_value = True return dynamic_model_information_from_py_type(self, py_type, requires_value=requires_value) @property def request_requires_value(self) -> bool: options = self.options if options: # if any of these are selected, they seem to serve as defaults - check out test_tools -> test_drill_down_first_by_default return not any_drill_down_options_selected(options) else: # I'm not sure how to handle dynamic options... they might or might not be required? # do we need to default to assuming they're not required? return False @property def default_option(self) -> Optional[str]: options = self.options if options: selected_options = selected_drill_down_options(options) if len(selected_options) > 0: return selected_options[0] return None @property def default_options(self) -> Optional[List[str]]: options = self.options if options: selected_options = selected_drill_down_options(options) return selected_options return None def any_drill_down_options_selected(options: List[DrillDownOptionsDict]) -> bool: for option in options: selected = option.get("selected") if selected: return True child_options = option.get("options", []) if any_drill_down_options_selected(child_options): return True return False def selected_drill_down_options(options: List[DrillDownOptionsDict]) -> List[str]: selected_options: List[str] = [] for option in options: selected = option.get("selected") value = option.get("value") if selected and value: selected_options.append(value) child_options = option.get("options", []) selected_options.extend(selected_drill_down_options(child_options)) return selected_options class DataColumnParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_data_column"] = "gx_data_column" type: Literal["data_column"] multiple: bool value: Optional[Union[int, List[int]]] = None @staticmethod def split_str(cls, data: Any) -> Any: if isinstance(data, str): return [int(x.strip()) for x in data.split(",")] elif isinstance(data, int): return [data] return data @property def py_type(self) -> Type: py_type: Type = StrictInt if self.multiple: py_type = list_type(py_type) return optional_if_needed(py_type, self.optional) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: if state_representation == "test_case_xml": if self.multiple: validators = { "from_string": field_validator(self.name, mode="before")(DataColumnParameterModel.split_str) } else: validators = {} requires_value = self.request_requires_value return dynamic_model_information_from_py_type( self, self.py_type, validators=validators, requires_value=requires_value ) else: requires_value = self.request_requires_value if state_representation in ("job_internal", "job_runtime"): requires_value = True return dynamic_model_information_from_py_type(self, self.py_type, requires_value=requires_value) @property def request_requires_value(self) -> bool: return self.multiple and not (self.optional or self.value) class GroupTagParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_group_tag"] = "gx_group_tag" type: Literal["group_tag"] multiple: bool @property def py_type(self) -> Type: py_type: Type = StrictStr if self.multiple: py_type = list_type(py_type) return optional_if_needed(py_type, self.optional) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: requires_value = self.request_requires_value if state_representation in ("job_internal", "job_runtime"): requires_value = True return dynamic_model_information_from_py_type(self, self.py_type, requires_value=requires_value) @property def request_requires_value(self) -> bool: return not self.optional class BaseUrlParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_baseurl"] = "gx_baseurl" type: Literal["baseurl"] @property def py_type(self) -> Type: return HttpUrl def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: return dynamic_model_information_from_py_type(self, self.py_type) @property def request_requires_value(self) -> bool: return True DiscriminatorType = Union[bool, str] def cond_test_parameter_default_value( test_parameter: Union[BooleanParameterModel, "SelectParameterModel"], ) -> Optional[DiscriminatorType]: default_value: Optional[DiscriminatorType] = None if isinstance(test_parameter, BooleanParameterModel): default_value = test_parameter.value elif isinstance(test_parameter, SelectParameterModel): select_default_value = test_parameter.default_value if select_default_value is not None: default_value = select_default_value return default_value class ConditionalWhen(StrictModel): discriminator: DiscriminatorType parameters: List["ToolParameterT"] is_default_when: bool class ConditionalParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_conditional"] = "gx_conditional" type: Literal["conditional"] test_parameter: Union[BooleanParameterModel, SelectParameterModel] whens: List[ConditionalWhen] def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: is_boolean = isinstance(self.test_parameter, BooleanParameterModel) test_param_name = self.test_parameter.name test_info = self.test_parameter.pydantic_template(state_representation) extra_validators = test_info.validators if state_representation in ("job_internal", "job_runtime"): test_parameter_requires_value = True else: test_parameter_requires_value = self.test_parameter.request_requires_value when_types: List[Type[BaseModel]] = [] default_type = None for when in self.whens: discriminator = when.discriminator parameters = when.parameters if test_parameter_requires_value: initialize_test = ... else: initialize_test = None tag = str(discriminator) if not is_boolean else str(discriminator).lower() extra_kwd = {test_param_name: (Literal[when.discriminator], initialize_test)} when_types.append( cast( Type[BaseModel], Annotated[ create_field_model( parameters, f"When_{test_param_name}_{discriminator}", state_representation, extra_kwd=extra_kwd, extra_validators=extra_validators, ), Tag(tag), ], ) ) # job_internal requires parameters are filled in - so don't allow the absent branch # here that most other state representations allow if state_representation not in ("job_internal", "job_runtime"): if when.is_default_when: extra_kwd = {} default_type = create_field_model( parameters, f"When_{test_param_name}___absent", state_representation, extra_kwd=extra_kwd, extra_validators={}, ) when_types.append(cast(Type[BaseModel], Annotated[default_type, Tag("__absent__")])) def model_x_discriminator(v: Any) -> Optional[str]: # returning None causes a validation error, this is what we would want if # if the conditional state is not a dictionary. if not isinstance(v, dict): return None if test_param_name not in v: return "__absent__" else: test_param_val = v[test_param_name] if test_param_val is True: return "true" elif test_param_val is False: return "false" else: return str(test_param_val) py_type: Type if len(when_types) > 1: cond_type = union_type(when_types) class ConditionalType(RootModel): root: cond_type = Field(..., discriminator=Discriminator(model_x_discriminator)) # type: ignore[valid-type] if default_type is not None: initialize_cond = None else: initialize_cond = ... py_type = ConditionalType else: py_type = when_types[0] # a better check here would be if any of the parameters below this have a required value, # in the case of job_internal though this is correct if state_representation in ("job_internal", "job_runtime"): initialize_cond = ... else: initialize_cond = None return DynamicModelInformation( self.name, (py_type, initialize_cond), {}, ) @property def request_requires_value(self) -> bool: return False # TODO class RepeatParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_repeat"] = "gx_repeat" type: Literal["repeat"] parameters: List["ToolParameterT"] min: Optional[int] = None max: Optional[int] = None def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: # Maybe validators for min and max... instance_class: Type[BaseModel] = create_field_model( self.parameters, f"Repeat_{self.name}", state_representation ) min_length = self.min max_length = self.max requires_value = self.request_requires_value if state_representation in ("job_internal", "job_runtime"): requires_value = True elif _is_landing_request(state_representation): requires_value = False min_length = 0 # in a landing request - parameters can be partially filled initialize_repeat: Any if requires_value: initialize_repeat = ... else: initialize_repeat = None class RepeatType(RootModel): root: List[instance_class] = Field(initialize_repeat, min_length=min_length, max_length=max_length) # type: ignore[valid-type] return DynamicModelInformation( self.name, (RepeatType, initialize_repeat), {}, ) @property def request_requires_value(self) -> bool: if self.min is None or self.min == 0: return False # so we know we need at least one value, but maybe none of the parameters in the list # are required for parameter in self.parameters: if parameter.request_requires_value: return True return False class SectionParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["gx_section"] = "gx_section" type: Literal["section"] parameters: List["ToolParameterT"] def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: instance_class: Type[BaseModel] = create_field_model( self.parameters, f"Section_{self.name}", state_representation ) requires_value = self.request_requires_value if state_representation in ("job_internal", "job_runtime"): requires_value = True if requires_value: initialize_section = ... else: initialize_section = None return DynamicModelInformation( self.name, (instance_class, initialize_section), {}, ) @property def request_requires_value(self) -> bool: any_request_parameters_required = False for parameter in self.parameters: if parameter.request_requires_value: any_request_parameters_required = True break return any_request_parameters_required LiteralNone: Type = Literal[None] # type: ignore[assignment] class CwlNullParameterModel(BaseToolParameterModelDefinition): parameter_type: Literal["cwl_null"] = "cwl_null" @property def py_type(self) -> Type: return LiteralNone def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: return DynamicModelInformation( self.name, (self.py_type, ...), {}, ) @property def request_requires_value(self) -> bool: return False class CwlStringParameterModel(BaseToolParameterModelDefinition): parameter_type: Literal["cwl_string"] = "cwl_string" @property def py_type(self) -> Type: return StrictStr def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: return DynamicModelInformation( self.name, (self.py_type, ...), {}, ) @property def request_requires_value(self) -> bool: return True class CwlIntegerParameterModel(BaseToolParameterModelDefinition): parameter_type: Literal["cwl_integer"] = "cwl_integer" @property def py_type(self) -> Type: return StrictInt def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: return DynamicModelInformation( self.name, (self.py_type, ...), {}, ) @property def request_requires_value(self) -> bool: return True class CwlFloatParameterModel(BaseToolParameterModelDefinition): parameter_type: Literal["cwl_float"] = "cwl_float" @property def py_type(self) -> Type: return union_type([StrictFloat, StrictInt]) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: return DynamicModelInformation( self.name, (self.py_type, ...), {}, ) @property def request_requires_value(self) -> bool: return True class CwlBooleanParameterModel(BaseToolParameterModelDefinition): parameter_type: Literal["cwl_boolean"] = "cwl_boolean" @property def py_type(self) -> Type: return StrictBool def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: return DynamicModelInformation( self.name, (self.py_type, ...), {}, ) @property def request_requires_value(self) -> bool: return True class CwlUnionParameterModel(BaseToolParameterModelDefinition): parameter_type: Literal["cwl_union"] = "cwl_union" parameters: List["CwlParameterT"] @property def py_type(self) -> Type: union_of_cwl_types: List[Type] = [] for parameter in self.parameters: union_of_cwl_types.append(parameter.py_type) return union_type(union_of_cwl_types) def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: return DynamicModelInformation( self.name, (self.py_type, ...), {}, ) @property def request_requires_value(self) -> bool: return False # TODO: class CwlFileParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["cwl_file"] = "cwl_file" @property def py_type(self) -> Type: return DataRequest def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: return dynamic_model_information_from_py_type(self, self.py_type) @property def request_requires_value(self) -> bool: return True class CwlDirectoryParameterModel(BaseGalaxyToolParameterModelDefinition): parameter_type: Literal["cwl_directory"] = "cwl_directory" @property def py_type(self) -> Type: return DataRequest def pydantic_template(self, state_representation: StateRepresentationT) -> DynamicModelInformation: return dynamic_model_information_from_py_type(self, self.py_type) @property def request_requires_value(self) -> bool: return True CwlParameterT = Union[ CwlIntegerParameterModel, CwlFloatParameterModel, CwlStringParameterModel, CwlBooleanParameterModel, CwlNullParameterModel, CwlFileParameterModel, CwlDirectoryParameterModel, CwlUnionParameterModel, ] GalaxyParameterT = Union[ TextParameterModel, IntegerParameterModel, FloatParameterModel, BooleanParameterModel, HiddenParameterModel, SelectParameterModel, DataParameterModel, DataCollectionParameterModel, DataColumnParameterModel, DirectoryUriParameterModel, RulesParameterModel, DrillDownParameterModel, GroupTagParameterModel, BaseUrlParameterModel, GenomeBuildParameterModel, ColorParameterModel, ConditionalParameterModel, RepeatParameterModel, SectionParameterModel, ] ToolParameterT = Union[ CwlParameterT, GalaxyParameterT, ] class ToolParameterModel(RootModel): root: ToolParameterT = Field(..., discriminator="parameter_type") class GalaxyToolParameterModel(RootModel): root: GalaxyParameterT = Field(..., discriminator="type") ConditionalWhen.model_rebuild() ConditionalParameterModel.model_rebuild() RepeatParameterModel.model_rebuild() CwlUnionParameterModel.model_rebuild() class ToolParameterBundle(Protocol): """An object having a dictionary of input models (i.e. a 'Tool')""" parameters: List[ToolParameterT] class ToolParameterBundleModel(BaseModel): parameters: List[ToolParameterT] def to_simple_model(input_parameter: Union[ToolParameterModel, ToolParameterT]) -> ToolParameterT: if input_parameter.__class__ == ToolParameterModel: assert isinstance(input_parameter, ToolParameterModel) return input_parameter.root else: return cast(ToolParameterT, input_parameter) def simple_input_models(parameters: Union[List[ToolParameterModel], List[ToolParameterT]]) -> Iterable[ToolParameterT]: return [to_simple_model(m) for m in parameters] def create_model_strict(*args, **kwd) -> Type[BaseModel]: # protected_namespaces here prevents tool with model_ parameter names from issuing warnings model_config = ConfigDict(extra="forbid", protected_namespaces=()) return create_model(*args, __config__=model_config, **kwd) def create_model_factory(state_representation: StateRepresentationT): def create_method(tool: ToolParameterBundle, name: Optional[str] = None) -> Type[BaseModel]: return create_field_model(tool.parameters, name or DEFAULT_MODEL_NAME, state_representation) return create_method create_relaxed_request_model = create_model_factory("relaxed_request") create_request_model = create_model_factory("request") create_request_internal_model = create_model_factory("request_internal") create_request_internal_dereferenced_model = create_model_factory("request_internal_dereferenced") create_landing_request_model = create_model_factory("landing_request") create_landing_request_internal_model = create_model_factory("landing_request_internal") create_job_internal_model = create_model_factory("job_internal") create_job_runtime_model = create_model_factory("job_runtime") create_test_case_model = create_model_factory("test_case_xml") create_workflow_step_model = create_model_factory("workflow_step") create_workflow_step_linked_model = create_model_factory("workflow_step_linked") def create_field_model( tool_parameter_models: Union[List[ToolParameterModel], List[ToolParameterT]], name: str, state_representation: StateRepresentationT, extra_kwd: Optional[Mapping[str, tuple]] = None, extra_validators: Optional[ValidatorDictT] = None, ) -> Type[BaseModel]: kwd: Dict[str, tuple] = {} if extra_kwd: kwd.update(extra_kwd) model_validators = (extra_validators or {}).copy() for input_model in tool_parameter_models: input_model = to_simple_model(input_model) pydantic_request_template = input_model.pydantic_template(state_representation) input_name = pydantic_request_template.name kwd[input_name] = pydantic_request_template.definition input_validators = pydantic_request_template.validators for validator_name, validator_callable in input_validators.items(): model_validators[f"{input_name}_{validator_name}"] = validator_callable pydantic_model = create_model_strict(name, __validators__=model_validators, **kwd) return pydantic_model def _is_landing_request(state_representation: StateRepresentationT): return state_representation in ["landing_request", "landing_request_internal"] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/sample_sheet.py0000644000175100017510000000344315166026066026070 0ustar00runnerrunner"""Sample sheet type definitions for Galaxy. This module contains type definitions for sample sheets, extracted to avoid circular imports. These types are used across the codebase for sample sheet metadata in collections. """ from typing import ( Any, Dict, List, Optional, Union, ) from typing_extensions import ( Literal, NotRequired, TypedDict, ) # Named in compatibility with CWL - trying to keep CWL fields in mind with # this implementation. https://www.commonwl.org/user_guide/topics/inputs.html#inputs # element_identifier is not like CWL - it is used to specify the value in the row should # be the element_identifier for another element if present. It is a way to specify relationships # between elements in the collection - specifically implemented for the "control" use case. SampleSheetColumnType = Literal[ "string", "int", "float", "boolean", "element_identifier" ] # excluding "long" and "double" and composite types from CWL for now - we don't think at this level of abstraction in Galaxy generally NoneType = type(None) SampleSheetColumnValueT = Union[int, float, bool, str, NoneType] # type ignore because mypy can't handle closed TypedDicts yet class SampleSheetColumnDefinition(TypedDict, closed=True): # type: ignore[call-arg] name: str description: NotRequired[Optional[str]] type: SampleSheetColumnType optional: bool default_value: NotRequired[Optional[SampleSheetColumnValueT]] validators: NotRequired[Optional[List[Dict[str, Any]]]] restrictions: NotRequired[Optional[List[SampleSheetColumnValueT]]] suggestions: NotRequired[Optional[List[SampleSheetColumnValueT]]] SampleSheetColumnDefinitions = List[SampleSheetColumnDefinition] SampleSheetRow = List[SampleSheetColumnValueT] SampleSheetRows = Dict[str, SampleSheetRow] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/tool_outputs.py0000644000175100017510000001357415166026066026205 0ustar00runnerrunner"""Modern pydantic based descriptions of Galaxy tool output objects. output_objects.py is still used for internals and contain references to the actual tool object but the goal here is to switch to using these overtime at least for external APIs and in library code where actual tool objects aren't created. """ from typing import ( Generic, List, Optional, Union, ) from pydantic import Field from typing_extensions import ( Annotated, Literal, TypeVar, ) from ._base import ToolSourceBaseModel AnyT = TypeVar("AnyT") NotRequired = Optional[AnyT] IncomingNotRequiredBoolT = TypeVar("IncomingNotRequiredBoolT") IncomingNotRequiredStringT = TypeVar("IncomingNotRequiredStringT") # Use IncomingNotRequired when concrete key: Optional[str] = None would be incorrect class GenericToolOutputBaseModel(ToolSourceBaseModel, Generic[IncomingNotRequiredBoolT, IncomingNotRequiredStringT]): name: Annotated[ IncomingNotRequiredStringT, Field(description="Parameter name. Used when referencing parameter in workflows.") ] label: Annotated[Optional[str], Field(description="Output label. Will be used as dataset name in history.")] = None hidden: Annotated[ IncomingNotRequiredBoolT, Field(description="If true, the output will not be shown in the history.") ] DiscoverViaT = Literal["tool_provided_metadata", "pattern"] SortKeyT = Literal["filename", "name", "designation", "dbkey"] SortCompT = Literal["lexical", "numeric"] class DatasetCollectionDescription(ToolSourceBaseModel): discover_via: DiscoverViaT format: Optional[str] visible: bool assign_primary_output: bool directory: Optional[str] recurse: bool match_relative_path: bool class ToolProvidedMetadataDatasetCollection(DatasetCollectionDescription): discover_via: Literal["tool_provided_metadata"] class FilePatternDatasetCollectionDescription(DatasetCollectionDescription): discover_via: Literal["pattern"] sort_key: SortKeyT sort_comp: SortCompT sort_reverse: bool = False pattern: str DatasetCollectionDescriptionT = Union[FilePatternDatasetCollectionDescription, ToolProvidedMetadataDatasetCollection] class GenericToolOutputDataset( GenericToolOutputBaseModel[IncomingNotRequiredBoolT, IncomingNotRequiredStringT], Generic[IncomingNotRequiredBoolT, IncomingNotRequiredStringT], ): type: Literal["data"] format: Annotated[IncomingNotRequiredStringT, Field(description="The short name for the output datatype.")] format_source: Annotated[ Optional[str], Field( description="This sets the data type of the output dataset(s) to be the same format as that of the specified tool input." ), ] = None metadata_source: Annotated[ Optional[str], Field( description="This copies the metadata information from the tool’s input dataset to serve as default for information that cannot be detected from the output. One prominent use case is interval data with a non-standard column order that cannot be deduced from a header line, but which is known to be identical in the input and output datasets." ), ] = None discover_datasets: Optional[List[DatasetCollectionDescriptionT]] = None from_work_dir: Annotated[ Optional[str], Field( title="from_work_dir", description="Relative path to a file produced by the tool in its working directory. Output’s contents are set to this file’s contents.", ), ] = None precreate_directory: Optional[bool] = False class ToolOutputDataset(GenericToolOutputDataset[bool, str]): ... class IncomingToolOutputDataset( GenericToolOutputDataset[ NotRequired[bool], NotRequired[str], ] ): name: Annotated[ Optional[str], Field(description="Parameter name. Used when referencing parameter in workflows.") ] = None hidden: Annotated[Optional[bool], Field(description="If true, the output will not be shown in the history.")] = None format: Annotated[Optional[str], Field(description="The short name for the output datatype.")] = None class ToolOutputCollectionStructure(ToolSourceBaseModel): collection_type: Optional[str] = None collection_type_source: Optional[str] = None collection_type_from_rules: Optional[str] = None structured_like: Optional[str] = None discover_datasets: Optional[List[DatasetCollectionDescriptionT]] = None class GenericToolOutputCollection( GenericToolOutputBaseModel[IncomingNotRequiredBoolT, IncomingNotRequiredStringT], Generic[IncomingNotRequiredBoolT, IncomingNotRequiredStringT], ): type: Literal["collection"] structure: ToolOutputCollectionStructure class ToolOutputCollection(GenericToolOutputCollection[bool, str]): ... class IncomingToolOutputCollection(GenericToolOutputCollection[NotRequired[bool], NotRequired[str]]): name: Annotated[ Optional[str], Field(description="Parameter name. Used when referencing parameter in workflows.") ] = None hidden: Annotated[Optional[bool], Field(description="If true, the output will not be shown in the history.")] = None class ToolOutputSimple(GenericToolOutputBaseModel): pass class ToolOutputText(ToolOutputSimple): type: Literal["text"] class ToolOutputInteger(ToolOutputSimple): type: Literal["integer"] class ToolOutputFloat(ToolOutputSimple): type: Literal["float"] class ToolOutputBoolean(ToolOutputSimple): type: Literal["boolean"] IncomingToolOutputT = Union[ IncomingToolOutputDataset, IncomingToolOutputCollection, ToolOutputText, ToolOutputInteger, ToolOutputFloat, ToolOutputBoolean, ] IncomingToolOutput = Annotated[IncomingToolOutputT, Field(discriminator="type")] ToolOutputT = Union[ ToolOutputDataset, ToolOutputCollection, ToolOutputText, ToolOutputInteger, ToolOutputFloat, ToolOutputBoolean ] ToolOutput = Annotated[ToolOutputT, Field(discriminator="type")] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/galaxy/tool_util_models/tool_source.py0000644000175100017510000001646715166026066025766 0ustar00runnerrunnerfrom enum import Enum from typing import ( List, Optional, Union, ) from pydantic import ( Field, ) from typing_extensions import ( Annotated, Literal, NotRequired, TypedDict, ) from ._base import ToolSourceBaseModel class Container(ToolSourceBaseModel): type: Literal["docker", "singularity"] container_id: str class Requirement(ToolSourceBaseModel): type: Literal["package", "set_environment"] class ContainerRequirement(ToolSourceBaseModel): type: Literal["container"] container: Container class PackageRequirement(Requirement): type: Literal["package"] name: str version: Optional[str] class SetEnvironmentRequirement(Requirement): type: Literal["set_environment"] environment: str cores_min_description = "Minimum reserved number of CPU cores." cores_max_description = "Maximum reserved number of CPU cores." cores_description = """May be a fractional value to indicate to a scheduling algorithm that one core can be allocated to multiple jobs. For example, a value of 0.25 indicates that up to 4 jobs may run in parallel on 1 core. A value of 1.25 means that up to 3 jobs can run on a 4 core system (4/1.25 ≈ 3). The reported number of CPU cores reserved for the process is a non-zero integer calculated by rounding up the cores request to the next whole number. """ ram_min_description = "Minimum reserved RAM in mebibytes (2**20)." ram_max_description = "Maximum reserved RAM in mebibytes (2**20)." ram_description = """May be a fractional value. If so, the actual RAM request is rounded up to the next whole number. The reported amount of RAM reserved for the process is a non-zero integer.""" class ResourceRequirement(ToolSourceBaseModel): type: Literal["resource"] cores_min: Annotated[ Union[int, float, None], Field(description=f"{cores_min_description}\n{cores_description}") ] = 1 cores_max: Annotated[ Union[int, float, None], Field(description=f"{cores_max_description}\n{cores_description}") ] = None ram_min: Annotated[Union[int, float, None], Field(description=f"{ram_min_description}\n{ram_description}")] = 256 ram_max: Annotated[Union[int, float, None], Field(description=f"{ram_max_description}\n{ram_description}")] = None tmpdir_min: Optional[Union[int, float]] = None tmpdir_max: Optional[Union[int, float]] = None cuda_version_min: Optional[Union[int, float]] = None cuda_compute_capability: Optional[Union[int, float]] = None gpu_memory_min: Optional[Union[int, float]] = None cuda_device_count_min: Optional[Union[int, float]] = None cuda_device_count_max: Optional[Union[int, float]] = None shm_size: Optional[Union[int, float]] = None class JavascriptRequirement(ToolSourceBaseModel): type: Literal["javascript"] expression_lib: Optional[ List[ Annotated[ str, Field( title="expression_lib", description="Provide Javascript/ECMAScript 5.1 code here that will be available for expressions inside the `shell_command` field.", examples=[r"""function pickValue() { if (inputs.conditional_parameter.test_parameter == "a") { return inputs.conditional_parameter.integer_parameter } else { return inputs.conditional_parameter.boolean_parameter } }"""], ), ] ] ] class XrefDict(TypedDict): value: str type: str class TemplateConfigFile(ToolSourceBaseModel): content: str name: Optional[str] = None filename: Optional[str] = None class InputConfigFileContent(ToolSourceBaseModel): format: Literal["json"] = "json" handle_files: Optional[Literal["paths", "staging_path_and_source_path"]] = None type: Literal["inputs"] = "inputs" class InputConfigFile(ToolSourceBaseModel): name: Optional[str] = None content: InputConfigFileContent filename: Optional[str] = None class FileSourceConfigFileContent(ToolSourceBaseModel): type: Literal["files"] = "files" class FileSourceConfigFile(ToolSourceBaseModel): name: Optional[str] filename: Optional[str] = None content: FileSourceConfigFileContent class XmlTemplateConfigFile(TemplateConfigFile): eval_engine: Literal["cheetah"] = "cheetah" class YamlTemplateConfigFile(TemplateConfigFile): eval_engine: Literal["ecmascript"] = "ecmascript" class Citation(ToolSourceBaseModel): type: str content: str class HelpContent(ToolSourceBaseModel): format: Literal["restructuredtext", "plain_text", "markdown"] content: str class OutputCompareType(str, Enum): diff = "diff" re_match = "re_match" sim_size = "sim_size" re_match_multiline = "re_match_multiline" contains = "contains" image_diff = "image_diff" class DrillDownOptionsDict(TypedDict): name: Optional[str] value: str options: List["DrillDownOptionsDict"] selected: bool # For fields... just implementing a subset of CWL for Galaxy flavors of these objects # so far. CwlType = Literal["File", "null", "boolean", "int", "float", "string"] FieldType = Union[CwlType, List[CwlType]] # type ignore because mypy can't handle closed TypedDicts yet class FieldDict(TypedDict, closed=True): # type: ignore[call-arg] name: str type: FieldType format: NotRequired[Optional[str]] JsonTestDatasetDefDict = TypedDict( "JsonTestDatasetDefDict", { "class": Literal["File"], "path": NotRequired[Optional[str]], "location": NotRequired[Optional[str]], "name": NotRequired[Optional[str]], "dbkey": NotRequired[Optional[str]], "filetype": NotRequired[Optional[str]], "composite_data": NotRequired[Optional[List[str]]], "tags": NotRequired[Optional[List[str]]], }, ) JsonTestCollectionDefElementDict = Union[ "JsonTestCollectionDefDatasetElementDict", "JsonTestCollectionDefCollectionElementDict" ] JsonTestCollectionDefDatasetElementDict = TypedDict( "JsonTestCollectionDefDatasetElementDict", { "identifier": str, "class": Literal["File"], "path": NotRequired[Optional[str]], "location": NotRequired[Optional[str]], "name": NotRequired[Optional[str]], "dbkey": NotRequired[Optional[str]], "filetype": NotRequired[Optional[str]], "composite_data": NotRequired[Optional[List[str]]], "tags": NotRequired[Optional[List[str]]], }, ) BaseJsonTestCollectionDefCollectionElementDict = TypedDict( "BaseJsonTestCollectionDefCollectionElementDict", { "class": Literal["Collection"], "collection_type": Optional[str], "elements": NotRequired[Optional[List[JsonTestCollectionDefElementDict]]], }, ) JsonTestCollectionDefCollectionElementDict = TypedDict( "JsonTestCollectionDefCollectionElementDict", { "identifier": str, "class": Literal["Collection"], "collection_type": Optional[str], "elements": NotRequired[Optional[List[JsonTestCollectionDefElementDict]]], }, ) JsonTestCollectionDefDict = TypedDict( "JsonTestCollectionDefDict", { "class": Literal["Collection"], "collection_type": Optional[str], "elements": NotRequired[Optional[List[JsonTestCollectionDefElementDict]]], "name": NotRequired[Optional[str]], "fields": NotRequired[Optional[List[FieldDict]]], }, ) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775774795.864695 galaxy_tool_util_models-26.0.0/galaxy_tool_util_models.egg-info/0000755000175100017510000000000015166026114024605 5ustar00runnerrunner././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774795.0 galaxy_tool_util_models-26.0.0/galaxy_tool_util_models.egg-info/PKG-INFO0000644000175100017510000001145615166026113025710 0ustar00runnerrunnerMetadata-Version: 2.4 Name: galaxy-tool-util-models Version: 26.0.0 Summary: Pydantic models for Galaxy tools Home-page: https://github.com/galaxyproject/galaxy Author: Galaxy Project and Community Author-email: galaxy-committers@lists.galaxyproject.org License: MIT Requires-Python: >=3.8 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: pydantic>=2.7.4 Requires-Dist: typing-extensions Provides-Extra: test Requires-Dist: pytest; extra == "test" Dynamic: license-file .. image:: https://badge.fury.io/py/galaxy-tool-util-models.svg :target: https://pypi.org/project/galaxy-tool-util-models/ Overview -------- Pydantic models to support Galaxy_ tool utilities. * Code: https://github.com/galaxyproject/galaxy/tree/dev/packages/tool_util_models .. _Galaxy: http://galaxyproject.org/ History ------- .. to_doc ------------------- 26.0.0 (2026-04-08) ------------------- ========= Bug fixes ========= * Fix Pydantic UnsupportedFieldAttributeWarning for Field defaults in Annotated by `@mvdbeek `_ in `#21923 `_ * Backport paired or unpaired mapping fix by `@mvdbeek `_ in `#21936 `_ * Fix validation of certain classes of text validators in tools. by `@jmchilton `_ in `#22280 `_ ============ Enhancements ============ * Tool Request API by `@jmchilton `_ in `#20935 `_ * Update fastapi to 0.123.4 and ``get_openapi()`` fork by `@nsoranzo `_ in `#21384 `_ * Enable attaching sample sheet to landing requests by `@mvdbeek `_ in `#21489 `_ * Apply 2026 black style by `@galaxybot `_ in `#21618 `_ ============= Other changes ============= * Merge 25.1 into dev, fix openapi schema generation for TypedDict by `@mvdbeek `_ in `#21168 `_ * Fix tool request API for multiple select parameters with defaults. by `@jmchilton `_ in `#21416 `_ ------------------- 25.1.2 (2026-03-09) ------------------- No recorded changes since last release ------------------- 25.1.1 (2026-02-03) ------------------- No recorded changes since last release ------------------- 25.1.0 (2025-12-12) ------------------- ============ Enhancements ============ * Implement Sample Sheets by `@jmchilton `_ in `#19305 `_ * Prepare ``ToolBox.dynamic_tool_to_tool()`` for CWL formats by `@nsoranzo `_ in `#20364 `_ * Type annotation fixes for mypy 1.16.0 by `@nsoranzo `_ in `#20424 `_ * Add configfiles support and various enhancements for user defined tools by `@mvdbeek `_ in `#20761 `_ * Add support for picking ``from_work_dir`` directory by `@mvdbeek `_ in `#20916 `_ * Include format in internal json model by `@mvdbeek `_ in `#20917 `_ * Add resource docs and tweak tool source schema title generation by `@mvdbeek `_ in `#20951 `_ * Use workflow-style payload in data landing request by `@mvdbeek `_ in `#21107 `_ ------------------- 25.0.4 (2025-11-18) ------------------- No recorded changes since last release ------------------- 25.0.3 (2025-09-23) ------------------- ========= Bug fixes ========= * Fix collection element sorting in extended_metadata by `@mvdbeek `_ in `#20928 `_ ------------------- 25.0.2 (2025-08-13) ------------------- ========= Bug fixes ========= * Fix parameter models for optional color params. by `@jmchilton `_ in `#20705 `_ ------------------- 25.0.1 (2025-06-20) ------------------- No recorded changes since last release ------------------- 25.0.0 (2025-06-18) ------------------- First release ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774795.0 galaxy_tool_util_models-26.0.0/galaxy_tool_util_models.egg-info/SOURCES.txt0000644000175100017510000000135615166026113026475 0ustar00runnerrunnerHISTORY.rst LICENSE MANIFEST.in README.rst dev-requirements.txt pyproject.toml setup.cfg galaxy/__init__.py galaxy/py.typed galaxy/tool_util_models/__init__.py galaxy/tool_util_models/_base.py galaxy/tool_util_models/_types.py galaxy/tool_util_models/assertions.py galaxy/tool_util_models/dynamic_tool_models.py galaxy/tool_util_models/parameter_validators.py galaxy/tool_util_models/parameters.py galaxy/tool_util_models/sample_sheet.py galaxy/tool_util_models/tool_outputs.py galaxy/tool_util_models/tool_source.py galaxy_tool_util_models.egg-info/PKG-INFO galaxy_tool_util_models.egg-info/SOURCES.txt galaxy_tool_util_models.egg-info/dependency_links.txt galaxy_tool_util_models.egg-info/requires.txt galaxy_tool_util_models.egg-info/top_level.txt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774795.0 galaxy_tool_util_models-26.0.0/galaxy_tool_util_models.egg-info/dependency_links.txt0000644000175100017510000000000115166026113030652 0ustar00runnerrunner ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774795.0 galaxy_tool_util_models-26.0.0/galaxy_tool_util_models.egg-info/requires.txt0000644000175100017510000000006115166026113027201 0ustar00runnerrunnerpydantic>=2.7.4 typing-extensions [test] pytest ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774795.0 galaxy_tool_util_models-26.0.0/galaxy_tool_util_models.egg-info/top_level.txt0000644000175100017510000000000715166026113027333 0ustar00runnerrunnergalaxy ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775774774.0 galaxy_tool_util_models-26.0.0/pyproject.toml0000644000175100017510000000044115166026066021112 0ustar00runnerrunner[build-system] build-backend = "setuptools.build_meta" requires = ["setuptools"] [project] dynamic = [ "authors", "dependencies", "description", "license", "optional-dependencies", "readme", "requires-python", "version", ] name = "galaxy-tool-util-models" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775774795.865837 galaxy_tool_util_models-26.0.0/setup.cfg0000644000175100017510000000244215166026114020014 0ustar00runnerrunner[metadata] author = Galaxy Project and Community author_email = galaxy-committers@lists.galaxyproject.org classifiers = Development Status :: 5 - Production/Stable Environment :: Console Intended Audience :: Developers Natural Language :: English Operating System :: POSIX Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 Programming Language :: Python :: 3.13 Programming Language :: Python :: 3.14 Topic :: Software Development Topic :: Software Development :: Code Generators Topic :: Software Development :: Testing description = Pydantic models for Galaxy tools keywords = Galaxy license = MIT license_files = LICENSE long_description = file: README.rst, HISTORY.rst long_description_content_type = text/x-rst name = galaxy-tool-util-models url = https://github.com/galaxyproject/galaxy version = 26.0.0 [options] include_package_data = True install_requires = pydantic>=2.7.4 typing-extensions packages = find: python_requires = >=3.8 [options.extras_require] test = pytest [options.entry_points] console_scripts = [options.packages.find] exclude = tests* [egg_info] tag_build = tag_date = 0