diff --git a/lib/galaxy/dependencies/pinned-requirements.txt b/lib/galaxy/dependencies/pinned-requirements.txt index 471c31e7b27..a7e3fce6afa 100644 --- a/lib/galaxy/dependencies/pinned-requirements.txt +++ b/lib/galaxy/dependencies/pinned-requirements.txt @@ -207,6 +207,7 @@ pydantic-ai==1.99.0 pydantic-ai-slim==1.99.0 pydantic-core==2.46.4 pydantic-evals==1.99.0 +pydantic-extra-types==2.11.1 pydantic-graph==1.99.0 pydantic-handlebars==0.1.0 pydantic-settings==2.14.1 diff --git a/lib/galaxy/tool_util_models/parameters.py b/lib/galaxy/tool_util_models/parameters.py index 4aa956fc41d..3922cf39b70 100644 --- a/lib/galaxy/tool_util_models/parameters.py +++ b/lib/galaxy/tool_util_models/parameters.py @@ -41,6 +41,7 @@ from pydantic import ( TypeAdapter, ) from pydantic.json_schema import SkipJsonSchema +from pydantic_extra_types.color import Color from typing_extensions import ( Annotated, Literal, @@ -1540,15 +1541,10 @@ def ensure_color_valid(value: Optional[Any]): 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") + try: + Color(value) + except Exception as e: + raise ValueError(f"Invalid color value {value!r}: {e}") class ColorParameterModel(BaseGalaxyToolParameterModelDefinition): @@ -1558,7 +1554,7 @@ class ColorParameterModel(BaseGalaxyToolParameterModelDefinition): def field_kwargs(self) -> Dict[str, Any]: kwargs = super().field_kwargs() - kwargs["json_schema_extra"]["pattern"] = "^#[0-9a-f]{6}$" + kwargs["json_schema_extra"]["format"] = "color" return kwargs @property diff --git a/packages/tool_util_models/setup.cfg b/packages/tool_util_models/setup.cfg index 7b39184dd28..ad7ded316f0 100644 --- a/packages/tool_util_models/setup.cfg +++ b/packages/tool_util_models/setup.cfg @@ -15,6 +15,7 @@ version = 26.1.dev0 include_package_data = True install_requires = pydantic>=2.7.4 + pydantic-extra-types typing-extensions package_dir = =src diff --git a/pyproject.toml b/pyproject.toml index 58136807761..c1c16e97891 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,6 +76,7 @@ dependencies = [ "pycryptodome", "pydantic[email]>=2.7.4", # https://github.com/pydantic/pydantic/pull/9639 "pydantic-ai>=1.56.0", # https://github.com/pydantic/pydantic-ai/security/advisories/GHSA-2jrp-274c-jhv3 + "pydantic-extra-types", "pydicom; python_version >= '3.10'", "PyJWT", "pykwalify", diff --git a/test/unit/tool_util/parameter_specification.yml b/test/unit/tool_util/parameter_specification.yml index dc34e7061ce..ca475a8c3e7 100644 --- a/test/unit/tool_util/parameter_specification.yml +++ b/test/unit/tool_util/parameter_specification.yml @@ -1233,10 +1233,10 @@ gx_color: request_valid: - parameter: '#aabbcc' - parameter: '#000000' + - parameter: '#abcd' request_invalid: - parameter: null - parameter: {} - - parameter: '#abcd' workflow_step_valid: - parameter: '#aabbcc' - parameter: '#000000' @@ -1253,6 +1253,9 @@ gx_color: - parameter: 'foobar' - parameter: 5 - parameter: {__class__: 'ConnectedValue2'} + _json_schema_skip: + workflow_step_invalid: "color validator uses AfterValidator" + workflow_step_linked_invalid: "color validator uses AfterValidator" gx_data: request_valid: