Skip to content

Commit da535ae

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 da535ae

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

jsonschema/cli.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,21 @@ def _json_file(path):
2121
parser = argparse.ArgumentParser(
2222
description="JSON Schema Validation CLI",
2323
)
24-
parser.add_argument(
24+
input_group = parser.add_mutually_exclusive_group()
25+
input_group.add_argument(
2526
"-i", "--instance",
2627
action="append",
2728
dest="instances",
2829
type=_json_file,
2930
help="a path to a JSON instance to validate "
3031
"(may be specified multiple times)",
3132
)
33+
input_group.add_argument(
34+
"-f", "--filter",
35+
action="store_true",
36+
help="read instances one to a line from stdin, output those that validate "
37+
"in compact form and log to stderr those that do not."
38+
)
3239
parser.add_argument(
3340
"-F", "--error-format",
3441
default="{error.instance}: {error.message}\n",
@@ -43,6 +50,7 @@ def _json_file(path):
4350
"validators that are registered with jsonschema, simply the name "
4451
"of the class.",
4552
)
53+
4654
parser.add_argument(
4755
"schema",
4856
help="the JSON Schema to validate with",
@@ -61,12 +69,18 @@ def main(args=sys.argv[1:]):
6169
sys.exit(run(arguments=parse_args(args=args)))
6270

6371

64-
def run(arguments, stdout=sys.stdout, stderr=sys.stderr):
72+
def run(arguments, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr):
6573
error_format = arguments["error_format"]
6674
validator = arguments["validator"](schema=arguments["schema"])
6775
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))
76+
instances = (json.loads(line) for line in stdin) if arguments.get("filter") else (arguments.get("instances") or ())
77+
for instance in instances:
78+
errors = tuple(validator.iter_errors(instance))
79+
if errors:
7180
errored = True
81+
for error in errors:
82+
stderr.write(error_format.format(error=error))
83+
elif arguments.get("filter"):
84+
json.dump(instance, stdout, separators=(',', ':'))
85+
stdout.write('\n')
7286
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)