codekingpro/portable-devtools
115k
1from contextlib import redirect_stderr, redirect_stdout2from importlib import metadata3from io import StringIO4from json import JSONDecodeError5from pathlib import Path6from textwrap import dedent7from unittest import TestCase8import json9import os10import subprocess11import sys12import tempfile13import warnings14 15from jsonschema import Draft4Validator, Draft202012Validator16from jsonschema.exceptions import (17 SchemaError,18 ValidationError,19 _RefResolutionError,20)21from jsonschema.validators import _LATEST_VERSION, validate22 23with warnings.catch_warnings():24 warnings.simplefilter("ignore")25 from jsonschema import cli26 27 28def fake_validator(*errors):29 errors = list(reversed(errors))30 31 class FakeValidator:32 def __init__(self, *args, **kwargs):33 pass34 35 def iter_errors(self, instance):36 if errors:37 return errors.pop()38 return [] # pragma: no cover39 40 @classmethod41 def check_schema(self, schema):42 pass43 44 return FakeValidator45 46 47def fake_open(all_contents):48 def open(path):49 contents = all_contents.get(path)50 if contents is None:51 raise FileNotFoundError(path)52 return StringIO(contents)53 return open54 55 56def _message_for(non_json):57 try:58 json.loads(non_json)59 except JSONDecodeError as error:60 return str(error)61 else: # pragma: no cover62 raise RuntimeError("Tried and failed to capture a JSON dump error.")63 64 65class TestCLI(TestCase):66 def run_cli(67 self, argv, files=None, stdin=StringIO(), exit_code=0, **override,68 ):69 arguments = cli.parse_args(argv)70 arguments.update(override)71 72 self.assertFalse(hasattr(cli, "open"))73 cli.open = fake_open(files or {})74 try:75 stdout, stderr = StringIO(), StringIO()76 actual_exit_code = cli.run(77 arguments,78 stdin=stdin,79 stdout=stdout,80 stderr=stderr,81 )82 finally:83 del cli.open84 85 self.assertEqual(86 actual_exit_code, exit_code, msg=dedent(87 f"""88 Expected an exit code of {exit_code} != {actual_exit_code}.89 90 stdout: {stdout.getvalue()}91 92 stderr: {stderr.getvalue()}93 """,94 ),95 )96 return stdout.getvalue(), stderr.getvalue()97 98 def assertOutputs(self, stdout="", stderr="", **kwargs):99 self.assertEqual(100 self.run_cli(**kwargs),101 (dedent(stdout), dedent(stderr)),102 )103 104 def test_invalid_instance(self):105 error = ValidationError("I am an error!", instance=12)106 self.assertOutputs(107 files=dict(108 some_schema='{"does not": "matter since it is stubbed"}',109 some_instance=json.dumps(error.instance),110 ),111 validator=fake_validator([error]),112 113 argv=["-i", "some_instance", "some_schema"],114 115 exit_code=1,116 stderr="12: I am an error!\n",117 )118 119 def test_invalid_instance_pretty_output(self):120 error = ValidationError("I am an error!", instance=12)121 self.assertOutputs(122 files=dict(123 some_schema='{"does not": "matter since it is stubbed"}',124 some_instance=json.dumps(error.instance),125 ),126 validator=fake_validator([error]),127 128 argv=["-i", "some_instance", "--output", "pretty", "some_schema"],129 130 exit_code=1,131 stderr="""\132 ===[ValidationError]===(some_instance)===133 134 I am an error!135 -----------------------------136 """,137 )138 139 def test_invalid_instance_explicit_plain_output(self):140 error = ValidationError("I am an error!", instance=12)141 self.assertOutputs(142 files=dict(143 some_schema='{"does not": "matter since it is stubbed"}',144 some_instance=json.dumps(error.instance),145 ),146 validator=fake_validator([error]),147 148 argv=["--output", "plain", "-i", "some_instance", "some_schema"],149 150 exit_code=1,151 stderr="12: I am an error!\n",152 )153 154 def test_invalid_instance_multiple_errors(self):155 instance = 12156 first = ValidationError("First error", instance=instance)157 second = ValidationError("Second error", instance=instance)158 159 self.assertOutputs(160 files=dict(161 some_schema='{"does not": "matter since it is stubbed"}',162 some_instance=json.dumps(instance),163 ),164 validator=fake_validator([first, second]),165 166 argv=["-i", "some_instance", "some_schema"],167 168 exit_code=1,169 stderr="""\170 12: First error171 12: Second error172 """,173 )174 175 def test_invalid_instance_multiple_errors_pretty_output(self):176 instance = 12177 first = ValidationError("First error", instance=instance)178 second = ValidationError("Second error", instance=instance)179 180 self.assertOutputs(181 files=dict(182 some_schema='{"does not": "matter since it is stubbed"}',183 some_instance=json.dumps(instance),184 ),185 validator=fake_validator([first, second]),186 187 argv=["-i", "some_instance", "--output", "pretty", "some_schema"],188 189 exit_code=1,190 stderr="""\191 ===[ValidationError]===(some_instance)===192 193 First error194 -----------------------------195 ===[ValidationError]===(some_instance)===196 197 Second error198 -----------------------------199 """,200 )201 202 def test_multiple_invalid_instances(self):203 first_instance = 12204 first_errors = [205 ValidationError("An error", instance=first_instance),206 ValidationError("Another error", instance=first_instance),207 ]208 second_instance = "foo"209 second_errors = [ValidationError("BOOM", instance=second_instance)]210 211 self.assertOutputs(212 files=dict(213 some_schema='{"does not": "matter since it is stubbed"}',214 some_first_instance=json.dumps(first_instance),215 some_second_instance=json.dumps(second_instance),216 ),217 validator=fake_validator(first_errors, second_errors),218 219 argv=[220 "-i", "some_first_instance",221 "-i", "some_second_instance",222 "some_schema",223 ],224 225 exit_code=1,226 stderr="""\227 12: An error228 12: Another error229 foo: BOOM230 """,231 )232 233 def test_multiple_invalid_instances_pretty_output(self):234 first_instance = 12235 first_errors = [236 ValidationError("An error", instance=first_instance),237 ValidationError("Another error", instance=first_instance),238 ]239 second_instance = "foo"240 second_errors = [ValidationError("BOOM", instance=second_instance)]241 242 self.assertOutputs(243 files=dict(244 some_schema='{"does not": "matter since it is stubbed"}',245 some_first_instance=json.dumps(first_instance),246 some_second_instance=json.dumps(second_instance),247 ),248 validator=fake_validator(first_errors, second_errors),249 250 argv=[251 "--output", "pretty",252 "-i", "some_first_instance",253 "-i", "some_second_instance",254 "some_schema",255 ],256 257 exit_code=1,258 stderr="""\259 ===[ValidationError]===(some_first_instance)===260 261 An error262 -----------------------------263 ===[ValidationError]===(some_first_instance)===264 265 Another error266 -----------------------------267 ===[ValidationError]===(some_second_instance)===268 269 BOOM270 -----------------------------271 """,272 )273 274 def test_custom_error_format(self):275 first_instance = 12276 first_errors = [277 ValidationError("An error", instance=first_instance),278 ValidationError("Another error", instance=first_instance),279 ]280 second_instance = "foo"281 second_errors = [ValidationError("BOOM", instance=second_instance)]282 283 self.assertOutputs(284 files=dict(285 some_schema='{"does not": "matter since it is stubbed"}',286 some_first_instance=json.dumps(first_instance),287 some_second_instance=json.dumps(second_instance),288 ),289 validator=fake_validator(first_errors, second_errors),290 291 argv=[292 "--error-format", ":{error.message}._-_.{error.instance}:",293 "-i", "some_first_instance",294 "-i", "some_second_instance",295 "some_schema",296 ],297 298 exit_code=1,299 stderr=":An error._-_.12::Another error._-_.12::BOOM._-_.foo:",300 )301 302 def test_invalid_schema(self):303 self.assertOutputs(304 files=dict(some_schema='{"type": 12}'),305 argv=["some_schema"],306 307 exit_code=1,308 stderr="""\309 12: 12 is not valid under any of the given schemas310 """,311 )312 313 def test_invalid_schema_pretty_output(self):314 schema = {"type": 12}315 316 with self.assertRaises(SchemaError) as e:317 validate(schema=schema, instance="")318 error = str(e.exception)319 320 self.assertOutputs(321 files=dict(some_schema=json.dumps(schema)),322 argv=["--output", "pretty", "some_schema"],323 324 exit_code=1,325 stderr=(326 "===[SchemaError]===(some_schema)===\n\n"327 + str(error)328 + "\n-----------------------------\n"329 ),330 )331 332 def test_invalid_schema_multiple_errors(self):333 self.assertOutputs(334 files=dict(some_schema='{"type": 12, "items": 57}'),335 argv=["some_schema"],336 337 exit_code=1,338 stderr="""\339 57: 57 is not of type 'object', 'boolean'340 """,341 )342 343 def test_invalid_schema_multiple_errors_pretty_output(self):344 schema = {"type": 12, "items": 57}345 346 with self.assertRaises(SchemaError) as e:347 validate(schema=schema, instance="")348 error = str(e.exception)349 350 self.assertOutputs(351 files=dict(some_schema=json.dumps(schema)),352 argv=["--output", "pretty", "some_schema"],353 354 exit_code=1,355 stderr=(356 "===[SchemaError]===(some_schema)===\n\n"357 + str(error)358 + "\n-----------------------------\n"359 ),360 )361 362 def test_invalid_schema_with_invalid_instance(self):363 """364 "Validating" an instance that's invalid under an invalid schema365 just shows the schema error.366 """367 self.assertOutputs(368 files=dict(369 some_schema='{"type": 12, "minimum": 30}',370 some_instance="13",371 ),372 argv=["-i", "some_instance", "some_schema"],373 374 exit_code=1,375 stderr="""\376 12: 12 is not valid under any of the given schemas377 """,378 )379 380 def test_invalid_schema_with_invalid_instance_pretty_output(self):381 instance, schema = 13, {"type": 12, "minimum": 30}382 383 with self.assertRaises(SchemaError) as e:384 validate(schema=schema, instance=instance)385 error = str(e.exception)386 387 self.assertOutputs(388 files=dict(389 some_schema=json.dumps(schema),390 some_instance=json.dumps(instance),391 ),392 argv=["--output", "pretty", "-i", "some_instance", "some_schema"],393 394 exit_code=1,395 stderr=(396 "===[SchemaError]===(some_schema)===\n\n"397 + str(error)398 + "\n-----------------------------\n"399 ),400 )401 402 def test_invalid_instance_continues_with_the_rest(self):403 self.assertOutputs(404 files=dict(405 some_schema='{"minimum": 30}',406 first_instance="not valid JSON!",407 second_instance="12",408 ),409 argv=[410 "-i", "first_instance",411 "-i", "second_instance",412 "some_schema",413 ],414 415 exit_code=1,416 stderr="""\417 Failed to parse 'first_instance': {}418 12: 12 is less than the minimum of 30419 """.format(_message_for("not valid JSON!")),420 )421 422 def test_custom_error_format_applies_to_schema_errors(self):423 instance, schema = 13, {"type": 12, "minimum": 30}424 425 with self.assertRaises(SchemaError):426 validate(schema=schema, instance=instance)427 428 self.assertOutputs(429 files=dict(some_schema=json.dumps(schema)),430 431 argv=[432 "--error-format", ":{error.message}._-_.{error.instance}:",433 "some_schema",434 ],435 436 exit_code=1,437 stderr=":12 is not valid under any of the given schemas._-_.12:",438 )439 440 def test_instance_is_invalid_JSON(self):441 instance = "not valid JSON!"442 443 self.assertOutputs(444 files=dict(some_schema="{}", some_instance=instance),445 argv=["-i", "some_instance", "some_schema"],446 447 exit_code=1,448 stderr=f"""\449 Failed to parse 'some_instance': {_message_for(instance)}450 """,451 )452 453 def test_instance_is_invalid_JSON_pretty_output(self):454 stdout, stderr = self.run_cli(455 files=dict(456 some_schema="{}",457 some_instance="not valid JSON!",458 ),459 460 argv=["--output", "pretty", "-i", "some_instance", "some_schema"],461 462 exit_code=1,463 )464 self.assertFalse(stdout)465 self.assertIn(466 "(some_instance)===\n\nTraceback (most recent call last):\n",467 stderr,468 )469 self.assertNotIn("some_schema", stderr)470 471 def test_instance_is_invalid_JSON_on_stdin(self):472 instance = "not valid JSON!"473 474 self.assertOutputs(475 files=dict(some_schema="{}"),476 stdin=StringIO(instance),477 478 argv=["some_schema"],479 480 exit_code=1,481 stderr=f"""\482 Failed to parse <stdin>: {_message_for(instance)}483 """,484 )485 486 def test_instance_is_invalid_JSON_on_stdin_pretty_output(self):487 stdout, stderr = self.run_cli(488 files=dict(some_schema="{}"),489 stdin=StringIO("not valid JSON!"),490 491 argv=["--output", "pretty", "some_schema"],492 493 exit_code=1,494 )495 self.assertFalse(stdout)496 self.assertIn(497 "(<stdin>)===\n\nTraceback (most recent call last):\n",498 stderr,499 )500 self.assertNotIn("some_schema", stderr)501 502 def test_schema_is_invalid_JSON(self):503 schema = "not valid JSON!"504 505 self.assertOutputs(506 files=dict(some_schema=schema),507 508 argv=["some_schema"],509 510 exit_code=1,511 stderr=f"""\512 Failed to parse 'some_schema': {_message_for(schema)}513 """,514 )515 516 def test_schema_is_invalid_JSON_pretty_output(self):517 stdout, stderr = self.run_cli(518 files=dict(some_schema="not valid JSON!"),519 520 argv=["--output", "pretty", "some_schema"],521 522 exit_code=1,523 )524 self.assertFalse(stdout)525 self.assertIn(526 "(some_schema)===\n\nTraceback (most recent call last):\n",527 stderr,528 )529 530 def test_schema_and_instance_are_both_invalid_JSON(self):531 """532 Only the schema error is reported, as we abort immediately.533 """534 schema, instance = "not valid JSON!", "also not valid JSON!"535 self.assertOutputs(536 files=dict(some_schema=schema, some_instance=instance),537 538 argv=["some_schema"],539 540 exit_code=1,541 stderr=f"""\542 Failed to parse 'some_schema': {_message_for(schema)}543 """,544 )545 546 def test_schema_and_instance_are_both_invalid_JSON_pretty_output(self):547 """548 Only the schema error is reported, as we abort immediately.549 """550 stdout, stderr = self.run_cli(551 files=dict(552 some_schema="not valid JSON!",553 some_instance="also not valid JSON!",554 ),555 556 argv=["--output", "pretty", "-i", "some_instance", "some_schema"],557 558 exit_code=1,559 )560 self.assertFalse(stdout)561 self.assertIn(562 "(some_schema)===\n\nTraceback (most recent call last):\n",563 stderr,564 )565 self.assertNotIn("some_instance", stderr)566 567 def test_instance_does_not_exist(self):568 self.assertOutputs(569 files=dict(some_schema="{}"),570 argv=["-i", "nonexisting_instance", "some_schema"],571 572 exit_code=1,573 stderr="""\574 'nonexisting_instance' does not exist.575 """,576 )577 578 def test_instance_does_not_exist_pretty_output(self):579 self.assertOutputs(580 files=dict(some_schema="{}"),581 argv=[582 "--output", "pretty",583 "-i", "nonexisting_instance",584 "some_schema",585 ],586 587 exit_code=1,588 stderr="""\589 ===[FileNotFoundError]===(nonexisting_instance)===590 591 'nonexisting_instance' does not exist.592 -----------------------------593 """,594 )595 596 def test_schema_does_not_exist(self):597 self.assertOutputs(598 argv=["nonexisting_schema"],599 600 exit_code=1,601 stderr="'nonexisting_schema' does not exist.\n",602 )603 604 def test_schema_does_not_exist_pretty_output(self):605 self.assertOutputs(606 argv=["--output", "pretty", "nonexisting_schema"],607 608 exit_code=1,609 stderr="""\610 ===[FileNotFoundError]===(nonexisting_schema)===611 612 'nonexisting_schema' does not exist.613 -----------------------------614 """,615 )616 617 def test_neither_instance_nor_schema_exist(self):618 self.assertOutputs(619 argv=["-i", "nonexisting_instance", "nonexisting_schema"],620 621 exit_code=1,622 stderr="'nonexisting_schema' does not exist.\n",623 )624 625 def test_neither_instance_nor_schema_exist_pretty_output(self):626 self.assertOutputs(627 argv=[628 "--output", "pretty",629 "-i", "nonexisting_instance",630 "nonexisting_schema",631 ],632 633 exit_code=1,634 stderr="""\635 ===[FileNotFoundError]===(nonexisting_schema)===636 637 'nonexisting_schema' does not exist.638 -----------------------------639 """,640 )641 642 def test_successful_validation(self):643 self.assertOutputs(644 files=dict(some_schema="{}", some_instance="{}"),645 argv=["-i", "some_instance", "some_schema"],646 stdout="",647 stderr="",648 )649 650 def test_successful_validation_pretty_output(self):651 self.assertOutputs(652 files=dict(some_schema="{}", some_instance="{}"),653 argv=["--output", "pretty", "-i", "some_instance", "some_schema"],654 stdout="===[SUCCESS]===(some_instance)===\n",655 stderr="",656 )657 658 def test_successful_validation_of_stdin(self):659 self.assertOutputs(660 files=dict(some_schema="{}"),661 stdin=StringIO("{}"),662 argv=["some_schema"],663 stdout="",664 stderr="",665 )666 667 def test_successful_validation_of_stdin_pretty_output(self):668 self.assertOutputs(669 files=dict(some_schema="{}"),670 stdin=StringIO("{}"),671 argv=["--output", "pretty", "some_schema"],672 stdout="===[SUCCESS]===(<stdin>)===\n",673 stderr="",674 )675 676 def test_successful_validation_of_just_the_schema(self):677 self.assertOutputs(678 files=dict(some_schema="{}", some_instance="{}"),679 argv=["-i", "some_instance", "some_schema"],680 stdout="",681 stderr="",682 )683 684 def test_successful_validation_of_just_the_schema_pretty_output(self):685 self.assertOutputs(686 files=dict(some_schema="{}", some_instance="{}"),687 argv=["--output", "pretty", "-i", "some_instance", "some_schema"],688 stdout="===[SUCCESS]===(some_instance)===\n",689 stderr="",690 )691 692 def test_successful_validation_via_explicit_base_uri(self):693 ref_schema_file = tempfile.NamedTemporaryFile(delete=False) # noqa: SIM115694 ref_schema_file.close()695 self.addCleanup(os.remove, ref_schema_file.name)696 697 ref_path = Path(ref_schema_file.name)698 ref_path.write_text('{"definitions": {"num": {"type": "integer"}}}')699 700 schema = f'{{"$ref": "{ref_path.name}#/definitions/num"}}'701 702 self.assertOutputs(703 files=dict(some_schema=schema, some_instance="1"),704 argv=[705 "-i", "some_instance",706 "--base-uri", ref_path.parent.as_uri() + "/",707 "some_schema",708 ],709 stdout="",710 stderr="",711 )712 713 def test_unsuccessful_validation_via_explicit_base_uri(self):714 ref_schema_file = tempfile.NamedTemporaryFile(delete=False) # noqa: SIM115715 ref_schema_file.close()716 self.addCleanup(os.remove, ref_schema_file.name)717 718 ref_path = Path(ref_schema_file.name)719 ref_path.write_text('{"definitions": {"num": {"type": "integer"}}}')720 721 schema = f'{{"$ref": "{ref_path.name}#/definitions/num"}}'722 723 self.assertOutputs(724 files=dict(some_schema=schema, some_instance='"1"'),725 argv=[726 "-i", "some_instance",727 "--base-uri", ref_path.parent.as_uri() + "/",728 "some_schema",729 ],730 exit_code=1,731 stdout="",732 stderr="1: '1' is not of type 'integer'\n",733 )734 735 def test_nonexistent_file_with_explicit_base_uri(self):736 schema = '{"$ref": "someNonexistentFile.json#definitions/num"}'737 instance = "1"738 739 with self.assertRaises(_RefResolutionError) as e:740 self.assertOutputs(741 files=dict(742 some_schema=schema,743 some_instance=instance,744 ),745 argv=[746 "-i", "some_instance",747 "--base-uri", Path.cwd().as_uri(),748 "some_schema",749 ],750 )751 error = str(e.exception)752 self.assertIn(f"{os.sep}someNonexistentFile.json'", error)753 754 def test_invalid_explicit_base_uri(self):755 schema = '{"$ref": "foo.json#definitions/num"}'756 instance = "1"757 758 with self.assertRaises(_RefResolutionError) as e:759 self.assertOutputs(760 files=dict(761 some_schema=schema,762 some_instance=instance,763 ),764 argv=[765 "-i", "some_instance",766 "--base-uri", "not@UR1",767 "some_schema",768 ],769 )770 error = str(e.exception)771 self.assertEqual(772 error, "unknown url type: 'foo.json'",773 )774 775 def test_it_validates_using_the_latest_validator_when_unspecified(self):776 # There isn't a better way now I can think of to ensure that the777 # latest version was used, given that the call to validator_for778 # is hidden inside the CLI, so guard that that's the case, and779 # this test will have to be updated when versions change until780 # we can think of a better way to ensure this behavior.781 self.assertIs(Draft202012Validator, _LATEST_VERSION)782 783 self.assertOutputs(784 files=dict(some_schema='{"const": "check"}', some_instance='"a"'),785 argv=["-i", "some_instance", "some_schema"],786 exit_code=1,787 stdout="",788 stderr="a: 'check' was expected\n",789 )790 791 def test_it_validates_using_draft7_when_specified(self):792 """793 Specifically, `const` validation applies for Draft 7.794 """795 schema = """796 {797 "$schema": "http://json-schema.org/draft-07/schema#",798 "const": "check"799 }800 """801 instance = '"foo"'802 self.assertOutputs(803 files=dict(some_schema=schema, some_instance=instance),804 argv=["-i", "some_instance", "some_schema"],805 exit_code=1,806 stdout="",807 stderr="foo: 'check' was expected\n",808 )809 810 def test_it_validates_using_draft4_when_specified(self):811 """812 Specifically, `const` validation *does not* apply for Draft 4.813 """814 schema = """815 {816 "$schema": "http://json-schema.org/draft-04/schema#",817 "const": "check"818 }819 """820 instance = '"foo"'821 self.assertOutputs(822 files=dict(some_schema=schema, some_instance=instance),823 argv=["-i", "some_instance", "some_schema"],824 stdout="",825 stderr="",826 )827 828 829class TestParser(TestCase):830 831 FakeValidator = fake_validator()832 833 def test_find_validator_by_fully_qualified_object_name(self):834 arguments = cli.parse_args(835 [836 "--validator",837 "jsonschema.tests.test_cli.TestParser.FakeValidator",838 "--instance", "mem://some/instance",839 "mem://some/schema",840 ],841 )842 self.assertIs(arguments["validator"], self.FakeValidator)843 844 def test_find_validator_in_jsonschema(self):845 arguments = cli.parse_args(846 [847 "--validator", "Draft4Validator",848 "--instance", "mem://some/instance",849 "mem://some/schema",850 ],851 )852 self.assertIs(arguments["validator"], Draft4Validator)853 854 def cli_output_for(self, *argv):855 stdout, stderr = StringIO(), StringIO()856 with redirect_stdout(stdout), redirect_stderr(stderr): # noqa: SIM117857 with self.assertRaises(SystemExit):858 cli.parse_args(argv)859 return stdout.getvalue(), stderr.getvalue()860 861 def test_unknown_output(self):862 stdout, stderr = self.cli_output_for(863 "--output", "foo",864 "mem://some/schema",865 )866 self.assertIn("invalid choice: 'foo'", stderr)867 self.assertFalse(stdout)868 869 def test_useless_error_format(self):870 stdout, stderr = self.cli_output_for(871 "--output", "pretty",872 "--error-format", "foo",873 "mem://some/schema",874 )875 self.assertIn(876 "--error-format can only be used with --output plain",877 stderr,878 )879 self.assertFalse(stdout)880 881 882class TestCLIIntegration(TestCase):883 def test_license(self):884 our_metadata = metadata.metadata("jsonschema")885 self.assertEqual(our_metadata.get("License-Expression"), "MIT")886 887 def test_version(self):888 version = subprocess.check_output(889 [sys.executable, "-W", "ignore", "-m", "jsonschema", "--version"],890 stderr=subprocess.STDOUT,891 )892 version = version.decode("utf-8").strip()893 self.assertEqual(version, metadata.version("jsonschema"))894 895 def test_no_arguments_shows_usage_notes(self):896 output = subprocess.check_output(897 [sys.executable, "-m", "jsonschema"],898 stderr=subprocess.STDOUT,899 )900 output_for_help = subprocess.check_output(901 [sys.executable, "-m", "jsonschema", "--help"],902 stderr=subprocess.STDOUT,903 )904 self.assertEqual(output, output_for_help)905 