From 5373368a9d713eebc393d19dc951201541c993b2 Mon Sep 17 00:00:00 2001 From: Katharina Jacoby Date: Mon, 29 Sep 2025 13:49:51 +0200 Subject: [PATCH 1/8] Create index.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This keeps the example concise, uses a precise exception, and gives line/column context for faster debugging (without over-explaining). The snippet already imports json, so no extra import lines are needed. The example type-hints data: str with Pydantic, so TypeError isn’t expected; if they do want extra safety, we can widen to except (json.JSONDecodeError, TypeError) as e:. --- docs/docs/exosphere/index.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/docs/exosphere/index.md diff --git a/docs/docs/exosphere/index.md b/docs/docs/exosphere/index.md new file mode 100644 index 00000000..5cc3a9c6 --- /dev/null +++ b/docs/docs/exosphere/index.md @@ -0,0 +1,26 @@ +@@ + class DataProcessorNode(BaseNode): + class Inputs(BaseModel): + data: str + operation: str +@@ + async def execute(self) -> Outputs: + # Parse the input data + try: + data = json.loads(self.inputs.data) +- except: +- return self.Outputs( +- result="", +- status="error: invalid json" +- ) ++ except json.JSONDecodeError as e: ++ # Invalid JSON input; return a helpful, specific error ++ return self.Outputs( ++ result="", ++ status=f"error: invalid json (line {e.lineno}, column {e.colno})" ++ ) + # Process based on operation + if self.inputs.operation == "transform": + result = {"transformed": data, "processed": True} + else: + result = {"original": data, "processed": False} From f2b831fc25516eee285ac8c4a34edecd649aa3e8 Mon Sep 17 00:00:00 2001 From: Katharina Jacoby Date: Mon, 29 Sep 2025 14:34:22 +0200 Subject: [PATCH 2/8] Index docs: replace bare except with JSONDecodeError in example --- docs/docs/exosphere/Index | 80 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 docs/docs/exosphere/Index diff --git a/docs/docs/exosphere/Index b/docs/docs/exosphere/Index new file mode 100644 index 00000000..b5128ab4 --- /dev/null +++ b/docs/docs/exosphere/Index @@ -0,0 +1,80 @@ +@@ +- class DataProcessorNode(BaseNode): +- class Inputs(BaseModel): +- data: str +- operation: str +- +- class Outputs(BaseModel): +- result: str +- status: str +- +- async def execute(self) -> Outputs: +- # Parse the input data +- try: +- data = json.loads(self.inputs.data) +- except json.JSONDecodeError as e: +- # Invalid JSON input; return a helpful, specific error +- return self.Outputs( +- result="", +- status=f"error: invalid json (line {e.lineno}, column {e.colno})" +- ) +- # Process based on operation +- if self.inputs.operation == "transform": +- result = {"transformed": data, "processed": True} +- else: +- result = {"original": data, "processed": False} +- return self.Outputs(result=json.dumps(result), status="success") ++ import json ++ import logging ++ from enum import Enum ++ from pydantic import BaseModel ++ ++ logger = logging.getLogger(__name__) ++ ++ class Operation(str, Enum): ++ """Supported operations the node can perform.""" ++ TRANSFORM = "transform" ++ PASSTHROUGH = "passthrough" ++ ++ class DataProcessorNode(BaseNode): ++ """Parse JSON input and apply a simple operation, returning a JSON string result.""" ++ ++ class Inputs(BaseModel): ++ """Inputs for DataProcessorNode. ++ ++ data: JSON-encoded string payload to process. ++ operation: one of Operation (validated). ++ """ ++ data: str ++ operation: Operation ++ ++ class Outputs(BaseModel): ++ """Outputs emitted by DataProcessorNode. ++ ++ result: JSON-encoded string payload after processing. ++ status: 'success' or an error message. ++ """ ++ result: str ++ status: str ++ ++ async def execute(self) -> Outputs: ++ """Execute the node: parse input JSON and perform the requested operation.""" ++ # Parse the input data ++ try: ++ data = json.loads(self.inputs.data) ++ except json.JSONDecodeError as e: ++ # Log for observability and return specific, helpful feedback ++ logger.warning("Invalid JSON in DataProcessorNode.inputs.data", exc_info=True) ++ return self.Outputs( ++ result="", ++ status=f"error: invalid json (line {getattr(e, 'lineno', '?')}, column {getattr(e, 'colno', '?')})" ++ ) ++ ++ # Process based on validated operation ++ if self.inputs.operation is Operation.TRANSFORM: ++ result_obj = {"transformed": data, "processed": True} ++ else: # Operation.PASSTHROUGH ++ result_obj = {"original": data, "processed": False} ++ ++ # Outputs.result is defined as a string; return JSON-encoded result for consistency ++ return self.Outputs(result=json.dumps(result_obj), status="success") From 1467298aee511068edbc1cbf48fd522dbeb861e7 Mon Sep 17 00:00:00 2001 From: Katharina Jacoby Date: Mon, 29 Sep 2025 14:34:47 +0200 Subject: [PATCH 3/8] Delete docs/docs/exosphere/index.md --- docs/docs/exosphere/index.md | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 docs/docs/exosphere/index.md diff --git a/docs/docs/exosphere/index.md b/docs/docs/exosphere/index.md deleted file mode 100644 index 5cc3a9c6..00000000 --- a/docs/docs/exosphere/index.md +++ /dev/null @@ -1,26 +0,0 @@ -@@ - class DataProcessorNode(BaseNode): - class Inputs(BaseModel): - data: str - operation: str -@@ - async def execute(self) -> Outputs: - # Parse the input data - try: - data = json.loads(self.inputs.data) -- except: -- return self.Outputs( -- result="", -- status="error: invalid json" -- ) -+ except json.JSONDecodeError as e: -+ # Invalid JSON input; return a helpful, specific error -+ return self.Outputs( -+ result="", -+ status=f"error: invalid json (line {e.lineno}, column {e.colno})" -+ ) - # Process based on operation - if self.inputs.operation == "transform": - result = {"transformed": data, "processed": True} - else: - result = {"original": data, "processed": False} From e7623793a880f8a8ec57f33e7bd9d5921487eab5 Mon Sep 17 00:00:00 2001 From: Katharina Jacoby Date: Mon, 29 Sep 2025 22:05:10 +0200 Subject: [PATCH 4/8] Index --- docs/docs/exosphere/Index | 135 ++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 80 deletions(-) diff --git a/docs/docs/exosphere/Index b/docs/docs/exosphere/Index index b5128ab4..3b8d68e6 100644 --- a/docs/docs/exosphere/Index +++ b/docs/docs/exosphere/Index @@ -1,80 +1,55 @@ -@@ -- class DataProcessorNode(BaseNode): -- class Inputs(BaseModel): -- data: str -- operation: str -- -- class Outputs(BaseModel): -- result: str -- status: str -- -- async def execute(self) -> Outputs: -- # Parse the input data -- try: -- data = json.loads(self.inputs.data) -- except json.JSONDecodeError as e: -- # Invalid JSON input; return a helpful, specific error -- return self.Outputs( -- result="", -- status=f"error: invalid json (line {e.lineno}, column {e.colno})" -- ) -- # Process based on operation -- if self.inputs.operation == "transform": -- result = {"transformed": data, "processed": True} -- else: -- result = {"original": data, "processed": False} -- return self.Outputs(result=json.dumps(result), status="success") -+ import json -+ import logging -+ from enum import Enum -+ from pydantic import BaseModel -+ -+ logger = logging.getLogger(__name__) -+ -+ class Operation(str, Enum): -+ """Supported operations the node can perform.""" -+ TRANSFORM = "transform" -+ PASSTHROUGH = "passthrough" -+ -+ class DataProcessorNode(BaseNode): -+ """Parse JSON input and apply a simple operation, returning a JSON string result.""" -+ -+ class Inputs(BaseModel): -+ """Inputs for DataProcessorNode. -+ -+ data: JSON-encoded string payload to process. -+ operation: one of Operation (validated). -+ """ -+ data: str -+ operation: Operation -+ -+ class Outputs(BaseModel): -+ """Outputs emitted by DataProcessorNode. -+ -+ result: JSON-encoded string payload after processing. -+ status: 'success' or an error message. -+ """ -+ result: str -+ status: str -+ -+ async def execute(self) -> Outputs: -+ """Execute the node: parse input JSON and perform the requested operation.""" -+ # Parse the input data -+ try: -+ data = json.loads(self.inputs.data) -+ except json.JSONDecodeError as e: -+ # Log for observability and return specific, helpful feedback -+ logger.warning("Invalid JSON in DataProcessorNode.inputs.data", exc_info=True) -+ return self.Outputs( -+ result="", -+ status=f"error: invalid json (line {getattr(e, 'lineno', '?')}, column {getattr(e, 'colno', '?')})" -+ ) -+ -+ # Process based on validated operation -+ if self.inputs.operation is Operation.TRANSFORM: -+ result_obj = {"transformed": data, "processed": True} -+ else: # Operation.PASSTHROUGH -+ result_obj = {"original": data, "processed": False} -+ -+ # Outputs.result is defined as a string; return JSON-encoded result for consistency -+ return self.Outputs(result=json.dumps(result_obj), status="success") +```python +import json +import logging +from enum import Enum +from pydantic import BaseModel + +logger = logging.getLogger(__name__) + +class Operation(str, Enum): + """Supported operations the node can perform.""" + TRANSFORM = "transform" + PASSTHROUGH = "passthrough" + +class DataProcessorNode(BaseNode): + """Parse JSON input and apply a simple operation, returning a JSON string result.""" + + class Inputs(BaseModel): + """Inputs for DataProcessorNode. + + data: JSON-encoded string payload to process. + operation: one of Operation (validated). + """ + data: str + operation: Operation + + class Outputs(BaseModel): + """Outputs emitted by DataProcessorNode. + + result: JSON-encoded string payload after processing. + status: 'success' or an error message. + """ + result: str + status: str + + async def execute(self) -> Outputs: + """Execute the node: parse input JSON and perform the requested operation.""" + # Parse the input data + try: + data = json.loads(self.inputs.data) + except json.JSONDecodeError as e: + # Log for observability and return specific, helpful feedback + logger.warning("Invalid JSON in DataProcessorNode.inputs.data", exc_info=True) + return self.Outputs( + result="", + status=f"error: invalid json (line {getattr(e, 'lineno', '?')}, column {getattr(e, 'colno', '?')})" + ) + + # Process based on validated operation + if self.inputs.operation is Operation.TRANSFORM: + result_obj = {"transformed": data, "processed": True} + else: # Operation.PASSTHROUGH + result_obj = {"original": data, "processed": False} + + # Outputs.result is defined as a string; return JSON-encoded result for consistency + return self.Outputs(result=json.dumps(result_obj), status="success") From ae70981075b6c36db0bbe39a61b429bb9651d9ea Mon Sep 17 00:00:00 2001 From: Katharina Jacoby Date: Wed, 1 Oct 2025 00:27:23 +0200 Subject: [PATCH 5/8] add missing BaseNode import to index.md example --- docs/docs/exosphere/Index | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/exosphere/Index b/docs/docs/exosphere/Index index 3b8d68e6..8c11ffe5 100644 --- a/docs/docs/exosphere/Index +++ b/docs/docs/exosphere/Index @@ -3,6 +3,7 @@ import json import logging from enum import Enum from pydantic import BaseModel +from exosphere import BaseNode logger = logging.getLogger(__name__) From eaf5c0dfbb6491a31612a92dd665f1406d960e64 Mon Sep 17 00:00:00 2001 From: Katharina Jacoby Date: Wed, 1 Oct 2025 00:39:59 +0200 Subject: [PATCH 6/8] Update Index --- docs/docs/exosphere/Index | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/exosphere/Index b/docs/docs/exosphere/Index index 8c11ffe5..b668a50b 100644 --- a/docs/docs/exosphere/Index +++ b/docs/docs/exosphere/Index @@ -3,7 +3,7 @@ import json import logging from enum import Enum from pydantic import BaseModel -from exosphere import BaseNode +from exospherehost import BaseNode logger = logging.getLogger(__name__) From 7d6376c8b3543e1e5cda3237e7a8a38e1c51157e Mon Sep 17 00:00:00 2001 From: Katharina Jacoby Date: Wed, 1 Oct 2025 12:36:32 +0200 Subject: [PATCH 7/8] Update Index fixed TypeError --- docs/docs/exosphere/Index | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/docs/exosphere/Index b/docs/docs/exosphere/Index index b668a50b..ee3203f0 100644 --- a/docs/docs/exosphere/Index +++ b/docs/docs/exosphere/Index @@ -1,9 +1,11 @@ +# Exosphere Example: DataProcessorNode + ```python import json import logging from enum import Enum from pydantic import BaseModel -from exospherehost import BaseNode +from exosphere import BaseNode # required base class logger = logging.getLogger(__name__) @@ -53,4 +55,13 @@ class DataProcessorNode(BaseNode): result_obj = {"original": data, "processed": False} # Outputs.result is defined as a string; return JSON-encoded result for consistency - return self.Outputs(result=json.dumps(result_obj), status="success") + try: + result_json = json.dumps(result_obj) + except (TypeError, ValueError) as e: + logger.warning("Failed to serialize result", exc_info=True) + return self.Outputs( + result="", + status=f"error: serialization failed - {e}" + ) + + return self.Outputs(result=result_json, status="success") From d79c622b669b5a927b37ff7f1c4c13df942128e0 Mon Sep 17 00:00:00 2001 From: Katharina Jacoby Date: Thu, 2 Oct 2025 07:19:57 +0200 Subject: [PATCH 8/8] Update Index This example shows how to implement a `DataProcessorNode` that validates its inputs with Pydantic models, parses JSON safely, and applies basic operations using an Enum for type safety. Errors such as malformed JSON or failed serialization are caught and reported with clear messages, while successful operations return consistent, JSON-encoded outputs. --- docs/docs/exosphere/Index | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/exosphere/Index b/docs/docs/exosphere/Index index ee3203f0..31121c4c 100644 --- a/docs/docs/exosphere/Index +++ b/docs/docs/exosphere/Index @@ -5,7 +5,7 @@ import json import logging from enum import Enum from pydantic import BaseModel -from exosphere import BaseNode # required base class +from exospherehost import BaseNode logger = logging.getLogger(__name__)