Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: handle deserialization errors #72

Merged
merged 2 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion jsondiff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import json
import yaml

from json import JSONDecodeError
from yaml import YAMLError

from .symbols import *
from .symbols import Symbol

Expand Down Expand Up @@ -55,6 +58,10 @@ def __init__(self, **kwargs):
self.kwargs = kwargs

def __call__(self, src):
"""Parse and return JSON data
:param src: str|file-like source
:return: dict parsed data
"""
if isinstance(src, string_types):
return json.loads(src, **self.kwargs)
else:
Expand All @@ -74,6 +81,47 @@ def __call__(self, src):
"""
return yaml.safe_load(src)

class Serializer:
"""Serializer helper loads and stores object data
:param file_format: str json or yaml
:param indent: int Output indentation in spaces
:raise ValueError: file_path does not contains valid file_format data
"""

def __init__(self, file_format, indent):
# pyyaml _can_ load json but is ~20 times slower and has known issues so use
# the json from stdlib when json is specified.
self.serializers = {
"json": (JsonLoader(), JsonDumper(indent=indent)),
"yaml": (YamlLoader(), YamlDumper(indent=indent)),
}
self.file_format = file_format
if file_format not in self.serializers:
raise ValueError(f"Unsupported serialization format {file_format}, expected one of {self.serializers.keys()}")

def deserialize_file(self, src):
"""Deserialize file from the specified format
:param file_path: str path to file
:param src: str|file-like source
:return dict
:raise ValueError: file_path does not contain valid file_format data
"""
loader, _ = self.serializers[self.file_format]
try:
parsed = loader(src)
except (JSONDecodeError, YAMLError) as ex:
raise ValueError(f"Invalid {self.file_format} file") from ex
return parsed

def serialize_data(self, obj, stream):
"""Serialize obj and write to stream
:param obj: dict to serialize
:param stream: Writeable stream
"""
_, dumper = self.serializers[self.file_format]
dumper(obj, stream)


class JsonDiffSyntax(object):
def emit_set_diff(self, a, b, s, added, removed):
raise NotImplementedError()
Expand Down Expand Up @@ -667,5 +715,6 @@ def similarity(a, b, cls=JsonDiffer, **kwargs):
"JsonDumper",
"JsonLoader",
"YamlDumper",
"YamlLoader"
"YamlLoader",
"Serializer",
]
66 changes: 37 additions & 29 deletions jsondiff/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
import jsondiff
import sys

def load_file(serializer, file_path):
with open(file_path, "r") as f:
parsed = None
try:
parsed = serializer.deserialize_file(f)
except ValueError:
print(f"{file_path} is not valid {serializer.file_format}")
except FileNotFoundError:
print(f"{file_path} does not exist")
return parsed

def main():
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
Expand All @@ -18,35 +28,33 @@ def main():

args = parser.parse_args()

# pyyaml _can_ load json but is ~20 times slower and has known issues so use
# the json from stdlib when json is specified.
serializers = {
"json": (jsondiff.JsonLoader(), jsondiff.JsonDumper(indent=args.indent)),
"yaml": (jsondiff.YamlLoader(), jsondiff.YamlDumper(indent=args.indent)),
}
loader, dumper = serializers[args.format]

with open(args.first, "r") as f:
with open(args.second, "r") as g:
jf = loader(f)
jg = loader(g)
if args.patch:
x = jsondiff.patch(
jf,
jg,
marshal=True,
syntax=args.syntax
)
else:
x = jsondiff.diff(
jf,
jg,
marshal=True,
syntax=args.syntax
)

dumper(x, sys.stdout)
serializer = jsondiff.Serializer(args.format, args.indent)

parsed_first = load_file(serializer, args.first)
parsed_second = load_file(serializer, args.second)

if not (parsed_first and parsed_second):
return 1

if args.patch:
x = jsondiff.patch(
parsed_first,
parsed_second,
marshal=True,
syntax=args.syntax
)
else:
x = jsondiff.diff(
parsed_first,
parsed_second,
marshal=True,
syntax=args.syntax
)

serializer.serialize_data(x, sys.stdout)

return 0

if __name__ == '__main__':
main()
ret = main()
sys.exit(ret)