From 96ee573a5b0e51a197810861fc24cf892e3e7ff3 Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Sat, 25 Jul 2020 23:34:25 -0400 Subject: [PATCH 01/19] #708 - improve pretty output format Unicode box characters, true JSON output, (slightly) more space between each error, etc. --- jsonschema/cli.py | 27 +++++++++++++++------------ jsonschema/exceptions.py | 5 +++-- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index abea97666..a7d6014ab 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -70,18 +70,21 @@ def validation_success(self, **kwargs): @attr.s class _PrettyFormatter(object): - _ERROR_MSG = dedent( - """\ - ===[{type}]===({path})=== + _WIDTH = 79 - {body} - ----------------------------- - """, - ) - _SUCCESS_MSG = "===[SUCCESS]===({path})===\n" + def _simple_msg(self, path, type, header=False): + begin_end_chars = '╒╕' if header is True else '══' + return '{}══[{}]═══({})'.format(begin_end_chars[0], type, path)\ + .ljust(self._WIDTH - 1, '═') + begin_end_chars[1] + + def _error_msg(self, path, type, body): + HEADER = self._simple_msg(path, type, header=True) + FOOTER = '└' + '─' * (self._WIDTH - 2) + '┘' + + return '\n'.join((HEADER, str(body), FOOTER, '\n')) def filenotfound_error(self, path, exc_info): - return self._ERROR_MSG.format( + return self._error_msg( path=path, type="FileNotFoundError", body="{!r} does not exist.".format(path), @@ -92,21 +95,21 @@ def parsing_error(self, path, exc_info): exc_lines = "".join( traceback.format_exception(exc_type, exc_value, exc_traceback), ) - return self._ERROR_MSG.format( + return self._error_msg( path=path, type=exc_type.__name__, body=exc_lines, ) def validation_error(self, instance_path, error): - return self._ERROR_MSG.format( + return self._error_msg( path=instance_path, type=error.__class__.__name__, body=error, ) def validation_success(self, instance_path): - return self._SUCCESS_MSG.format(path=instance_path) + return self._simple_msg(path=instance_path, type='SUCCESS') + '\n\n' @attr.s diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index deae87260..5ad84342d 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -1,6 +1,7 @@ """ Validation errors, and some surrounding helpers. """ +import json from collections import defaultdict, deque import itertools import pprint @@ -68,7 +69,7 @@ def __unicode__(self): if any(m is _unset for m in essential_for_verbose): return self.message - pschema = pprint.pformat(self.schema, width=72) + pschema = json.dumps(self.schema, separators=(',\n', ': '), sort_keys=True) pinstance = pprint.pformat(self.instance, width=72) return self.message + textwrap.dedent(""" @@ -200,7 +201,7 @@ def __init__(self, type, instance, schema): self.schema = schema def __unicode__(self): - pschema = pprint.pformat(self.schema, width=72) + pschema = json.dumps(self.schema, separators=(',\n', ': '), sort_keys=True) pinstance = pprint.pformat(self.instance, width=72) return textwrap.dedent(""" Unknown type %r for validator with schema: From 46a5828e2a8bceeee2318db6c234ebfe4a10f1f2 Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Sun, 26 Jul 2020 22:44:28 -0400 Subject: [PATCH 02/19] #708 - limit JSON formatting Only use JSON formatting for exception messages when explicitly requested. --- jsonschema/cli.py | 8 ++++---- jsonschema/exceptions.py | 13 ++++++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index a7d6014ab..d8b7d1f11 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -3,14 +3,14 @@ """ from __future__ import absolute_import -from textwrap import dedent + import argparse -import errno import json -import sys import traceback import attr +import errno +import sys from jsonschema import __version__ from jsonschema._reflect import namedAny @@ -105,7 +105,7 @@ def validation_error(self, instance_path, error): return self._error_msg( path=instance_path, type=error.__class__.__name__, - body=error, + body=error.formatted_message(pretty=True), ) def validation_success(self, instance_path): diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index 5ad84342d..27639bbe6 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -62,14 +62,18 @@ def __init__( def __repr__(self): return "<%s: %r>" % (self.__class__.__name__, self.message) - def __unicode__(self): + def formatted_message(self, pretty=False): essential_for_verbose = ( self.validator, self.validator_value, self.instance, self.schema, ) if any(m is _unset for m in essential_for_verbose): return self.message - pschema = json.dumps(self.schema, separators=(',\n', ': '), sort_keys=True) + if (pretty is True): + pschema = json.dumps(self.schema, separators=(',\n', ': '), sort_keys=True) + else: + pschema = pprint.pformat(self.schema, width=72) + pinstance = pprint.pformat(self.instance, width=72) return self.message + textwrap.dedent(""" @@ -89,6 +93,9 @@ def __unicode__(self): _utils.indent(pinstance), ) + def __unicode__(self): + return self.formatted_message(pretty=False) + if PY3: __str__ = __unicode__ else: @@ -201,7 +208,7 @@ def __init__(self, type, instance, schema): self.schema = schema def __unicode__(self): - pschema = json.dumps(self.schema, separators=(',\n', ': '), sort_keys=True) + pschema = pprint.pformat(self.schema, width=72) pinstance = pprint.pformat(self.instance, width=72) return textwrap.dedent(""" Unknown type %r for validator with schema: From 917f384e3272b20de3d2706de3558fddfc814d74 Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Tue, 28 Jul 2020 16:09:57 -0400 Subject: [PATCH 03/19] #708 - flexible exception formatter; test updates As clarified in PR, not all exception output needs to use valid JSON. Add a message formatter method to the exception class and pass in a formatter lambda/function when needed. Updated tests with the new expected output format. --- jsonschema/cli.py | 8 +++- jsonschema/exceptions.py | 8 ++-- jsonschema/tests/test_cli.py | 89 +++++++++++++++++++----------------- 3 files changed, 56 insertions(+), 49 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index d8b7d1f11..651d622d3 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -72,9 +72,13 @@ class _PrettyFormatter(object): _WIDTH = 79 + @classmethod + def _json_formatter(cls, x): + return json.dumps(x, separators=(',\n', ': '), sort_keys=True) + def _simple_msg(self, path, type, header=False): begin_end_chars = '╒╕' if header is True else '══' - return '{}══[{}]═══({})'.format(begin_end_chars[0], type, path)\ + return '{}══[{}]═══({})'.format(begin_end_chars[0], type, path) \ .ljust(self._WIDTH - 1, '═') + begin_end_chars[1] def _error_msg(self, path, type, body): @@ -105,7 +109,7 @@ def validation_error(self, instance_path, error): return self._error_msg( path=instance_path, type=error.__class__.__name__, - body=error.formatted_message(pretty=True), + body=error._formatted_message(formatter=self._json_formatter), ) def validation_success(self, instance_path): diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index 27639bbe6..a86aa901a 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -62,15 +62,15 @@ def __init__( def __repr__(self): return "<%s: %r>" % (self.__class__.__name__, self.message) - def formatted_message(self, pretty=False): + def _formatted_message(self, formatter=None): essential_for_verbose = ( self.validator, self.validator_value, self.instance, self.schema, ) if any(m is _unset for m in essential_for_verbose): return self.message - if (pretty is True): - pschema = json.dumps(self.schema, separators=(',\n', ': '), sort_keys=True) + if (callable(formatter)): + pschema = formatter(self.schema) else: pschema = pprint.pformat(self.schema, width=72) @@ -94,7 +94,7 @@ def formatted_message(self, pretty=False): ) def __unicode__(self): - return self.formatted_message(pretty=False) + return self._formatted_message() if PY3: __str__ = __unicode__ diff --git a/jsonschema/tests/test_cli.py b/jsonschema/tests/test_cli.py index c4affaf4f..877438b39 100644 --- a/jsonschema/tests/test_cli.py +++ b/jsonschema/tests/test_cli.py @@ -122,10 +122,10 @@ def test_invalid_instance_pretty_output(self): exit_code=1, stderr="""\ - ===[ValidationError]===(some_instance)=== - + ╒══[ValidationError]═══(some_instance)════════════════════════════════════════╕ I am an error! - ----------------------------- + └─────────────────────────────────────────────────────────────────────────────┘ + """, ), @@ -181,14 +181,14 @@ def test_invalid_instance_multiple_errors_pretty_output(self): exit_code=1, stderr="""\ - ===[ValidationError]===(some_instance)=== - + ╒══[ValidationError]═══(some_instance)════════════════════════════════════════╕ First error - ----------------------------- - ===[ValidationError]===(some_instance)=== + └─────────────────────────────────────────────────────────────────────────────┘ + ╒══[ValidationError]═══(some_instance)════════════════════════════════════════╕ Second error - ----------------------------- + └─────────────────────────────────────────────────────────────────────────────┘ + """, ) @@ -249,18 +249,18 @@ def test_multiple_invalid_instances_pretty_output(self): exit_code=1, stderr="""\ - ===[ValidationError]===(some_first_instance)=== - + ╒══[ValidationError]═══(some_first_instance)══════════════════════════════════╕ An error - ----------------------------- - ===[ValidationError]===(some_first_instance)=== + └─────────────────────────────────────────────────────────────────────────────┘ + ╒══[ValidationError]═══(some_first_instance)══════════════════════════════════╕ Another error - ----------------------------- - ===[ValidationError]===(some_second_instance)=== + └─────────────────────────────────────────────────────────────────────────────┘ + ╒══[ValidationError]═══(some_second_instance)═════════════════════════════════╕ BOOM - ----------------------------- + └─────────────────────────────────────────────────────────────────────────────┘ + """, ) @@ -308,7 +308,8 @@ def test_invalid_schema_pretty_output(self): with self.assertRaises(SchemaError) as e: validate(schema=schema, instance="") - error = str(e.exception) + error = str(e.exception._formatted_message(formatter=cli._PrettyFormatter._json_formatter)) + self.assertOutputs( files=dict(some_schema=json.dumps(schema)), @@ -316,9 +317,9 @@ def test_invalid_schema_pretty_output(self): exit_code=1, stderr=( - "===[SchemaError]===(some_schema)===\n\n" - + str(error) - + "\n-----------------------------\n" + "╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + + str(error) + + "\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" ), ) @@ -338,7 +339,8 @@ def test_invalid_schema_multiple_errors_pretty_output(self): with self.assertRaises(SchemaError) as e: validate(schema=schema, instance="") - error = str(e.exception) + error = str(e.exception._formatted_message(formatter=cli._PrettyFormatter._json_formatter)) + self.assertOutputs( files=dict(some_schema=json.dumps(schema)), @@ -346,9 +348,9 @@ def test_invalid_schema_multiple_errors_pretty_output(self): exit_code=1, stderr=( - "===[SchemaError]===(some_schema)===\n\n" - + str(error) - + "\n-----------------------------\n" + "╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + + str(error) + + "\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" ), ) @@ -375,7 +377,8 @@ def test_invalid_schema_with_invalid_instance_pretty_output(self): with self.assertRaises(SchemaError) as e: validate(schema=schema, instance=instance) - error = str(e.exception) + error = str(e.exception._formatted_message(formatter=cli._PrettyFormatter._json_formatter)) + self.assertOutputs( files=dict( @@ -386,9 +389,9 @@ def test_invalid_schema_with_invalid_instance_pretty_output(self): exit_code=1, stderr=( - "===[SchemaError]===(some_schema)===\n\n" - + str(error) - + "\n-----------------------------\n" + "╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + + str(error) + + "\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" ), ) @@ -456,7 +459,7 @@ def test_instance_is_invalid_JSON_pretty_output(self): ) self.assertFalse(stdout) self.assertIn( - "(some_instance)===\n\nTraceback (most recent call last):\n", + "(some_instance)════════════════════════════════════════╕\nTraceback (most recent call last):\n", stderr, ) self.assertNotIn("some_schema", stderr) @@ -487,7 +490,7 @@ def test_instance_is_invalid_JSON_on_stdin_pretty_output(self): ) self.assertFalse(stdout) self.assertIn( - "()===\n\nTraceback (most recent call last):\n", + "()══════════════════════════════════════════════╕\nTraceback (most recent call last):\n", stderr, ) self.assertNotIn("some_schema", stderr) @@ -516,7 +519,7 @@ def test_schema_is_invalid_JSON_pretty_output(self): ) self.assertFalse(stdout) self.assertIn( - "(some_schema)===\n\nTraceback (most recent call last):\n", + "(some_schema)══════════════════════════════════════════╕\nTraceback (most recent call last):\n", stderr, ) @@ -552,7 +555,7 @@ def test_schema_and_instance_are_both_invalid_JSON_pretty_output(self): ) self.assertFalse(stdout) self.assertIn( - "(some_schema)===\n\nTraceback (most recent call last):\n", + "(some_schema)══════════════════════════════════════════╕\nTraceback (most recent call last):\n", stderr, ) self.assertNotIn("some_instance", stderr) @@ -579,10 +582,10 @@ def test_instance_does_not_exist_pretty_output(self): exit_code=1, stderr="""\ - ===[FileNotFoundError]===(nonexisting_instance)=== - + ╒══[FileNotFoundError]═══(nonexisting_instance)═══════════════════════════════╕ 'nonexisting_instance' does not exist. - ----------------------------- + └─────────────────────────────────────────────────────────────────────────────┘ + """, ) @@ -600,10 +603,10 @@ def test_schema_does_not_exist_pretty_output(self): exit_code=1, stderr="""\ - ===[FileNotFoundError]===(nonexisting_schema)=== - + ╒══[FileNotFoundError]═══(nonexisting_schema)═════════════════════════════════╕ 'nonexisting_schema' does not exist. - ----------------------------- + └─────────────────────────────────────────────────────────────────────────────┘ + """, ) @@ -625,10 +628,10 @@ def test_neither_instance_nor_schema_exist_pretty_output(self): exit_code=1, stderr="""\ - ===[FileNotFoundError]===(nonexisting_schema)=== - + ╒══[FileNotFoundError]═══(nonexisting_schema)═════════════════════════════════╕ 'nonexisting_schema' does not exist. - ----------------------------- + └─────────────────────────────────────────────────────────────────────────────┘ + """, ) @@ -644,7 +647,7 @@ def test_successful_validation_pretty_output(self): self.assertOutputs( files=dict(some_schema="{}", some_instance="{}"), argv=["--output", "pretty", "-i", "some_instance", "some_schema"], - stdout="===[SUCCESS]===(some_instance)===\n", + stdout="═══[SUCCESS]═══(some_instance)═════════════════════════════════════════════════\n\n", stderr="", ) @@ -662,7 +665,7 @@ def test_successful_validation_of_stdin_pretty_output(self): files=dict(some_schema="{}"), stdin=NativeIO("{}"), argv=["--output", "pretty", "some_schema"], - stdout="===[SUCCESS]===()===\n", + stdout="═══[SUCCESS]═══()═══════════════════════════════════════════════════════\n\n", stderr="", ) @@ -678,7 +681,7 @@ def test_successful_validation__of_just_the_schema_pretty_output(self): self.assertOutputs( files=dict(some_schema="{}", some_instance="{}"), argv=["--output", "pretty", "-i", "some_instance", "some_schema"], - stdout="===[SUCCESS]===(some_instance)===\n", + stdout="═══[SUCCESS]═══(some_instance)═════════════════════════════════════════════════\n\n", stderr="", ) From bff4f005be6cd1ffd60346c4e50a7c8520bcf6aa Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Tue, 28 Jul 2020 16:40:20 -0400 Subject: [PATCH 04/19] #708 - satisfy flake8 style CI checks - attempt 1 --- jsonschema/cli.py | 2 +- jsonschema/exceptions.py | 1 - jsonschema/tests/test_cli.py | 12 ++++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index 651d622d3..90326f0d6 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -79,7 +79,7 @@ def _json_formatter(cls, x): def _simple_msg(self, path, type, header=False): begin_end_chars = '╒╕' if header is True else '══' return '{}══[{}]═══({})'.format(begin_end_chars[0], type, path) \ - .ljust(self._WIDTH - 1, '═') + begin_end_chars[1] + .ljust(self._WIDTH - 1, '═') + begin_end_chars[1] def _error_msg(self, path, type, body): HEADER = self._simple_msg(path, type, header=True) diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index a86aa901a..b42bc5cbd 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -1,7 +1,6 @@ """ Validation errors, and some surrounding helpers. """ -import json from collections import defaultdict, deque import itertools import pprint diff --git a/jsonschema/tests/test_cli.py b/jsonschema/tests/test_cli.py index 877438b39..e09ac5ad3 100644 --- a/jsonschema/tests/test_cli.py +++ b/jsonschema/tests/test_cli.py @@ -308,8 +308,8 @@ def test_invalid_schema_pretty_output(self): with self.assertRaises(SchemaError) as e: validate(schema=schema, instance="") - error = str(e.exception._formatted_message(formatter=cli._PrettyFormatter._json_formatter)) - + error = str(e.exception._formatted_message( + formatter=cli._PrettyFormatter._json_formatter)) self.assertOutputs( files=dict(some_schema=json.dumps(schema)), @@ -339,8 +339,8 @@ def test_invalid_schema_multiple_errors_pretty_output(self): with self.assertRaises(SchemaError) as e: validate(schema=schema, instance="") - error = str(e.exception._formatted_message(formatter=cli._PrettyFormatter._json_formatter)) - + error = str(e.exception._formatted_message( + formatter=cli._PrettyFormatter._json_formatter)) self.assertOutputs( files=dict(some_schema=json.dumps(schema)), @@ -377,8 +377,8 @@ def test_invalid_schema_with_invalid_instance_pretty_output(self): with self.assertRaises(SchemaError) as e: validate(schema=schema, instance=instance) - error = str(e.exception._formatted_message(formatter=cli._PrettyFormatter._json_formatter)) - + error = str(e.exception._formatted_message( + formatter=cli._PrettyFormatter._json_formatter)) self.assertOutputs( files=dict( From e0719d5e4dab9b47383442b0ff028090f56f93f8 Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Wed, 29 Jul 2020 08:50:58 -0400 Subject: [PATCH 05/19] #708 - satisfy Python2 unicode tests - attempt 1 --- jsonschema/cli.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index 90326f0d6..b63e74635 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -77,13 +77,13 @@ def _json_formatter(cls, x): return json.dumps(x, separators=(',\n', ': '), sort_keys=True) def _simple_msg(self, path, type, header=False): - begin_end_chars = '╒╕' if header is True else '══' - return '{}══[{}]═══({})'.format(begin_end_chars[0], type, path) \ - .ljust(self._WIDTH - 1, '═') + begin_end_chars[1] + begin_end_chars = u'╒╕' if header is True else u'══' + return u'{}══[{}]═══({})'.format(begin_end_chars[0], type, path) \ + .ljust(self._WIDTH - 1, u'═') + begin_end_chars[1] def _error_msg(self, path, type, body): HEADER = self._simple_msg(path, type, header=True) - FOOTER = '└' + '─' * (self._WIDTH - 2) + '┘' + FOOTER = u'└' + u'─' * (self._WIDTH - 2) + u'┘' return '\n'.join((HEADER, str(body), FOOTER, '\n')) From e4089a8a12fb55f195f8f5999e123b30685e8b50 Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Wed, 29 Jul 2020 09:03:43 -0400 Subject: [PATCH 06/19] #708 - satisfy Python2 unicode tests - attempt 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also update the tests! 🤦‍♂️ --- jsonschema/tests/test_cli.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/jsonschema/tests/test_cli.py b/jsonschema/tests/test_cli.py index e09ac5ad3..d27e92489 100644 --- a/jsonschema/tests/test_cli.py +++ b/jsonschema/tests/test_cli.py @@ -121,7 +121,7 @@ def test_invalid_instance_pretty_output(self): argv=["-i", "some_instance", "--output", "pretty", "some_schema"], exit_code=1, - stderr="""\ + stderr=u"""\ ╒══[ValidationError]═══(some_instance)════════════════════════════════════════╕ I am an error! └─────────────────────────────────────────────────────────────────────────────┘ @@ -180,7 +180,7 @@ def test_invalid_instance_multiple_errors_pretty_output(self): argv=["-i", "some_instance", "--output", "pretty", "some_schema"], exit_code=1, - stderr="""\ + stderr=u"""\ ╒══[ValidationError]═══(some_instance)════════════════════════════════════════╕ First error └─────────────────────────────────────────────────────────────────────────────┘ @@ -248,7 +248,7 @@ def test_multiple_invalid_instances_pretty_output(self): ], exit_code=1, - stderr="""\ + stderr=u"""\ ╒══[ValidationError]═══(some_first_instance)══════════════════════════════════╕ An error └─────────────────────────────────────────────────────────────────────────────┘ @@ -317,9 +317,9 @@ def test_invalid_schema_pretty_output(self): exit_code=1, stderr=( - "╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + u"╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + str(error) - + "\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" + + u"\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" ), ) @@ -348,9 +348,9 @@ def test_invalid_schema_multiple_errors_pretty_output(self): exit_code=1, stderr=( - "╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + u"╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + str(error) - + "\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" + + u"\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" ), ) @@ -389,9 +389,9 @@ def test_invalid_schema_with_invalid_instance_pretty_output(self): exit_code=1, stderr=( - "╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + u"╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + str(error) - + "\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" + + u"\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" ), ) @@ -581,7 +581,7 @@ def test_instance_does_not_exist_pretty_output(self): ], exit_code=1, - stderr="""\ + stderr=u"""\ ╒══[FileNotFoundError]═══(nonexisting_instance)═══════════════════════════════╕ 'nonexisting_instance' does not exist. └─────────────────────────────────────────────────────────────────────────────┘ @@ -602,7 +602,7 @@ def test_schema_does_not_exist_pretty_output(self): argv=["--output", "pretty", "nonexisting_schema"], exit_code=1, - stderr="""\ + stderr=u"""\ ╒══[FileNotFoundError]═══(nonexisting_schema)═════════════════════════════════╕ 'nonexisting_schema' does not exist. └─────────────────────────────────────────────────────────────────────────────┘ @@ -627,7 +627,7 @@ def test_neither_instance_nor_schema_exist_pretty_output(self): ], exit_code=1, - stderr="""\ + stderr=u"""\ ╒══[FileNotFoundError]═══(nonexisting_schema)═════════════════════════════════╕ 'nonexisting_schema' does not exist. └─────────────────────────────────────────────────────────────────────────────┘ From b16582d609f26535887d9768751266622fe9b4cd Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Wed, 29 Jul 2020 14:44:25 -0400 Subject: [PATCH 07/19] #708 - add "coding" line to resolve pypy2 problems --- jsonschema/cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index b63e74635..9904ec70f 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -1,3 +1,4 @@ +# -*- coding: UTF-8 -*- """ The ``jsonschema`` command line. """ From 39c6a470ec171039f024fc0dadc8068089d139d7 Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Wed, 29 Jul 2020 15:12:16 -0400 Subject: [PATCH 08/19] #708 - add "coding" to fix pypy2 test problems --- jsonschema/tests/test_cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jsonschema/tests/test_cli.py b/jsonschema/tests/test_cli.py index d27e92489..abfedaee8 100644 --- a/jsonschema/tests/test_cli.py +++ b/jsonschema/tests/test_cli.py @@ -1,3 +1,4 @@ +# -*- coding: UTF-8 -*- from textwrap import dedent from unittest import TestCase import errno From 62fd301b181422bd9361dee7a89fde24401e2766 Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Mon, 3 Aug 2020 12:04:57 -0400 Subject: [PATCH 09/19] #708 - removing Unicode marker These are not needed, as I expected. They may even contribute to the "buffer interface" errors. --- jsonschema/tests/test_cli.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/jsonschema/tests/test_cli.py b/jsonschema/tests/test_cli.py index abfedaee8..71408e87e 100644 --- a/jsonschema/tests/test_cli.py +++ b/jsonschema/tests/test_cli.py @@ -122,7 +122,7 @@ def test_invalid_instance_pretty_output(self): argv=["-i", "some_instance", "--output", "pretty", "some_schema"], exit_code=1, - stderr=u"""\ + stderr="""\ ╒══[ValidationError]═══(some_instance)════════════════════════════════════════╕ I am an error! └─────────────────────────────────────────────────────────────────────────────┘ @@ -181,7 +181,7 @@ def test_invalid_instance_multiple_errors_pretty_output(self): argv=["-i", "some_instance", "--output", "pretty", "some_schema"], exit_code=1, - stderr=u"""\ + stderr="""\ ╒══[ValidationError]═══(some_instance)════════════════════════════════════════╕ First error └─────────────────────────────────────────────────────────────────────────────┘ @@ -249,7 +249,7 @@ def test_multiple_invalid_instances_pretty_output(self): ], exit_code=1, - stderr=u"""\ + stderr="""\ ╒══[ValidationError]═══(some_first_instance)══════════════════════════════════╕ An error └─────────────────────────────────────────────────────────────────────────────┘ @@ -318,9 +318,9 @@ def test_invalid_schema_pretty_output(self): exit_code=1, stderr=( - u"╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + "╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + str(error) - + u"\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" + + "\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" ), ) @@ -349,9 +349,9 @@ def test_invalid_schema_multiple_errors_pretty_output(self): exit_code=1, stderr=( - u"╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + "╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + str(error) - + u"\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" + + "\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" ), ) @@ -390,9 +390,9 @@ def test_invalid_schema_with_invalid_instance_pretty_output(self): exit_code=1, stderr=( - u"╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + "╒══[SchemaError]═══(some_schema)══════════════════════════════════════════════╕\n" + str(error) - + u"\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" + + "\n└─────────────────────────────────────────────────────────────────────────────┘\n\n" ), ) @@ -582,7 +582,7 @@ def test_instance_does_not_exist_pretty_output(self): ], exit_code=1, - stderr=u"""\ + stderr="""\ ╒══[FileNotFoundError]═══(nonexisting_instance)═══════════════════════════════╕ 'nonexisting_instance' does not exist. └─────────────────────────────────────────────────────────────────────────────┘ @@ -603,7 +603,7 @@ def test_schema_does_not_exist_pretty_output(self): argv=["--output", "pretty", "nonexisting_schema"], exit_code=1, - stderr=u"""\ + stderr="""\ ╒══[FileNotFoundError]═══(nonexisting_schema)═════════════════════════════════╕ 'nonexisting_schema' does not exist. └─────────────────────────────────────────────────────────────────────────────┘ @@ -628,7 +628,7 @@ def test_neither_instance_nor_schema_exist_pretty_output(self): ], exit_code=1, - stderr=u"""\ + stderr="""\ ╒══[FileNotFoundError]═══(nonexisting_schema)═════════════════════════════════╕ 'nonexisting_schema' does not exist. └─────────────────────────────────────────────────────────────────────────────┘ From f584741b9e1c959f02fd125149a58d767fb14464 Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Mon, 3 Aug 2020 12:08:08 -0400 Subject: [PATCH 10/19] #708 - attempted fix for old Python interpreters --- jsonschema/cli.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index 9904ec70f..b1541b3dd 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -15,7 +15,7 @@ from jsonschema import __version__ from jsonschema._reflect import namedAny -from jsonschema.compat import JSONDecodeError +from jsonschema.compat import PY3, JSONDecodeError from jsonschema.exceptions import SchemaError from jsonschema.validators import validator_for @@ -72,19 +72,33 @@ def validation_success(self, **kwargs): class _PrettyFormatter(object): _WIDTH = 79 + _HEADER_LINE = '═' + _MESSAGE_FORMAT = '{}══[{}]═══({})' @classmethod def _json_formatter(cls, x): return json.dumps(x, separators=(',\n', ': '), sort_keys=True) - def _simple_msg(self, path, type, header=False): - begin_end_chars = u'╒╕' if header is True else u'══' - return u'{}══[{}]═══({})'.format(begin_end_chars[0], type, path) \ - .ljust(self._WIDTH - 1, u'═') + begin_end_chars[1] + def _simple_msg_v3(self, path, type, header=False): + begin_end_chars = ('╒', '╕') if header is True else ('═', '═') + return '{}══[{}]═══({})'.format(begin_end_chars[0], type, path) \ + .ljust(self._WIDTH - 1, '═') + begin_end_chars[1] + + def _simple_msg_v2(self, path, type, header=False): + begin_end_chars = ('╒', '╕') if header is True else ('═', '═') + + # printed length of the static charaters: left end, brackets, bar characters + format_length = 11 # TODO: calculate fixed chars printed length + desired_length = self._WIDTH - len(type) - len(path) - format_length + + return self._MESSAGE_FORMAT.format(begin_end_chars[0], type, path) + \ + self._HEADER_LINE * desired_length + begin_end_chars[1] + + _simple_msg = _simple_msg_v3 if len(_HEADER_LINE) == 1 else _simple_msg_v2 def _error_msg(self, path, type, body): HEADER = self._simple_msg(path, type, header=True) - FOOTER = u'└' + u'─' * (self._WIDTH - 2) + u'┘' + FOOTER = '└' + '─' * (self._WIDTH - 2) + '┘' return '\n'.join((HEADER, str(body), FOOTER, '\n')) From 55998e6bd569ff5f846ec8b5336ab83b4dca3dea Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Mon, 3 Aug 2020 16:03:17 -0400 Subject: [PATCH 11/19] #708 - varying length fix From one version of Python to another, the "type" that appears in brackets may be of differing lengths. (E.g., `JSONDecodeError` in Python 3, but `ValueError` in Python 2.) That would make the length of the Unicode bar different under each version of Python. When these tests were corrected to work under Python 2, they wouldn't work under Python 3 and vice versa. Therefore, it's simpler to break the single assertions into two shorter ones that use very little of the bar characters. --- jsonschema/tests/test_cli.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/jsonschema/tests/test_cli.py b/jsonschema/tests/test_cli.py index 71408e87e..502dc5468 100644 --- a/jsonschema/tests/test_cli.py +++ b/jsonschema/tests/test_cli.py @@ -460,7 +460,11 @@ def test_instance_is_invalid_JSON_pretty_output(self): ) self.assertFalse(stdout) self.assertIn( - "(some_instance)════════════════════════════════════════╕\nTraceback (most recent call last):\n", + "(some_instance)═", + stderr, + ) + self.assertIn( + "═╕\nTraceback (most recent call last):\n", stderr, ) self.assertNotIn("some_schema", stderr) @@ -491,7 +495,11 @@ def test_instance_is_invalid_JSON_on_stdin_pretty_output(self): ) self.assertFalse(stdout) self.assertIn( - "()══════════════════════════════════════════════╕\nTraceback (most recent call last):\n", + "()═", + stderr, + ) + self.assertIn( + "═╕\nTraceback (most recent call last):\n", stderr, ) self.assertNotIn("some_schema", stderr) @@ -520,7 +528,11 @@ def test_schema_is_invalid_JSON_pretty_output(self): ) self.assertFalse(stdout) self.assertIn( - "(some_schema)══════════════════════════════════════════╕\nTraceback (most recent call last):\n", + "(some_schema)═", + stderr, + ) + self.assertIn( + "═╕\nTraceback (most recent call last):\n", stderr, ) @@ -556,7 +568,11 @@ def test_schema_and_instance_are_both_invalid_JSON_pretty_output(self): ) self.assertFalse(stdout) self.assertIn( - "(some_schema)══════════════════════════════════════════╕\nTraceback (most recent call last):\n", + "(some_schema)═", + stderr, + ) + self.assertIn( + "═╕\nTraceback (most recent call last):\n", stderr, ) self.assertNotIn("some_instance", stderr) From f618b7f7a3129c0eb4e57c4c67d49d2aa4bffa67 Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Mon, 3 Aug 2020 16:06:36 -0400 Subject: [PATCH 12/19] #708 - conditional message code for old Python For Python interpreters that don't treat multibyte Unicode characters as single characters, add some code to manually calculate the number of bar characters required to reach the desired line length. This code is confined to a conditional block and may be removed when support for older interpreters is no longer needed. --- jsonschema/cli.py | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index b1541b3dd..399abae31 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -15,7 +15,7 @@ from jsonschema import __version__ from jsonschema._reflect import namedAny -from jsonschema.compat import PY3, JSONDecodeError +from jsonschema.compat import JSONDecodeError from jsonschema.exceptions import SchemaError from jsonschema.validators import validator_for @@ -70,35 +70,44 @@ def validation_success(self, **kwargs): @attr.s class _PrettyFormatter(object): - - _WIDTH = 79 - _HEADER_LINE = '═' + _MESSAGE_BAR_CHAR = '═' + _MESSAGE_CORNER_CHARS = ('╒', '╕') _MESSAGE_FORMAT = '{}══[{}]═══({})' + _MESSAGE_MAX_LENGTH = 79 @classmethod def _json_formatter(cls, x): return json.dumps(x, separators=(',\n', ': '), sort_keys=True) - def _simple_msg_v3(self, path, type, header=False): - begin_end_chars = ('╒', '╕') if header is True else ('═', '═') - return '{}══[{}]═══({})'.format(begin_end_chars[0], type, path) \ - .ljust(self._WIDTH - 1, '═') + begin_end_chars[1] + def _message_end_chars(self, header=False): + return self._MESSAGE_CORNER_CHARS if header is True else [self._MESSAGE_BAR_CHAR] * 2 + + def _message_line(self, path, type, header=False): + begin_char, end_char = self._message_end_chars(header) + return self._MESSAGE_FORMAT.format(begin_char, type, path) \ + .ljust(self._MESSAGE_MAX_LENGTH - 1, self._MESSAGE_BAR_CHAR) + end_char + + if len(_MESSAGE_BAR_CHAR) != 1: + # The code in this if-block is for Python interpreters that don't + # treat multibyte Unicode characters as single characters. + # E.g., some versions of Python 2.x. This block may be removed + # when support for those interpreters is no longer needed. + + _FORMAT_LENGTH = len( + _MESSAGE_FORMAT.replace(_MESSAGE_BAR_CHAR, '.').format('.', '', '')) + 1 - def _simple_msg_v2(self, path, type, header=False): - begin_end_chars = ('╒', '╕') if header is True else ('═', '═') + def _message_line(self, path, type, header=False): + begin_char, end_char = self._message_end_chars(header) - # printed length of the static charaters: left end, brackets, bar characters - format_length = 11 # TODO: calculate fixed chars printed length - desired_length = self._WIDTH - len(type) - len(path) - format_length + bar_length = self._MESSAGE_MAX_LENGTH - len(type) - len(path) - self._FORMAT_LENGTH - return self._MESSAGE_FORMAT.format(begin_end_chars[0], type, path) + \ - self._HEADER_LINE * desired_length + begin_end_chars[1] + return self._MESSAGE_FORMAT.format(begin_char, type, path) + \ + self._MESSAGE_BAR_CHAR * bar_length + end_char - _simple_msg = _simple_msg_v3 if len(_HEADER_LINE) == 1 else _simple_msg_v2 def _error_msg(self, path, type, body): - HEADER = self._simple_msg(path, type, header=True) - FOOTER = '└' + '─' * (self._WIDTH - 2) + '┘' + HEADER = self._message_line(path, type, header=True) + FOOTER = '└' + '─' * (self._MESSAGE_MAX_LENGTH - 2) + '┘' return '\n'.join((HEADER, str(body), FOOTER, '\n')) @@ -128,7 +137,7 @@ def validation_error(self, instance_path, error): ) def validation_success(self, instance_path): - return self._simple_msg(path=instance_path, type='SUCCESS') + '\n\n' + return self._message_line(path=instance_path, type='SUCCESS') + '\n\n' @attr.s From 7b8c8020ae110440a4dc9d7c5d9f5eddcf0a919e Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Tue, 4 Aug 2020 09:55:59 -0400 Subject: [PATCH 13/19] #708 - Resolve CI style issues With any luck, it will resolve the coverage issues, too. --- jsonschema/cli.py | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index 399abae31..bbe3d1db2 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -80,30 +80,46 @@ def _json_formatter(cls, x): return json.dumps(x, separators=(',\n', ': '), sort_keys=True) def _message_end_chars(self, header=False): - return self._MESSAGE_CORNER_CHARS if header is True else [self._MESSAGE_BAR_CHAR] * 2 + return self._MESSAGE_CORNER_CHARS if header is True \ + else [self._MESSAGE_BAR_CHAR] * 2 - def _message_line(self, path, type, header=False): + def _message_line_good_unicode(self, path, type, header=False): + """ + Message formatter for Python interpreters with good Unicode support. + + TODO: Rename method when support for old Python no longer needed. + """ begin_char, end_char = self._message_end_chars(header) return self._MESSAGE_FORMAT.format(begin_char, type, path) \ - .ljust(self._MESSAGE_MAX_LENGTH - 1, self._MESSAGE_BAR_CHAR) + end_char + .ljust(self._MESSAGE_MAX_LENGTH - 1, self._MESSAGE_BAR_CHAR) + \ + end_char - if len(_MESSAGE_BAR_CHAR) != 1: - # The code in this if-block is for Python interpreters that don't - # treat multibyte Unicode characters as single characters. - # E.g., some versions of Python 2.x. This block may be removed - # when support for those interpreters is no longer needed. + def _message_line_poor_unicode(self, path, type, header=False): + """ + Message formatter for Python interpreters with poor Unicode support. - _FORMAT_LENGTH = len( - _MESSAGE_FORMAT.replace(_MESSAGE_BAR_CHAR, '.').format('.', '', '')) + 1 + TODO: Remove method when support for old Python no longer needed. + """ + begin_char, end_char = self._message_end_chars(header) - def _message_line(self, path, type, header=False): - begin_char, end_char = self._message_end_chars(header) + bar_length = self._MESSAGE_MAX_LENGTH - self._FORMAT_LENGTH - \ + len(type) - len(path) - bar_length = self._MESSAGE_MAX_LENGTH - len(type) - len(path) - self._FORMAT_LENGTH + return self._MESSAGE_FORMAT.format(begin_char, type, path) + \ + self._MESSAGE_BAR_CHAR * bar_length + end_char - return self._MESSAGE_FORMAT.format(begin_char, type, path) + \ - self._MESSAGE_BAR_CHAR * bar_length + end_char + if len(_MESSAGE_BAR_CHAR) != 1: + # The code in this if-block is for Python interpreters that don't + # treat multibyte Unicode characters as single characters. + # E.g., most versions of Python 2.x. + # TODO: Remove if-block when support for old Python no longer needed. + _message_line = _message_line_poor_unicode + _FORMAT_LENGTH = len( + _MESSAGE_FORMAT.replace(_MESSAGE_BAR_CHAR, '.') + .format('.', '', '')) + 1 + else: + _message_line = _message_line_good_unicode def _error_msg(self, path, type, body): HEADER = self._message_line(path, type, header=True) From fb33196bcb7cecb1be4d17dbbb9ec7c6e4c0209d Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Tue, 4 Aug 2020 15:39:30 -0400 Subject: [PATCH 14/19] #708 - code review response; Py2 support removal --- jsonschema/cli.py | 53 +++++++--------------------------------- jsonschema/exceptions.py | 17 ++++++------- 2 files changed, 16 insertions(+), 54 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index 8e6878ec9..d46bd8c8f 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -68,62 +68,27 @@ def validation_success(self, **kwargs): @attr.s class _PrettyFormatter(object): - _MESSAGE_BAR_CHAR = '═' - _MESSAGE_CORNER_CHARS = ('╒', '╕') - _MESSAGE_FORMAT = '{}══[{}]═══({})' + _MESSAGE_BAR_CHAR = "═" + _MESSAGE_CORNER_CHARS = "╒", "╕" + _MESSAGE_FORMAT = "{}══[{}]═══({})" _MESSAGE_MAX_LENGTH = 79 @classmethod def _json_formatter(cls, x): - return json.dumps(x, separators=(',\n', ': '), sort_keys=True) + return json.dumps(x, indent=4, sort_keys=True) - def _message_end_chars(self, header=False): - return self._MESSAGE_CORNER_CHARS if header is True \ + def _message_line(self, path, type, header=False): + begin_char, end_char = self._MESSAGE_CORNER_CHARS if header \ else [self._MESSAGE_BAR_CHAR] * 2 - - def _message_line_good_unicode(self, path, type, header=False): - """ - Message formatter for Python interpreters with good Unicode support. - - TODO: Rename method when support for old Python no longer needed. - """ - begin_char, end_char = self._message_end_chars(header) return self._MESSAGE_FORMAT.format(begin_char, type, path) \ .ljust(self._MESSAGE_MAX_LENGTH - 1, self._MESSAGE_BAR_CHAR) + \ end_char - def _message_line_poor_unicode(self, path, type, header=False): - """ - Message formatter for Python interpreters with poor Unicode support. - - TODO: Remove method when support for old Python no longer needed. - """ - begin_char, end_char = self._message_end_chars(header) - - bar_length = self._MESSAGE_MAX_LENGTH - self._FORMAT_LENGTH - \ - len(type) - len(path) - - return self._MESSAGE_FORMAT.format(begin_char, type, path) + \ - self._MESSAGE_BAR_CHAR * bar_length + end_char - - if len(_MESSAGE_BAR_CHAR) != 1: - # The code in this if-block is for Python interpreters that don't - # treat multibyte Unicode characters as single characters. - # E.g., most versions of Python 2.x. - # TODO: Remove if-block when support for old Python no longer needed. - - _message_line = _message_line_poor_unicode - _FORMAT_LENGTH = len( - _MESSAGE_FORMAT.replace(_MESSAGE_BAR_CHAR, '.') - .format('.', '', '')) + 1 - else: - _message_line = _message_line_good_unicode - def _error_msg(self, path, type, body): HEADER = self._message_line(path, type, header=True) - FOOTER = '└' + '─' * (self._MESSAGE_MAX_LENGTH - 2) + '┘' + FOOTER = "└" + "─" * (self._MESSAGE_MAX_LENGTH - 2) + "┘" - return '\n'.join((HEADER, str(body), FOOTER, '\n')) + return "\n".join((HEADER, body, FOOTER, "\n")) def filenotfound_error(self, path, exc_info): return self._error_msg( @@ -151,7 +116,7 @@ def validation_error(self, instance_path, error): ) def validation_success(self, instance_path): - return self._message_line(path=instance_path, type='SUCCESS') + '\n\n' + return self._message_line(path=instance_path, type="SUCCESS") + "\n\n" @attr.s diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index b42bc5cbd..81d7eefbc 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -3,15 +3,15 @@ """ from collections import defaultdict, deque import itertools -import pprint import textwrap +from functools import partial +from pprint import pformat import attr from jsonschema import _utils from jsonschema.compat import PY3, iteritems - WEAK_MATCHES = frozenset(["anyOf", "oneOf"]) STRONG_MATCHES = frozenset() @@ -61,19 +61,16 @@ def __init__( def __repr__(self): return "<%s: %r>" % (self.__class__.__name__, self.message) - def _formatted_message(self, formatter=None): + def _formatted_message(self, formatter=partial(pformat, width=72)): essential_for_verbose = ( self.validator, self.validator_value, self.instance, self.schema, ) if any(m is _unset for m in essential_for_verbose): return self.message - if (callable(formatter)): - pschema = formatter(self.schema) - else: - pschema = pprint.pformat(self.schema, width=72) + pschema = formatter(self.schema) - pinstance = pprint.pformat(self.instance, width=72) + pinstance = pformat(self.instance, width=72) return self.message + textwrap.dedent(""" Failed validating %r in %s%s: @@ -207,8 +204,8 @@ def __init__(self, type, instance, schema): self.schema = schema def __unicode__(self): - pschema = pprint.pformat(self.schema, width=72) - pinstance = pprint.pformat(self.instance, width=72) + pschema = pformat(self.schema, width=72) + pinstance = pformat(self.instance, width=72) return textwrap.dedent(""" Unknown type %r for validator with schema: %s From e64acd7e707db74bb8119c2d87fe4439221c713c Mon Sep 17 00:00:00 2001 From: "Lance E Sloan (sloanlance)" Date: Tue, 4 Aug 2020 19:54:23 -0400 Subject: [PATCH 15/19] #708 - move _json_formatter from _PrettyFormatter As per code review discussion. --- jsonschema/cli.py | 10 +++++----- jsonschema/tests/test_cli.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index d46bd8c8f..be95ce65b 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -66,6 +66,10 @@ def validation_success(self, **kwargs): self._stdout.write(self._formatter.validation_success(**kwargs)) +def _json_formatter(x): + return json.dumps(x, indent=4, sort_keys=True) + + @attr.s class _PrettyFormatter(object): _MESSAGE_BAR_CHAR = "═" @@ -73,10 +77,6 @@ class _PrettyFormatter(object): _MESSAGE_FORMAT = "{}══[{}]═══({})" _MESSAGE_MAX_LENGTH = 79 - @classmethod - def _json_formatter(cls, x): - return json.dumps(x, indent=4, sort_keys=True) - def _message_line(self, path, type, header=False): begin_char, end_char = self._MESSAGE_CORNER_CHARS if header \ else [self._MESSAGE_BAR_CHAR] * 2 @@ -112,7 +112,7 @@ def validation_error(self, instance_path, error): return self._error_msg( path=instance_path, type=error.__class__.__name__, - body=error._formatted_message(formatter=self._json_formatter), + body=error._formatted_message(formatter=_json_formatter), ) def validation_success(self, instance_path): diff --git a/jsonschema/tests/test_cli.py b/jsonschema/tests/test_cli.py index 502dc5468..38587fd6a 100644 --- a/jsonschema/tests/test_cli.py +++ b/jsonschema/tests/test_cli.py @@ -310,7 +310,7 @@ def test_invalid_schema_pretty_output(self): with self.assertRaises(SchemaError) as e: validate(schema=schema, instance="") error = str(e.exception._formatted_message( - formatter=cli._PrettyFormatter._json_formatter)) + formatter=cli._json_formatter)) self.assertOutputs( files=dict(some_schema=json.dumps(schema)), @@ -341,7 +341,7 @@ def test_invalid_schema_multiple_errors_pretty_output(self): with self.assertRaises(SchemaError) as e: validate(schema=schema, instance="") error = str(e.exception._formatted_message( - formatter=cli._PrettyFormatter._json_formatter)) + formatter=cli._json_formatter)) self.assertOutputs( files=dict(some_schema=json.dumps(schema)), @@ -379,7 +379,7 @@ def test_invalid_schema_with_invalid_instance_pretty_output(self): with self.assertRaises(SchemaError) as e: validate(schema=schema, instance=instance) error = str(e.exception._formatted_message( - formatter=cli._PrettyFormatter._json_formatter)) + formatter=cli._json_formatter)) self.assertOutputs( files=dict( From 86d8a790b0df1997146db3a1829adde6d1e64124 Mon Sep 17 00:00:00 2001 From: Julian Berman Date: Sat, 29 Aug 2020 16:43:04 -0400 Subject: [PATCH 16/19] Fix imports. --- jsonschema/cli.py | 2 +- jsonschema/exceptions.py | 4 ++-- jsonschema/tests/test_cli.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index bf8f102ba..9d0ce2ac5 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -3,12 +3,12 @@ The ``jsonschema`` command line. """ +from json import JSONDecodeError import argparse import errno import json import sys import traceback -from json import JSONDecodeError import attr diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index 1dd83affc..3e5634e2d 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -1,11 +1,11 @@ """ Validation errors, and some surrounding helpers. """ -import itertools -import textwrap from collections import defaultdict, deque from functools import partial from pprint import pformat +import itertools +import textwrap import attr diff --git a/jsonschema/tests/test_cli.py b/jsonschema/tests/test_cli.py index e01073302..cd7b7ab15 100644 --- a/jsonschema/tests/test_cli.py +++ b/jsonschema/tests/test_cli.py @@ -1,13 +1,13 @@ # -*- coding: UTF-8 -*- +from io import StringIO +from json import JSONDecodeError +from textwrap import dedent +from unittest import TestCase import errno import json import os import subprocess import sys -from io import StringIO -from json import JSONDecodeError -from textwrap import dedent -from unittest import TestCase from jsonschema import Draft4Validator, Draft7Validator, __version__, cli from jsonschema.exceptions import SchemaError, ValidationError From 6b53a7b08adb888a50066b561f3cc24a87a45ba5 Mon Sep 17 00:00:00 2001 From: Julian Berman Date: Sat, 29 Aug 2020 17:15:25 -0400 Subject: [PATCH 17/19] Remove the Py2 coding declarations. --- jsonschema/cli.py | 1 - jsonschema/tests/test_cli.py | 1 - 2 files changed, 2 deletions(-) diff --git a/jsonschema/cli.py b/jsonschema/cli.py index 9d0ce2ac5..94b24cd90 100644 --- a/jsonschema/cli.py +++ b/jsonschema/cli.py @@ -1,4 +1,3 @@ -# -*- coding: UTF-8 -*- """ The ``jsonschema`` command line. """ diff --git a/jsonschema/tests/test_cli.py b/jsonschema/tests/test_cli.py index cd7b7ab15..4bdbc84ba 100644 --- a/jsonschema/tests/test_cli.py +++ b/jsonschema/tests/test_cli.py @@ -1,4 +1,3 @@ -# -*- coding: UTF-8 -*- from io import StringIO from json import JSONDecodeError from textwrap import dedent From dd620888702c9286b57b0e79d1eb59f22dddae78 Mon Sep 17 00:00:00 2001 From: Julian Berman Date: Sat, 29 Aug 2020 17:17:50 -0400 Subject: [PATCH 18/19] Run pre-commit in CI. --- .github/workflows/pre-commit.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/pre-commit.yml diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 000000000..306e1add4 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,13 @@ +name: pre-commit + +on: + pull_request: + push: + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - uses: pre-commit/action@v2.0.0 From 5185d44adc4bcab3a607184a9b88e3c6fc090fc0 Mon Sep 17 00:00:00 2001 From: Lance E Sloan Date: Tue, 1 Sep 2020 09:37:07 -0400 Subject: [PATCH 19/19] Update jsonschema/tests/test_cli.py Co-authored-by: Julian Berman --- jsonschema/tests/test_cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jsonschema/tests/test_cli.py b/jsonschema/tests/test_cli.py index 4bdbc84ba..ec318bd9c 100644 --- a/jsonschema/tests/test_cli.py +++ b/jsonschema/tests/test_cli.py @@ -309,7 +309,9 @@ def test_invalid_schema_pretty_output(self): with self.assertRaises(SchemaError) as e: validate(schema=schema, instance="") - error = str(e.exception._formatted_message( +str( + e.exception._formatted_message(formatter=cli._json_formatter), +)``` formatter=cli._json_formatter)) self.assertOutputs(