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

[DRAFT] #708 - Make pretty formatter output prettier #712

Closed
Closed
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
96ee573
#708 - improve pretty output format
sloanlance Jul 26, 2020
d3c2c03
Merge branch 'master' of https://github.com/Julian/jsonschema into 70…
sloanlance Jul 26, 2020
46a5828
#708 - limit JSON formatting
sloanlance Jul 27, 2020
917f384
#708 - flexible exception formatter; test updates
sloanlance Jul 28, 2020
bff4f00
#708 - satisfy flake8 style CI checks - attempt 1
sloanlance Jul 28, 2020
e0719d5
#708 - satisfy Python2 unicode tests - attempt 1
sloanlance Jul 29, 2020
e4089a8
#708 - satisfy Python2 unicode tests - attempt 2
sloanlance Jul 29, 2020
b16582d
#708 - add "coding" line to resolve pypy2 problems
sloanlance Jul 29, 2020
39c6a47
#708 - add "coding" to fix pypy2 test problems
sloanlance Jul 29, 2020
62fd301
#708 - removing Unicode marker
sloanlance Aug 3, 2020
f584741
#708 - attempted fix for old Python interpreters
sloanlance Aug 3, 2020
55998e6
#708 - varying length fix
sloanlance Aug 3, 2020
f618b7f
#708 - conditional message code for old Python
sloanlance Aug 3, 2020
7b8c802
#708 - Resolve CI style issues
sloanlance Aug 4, 2020
ef9c2b8
Merge branch 'master' of https://github.com/Julian/jsonschema into 70…
sloanlance Aug 4, 2020
fb33196
#708 - code review response; Py2 support removal
sloanlance Aug 4, 2020
e64acd7
#708 - move _json_formatter from _PrettyFormatter
sloanlance Aug 4, 2020
6ce8ae8
Merge upstream master into 708-prettier-pretty
sloanlance Aug 7, 2020
14e1e09
Merge remote-tracking branch 'origin/master' into 708-prettier-pretty
Julian Aug 29, 2020
86d8a79
Fix imports.
Julian Aug 29, 2020
6b53a7b
Remove the Py2 coding declarations.
Julian Aug 29, 2020
dd62088
Run pre-commit in CI.
Julian Aug 29, 2020
5185d44
Update jsonschema/tests/test_cli.py
sloanlance Sep 1, 2020
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
41 changes: 26 additions & 15 deletions jsonschema/cli.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# -*- coding: UTF-8 -*-
"""
The ``jsonschema`` command line.
"""

from json import JSONDecodeError
from textwrap import dedent
import argparse
import errno
import json
import sys
import traceback
from json import JSONDecodeError
sloanlance marked this conversation as resolved.
Show resolved Hide resolved

import attr

Expand Down Expand Up @@ -66,21 +66,32 @@ 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 = "═"
_MESSAGE_CORNER_CHARS = "╒", "╕"
_MESSAGE_FORMAT = "{}══[{}]═══({})"
_MESSAGE_MAX_LENGTH = 79

_ERROR_MSG = dedent(
"""\
===[{type}]===({path})===
def _message_line(self, path, type, header=False):
begin_char, end_char = self._MESSAGE_CORNER_CHARS if header \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor style: jsonschema follows PEP8 / generally avoids using backslashes for continuation. Can you switch these to use parentheses instead?

Though, I think this will be a lot clearer if we split it into _header_line and _non_header_line -- can you give that a shot? It should remove a bunch of the conditional behavior here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually don't like backslashes, either. I got in the habit of using it because my coworkers do like them. I'll change this code.

else [self._MESSAGE_BAR_CHAR] * 2
return self._MESSAGE_FORMAT.format(begin_char, type, path) \
.ljust(self._MESSAGE_MAX_LENGTH - 1, self._MESSAGE_BAR_CHAR) + \
end_char

{body}
-----------------------------
""",
)
_SUCCESS_MSG = "===[SUCCESS]===({path})===\n"
def _error_msg(self, path, type, body):
HEADER = self._message_line(path, type, header=True)
FOOTER = "└" + "─" * (self._MESSAGE_MAX_LENGTH - 2) + "┘"

return "\n".join((HEADER, 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),
Expand All @@ -91,21 +102,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,
body=error._formatted_message(formatter=_json_formatter),
)

def validation_success(self, instance_path):
return self._SUCCESS_MSG.format(path=instance_path)
return self._message_line(path=instance_path, type="SUCCESS") + "\n\n"


@attr.s
Expand Down
18 changes: 11 additions & 7 deletions jsonschema/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
"""
Validation errors, and some surrounding helpers.
"""
from collections import defaultdict, deque
import itertools
import pprint
import textwrap
from collections import defaultdict, deque
Julian marked this conversation as resolved.
Show resolved Hide resolved
from functools import partial
from pprint import pformat

import attr

from jsonschema import _utils


sloanlance marked this conversation as resolved.
Show resolved Hide resolved
WEAK_MATCHES = frozenset(["anyOf", "oneOf"])
STRONG_MATCHES = frozenset()

Expand Down Expand Up @@ -61,14 +61,18 @@ def __repr__(self):
return "<%s: %r>" % (self.__class__.__name__, self.message)

def __str__(self):
return self._formatted_message()

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

pschema = pprint.pformat(self.schema, width=72)
pinstance = pprint.pformat(self.instance, width=72)
pschema = formatter(self.schema)

pinstance = pformat(self.instance, width=72)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're only using the formatter for the schema, not the instance here, so there's going to be inconsistency in the output.

Can we add a test that covers the instance too and then fix this to make sure the formatter is used for both?

return self.message + textwrap.dedent("""

Failed validating %r in %s%s:
Expand Down Expand Up @@ -187,8 +191,8 @@ def __init__(self, type, instance, schema):
self.schema = schema

def __str__(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
Expand Down
Loading