4
4
import re
5
5
import sys
6
6
from datetime import datetime
7
- from typing import Any , Sequence
7
+ from typing import Any , Sequence , Final
8
8
9
9
from rapidfuzz import fuzz
10
10
11
+ DEFAULT_LICENSE_FILEPATH : Final [str ] = "LICENSE.txt"
12
+
11
13
FUZZY_MATCH_TODO_COMMENT = (
12
14
" TODO: This license is not consistent with the license used in the project."
13
15
)
@@ -41,10 +43,15 @@ def __init__(self, message):
41
43
self .message = message
42
44
43
45
44
- def main (argv = None ):
46
+ def main (argv = None ) -> int :
45
47
parser = argparse .ArgumentParser ()
46
48
parser .add_argument ("filenames" , nargs = "*" , help = "filenames to check" )
47
- parser .add_argument ("--license-filepath" , default = "LICENSE.txt" )
49
+ parser .add_argument (
50
+ "--license-filepath" ,
51
+ action = "extend" ,
52
+ nargs = 1 ,
53
+ help = f"list of file names to consider. When omitted, it defaults to '{ DEFAULT_LICENSE_FILEPATH } '" ,
54
+ )
48
55
parser .add_argument (
49
56
"--comment-style" ,
50
57
default = "#" ,
@@ -100,13 +107,15 @@ def main(argv=None):
100
107
args = parser .parse_args (argv )
101
108
if args .use_current_year :
102
109
args .allow_past_years = True
110
+ if not args .license_filepath :
111
+ args .license_filepath = [DEFAULT_LICENSE_FILEPATH ]
103
112
104
- license_info = get_license_info (args )
113
+ license_info_list = get_license_info_list (args )
105
114
106
115
changed_files : list [str ] = []
107
116
todo_files : list [str ] = []
108
117
109
- check_failed = process_files (args , changed_files , todo_files , license_info )
118
+ check_failed = process_files (args , changed_files , todo_files , license_info_list )
110
119
111
120
if check_failed :
112
121
print ("" )
@@ -135,58 +144,68 @@ def _replace_year_in_license_with_current(plain_license: list[str], filepath: st
135
144
return plain_license
136
145
137
146
138
- def get_license_info (args ) -> LicenseInfo :
147
+ def get_license_info_list (args ) -> list [ LicenseInfo ] :
139
148
comment_start , comment_end = None , None
140
149
comment_prefix = args .comment_style .replace ("\\ t" , "\t " )
141
150
extra_space = (
142
151
" " if not args .no_space_in_comment_prefix and comment_prefix != "" else ""
143
152
)
144
153
if "|" in comment_prefix :
145
154
comment_start , comment_prefix , comment_end = comment_prefix .split ("|" )
146
- with open (args .license_filepath , encoding = "utf8" , newline = "" ) as license_file :
147
- plain_license = license_file .readlines ()
148
155
149
- if args .use_current_year :
150
- plain_license = _replace_year_in_license_with_current (
151
- plain_license , args .license_filepath
156
+ license_info_list = []
157
+ for filepath in args .license_filepath :
158
+ with open (filepath , encoding = "utf8" , newline = "" ) as license_file :
159
+ plain_license = license_file .readlines ()
160
+
161
+ if args .use_current_year :
162
+ plain_license = _replace_year_in_license_with_current (
163
+ plain_license , args .license_filepath
164
+ )
165
+
166
+ prefixed_license = [
167
+ f'{ comment_prefix } { extra_space if line .strip () else "" } { line } '
168
+ for line in plain_license
169
+ ]
170
+ eol = "\r \n " if prefixed_license [0 ][- 2 :] == "\r \n " else "\n "
171
+ num_extra_lines = 0
172
+
173
+ if not prefixed_license [- 1 ].endswith (eol ):
174
+ prefixed_license [- 1 ] += eol
175
+ num_extra_lines += 1
176
+ if comment_start :
177
+ prefixed_license = [comment_start + eol ] + prefixed_license
178
+ num_extra_lines += 1
179
+ if comment_end :
180
+ prefixed_license = prefixed_license + [comment_end + eol ]
181
+ num_extra_lines += 1
182
+
183
+ license_info = LicenseInfo (
184
+ prefixed_license = prefixed_license ,
185
+ plain_license = plain_license ,
186
+ eol = "" if args .no_extra_eol else eol ,
187
+ comment_start = comment_start ,
188
+ comment_prefix = comment_prefix ,
189
+ comment_end = comment_end ,
190
+ num_extra_lines = num_extra_lines ,
152
191
)
153
192
154
- prefixed_license = [
155
- f'{ comment_prefix } { extra_space if line .strip () else "" } { line } '
156
- for line in plain_license
157
- ]
158
- eol = "\r \n " if prefixed_license [0 ][- 2 :] == "\r \n " else "\n "
159
- num_extra_lines = 0
160
-
161
- if not prefixed_license [- 1 ].endswith (eol ):
162
- prefixed_license [- 1 ] += eol
163
- num_extra_lines += 1
164
- if comment_start :
165
- prefixed_license = [comment_start + eol ] + prefixed_license
166
- num_extra_lines += 1
167
- if comment_end :
168
- prefixed_license = prefixed_license + [comment_end + eol ]
169
- num_extra_lines += 1
170
-
171
- license_info = LicenseInfo (
172
- prefixed_license = prefixed_license ,
173
- plain_license = plain_license ,
174
- eol = "" if args .no_extra_eol else eol ,
175
- comment_start = comment_start ,
176
- comment_prefix = comment_prefix ,
177
- comment_end = comment_end ,
178
- num_extra_lines = num_extra_lines ,
179
- )
180
- return license_info
193
+ license_info_list .append (license_info )
194
+ return license_info_list
181
195
182
196
183
- def process_files (args , changed_files , todo_files , license_info : LicenseInfo ):
197
+ def process_files (
198
+ args ,
199
+ changed_files : list [str ],
200
+ todo_files : list [str ],
201
+ license_info_list : list [LicenseInfo ],
202
+ ) -> list [str ] | bool :
184
203
"""
185
204
Processes all license files
186
205
:param args: arguments of the hook
187
206
:param changed_files: list of changed files
188
207
:param todo_files: list of files where t.o.d.o. is detected
189
- :param license_info: license info named tuple
208
+ :param license_info_list: list of license info named tuples
190
209
:return: True if some files were changed, t.o.d.o is detected or an error occurred while updating the year
191
210
"""
192
211
license_update_failed = False
@@ -206,21 +225,30 @@ def process_files(args, changed_files, todo_files, license_info: LicenseInfo):
206
225
):
207
226
todo_files .append (src_filepath )
208
227
continue
209
- license_header_index = find_license_header_index (
210
- src_file_content = src_file_content ,
211
- license_info = license_info ,
212
- top_lines_count = args .detect_license_in_X_top_lines ,
213
- match_years_strictly = not args .allow_past_years ,
214
- )
215
- fuzzy_match_header_index = None
216
- if args .fuzzy_match_generates_todo and license_header_index is None :
217
- fuzzy_match_header_index = fuzzy_find_license_header_index (
228
+
229
+ license_header_index = None
230
+ license_info = None
231
+ for license_info in license_info_list :
232
+ license_header_index = find_license_header_index (
218
233
src_file_content = src_file_content ,
219
234
license_info = license_info ,
220
235
top_lines_count = args .detect_license_in_X_top_lines ,
221
- fuzzy_match_extra_lines_to_check = args .fuzzy_match_extra_lines_to_check ,
222
- fuzzy_ratio_cut_off = args .fuzzy_ratio_cut_off ,
236
+ match_years_strictly = not args .allow_past_years ,
223
237
)
238
+ if license_header_index is not None :
239
+ break
240
+ fuzzy_match_header_index = None
241
+ if args .fuzzy_match_generates_todo and license_header_index is None :
242
+ for license_info in license_info_list :
243
+ fuzzy_match_header_index = fuzzy_find_license_header_index (
244
+ src_file_content = src_file_content ,
245
+ license_info = license_info ,
246
+ top_lines_count = args .detect_license_in_X_top_lines ,
247
+ fuzzy_match_extra_lines_to_check = args .fuzzy_match_extra_lines_to_check ,
248
+ fuzzy_ratio_cut_off = args .fuzzy_ratio_cut_off ,
249
+ )
250
+ if fuzzy_match_header_index is not None :
251
+ break
224
252
if license_header_index is not None :
225
253
try :
226
254
if license_found (
@@ -251,7 +279,7 @@ def process_files(args, changed_files, todo_files, license_info: LicenseInfo):
251
279
else :
252
280
if license_not_found (
253
281
remove_header = args .remove_header ,
254
- license_info = license_info ,
282
+ license_info = license_info_list [ 0 ] ,
255
283
src_file_content = src_file_content ,
256
284
src_filepath = src_filepath ,
257
285
encoding = encoding ,
@@ -520,7 +548,7 @@ def _license_line_matches(license_line, src_file_line, match_years_strictly):
520
548
521
549
def find_license_header_index (
522
550
src_file_content , license_info : LicenseInfo , top_lines_count , match_years_strictly
523
- ):
551
+ ) -> int | None :
524
552
"""
525
553
Returns the line number, starting from 0 and lower than `top_lines_count`,
526
554
where the license header comment starts in this file, or else None.
@@ -574,7 +602,7 @@ def fuzzy_find_license_header_index(
574
602
top_lines_count ,
575
603
fuzzy_match_extra_lines_to_check ,
576
604
fuzzy_ratio_cut_off ,
577
- ):
605
+ ) -> int | None :
578
606
"""
579
607
Returns the line number, starting from 0 and lower than `top_lines_count`,
580
608
where the fuzzy matching found best match with ratio higher than the cutoff ratio.
0 commit comments