Skip to content

Commit 01e7653

Browse files
committed
Addresses #6:
- Specifying UTF-8 during JSON load allows the parser to handle line encodings. - More detailed error handling to make JSON laoding issues clearing. (done by @dvanselow)
1 parent 3f5311f commit 01e7653

File tree

2 files changed

+53
-20
lines changed

2 files changed

+53
-20
lines changed

python/ouroboros/cli.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from multiprocessing import freeze_support
2-
32
import argparse
3+
import sys
44

55
from ouroboros.common.pipelines import backproject_pipeline, slice_pipeline
66
from ouroboros.helpers.models import pretty_json_output
@@ -73,14 +73,20 @@ def main():
7373

7474

7575
def handle_slice(args):
76+
print(f"Loading slice options from: {args.options}")
7677
slice_options = SliceOptions.load_from_json(args.options)
7778

79+
if slice_options is None:
80+
print("Exiting due to errors loading slice options.", file=sys.stderr)
81+
sys.exit(1)
82+
83+
print("Slice options loaded successfully.")
7884
pipeline, input_data = slice_pipeline(slice_options, True)
7985

8086
_, error = pipeline.process(input_data)
8187

8288
if error:
83-
print(error)
89+
print(f"Pipeline Error: {error}", file=sys.stderr)
8490

8591
if args.verbose:
8692
print("\nCalculation Statistics:\n")
@@ -90,16 +96,30 @@ def handle_slice(args):
9096

9197

9298
def handle_backproject(args):
99+
print(f"Loading backproject options from: {args.options}")
93100
backproject_options = BackprojectOptions.load_from_json(args.options)
94101

102+
if backproject_options is None:
103+
print("Exiting due to errors loading backproject options.", file=sys.stderr)
104+
sys.exit(1)
105+
106+
print("Backproject options loaded successfully."
107+
f"Loading slice options from: {backproject_options.slice_options_path}")
108+
95109
slice_options = SliceOptions.load_from_json(backproject_options.slice_options_path)
96-
110+
111+
if slice_options is None:
112+
print("Exiting due to errors loading slice options specified within backproject options"
113+
f"({backproject_options.slice_options_path}).", file=sys.stderr)
114+
sys.exit(1)
115+
116+
print("Slice options loaded successfully.")
97117
pipeline, input_data = backproject_pipeline(backproject_options, slice_options, True)
98118

99119
_, error = pipeline.process(input_data)
100120

101121
if error:
102-
print(error)
122+
print(f"Pipeline Error: {error}", file=sys.stderr)
103123

104124
if args.verbose:
105125
print("\nCalculation Statistics:\n")

python/ouroboros/helpers/models.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
from pydantic import BaseModel, ValidationError
3+
import sys
34

45

56
def pretty_json_output(obj: object) -> str:
@@ -37,28 +38,40 @@ def model_with_json(cls):
3738

3839

3940
def save_to_json(self: BaseModel, json_path: str):
40-
with open(json_path, "w") as f:
41-
f.write(self.to_json())
41+
# Also specify encoding here for consistency
42+
with open(json_path, "w", encoding='utf-8') as f:
43+
# Add indent here for consistency? Or handle in Pydantic model
44+
f.write(self.to_json(indent=4))
4245

4346

4447
@classmethod
45-
def load_from_json(cls: BaseModel, json_path: str):
48+
def load_from_json(cls: type[BaseModel], json_path: str) -> BaseModel | None:
49+
"""Loads a Pydantic model from a JSON file.
50+
51+
Args:
52+
cls: The Pydantic model class.
53+
json_path: Path to the JSON file.
54+
55+
Returns:
56+
An instance of the model, or None if loading fails.
57+
"""
4658
try:
47-
with open(json_path, "r") as f:
48-
result = cls.from_json(f.read())
59+
# Explicitly use utf-8 encoding
60+
with open(json_path, "r", encoding='utf-8') as f:
61+
# Use model_validate_json directly for better error context from Pydantic
62+
result = cls.model_validate_json(f.read())
4963
return result
50-
except ValidationError as e:
51-
err = f"Error loading {cls.__name__} from JSON: {e}"
52-
print(err)
53-
return err
5464
except FileNotFoundError:
55-
err = f"File not found at {json_path}"
56-
print(err)
57-
return err
58-
except BaseException:
59-
err = f"File at {json_path} is not a valid JSON file"
60-
print(err)
61-
return err
65+
print(f"Error: File not found at {json_path}", file=sys.stderr)
66+
return None
67+
except (ValidationError, json.JSONDecodeError) as e:
68+
# Catch specific Pydantic validation errors and JSON syntax errors
69+
print(f"Error loading {cls.__name__} from JSON file '{json_path}':\n{e}", file=sys.stderr)
70+
return None
71+
except Exception as e:
72+
# Catch other potential errors like permission denied, unicode issues etc.
73+
print(f"Error reading or parsing file '{json_path}': {e}", file=sys.stderr)
74+
return None
6275

6376

6477
def copy_values_from_other(self: BaseModel, other: BaseModel):

0 commit comments

Comments
 (0)