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

Conversation

sloanlance
Copy link

Fixes #708.

This makes the pretty output look better by making error header and footer lines equal lengths and to use Unicode characters in them. It also shows schema JSON in error messages as valid JSON, not Python data structure representations. (Mostly a difference in quote marks used for strings.)

For the error messages, I wanted to continue using the strings with str.format(). However, I couldn't find a clean way of using the left justification and padding that's available in the formatting micro-language. If the technique I used isn't satisfactory, I can try for another approach.

To get the schema instance to display as true JSON, I thought encoding the schema back to JSON was the best approach. Using newline with the separator and sorting keys seems to give close to the same output format as pprint.pformat(). The difference is that it ends up being a little prettier, because there are always line breaks, but in my testing, pprint only broke the lines when the schema was longer than 72 characters. I didn't test extensively, so I don't know how well the json.dumps() technique compares with pprint for lines that longer than 72 characters and aren't easily broken.

Please feel free to be critical with the code review. I'm very much open to updating my changes to fit with the style of the project.

Unicode box characters, true JSON output, (slightly) more space between each error, etc.
@sloanlance

This comment has been minimized.

@sloanlance sloanlance marked this pull request as draft July 26, 2020 13:06
@sloanlance sloanlance changed the title #708 - Make pretty formatter output prettier [DRAFT] #708 - Make pretty formatter output prettier Jul 26, 2020
@Julian
Copy link
Member

Julian commented Jul 26, 2020

Awesome thanks! I will take a closer look at this (possibly after you get to the tests) but just to save you from one wrong turn -- the "use JSON" desire I had should only apply to the CLI, not to general formatting of the exception -- in other words, we can't change it within e.g. ValidationError.__str__, otherwise it will apply everywhere.

We have some options there. The least coupled is to just construct a custom way to format the errors within the CLI (using all the attributes present on a ValidationError, same as ValidationError.__str__ does). But we also could factor out the body of ValidationError.__str__ into a parametrized helper function, and then call it in 2 different ways from ValidationError.__str__ and from the CLI.

But yeah want to save you from that otherwise you'll find you have to change lots of tests (and then find out later that I didn't mean that :D).

But yeah thanks for all you've done already! Seems like we're on the way here...

@sloanlance

This comment has been minimized.

@sloanlance

This comment has been minimized.

Only use JSON formatting for exception messages when explicitly requested.
@Julian
Copy link
Member

Julian commented Jul 27, 2020

Cool. Yes, think you're getting to the same place I was describing -- I'd probably want it to just be a private helper function, so consider something like:

class _Error:
    ....

    def __unicode__(self):
        return _formatted_error(self, to_str=lambda thing: pformat(thing, width=72))

and then in the CLI you'd use _formatted_error(some_error, to_str=lambda thing: json.dumps(thing, indent=True, ...))

@sloanlance

This comment has been minimized.

@Julian
Copy link
Member

Julian commented Jul 27, 2020

OK. I see, passing in a lambda for formatting will make the code more flexible. Would it be helpful to set pformat() as the default formatter in _formatted_error()? Then when called from unicode(), we wouldn't need to specify the formatter.

Sounds reasonable!

Also, I'm going to look at the documentation about running the tests locally, but if you can tell me off the top of your head the command for testing with my local environment, that'd be helpful. Tox is very cool, but it's more information than I need for initial testing.

Maybe this helps?:

python3.8 -m venv venv
venv/bin/python -m pip install ./ twisted
PYTHONPATH=./ venv/bin/trial jsonschema

(In theory you can replace twisted and trial there with any test runner, including pytest if you're more familiar with that, but the above is what I use.)

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.
@codecov
Copy link

codecov bot commented Jul 28, 2020

Codecov Report

Merging #712 into master will increase coverage by 0.02%.
The diff coverage is 93.75%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #712      +/-   ##
==========================================
+ Coverage   96.02%   96.04%   +0.02%     
==========================================
  Files          17       17              
  Lines        2664     2681      +17     
  Branches      310      310              
==========================================
+ Hits         2558     2575      +17     
  Misses         87       87              
  Partials       19       19              
Impacted Files Coverage Δ
jsonschema/exceptions.py 92.19% <75.00%> (+0.16%) ⬆️
jsonschema/cli.py 97.16% <100.00%> (+0.21%) ⬆️
jsonschema/tests/test_cli.py 99.09% <100.00%> (+0.01%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7ce8dd3...5185d44. Read the comment docs.

@sloanlance

This comment has been minimized.

@sloanlance

This comment has been minimized.

@Julian
Copy link
Member

Julian commented Jul 29, 2020

Hey! Sorry, I haven't really gotten a chance to look at the error you were hitting -- let me know though if you're still having difficulty -- replacing ljust with just the manual calculation sounds fine to me, but if it continues to be an issue I can have a look (maybe tomorrow)

jsonschema/cli.py Outdated Show resolved Hide resolved
@sloanlance

This comment has been minimized.

@sloanlance

This comment has been minimized.

@Julian
Copy link
Member

Julian commented Aug 1, 2020

Will have a look at this in a few hours I hope -- but are you sure this passes locally for you? It certainly fails here.

From glancing at the diff, it seems like you probably want native strings (i.e. no u prefix). stdout/stderr accepts native strings -- so str on both Py2 and Py3. Here it looks like you're giving it unicode on both, which yeah will blow up with that exception.

Will have some more specific guidance after having a look.

And thanks for the work so far it's much appreciated!

@sloanlance

This comment has been minimized.

These are not needed, as I expected.  They may even contribute to the "buffer interface" errors.
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.
…8-prettier-pretty

% Conflicts:
%	jsonschema/cli.py
Nice to see removal of Py2 support.
@sloanlance

This comment has been minimized.

@Julian
Copy link
Member

Julian commented Aug 4, 2020

The CI failures are python-hyper/hyperlink#133 which is being resolved one way or another (either upstream or if not quickly then I'll shove a fix in here somehow)

@Julian
Copy link
Member

Julian commented Aug 4, 2020

sorry, to be clearer about what I mean there, since maybe that was too short -- that function doesn't use anything relevant to an instance, or to the class itself, so it should just be a global function, completely unattached to the class (and then you won't need neither @classmethod nor @staticmethod)

@sloanlance

This comment has been minimized.

@Julian
Copy link
Member

Julian commented Aug 4, 2020

Generally I'd just leave it right alongside _PrettyFormatter -- it's fine to have a function just used in one place, putting it next to the class is just as good as having it on it. As you say, it's possible it'll be used again, if it is, it can move to a module that makes sense for more locations.

@sloanlance sloanlance marked this pull request as ready for review August 5, 2020 02:36
@sloanlance

This comment has been minimized.

@sloanlance

This comment has been minimized.

Resolved conflicts:
* jsonschema/cli.py
* jsonschema/exceptions.py
* jsonschema/tests/test_cli.py
@sloanlance
Copy link
Author

sloanlance commented Aug 7, 2020

All CI checks except coverage passed. Automated coverage check isn't something I know much about, but I'd like to learn. According to the raw logs of the check, it looks like a configuration file for running the check is missing. Is that right?

I saw a coverage report from closer to the time I opened this PR. There have been several changes to my code since then, so it's probably out of date. Is there any value to using that report? I'm unsure how to interpret the report anyway. 🙈

Edit: I see that although the comment from the coverage report is 10 days old, the report itself is current. I'll look up the coverage documentation and try to understand what the report is telling us.

@Julian
Copy link
Member

Julian commented Aug 7, 2020

The failure there is codecov.io (which is the free service for hosting those reports) being flaky, likely nothing you did/changed.

I just re-ran the job, it's unfortunately common for it to barf. Let's see if it passes this time.

@sloanlance
Copy link
Author

Yes, they ran this time. The only failure was to hit the 100% coverage target. Looks like there's an overall slight increase in coverage. Does this satisfy your requirements for merging, or would you like me to look into increasing coverage for code I added?

@Julian
Copy link
Member

Julian commented Aug 9, 2020

Yeah don't mind the coverage being confused not sure why codecov.io has begun to do that as well recently but you're fine there -- will give this another review I hope later today!

Copy link
Member

@Julian Julian left a comment

Choose a reason for hiding this comment

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

Awesome! Left a few minor comments but think we're almost there! Appreciated.

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?

jsonschema/tests/test_cli.py Outdated Show resolved Hide resolved
jsonschema/cli.py Outdated Show resolved Hide resolved
"""\
===[{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.

jsonschema/exceptions.py Show resolved Hide resolved
jsonschema/exceptions.py Outdated Show resolved Hide resolved
jsonschema/tests/test_cli.py Outdated Show resolved Hide resolved
@Julian
Copy link
Member

Julian commented Aug 14, 2020

In case it helps, I added a pre-commit config for you/others so you should be able to fix some of the style comments here by just running pre-commit (which will e.g. restore the import order).

Julian and others added 5 commits August 29, 2020 16:42
* origin/master:
  Remove docformatter, it's too unwieldy.
  Minor doc formatting.
  Sigh, this appears to be a regex, not exact match, so it was ignoring everything.
  Update pre-commit hooks.
  And two last ones...
  Remove the rest of the notebooks.ai related resources.
  Remove demo links from README file
  Move the latest validator CLI test to where it must go now.
  Simplify the schemas in the tests for CLI validator detection.
  add test case
  Delete the failed test case
  Fix the bug of processing arguments[validator] in parse_args
  use correct format check globally
  renames message and function
  better handling for Python 3.6
  fix formatting error
  skip tests on python 3.6
  unskip tests
  Temporarily skip the tests that will be unskipped by python-jsonschema#722.
  Squashed 'json/' changes from 86f52b87..fce9e9b3
  Validate IP addresses using the ipaddress module.
  Skip more tests for python-jsonschema#686.
  Squashed 'json/' changes from ea415537..86f52b87
  improve date parsing
  Bump the version in the notebooks.io requirements.
  pre-commit setup.
  Trailing whitespace
  Kill Py2 in the sphinx role.
  isorted.
  Unbreak non-format-setuptools-extra-installed jsonschema.
  Run CI against all setuptools extras separately.
  Pick a format checker that has no external dep now that fqdn exists.
  regenerated the requirements.txt
  Modify the code based on review comments
  fix bug about hostname by import fqdn
Co-authored-by: Julian Berman <Julian@GrayVines.com>
@sloanlance
Copy link
Author

In case it helps, I added a pre-commit config for you/others so you should be able to fix some of the style comments here by just running pre-commit (which will e.g. restore the import order).

What is the definition of the order of imports preferred for this project? I often use IntelliJ IDEA's reformatting feature. In addition to cleaning up lines of code, it reorganizes imports. Off the top of my head, I think the order is something like sections of core Python modules, third-party modules, and project modules, each section alphabetized.

@Julian
Copy link
Member

Julian commented Sep 1, 2020

What is the definition of the order of imports preferred for this project? I often use IntelliJ IDEA's reformatting feature. In addition to cleaning up lines of code, it reorganizes imports. Off the top of my head, I think the order is something like sections of core Python modules, third-party modules, and project modules, each section alphabetized.

It's exactly that in fact :), just actually alphabetized (some tools, e.g. isort by default, and it sounds like IntelliJ), will put from imports on the bottom rather than on the top, whereas "from" < "import".

@Julian Julian closed this Oct 20, 2020
@Julian Julian deleted the branch python-jsonschema:master October 20, 2020 16:37
@sloanlance sloanlance deleted the 708-prettier-pretty branch September 21, 2023 15:16
Julian added a commit that referenced this pull request Jan 16, 2024
544f7c3d Merge pull request #712 from otto-ifak/main
9dad3ebe Add tests for enum with array of bool
589a0858 Merge pull request #706 from marksparkza/unevaluated-before-ref
64d5cab9 Merge pull request #710 from spacether/patch-1
418cdbd6 Removes idea folder
e0a9e066 Updates all other tests to mention grapheme/graphemes
69136952 Update minLength.json
4a2c61e8 Test unevaluatedItems|Properties before $ref

git-subtree-dir: json
git-subtree-split: 544f7c3df93b69f84f587b345f2835c380e43226
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

cleanup of pretty output renderer
2 participants