Skip to content

Commit 80336ae

Browse files
committed
Merge branch '183229025-db' into 'master'
[Delivers #183229025] Add duplicate entry and sort feature for json See merge request voereir/pre-commit-hooks!2
2 parents 78506ce + 2331edc commit 80336ae

File tree

4 files changed

+112
-27
lines changed

4 files changed

+112
-27
lines changed

.pre-commit-hooks.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,8 @@
191191
language: python
192192
types: [text]
193193
stages: [commit, push, manual]
194+
- id: notify-duplicate-entry
195+
name: Notify duplicate entry
196+
description: Notifies duplicate entry in the same file
197+
entry: notify-duplicate-entry
198+
language: python
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import argparse
2+
import json
3+
from typing import Optional
4+
from typing import Sequence
5+
from pathlib import Path
6+
7+
def _check_duplicate_entry(json_contents, key):
8+
json_dict = {}
9+
duplicate_uuids = set()
10+
for row in json_contents:
11+
if row[key] not in json_dict:
12+
json_dict[row[key]] = row
13+
else:
14+
duplicate_uuids.add(row[key])
15+
return duplicate_uuids, len(duplicate_uuids)
16+
17+
18+
def main(argv: Optional[Sequence[str]] = None) -> int:
19+
parser = argparse.ArgumentParser()
20+
parser.add_argument('filenames', nargs='*', type=str,
21+
help='Names of the JSON files to check duplicate entries'
22+
)
23+
table_uuid_mapping = {
24+
'action': 'uuid', 'env_property_group': 'uuid',
25+
'environment': 'uuid', 'environment_property': 'code',
26+
'report_summary': 'uuid',
27+
'runner': 'uuid', 'scenario': 'uuid',
28+
'sla': 'uuid', 'sla_scenario_association': 'sla', 'tag': 'uuid',
29+
'tag_action_association': 'tag_uuid',
30+
'tag_case_association': 'test_case_uuid',
31+
'teams': 'uuid',
32+
'test_case': 'uuid',
33+
'test_suit': 'uuid', 'test_supported_version': 'test_case_uuid',
34+
'testcase_workload_association': 'uuid', 'user': 'uuid',
35+
'user_tokens': 'user_token', 'workflow_task': 'workflow_id'
36+
}
37+
38+
args = vars(parser.parse_args(argv))
39+
filenames = args['filenames']
40+
flag = False
41+
42+
for i in range(len(filenames)):
43+
json_file = filenames[i]
44+
file_name = Path(filenames[i]).stem
45+
key = table_uuid_mapping[file_name]
46+
with open(json_file, encoding='UTF-8') as f:
47+
contents = json.load(f)
48+
duplicate_uuids, status = _check_duplicate_entry(contents, key)
49+
50+
if status:
51+
print(f"Duplicate UUIDs found - {duplicate_uuids} in file "
52+
f"{json_file}")
53+
flag = True
54+
55+
return flag
56+
57+
58+
if __name__ == "__main__":
59+
exit(main())

pre_commit_hooks/pretty_format_json.py

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,22 @@
1212
INFINITY = float('inf')
1313

1414

15-
def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
16-
_key_separator, _item_separator, _sort_keys, _skipkeys,
17-
_one_shot,
18-
## HACK: hand-optimized bytecode; turn globals into locals
19-
ValueError=ValueError,
20-
dict=dict,
21-
float=float,
22-
id=id,
23-
int=int,
24-
isinstance=isinstance,
25-
list=list,
26-
str=str,
27-
tuple=tuple,
28-
_intstr=int.__str__,
29-
):
15+
def _make_iterencode(
16+
markers, _default, _encoder, _indent, _floatstr,
17+
_key_separator, _item_separator, _sort_keys, _skipkeys,
18+
_one_shot,
19+
## HACK: hand-optimized bytecode; turn globals into locals
20+
ValueError=ValueError,
21+
dict=dict,
22+
float=float,
23+
id=id,
24+
int=int,
25+
isinstance=isinstance,
26+
list=list,
27+
str=str,
28+
tuple=tuple,
29+
_intstr=int.__str__,
30+
):
3031

3132
if _indent is not None and not isinstance(_indent, str):
3233
_indent = ' ' * _indent
@@ -38,7 +39,7 @@ def _iterencode_list(lst, _current_indent_level):
3839
if markers is not None:
3940
markerid = id(lst)
4041
if markerid in markers:
41-
raise ValueError("Circular reference detected")
42+
raise ValueError('Circular reference detected')
4243
markers[markerid] = lst
4344
buf = '['
4445
if _indent is not None:
@@ -95,7 +96,7 @@ def _iterencode_dict(dct, _current_indent_level):
9596
if markers is not None:
9697
markerid = id(dct)
9798
if markerid in markers:
98-
raise ValueError("Circular reference detected")
99+
raise ValueError('Circular reference detected')
99100
markers[markerid] = dct
100101
yield '{'
101102
if _indent is not None:
@@ -131,8 +132,10 @@ def _iterencode_dict(dct, _current_indent_level):
131132
elif _skipkeys:
132133
continue
133134
else:
134-
raise TypeError(f'keys must be str, int, float, bool or None, '
135-
f'not {key.__class__.__name__}')
135+
raise TypeError(
136+
f'keys must be str, int, float, bool or None, '
137+
f'not {key.__class__.__name__}',
138+
)
136139
if first:
137140
first = False
138141
else:
@@ -191,7 +194,7 @@ def _iterencode(o, _current_indent_level):
191194
if markers is not None:
192195
markerid = id(o)
193196
if markerid in markers:
194-
raise ValueError("Circular reference detected")
197+
raise ValueError('Circular reference detected')
195198
markers[markerid] = o
196199
o = _default(o)
197200
yield from _iterencode(o, _current_indent_level)
@@ -216,8 +219,10 @@ def iterencode(self, o, _one_shot=False):
216219
else:
217220
markers = None
218221

219-
def floatstr(o, allow_nan=self.allow_nan,
220-
_repr=float.__repr__, _inf=INFINITY, _neginf=-INFINITY):
222+
def floatstr(
223+
o, allow_nan=self.allow_nan,
224+
_repr=float.__repr__, _inf=INFINITY, _neginf=-INFINITY,
225+
):
221226
# Check for specials. Note that this type of test is processor
222227
# and/or platform-specific, so do tests which don't depend on the
223228
# internals.
@@ -233,8 +238,9 @@ def floatstr(o, allow_nan=self.allow_nan,
233238

234239
if not allow_nan:
235240
raise ValueError(
236-
"Out of range float values are not JSON compliant: " +
237-
repr(o))
241+
'Out of range float values are not JSON compliant: ' +
242+
repr(o),
243+
)
238244

239245
return text
240246

@@ -243,7 +249,8 @@ def floatstr(o, allow_nan=self.allow_nan,
243249
_iterencode = _make_iterencode(
244250
markers, self.default, _encoder, self.indent, floatstr,
245251
self.key_separator, self.item_separator, self.sort_keys,
246-
self.skipkeys, _one_shot)
252+
self.skipkeys, _one_shot,
253+
)
247254
return _iterencode(o, 0)
248255

249256

@@ -253,6 +260,7 @@ def _get_pretty_format(
253260
ensure_ascii: bool = True,
254261
sort_keys: bool = True,
255262
top_keys: Sequence[str] = (),
263+
sort_by_first_key: bool = False,
256264
) -> str:
257265
def pairs_first(pairs: Sequence[Tuple[str, str]]) -> Mapping[str, str]:
258266
before = [pair for pair in pairs if pair[0] in top_keys]
@@ -261,12 +269,16 @@ def pairs_first(pairs: Sequence[Tuple[str, str]]) -> Mapping[str, str]:
261269
if sort_keys:
262270
after.sort()
263271
return dict(before + after)
272+
273+
json_contents = json.loads(contents, object_pairs_hook=pairs_first)
274+
if sort_by_first_key:
275+
json_contents.sort(key=lambda row: list(row.values())[0])
264276
json_pretty = json.dumps(
265-
json.loads(contents, object_pairs_hook=pairs_first),
277+
json_contents,
266278
indent=indent,
267279
ensure_ascii=ensure_ascii,
268280
cls=CustomJSONEncoder,
269-
separators=(', ', ': ')
281+
separators=(', ', ': '),
270282
)
271283
return f'{json_pretty}\n'
272284

@@ -337,6 +349,13 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
337349
default=[],
338350
help='Ordered list of keys to keep at the top of JSON hashes',
339351
)
352+
parser.add_argument(
353+
'--sort-by-first-key',
354+
dest='sort_by_first_key',
355+
action='store_true',
356+
default=False,
357+
help='Sort the json by a specific key',
358+
)
340359
parser.add_argument('filenames', nargs='*', help='Filenames to fix')
341360
args = parser.parse_args(argv)
342361

@@ -350,6 +369,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
350369
pretty_contents = _get_pretty_format(
351370
contents, args.indent, ensure_ascii=not args.no_ensure_ascii,
352371
sort_keys=not args.no_sort_keys, top_keys=args.top_keys,
372+
sort_by_first_key=args.sort_by_first_key,
353373
)
354374
except ValueError:
355375
print(

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ console_scripts =
6161
forbid-new-submodules = pre_commit_hooks.forbid_new_submodules:main
6262
mixed-line-ending = pre_commit_hooks.mixed_line_ending:main
6363
name-tests-test = pre_commit_hooks.tests_should_end_in_test:main
64+
notify-duplicate-entry = pre_commit_hooks.notify_duplicate_entry:main
6465
no-commit-to-branch = pre_commit_hooks.no_commit_to_branch:main
6566
pre-commit-hooks-removed = pre_commit_hooks.removed:main
6667
pretty-format-json = pre_commit_hooks.pretty_format_json:main

0 commit comments

Comments
 (0)