Skip to content

Commit 1d6bd5f

Browse files
file_contents_sorter: add --group-cases-together -- a better case-insensitive sort
1 parent a8f8651 commit 1d6bd5f

File tree

3 files changed

+69
-3
lines changed

3 files changed

+69
-3
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ Note that this hook WILL remove blank lines and does NOT respect any comments.
123123
All newlines will be converted to line feeds (`\n`).
124124

125125
The following arguments are available:
126-
- `--ignore-case` - fold lower case to upper case characters.
126+
- `--ignore-case` - fold lower case to upper case characters. this retains the original order of lines that differ only in case, so you probably want `--group-cases-together` instead.
127+
- `--group-cases-together` - group lines that differ only in case together, so e.g. `c`, `b`, `a`, and `B` are sorted to `a`, `B`, `b`, and `c` instead of `B`, `a`, `b`, and `c`.
127128
- `--unique` - ensure each line is unique.
128129

129130
#### `fix-byte-order-marker`

pre_commit_hooks/file_contents_sorter.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ def sort_file_contents(
5151
return FAIL
5252

5353

54+
def group_cases_together_key(s: bytes) -> tuple[bytes, bytes]:
55+
return s.lower(), s
56+
57+
5458
def main(argv: Sequence[str] | None = None) -> int:
5559
parser = argparse.ArgumentParser()
5660
parser.add_argument('filenames', nargs='+', help='Files to sort')
@@ -61,22 +65,41 @@ def main(argv: Sequence[str] | None = None) -> int:
6165
action='store_const',
6266
const=bytes.lower,
6367
default=None,
64-
help='fold lower case to upper case characters',
68+
help='fold lower case to upper case characters. this retains\n'
69+
'the original order of lines that differ only in case,\n'
70+
'so you probably want --group-cases-together instead.',
6571
)
6672
mutex.add_argument(
73+
'--group-cases-together',
74+
action='store_const',
75+
const=group_cases_together_key,
76+
default=None,
77+
help='group lines that differ only in case together,\n'
78+
'so e.g. `c`, `b`, `a`, and `B` are sorted to\n'
79+
'`a`, `B`, `b`, and `c` instead of `B`, `a`, `b`, and `c`.',
80+
)
81+
parser.add_argument(
6782
'--unique',
6883
action='store_true',
6984
help='ensure each line is unique',
7085
)
7186

7287
args = parser.parse_args(argv)
88+
# we can't just use add_mutually_exclusive_group for this since
89+
# --unique is allowed with --group-cases-together
90+
if args.ignore_case and args.unique:
91+
parser.error(
92+
'argument --ignore-case: not allowed with argument --unique',
93+
)
94+
95+
key = args.ignore_case or args.group_cases_together
7396

7497
retv = PASS
7598

7699
for arg in args.filenames:
77100
with open(arg, 'rb+') as file_obj:
78101
ret_for_file = sort_file_contents(
79-
file_obj, key=args.ignore_case, unique=args.unique,
102+
file_obj, key=key, unique=args.unique,
80103
)
81104

82105
if ret_for_file:

tests/file_contents_sorter_test.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,30 @@
5555
FAIL,
5656
b'fee\nfee\nFie\nFoe\nfum\n',
5757
),
58+
(
59+
b'a\nb\nB\nb\nc\n',
60+
['--ignore-case'],
61+
PASS,
62+
b'a\nb\nB\nb\nc\n',
63+
),
64+
(
65+
b'a\nb\nB\nb\nc\n',
66+
['--group-cases-together'],
67+
FAIL,
68+
b'a\nB\nb\nb\nc\n',
69+
),
70+
(
71+
b'fee\nFie\nFoe\nfum\n',
72+
['--group-cases-together'],
73+
PASS,
74+
b'fee\nFie\nFoe\nfum\n',
75+
),
76+
(
77+
b'Fie\nFoe\nfee\nfee\nfum\n',
78+
['--group-cases-together'],
79+
FAIL,
80+
b'fee\nfee\nFie\nFoe\nfum\n',
81+
),
5882
(
5983
b'Fie\nFoe\nfee\nfum\n',
6084
['--unique'],
@@ -67,6 +91,24 @@
6791
FAIL,
6892
b'Fie\nFoe\nfee\nfum\n',
6993
),
94+
(
95+
b'a\nb\nB\nb\nc\n',
96+
['--group-cases-together', '--unique'],
97+
FAIL,
98+
b'a\nB\nb\nc\n',
99+
),
100+
(
101+
b'fee\nFie\nFoe\nfum\n',
102+
['--group-cases-together', '--unique'],
103+
PASS,
104+
b'fee\nFie\nFoe\nfum\n',
105+
),
106+
(
107+
b'Fie\nFoe\nfee\nfee\nfum\n',
108+
['--group-cases-together', '--unique'],
109+
FAIL,
110+
b'fee\nFie\nFoe\nfum\n',
111+
),
70112
),
71113
)
72114
def test_integration(input_s, argv, expected_retval, output, tmpdir):

0 commit comments

Comments
 (0)