From 41dc0375f77a8b629e137e7759a43e19d0c56795 Mon Sep 17 00:00:00 2001 From: liamhuber Date: Thu, 19 Feb 2026 11:25:27 -0800 Subject: [PATCH 1/8] Restrict input values To a subset of JSONifiable types Signed-off-by: liamhuber --- src/python_workflow_definition/models.py | 5 ++++- tests/test_models.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/python_workflow_definition/models.py b/src/python_workflow_definition/models.py index 4980cfa..8b6ec0e 100644 --- a/src/python_workflow_definition/models.py +++ b/src/python_workflow_definition/models.py @@ -19,6 +19,9 @@ ) +JsonPrimitive = Union[str, int, float, bool, None] + + class PythonWorkflowDefinitionBaseNode(BaseModel): """Base model for all node types, containing common fields.""" @@ -33,7 +36,7 @@ class PythonWorkflowDefinitionInputNode(PythonWorkflowDefinitionBaseNode): type: Literal["input"] name: str - value: Optional[Any] = None + value: Optional[JsonPrimitive] = None class PythonWorkflowDefinitionOutputNode(PythonWorkflowDefinitionBaseNode): diff --git a/tests/test_models.py b/tests/test_models.py index 83f6066..eca7e2a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -4,6 +4,7 @@ from unittest import mock from pydantic import ValidationError from python_workflow_definition.models import ( + JsonPrimitive, PythonWorkflowDefinitionInputNode, PythonWorkflowDefinitionOutputNode, PythonWorkflowDefinitionFunctionNode, @@ -40,6 +41,19 @@ def test_input_node(self): ) self.assertEqual(node_with_value.value, 42) + def test_input_node_invalid_value_raises(self): + bad_value = (1, 2) + self.assertNotIsInstance(bad_value, JsonPrimitive) + with self.assertRaises(ValidationError): + PythonWorkflowDefinitionInputNode.model_validate( + { + "id": 0, + "type": "input", + "name": "x", + "value": bad_value, + } + ) + def test_output_node(self): node = PythonWorkflowDefinitionOutputNode(id=1, type="output", name="test_output") self.assertEqual(node.id, 1) From aa565783b55076c68cbbdc6633a593def15a0704 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 20 Feb 2026 10:10:25 +0100 Subject: [PATCH 2/8] Allow dictionaries and lists of primitive types --- src/python_workflow_definition/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/python_workflow_definition/models.py b/src/python_workflow_definition/models.py index 8b6ec0e..aa9a2f7 100644 --- a/src/python_workflow_definition/models.py +++ b/src/python_workflow_definition/models.py @@ -20,6 +20,7 @@ JsonPrimitive = Union[str, int, float, bool, None] +JsonValue = Union[JsonPrimitive, list[JsonPrimitive], dict[str, JsonPrimitive]] class PythonWorkflowDefinitionBaseNode(BaseModel): @@ -36,7 +37,7 @@ class PythonWorkflowDefinitionInputNode(PythonWorkflowDefinitionBaseNode): type: Literal["input"] name: str - value: Optional[JsonPrimitive] = None + value: Optional[JsonValue] = None class PythonWorkflowDefinitionOutputNode(PythonWorkflowDefinitionBaseNode): From 89ac664514f96cc9a0af23b795e1c79539cf1f77 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 20 Feb 2026 10:14:13 +0100 Subject: [PATCH 3/8] Update test_models.py --- tests/test_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_models.py b/tests/test_models.py index eca7e2a..e431726 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -42,7 +42,7 @@ def test_input_node(self): self.assertEqual(node_with_value.value, 42) def test_input_node_invalid_value_raises(self): - bad_value = (1, 2) + bad_value = [(1, 2)] self.assertNotIsInstance(bad_value, JsonPrimitive) with self.assertRaises(ValidationError): PythonWorkflowDefinitionInputNode.model_validate( From fa3064f074d58b9d5da925324f8935864e752729 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 20 Feb 2026 20:00:28 +0100 Subject: [PATCH 4/8] Update models.py --- src/python_workflow_definition/models.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/python_workflow_definition/models.py b/src/python_workflow_definition/models.py index aa9a2f7..4447fdc 100644 --- a/src/python_workflow_definition/models.py +++ b/src/python_workflow_definition/models.py @@ -1,5 +1,15 @@ from pathlib import Path -from typing import List, Union, Optional, Literal, Any, Annotated, Type, TypeVar +from typing import ( + List, + Union, + Optional, + Literal, + Any, + Annotated, + Type, + TypeVar, +) +from typing_extensions import TypeAliasType from pydantic import BaseModel, Field, field_validator, field_serializer from pydantic import ValidationError import json @@ -20,7 +30,9 @@ JsonPrimitive = Union[str, int, float, bool, None] -JsonValue = Union[JsonPrimitive, list[JsonPrimitive], dict[str, JsonPrimitive]] +AllowableDefaults = TypeAliasType( + "AllowableDefaults", "Union[JsonPrimitive, dict[str, AllowableDefaults], tuple[AllowableDefaults, ...]]" +) class PythonWorkflowDefinitionBaseNode(BaseModel): @@ -37,7 +49,7 @@ class PythonWorkflowDefinitionInputNode(PythonWorkflowDefinitionBaseNode): type: Literal["input"] name: str - value: Optional[JsonValue] = None + value: Optional[AllowableDefaults] = None class PythonWorkflowDefinitionOutputNode(PythonWorkflowDefinitionBaseNode): From 839c987f3dbd9c32d3ebd9573dc817ebaed7530c Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 20 Feb 2026 20:01:36 +0100 Subject: [PATCH 5/8] Update models.py --- src/python_workflow_definition/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/python_workflow_definition/models.py b/src/python_workflow_definition/models.py index 4447fdc..c86b04c 100644 --- a/src/python_workflow_definition/models.py +++ b/src/python_workflow_definition/models.py @@ -31,7 +31,8 @@ JsonPrimitive = Union[str, int, float, bool, None] AllowableDefaults = TypeAliasType( - "AllowableDefaults", "Union[JsonPrimitive, dict[str, AllowableDefaults], tuple[AllowableDefaults, ...]]" + "AllowableDefaults", + "Union[JsonPrimitive, dict[str, AllowableDefaults], tuple[AllowableDefaults, ...]]", ) From d88f9a94d20215cfb4ea96eddf86309dfce95d7d Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 20 Feb 2026 20:03:41 +0100 Subject: [PATCH 6/8] Update test_models.py --- tests/test_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_models.py b/tests/test_models.py index e431726..318e355 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -42,7 +42,7 @@ def test_input_node(self): self.assertEqual(node_with_value.value, 42) def test_input_node_invalid_value_raises(self): - bad_value = [(1, 2)] + bad_value = {1: 2} self.assertNotIsInstance(bad_value, JsonPrimitive) with self.assertRaises(ValidationError): PythonWorkflowDefinitionInputNode.model_validate( From ece89c87a19b127a46b89fd7b6f2817559a7b05f Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 20 Feb 2026 20:07:01 +0100 Subject: [PATCH 7/8] Update models.py --- src/python_workflow_definition/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python_workflow_definition/models.py b/src/python_workflow_definition/models.py index c86b04c..8b25be8 100644 --- a/src/python_workflow_definition/models.py +++ b/src/python_workflow_definition/models.py @@ -32,7 +32,7 @@ JsonPrimitive = Union[str, int, float, bool, None] AllowableDefaults = TypeAliasType( "AllowableDefaults", - "Union[JsonPrimitive, dict[str, AllowableDefaults], tuple[AllowableDefaults, ...]]", + "Union[JsonPrimitive, dict[str, AllowableDefaults], list[AllowableDefaults]]", ) From e4c8bafe199fb777cb3c1c583fc9fdb2567c52db Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Mon, 23 Feb 2026 19:37:25 +0100 Subject: [PATCH 8/8] extend tests --- tests/test_models.py | 58 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index 318e355..d353f46 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -13,6 +13,11 @@ INTERNAL_DEFAULT_HANDLE, ) + +class _NoTrivialSerialization: + pass + + class TestModels(unittest.TestCase): def setUp(self): self.valid_workflow_dict = { @@ -41,18 +46,49 @@ def test_input_node(self): ) self.assertEqual(node_with_value.value, 42) + def test_input_node_valid_values(self): + good_values = ( + 1, + 1.1, + "string", + True, + None, + [1, 2], + [["recursive", "tuple"], [True, False]], + ) + for value in good_values: + with self.subTest(value=value): + model = PythonWorkflowDefinitionInputNode.model_validate( + { + "id": 0, + "type": "input", + "name": "x", + "value": value, + } + ) + self.assertEqual( + value, + PythonWorkflowDefinitionInputNode.model_validate( + model.model_dump(mode="json") + ).value + ) + def test_input_node_invalid_value_raises(self): - bad_value = {1: 2} - self.assertNotIsInstance(bad_value, JsonPrimitive) - with self.assertRaises(ValidationError): - PythonWorkflowDefinitionInputNode.model_validate( - { - "id": 0, - "type": "input", - "name": "x", - "value": bad_value, - } - ) + bad_values = ( + {1: 2}, + _NoTrivialSerialization(), + ) + for value in bad_values: + with self.subTest(value=value): + with self.assertRaises(ValidationError): + PythonWorkflowDefinitionInputNode.model_validate( + { + "id": 0, + "type": "input", + "name": "x", + "value": value, + } + ) def test_output_node(self): node = PythonWorkflowDefinitionOutputNode(id=1, type="output", name="test_output")