32
32
"""
33
33
34
34
import os
35
+ import argparse
36
+
37
+ from typing import (
38
+ Callable ,
39
+ Dict ,
40
+ Generator ,
41
+ List ,
42
+ Optional ,
43
+ Set ,
44
+ Tuple ,
45
+ )
46
+
47
+
48
+ # Report: word, line, column.
49
+ Report = Tuple [str , int , int ]
50
+ # Cache: {filepath: length, hash, reports}.
51
+ CacheData = Dict [str , Tuple [int , bytes , List [Report ]]]
52
+ # Map word to suggestions.
53
+ SuggestMap = Dict [str , str ]
35
54
36
55
ONLY_ONCE = True
37
56
USE_COLOR = True
40
59
_files_visited = set ()
41
60
42
61
# Lowercase word -> suggestion list.
43
- _suggest_map = {}
62
+ _suggest_map : SuggestMap = {}
44
63
45
64
VERBOSE_CACHE = False
46
65
64
83
# -----------------------------------------------------------------------------
65
84
# General Utilities
66
85
67
- def hash_of_file_and_len (fp ) :
86
+ def hash_of_file_and_len (fp : str ) -> Tuple [ bytes , int ] :
68
87
import hashlib
69
88
with open (fp , 'rb' ) as fh :
70
89
data = fh .read ()
@@ -129,11 +148,11 @@ def hash_of_file_and_len(fp):
129
148
re_not_newline = re .compile ("[^\n ]" )
130
149
131
150
132
- def words_from_text (text ) :
151
+ def words_from_text (text : str ) -> List [ Tuple [ str , int ]] :
133
152
""" Extract words to treat as English for spell checking.
134
153
"""
135
154
# Replace non-newlines with white-space, so all alignment is kept.
136
- def replace_ignore (match ) :
155
+ def replace_ignore (match : re . Match [ str ]) -> str :
137
156
start , end = match .span ()
138
157
return re_not_newline .sub (" " , match .string [start :end ])
139
158
@@ -148,7 +167,7 @@ def replace_ignore(match):
148
167
for match in re_words .finditer (text ):
149
168
words .append ((match .group (0 ), match .start ()))
150
169
151
- def word_ok (w ) :
170
+ def word_ok (w : str ) -> bool :
152
171
# Ignore all uppercase words.
153
172
if w .isupper ():
154
173
return False
@@ -165,16 +184,16 @@ class Comment:
165
184
"type" ,
166
185
)
167
186
168
- def __init__ (self , file , text , line , type ):
187
+ def __init__ (self , file : str , text : str , line : int , type : str ):
169
188
self .file = file
170
189
self .text = text
171
190
self .line = line
172
191
self .type = type
173
192
174
- def parse (self ):
193
+ def parse (self ) -> List [ Tuple [ str , int ]] :
175
194
return words_from_text (self .text )
176
195
177
- def line_and_column_from_comment_offset (self , pos ) :
196
+ def line_and_column_from_comment_offset (self , pos : int ) -> Tuple [ int , int ] :
178
197
text = self .text
179
198
slineno = self .line + text .count ("\n " , 0 , pos )
180
199
# Allow for -1 to be not found.
@@ -187,7 +206,7 @@ def line_and_column_from_comment_offset(self, pos):
187
206
return slineno , scol
188
207
189
208
190
- def extract_code_strings (filepath ) :
209
+ def extract_code_strings (filepath : str ) -> Tuple [ List [ Comment ], Set [ str ]] :
191
210
import pygments
192
211
from pygments import lexers
193
212
from pygments .token import Token
@@ -219,7 +238,7 @@ def extract_code_strings(filepath):
219
238
return comments , code_words
220
239
221
240
222
- def extract_py_comments (filepath ) :
241
+ def extract_py_comments (filepath : str ) -> Tuple [ List [ Comment ], Set [ str ]] :
223
242
224
243
import token
225
244
import tokenize
@@ -248,7 +267,7 @@ def extract_py_comments(filepath):
248
267
return comments , code_words
249
268
250
269
251
- def extract_c_comments (filepath ) :
270
+ def extract_c_comments (filepath : str ) -> Tuple [ List [ Comment ], Set [ str ]] :
252
271
"""
253
272
Extracts comments like this:
254
273
@@ -344,7 +363,7 @@ def extract_c_comments(filepath):
344
363
return comments , code_words
345
364
346
365
347
- def spell_check_report (filepath , report ) :
366
+ def spell_check_report (filepath : str , report : Report ) -> None :
348
367
w , slineno , scol = report
349
368
w_lower = w .lower ()
350
369
@@ -369,7 +388,10 @@ def spell_check_report(filepath, report):
369
388
))
370
389
371
390
372
- def spell_check_file (filepath , check_type = 'COMMENTS' ):
391
+ def spell_check_file (
392
+ filepath : str ,
393
+ check_type : str = 'COMMENTS' ,
394
+ ) -> Generator [Report , None , None ]:
373
395
if check_type == 'COMMENTS' :
374
396
if filepath .endswith (".py" ):
375
397
comment_list , code_words = extract_py_comments (filepath )
@@ -396,11 +418,15 @@ def spell_check_file(filepath, check_type='COMMENTS'):
396
418
yield (w , slineno , scol )
397
419
398
420
399
- def spell_check_file_recursive (dirpath , check_type = 'COMMENTS' , cache_data = None ):
421
+ def spell_check_file_recursive (
422
+ dirpath : str ,
423
+ check_type : str = 'COMMENTS' ,
424
+ cache_data : Optional [CacheData ]= None ,
425
+ ) -> None :
400
426
import os
401
427
from os .path import join , splitext
402
428
403
- def source_list (path , filename_check = None ):
429
+ def source_list (path : str , filename_check : Optional [ Callable [[ str ], bool ]] = None ) -> Generator [ str , None , None ] :
404
430
for dirpath , dirnames , filenames in os .walk (path ):
405
431
# skip '.git'
406
432
dirnames [:] = [d for d in dirnames if not d .startswith ("." )]
@@ -411,7 +437,7 @@ def source_list(path, filename_check=None):
411
437
if filename_check is None or filename_check (filepath ):
412
438
yield filepath
413
439
414
- def is_source (filename ) :
440
+ def is_source (filename : str ) -> bool :
415
441
ext = splitext (filename )[1 ]
416
442
return (ext in {
417
443
".c" ,
@@ -447,22 +473,26 @@ def is_source(filename):
447
473
# )
448
474
#
449
475
450
- def spell_cache_read (cache_filepath ) :
476
+ def spell_cache_read (cache_filepath : str ) -> Tuple [ CacheData , SuggestMap ] :
451
477
import pickle
452
- cache_data = {}, {}
478
+ cache_store : Tuple [ CacheData , SuggestMap ] = {}, {}
453
479
if os .path .exists (cache_filepath ):
454
480
with open (cache_filepath , 'rb' ) as fh :
455
- cache_data = pickle .load (fh )
456
- return cache_data
481
+ cache_store = pickle .load (fh )
482
+ return cache_store
457
483
458
484
459
- def spell_cache_write (cache_filepath , cache_data ) :
485
+ def spell_cache_write (cache_filepath : str , cache_store : Tuple [ CacheData , SuggestMap ]) -> None :
460
486
import pickle
461
487
with open (cache_filepath , 'wb' ) as fh :
462
- pickle .dump (cache_data , fh )
488
+ pickle .dump (cache_store , fh )
463
489
464
490
465
- def spell_check_file_with_cache_support (filepath , check_type = 'COMMENTS' , cache_data = None ):
491
+ def spell_check_file_with_cache_support (
492
+ filepath : str ,
493
+ check_type : str = 'COMMENTS' ,
494
+ cache_data : Optional [CacheData ] = None ,
495
+ ) -> Generator [Report , None , None ]:
466
496
"""
467
497
Iterator each item is a report: (word, line_number, column_number)
468
498
"""
@@ -502,8 +532,7 @@ def spell_check_file_with_cache_support(filepath, check_type='COMMENTS', cache_d
502
532
# -----------------------------------------------------------------------------
503
533
# Main & Argument Parsing
504
534
505
- def argparse_create ():
506
- import argparse
535
+ def argparse_create () -> argparse .ArgumentParser :
507
536
508
537
# When --help or no args are given, print this help
509
538
description = __doc__
@@ -543,7 +572,7 @@ def argparse_create():
543
572
return parser
544
573
545
574
546
- def main ():
575
+ def main () -> None :
547
576
global _suggest_map
548
577
549
578
import os
@@ -553,7 +582,7 @@ def main():
553
582
check_type = args .extract
554
583
cache_filepath = args .cache_file
555
584
556
- cache_data = None
585
+ cache_data : Optional [ CacheData ] = None
557
586
if cache_filepath :
558
587
cache_data , _suggest_map = spell_cache_read (cache_filepath )
559
588
clear_stale_cache = True
@@ -573,6 +602,7 @@ def main():
573
602
clear_stale_cache = False
574
603
575
604
if cache_filepath :
605
+ assert (cache_data is not None )
576
606
if VERBOSE_CACHE :
577
607
print ("Writing cache:" , len (cache_data ))
578
608
0 commit comments