Skip to content

Commit 27c596f

Browse files
Preserve line endings when editing file in insert_license hook (#84)
1 parent 9890e12 commit 27c596f

2 files changed

Lines changed: 222 additions & 10 deletions

File tree

pre_commit_hooks/insert_license.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def get_license_info(args) -> LicenseInfo:
143143
)
144144
if "|" in comment_prefix:
145145
comment_start, comment_prefix, comment_end = comment_prefix.split("|")
146-
with open(args.license_filepath, encoding="utf8") as license_file:
146+
with open(args.license_filepath, encoding="utf8", newline="") as license_file:
147147
plain_license = license_file.readlines()
148148

149149
if args.use_current_year:
@@ -268,7 +268,7 @@ def _read_file_content(src_filepath):
268268
"ISO-8859-1",
269269
): # we could use the chardet library to support more encodings
270270
try:
271-
with open(src_filepath, encoding=encoding) as src_file:
271+
with open(src_filepath, encoding=encoding, newline="") as src_file:
272272
return src_file.readlines(), encoding
273273
except UnicodeDecodeError as error:
274274
last_error = error
@@ -324,7 +324,7 @@ def license_not_found( # pylint: disable=too-many-arguments
324324
+ [license_info.eol]
325325
+ src_file_content[index:]
326326
)
327-
with open(src_filepath, "w", encoding=encoding) as src_file:
327+
with open(src_filepath, "w", encoding=encoding, newline="") as src_file:
328328
src_file.write("".join(src_file_content))
329329
return True
330330
return False
@@ -458,7 +458,7 @@ def license_found(
458458
)
459459

460460
if updated:
461-
with open(src_filepath, "w", encoding=encoding) as src_file:
461+
with open(src_filepath, "w", encoding=encoding, newline="") as src_file:
462462
src_file.write("".join(src_file_content))
463463

464464
return updated
@@ -494,7 +494,7 @@ def fuzzy_license_found(
494494
]
495495
+ src_file_content[fuzzy_match_header_index:]
496496
)
497-
with open(src_filepath, "w", encoding=encoding) as src_file:
497+
with open(src_filepath, "w", encoding=encoding, newline="") as src_file:
498498
src_file.write("".join(src_file_content))
499499
return True
500500

@@ -661,7 +661,8 @@ def get_license_candidate_string(candidate_array, license_info):
661661
)
662662
else:
663663
in_license = True
664-
found_license_offset = current_offset # We have no data :(. We start license immediately
664+
# We have no data :(. We start license immediately
665+
found_license_offset = current_offset
665666
else:
666667
if stripped_comment_end and stripped_line.startswith(stripped_comment_end):
667668
break

tests/insert_license_test.py

Lines changed: 215 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,40 @@
88

99
from .utils import chdir_to_test_resources, capture_stdout
1010

11+
1112
# pylint: disable=too-many-arguments
1213

1314

15+
def _convert_line_ending(file_path, new_line_endings):
16+
for encoding in (
17+
"utf8",
18+
"ISO-8859-1",
19+
): # we could use the chardet library to support more encodings
20+
last_error = None
21+
try:
22+
with open(file_path, encoding=encoding, newline="") as f_in:
23+
content = f_in.read()
24+
25+
with open(
26+
file_path, "w", encoding=encoding, newline=new_line_endings
27+
) as f_out:
28+
f_out.write(content)
29+
30+
return
31+
except UnicodeDecodeError as error:
32+
last_error = error
33+
print(
34+
f"Error while processing: {file_path} - file encoding is probably not supported"
35+
)
36+
if last_error is not None: # Avoid mypy message
37+
raise last_error
38+
raise RuntimeError("Unexpected branch taken (_convert_line_ending)")
39+
40+
1441
@pytest.mark.parametrize(
1542
(
1643
"license_file_path",
44+
"line_ending",
1745
"src_file_path",
1846
"comment_prefix",
1947
"new_src_file_expected",
@@ -22,13 +50,14 @@
2250
"extra_args",
2351
),
2452
map(
25-
lambda a: a[:1] + a[1],
53+
lambda a: a[:2] + a[2],
2654
chain(
2755
product( # combine license files with other args
2856
(
2957
"LICENSE_with_trailing_newline.txt",
3058
"LICENSE_without_trailing_newline.txt",
3159
),
60+
("\n", "\r\n"),
3261
(
3362
(
3463
"module_without_license.py",
@@ -209,10 +238,182 @@
209238
True,
210239
["--use-current-year"],
211240
),
241+
(
242+
"module_without_license.py",
243+
"#",
244+
"module_with_license.py",
245+
"",
246+
True,
247+
None,
248+
),
249+
("module_without_license_skip.py", "#", None, "", False, None),
250+
("module_with_license.py", "#", None, "", False, None),
251+
("module_with_license_todo.py", "#", None, "", True, None),
252+
(
253+
"module_without_license.jinja",
254+
"{#||#}",
255+
"module_with_license.jinja",
256+
"",
257+
True,
258+
None,
259+
),
260+
(
261+
"module_without_license_skip.jinja",
262+
"{#||#}",
263+
None,
264+
"",
265+
False,
266+
None,
267+
),
268+
("module_with_license.jinja", "{#||#}", None, "", False, None),
269+
("module_with_license_todo.jinja", "{#||#}", None, "", True, None),
270+
(
271+
"module_without_license_and_shebang.py",
272+
"#",
273+
"module_with_license_and_shebang.py",
274+
"",
275+
True,
276+
None,
277+
),
278+
(
279+
"module_without_license_and_shebang_skip.py",
280+
"#",
281+
None,
282+
"",
283+
False,
284+
None,
285+
),
286+
("module_with_license_and_shebang.py", "#", None, "", False, None),
287+
(
288+
"module_with_license_and_shebang_todo.py",
289+
"#",
290+
None,
291+
"",
292+
True,
293+
None,
294+
),
295+
(
296+
"module_without_license.groovy",
297+
"//",
298+
"module_with_license.groovy",
299+
"",
300+
True,
301+
None,
302+
),
303+
("module_without_license_skip.groovy", "//", None, "", False, None),
304+
("module_with_license.groovy", "//", None, "", False, None),
305+
("module_with_license_todo.groovy", "//", None, "", True, None),
306+
(
307+
"module_without_license.css",
308+
"/*| *| */",
309+
"module_with_license.css",
310+
"",
311+
True,
312+
None,
313+
),
314+
(
315+
"module_without_license_and_few_words.css",
316+
"/*| *| */",
317+
"module_with_license_and_few_words.css",
318+
"",
319+
True,
320+
None,
321+
), # Test fuzzy match does not match greedily
322+
(
323+
"module_without_license_skip.css",
324+
"/*| *| */",
325+
None,
326+
"",
327+
False,
328+
None,
329+
),
330+
("module_with_license.css", "/*| *| */", None, "", False, None),
331+
("module_with_license_todo.css", "/*| *| */", None, "", True, None),
332+
(
333+
"main_without_license.cpp",
334+
"/*|\t| */",
335+
"main_with_license.cpp",
336+
"",
337+
True,
338+
None,
339+
),
340+
(
341+
"main_iso8859_without_license.cpp",
342+
"/*|\t| */",
343+
"main_iso8859_with_license.cpp",
344+
"",
345+
True,
346+
None,
347+
),
348+
(
349+
"module_without_license.txt",
350+
"",
351+
"module_with_license_noprefix.txt",
352+
"",
353+
True,
354+
None,
355+
),
356+
(
357+
"module_without_license.py",
358+
"#",
359+
"module_with_license_nospace.py",
360+
"",
361+
True,
362+
["--no-space-in-comment-prefix"],
363+
),
364+
(
365+
"module_without_license.php",
366+
"/*| *| */",
367+
"module_with_license.php",
368+
"",
369+
True,
370+
["--insert-license-after-regex", "^<\\?php$"],
371+
),
372+
(
373+
"module_without_license.py",
374+
"#",
375+
"module_with_license_noeol.py",
376+
"",
377+
True,
378+
["--no-extra-eol"],
379+
),
380+
(
381+
"module_without_license.groovy",
382+
"//",
383+
"module_with_license.groovy",
384+
"",
385+
True,
386+
["--use-current-year"],
387+
),
388+
(
389+
"module_with_stale_year_in_license.py",
390+
"#",
391+
"module_with_year_range_in_license.py",
392+
"",
393+
True,
394+
["--use-current-year"],
395+
),
396+
(
397+
"module_with_stale_year_range_in_license.py",
398+
"#",
399+
"module_with_year_range_in_license.py",
400+
"",
401+
True,
402+
["--use-current-year"],
403+
),
404+
(
405+
"module_with_badly_formatted_stale_year_range_in_license.py",
406+
"#",
407+
"module_with_badly_formatted_stale_year_range_in_license.py",
408+
"module_with_badly_formatted_stale_year_range_in_license.py",
409+
True,
410+
["--use-current-year"],
411+
),
212412
),
213413
),
214414
product(
215415
("LICENSE_with_year_range_and_trailing_newline.txt",),
416+
("\n", "\r\n"),
216417
(
217418
(
218419
"module_without_license.groovy",
@@ -229,6 +430,7 @@
229430
)
230431
def test_insert_license(
231432
license_file_path,
433+
line_ending,
232434
src_file_path,
233435
comment_prefix,
234436
new_src_file_expected,
@@ -241,6 +443,7 @@ def test_insert_license(
241443
with chdir_to_test_resources():
242444
path = tmpdir.join(src_file_path)
243445
shutil.copy(src_file_path, path.strpath)
446+
_convert_line_ending(path.strpath, line_ending)
244447
args = [
245448
"--license-filepath",
246449
license_file_path,
@@ -257,7 +460,7 @@ def test_insert_license(
257460

258461
if new_src_file_expected:
259462
with open(
260-
new_src_file_expected, encoding=encoding
463+
new_src_file_expected, encoding=encoding, newline=line_ending
261464
) as expected_content_file:
262465
expected_content = expected_content_file.read()
263466
if "--use-current-year" in args:
@@ -315,18 +518,20 @@ def test_insert_license_current_year_already_there(
315518
@pytest.mark.parametrize(
316519
(
317520
"license_file_path",
521+
"line_ending",
318522
"src_file_path",
319523
"comment_style",
320524
"new_src_file_expected",
321525
"fail_check",
322526
),
323527
map(
324-
lambda a: a[:1] + a[1],
528+
lambda a: a[:2] + a[2],
325529
product( # combine license files with other args
326530
(
327531
"LICENSE_with_trailing_newline.txt",
328532
"LICENSE_without_trailing_newline.txt",
329533
),
534+
("\n", "\r\n"),
330535
(
331536
(
332537
"module_without_license.jinja",
@@ -393,6 +598,7 @@ def test_insert_license_current_year_already_there(
393598
)
394599
def test_fuzzy_match_license(
395600
license_file_path,
601+
line_ending,
396602
src_file_path,
397603
comment_style,
398604
new_src_file_expected,
@@ -402,6 +608,7 @@ def test_fuzzy_match_license(
402608
with chdir_to_test_resources():
403609
path = tmpdir.join("src_file_path")
404610
shutil.copy(src_file_path, path.strpath)
611+
_convert_line_ending(path.strpath, line_ending)
405612
args = [
406613
"--license-filepath",
407614
license_file_path,
@@ -470,6 +677,7 @@ def test_is_license_present(src_file_content, expected_index, match_years_strict
470677
@pytest.mark.parametrize(
471678
(
472679
"license_file_path",
680+
"line_ending",
473681
"src_file_path",
474682
"comment_style",
475683
"fuzzy_match",
@@ -478,12 +686,13 @@ def test_is_license_present(src_file_content, expected_index, match_years_strict
478686
"use_current_year",
479687
),
480688
map(
481-
lambda a: a[:1] + a[1],
689+
lambda a: a[:2] + a[2],
482690
product( # combine license files with other args
483691
(
484692
"LICENSE_with_trailing_newline.txt",
485693
"LICENSE_without_trailing_newline.txt",
486694
),
695+
("\n", "\r\n"),
487696
(
488697
(
489698
"module_with_license.css",
@@ -625,6 +834,7 @@ def test_is_license_present(src_file_content, expected_index, match_years_strict
625834
)
626835
def test_remove_license(
627836
license_file_path,
837+
line_ending,
628838
src_file_path,
629839
comment_style,
630840
fuzzy_match,
@@ -636,6 +846,7 @@ def test_remove_license(
636846
with chdir_to_test_resources():
637847
path = tmpdir.join("src_file_path")
638848
shutil.copy(src_file_path, path.strpath)
849+
_convert_line_ending(path.strpath, line_ending)
639850
argv = [
640851
"--license-filepath",
641852
license_file_path,

0 commit comments

Comments
 (0)