mirror of
https://github.com/pre-commit/pre-commit-hooks.git
synced 2025-08-14 09:27:21 +08:00

This commit uses capsys to test the output of the diff, which is now hidden behind the autofix flag if it's disabled
144 lines
4.2 KiB
Python
144 lines
4.2 KiB
Python
from __future__ import print_function
|
|
|
|
import argparse
|
|
import difflib
|
|
import io
|
|
import json
|
|
import sys
|
|
from collections import OrderedDict
|
|
from typing import List
|
|
from typing import Mapping
|
|
from typing import Optional
|
|
from typing import Sequence
|
|
from typing import Tuple
|
|
from typing import Union
|
|
|
|
from six import text_type
|
|
|
|
|
|
def _get_pretty_format(
|
|
contents, indent, ensure_ascii=True, sort_keys=True, top_keys=(),
|
|
): # type: (str, str, bool, bool, Sequence[str]) -> str
|
|
def pairs_first(pairs):
|
|
# type: (Sequence[Tuple[str, str]]) -> Mapping[str, str]
|
|
before = [pair for pair in pairs if pair[0] in top_keys]
|
|
before = sorted(before, key=lambda x: top_keys.index(x[0]))
|
|
after = [pair for pair in pairs if pair[0] not in top_keys]
|
|
if sort_keys:
|
|
after = sorted(after, key=lambda x: x[0])
|
|
return OrderedDict(before + after)
|
|
json_pretty = json.dumps(
|
|
json.loads(contents, object_pairs_hook=pairs_first),
|
|
indent=indent,
|
|
ensure_ascii=ensure_ascii,
|
|
# Workaround for https://bugs.python.org/issue16333
|
|
separators=(',', ': '),
|
|
)
|
|
# Ensure unicode (Py2) and add the newline that dumps does not end with.
|
|
return text_type(json_pretty) + '\n'
|
|
|
|
|
|
def _autofix(filename, new_contents): # type: (str, str) -> None
|
|
print('Fixing file {}'.format(filename))
|
|
with io.open(filename, 'w', encoding='UTF-8') as f:
|
|
f.write(new_contents)
|
|
|
|
|
|
def parse_num_to_int(s): # type: (str) -> Union[int, str]
|
|
"""Convert string numbers to int, leaving strings as is."""
|
|
try:
|
|
return int(s)
|
|
except ValueError:
|
|
return s
|
|
|
|
|
|
def parse_topkeys(s): # type: (str) -> List[str]
|
|
return s.split(',')
|
|
|
|
|
|
def get_diff(source, target): # type: (str, str) -> str
|
|
source_lines = source.splitlines(True)
|
|
target_lines = target.splitlines(True)
|
|
diff = ''.join(difflib.ndiff(source_lines, target_lines))
|
|
print(diff)
|
|
return diff
|
|
|
|
|
|
def main(argv=None): # type: (Optional[Sequence[str]]) -> int
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
'--autofix',
|
|
action='store_true',
|
|
dest='autofix',
|
|
help='Automatically fixes encountered not-pretty-formatted files',
|
|
)
|
|
parser.add_argument(
|
|
'--indent',
|
|
type=parse_num_to_int,
|
|
default='2',
|
|
help=(
|
|
'The number of indent spaces or a string to be used as delimiter'
|
|
' for indentation level e.g. 4 or "\t" (Default: 2)'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
'--no-ensure-ascii',
|
|
action='store_true',
|
|
dest='no_ensure_ascii',
|
|
default=False,
|
|
help=(
|
|
'Do NOT convert non-ASCII characters to Unicode escape sequences '
|
|
'(\\uXXXX)'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
'--no-sort-keys',
|
|
action='store_true',
|
|
dest='no_sort_keys',
|
|
default=False,
|
|
help='Keep JSON nodes in the same order',
|
|
)
|
|
parser.add_argument(
|
|
'--top-keys',
|
|
type=parse_topkeys,
|
|
dest='top_keys',
|
|
default=[],
|
|
help='Ordered list of keys to keep at the top of JSON hashes',
|
|
)
|
|
parser.add_argument('filenames', nargs='*', help='Filenames to fix')
|
|
args = parser.parse_args(argv)
|
|
|
|
status = 0
|
|
|
|
for json_file in args.filenames:
|
|
with io.open(json_file, encoding='UTF-8') as f:
|
|
contents = f.read()
|
|
|
|
try:
|
|
pretty_contents = _get_pretty_format(
|
|
contents, args.indent, ensure_ascii=not args.no_ensure_ascii,
|
|
sort_keys=not args.no_sort_keys, top_keys=args.top_keys,
|
|
)
|
|
|
|
if contents != pretty_contents:
|
|
print('File {} is not pretty-formatted'.format(json_file))
|
|
|
|
if args.autofix:
|
|
_autofix(json_file, pretty_contents)
|
|
else:
|
|
print(get_diff(''.join(contents), pretty_contents))
|
|
|
|
status = 1
|
|
except ValueError:
|
|
print(
|
|
'Input File {} is not a valid JSON, consider using check-json'
|
|
.format(json_file),
|
|
)
|
|
return 1
|
|
|
|
return status
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|