Skip to content

Commit 4ac8967

Browse files
author
jkleint
committed
CLI option --filter: validate multiple instances from stdin.
Reads JSON instances one to a line from stdin, outputs those that validate in compact form (one to a line). Print errors to stderr for those that do not validate.
1 parent 1a3203b commit 4ac8967

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

jsonschema/cli.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import argparse
33
import json
44
import sys
5+
from itertools import imap
56

67
from jsonschema._reflect import namedAny
78
from jsonschema.validators import validator_for
@@ -21,14 +22,21 @@ def _json_file(path):
2122
parser = argparse.ArgumentParser(
2223
description="JSON Schema Validation CLI",
2324
)
24-
parser.add_argument(
25+
input_group = parser.add_mutually_exclusive_group()
26+
input_group.add_argument(
2527
"-i", "--instance",
2628
action="append",
2729
dest="instances",
2830
type=_json_file,
2931
help="a path to a JSON instance to validate "
3032
"(may be specified multiple times)",
3133
)
34+
input_group.add_argument(
35+
"-f", "--filter",
36+
action="store_true",
37+
help="read instances one to a line from stdin, output those that validate "
38+
"in compact form and log to stderr those that do not."
39+
)
3240
parser.add_argument(
3341
"-F", "--error-format",
3442
default="{error.instance}: {error.message}\n",
@@ -43,6 +51,7 @@ def _json_file(path):
4351
"validators that are registered with jsonschema, simply the name "
4452
"of the class.",
4553
)
54+
4655
parser.add_argument(
4756
"schema",
4857
help="the JSON Schema to validate with",
@@ -61,12 +70,18 @@ def main(args=sys.argv[1:]):
6170
sys.exit(run(arguments=parse_args(args=args)))
6271

6372

64-
def run(arguments, stdout=sys.stdout, stderr=sys.stderr):
73+
def run(arguments, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr):
6574
error_format = arguments["error_format"]
6675
validator = arguments["validator"](schema=arguments["schema"])
6776
errored = False
68-
for instance in arguments["instances"] or ():
69-
for error in validator.iter_errors(instance):
70-
stderr.write(error_format.format(error=error))
77+
instances = imap(json.loads, stdin) if arguments.get("filter") else (arguments.get("instances") or ())
78+
for instance in instances:
79+
errors = tuple(validator.iter_errors(instance))
80+
if errors:
7181
errored = True
82+
for error in errors:
83+
stderr.write(error_format.format(error=error))
84+
elif arguments.get("filter"):
85+
json.dump(instance, stdout, separators=(',', ':'))
86+
stdout.write('\n')
7287
return errored

jsonschema/tests/test_cli.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import json
2+
13
from jsonschema import Draft4Validator, ValidationError, cli
24
from jsonschema.compat import StringIO
35
from jsonschema.tests.compat import mock, unittest
@@ -17,6 +19,11 @@ def iter_errors(self, instance):
1719
return FakeValidator
1820

1921

22+
def serialize_instances(*instances):
23+
""":Return: a string with `instances` serialized to JSON, one per line."""
24+
return '\n'.join(json.dumps(instance, separators=(',', ':')) for instance in instances) + '\n'
25+
26+
2027
class TestParser(unittest.TestCase):
2128
FakeValidator = fake_validator()
2229

@@ -108,3 +115,31 @@ def test_unsuccessful_validation_multiple_instances(self):
108115
self.assertFalse(stdout.getvalue())
109116
self.assertEqual(stderr.getvalue(), "1 - 9\t1 - 8\t2 - 7\t")
110117
self.assertEqual(exit_code, 1)
118+
119+
def test_filter_valid(self):
120+
serialized = serialize_instances(1, [2], {3: 4})
121+
stdin, stdout, stderr = StringIO(serialized), StringIO(), StringIO()
122+
exit_code = cli.run({
123+
"validator": fake_validator(),
124+
"schema": {},
125+
"error_format": "{error.message}",
126+
"filter": True,
127+
}, stdin=stdin, stdout=stdout, stderr=stderr)
128+
self.assertFalse(stderr.getvalue())
129+
self.assertEquals(stdout.getvalue(), serialized)
130+
self.assertEqual(exit_code, 0)
131+
132+
def test_filter_invalid(self):
133+
schema = {"type": "object"}
134+
serialized_in = serialize_instances({}, [2], {3: 4})
135+
serialized_out = serialize_instances({}, {3: 4})
136+
stdin, stdout, stderr = StringIO(serialized_in), StringIO(), StringIO()
137+
exit_code = cli.run({
138+
"validator": Draft4Validator,
139+
"schema": schema,
140+
"error_format": "{error.message}",
141+
"filter": True,
142+
}, stdin=stdin, stdout=stdout, stderr=stderr)
143+
self.assertEquals(stderr.getvalue(), "[2] is not of type 'object'")
144+
self.assertEquals(stdout.getvalue(), serialized_out)
145+
self.assertEqual(exit_code, 1)

0 commit comments

Comments
 (0)