mirror of
https://github.com/pre-commit/pre-commit-hooks.git
synced 2025-08-14 09:27:21 +08:00
143 lines
4.1 KiB
Python
143 lines
4.1 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))
|
|
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())
|