@@ -294,7 +294,8 @@ def break_up_import(line):
294
294
def filter_code (source , additional_imports = None ,
295
295
expand_star_imports = False ,
296
296
remove_all_unused_imports = False ,
297
- remove_unused_variables = False ):
297
+ remove_unused_variables = False ,
298
+ populate_all = False ):
298
299
"""Yield code with unused imports removed."""
299
300
imports = SAFE_IMPORTS
300
301
if additional_imports :
@@ -335,6 +336,10 @@ def filter_code(source, additional_imports=None,
335
336
else :
336
337
marked_variable_line_numbers = frozenset ()
337
338
339
+ if populate_all :
340
+ marked_import_line_numbers = frozenset ()
341
+ source = populate_all_with_modules (source , marked_unused_module )
342
+
338
343
sio = io .StringIO (source )
339
344
previous_line = ''
340
345
for line_number , line in enumerate (sio .readlines (), start = 1 ):
@@ -478,6 +483,44 @@ def filter_useless_pass(source):
478
483
yield line
479
484
480
485
486
+ def populate_all_with_modules (source , marked_unused_module ):
487
+ all_syntax = re .search ('^__all__(.)+\]' , source , flags = re .MULTILINE )
488
+ if all_syntax :
489
+ # If there are existing `__all__`, parse it and append to it
490
+ insert_position = all_syntax .span ()[0 ]
491
+ end_position = all_syntax .span ()[1 ]
492
+ all_modules = all_syntax .group ().split ('=' )[1 ].strip ()
493
+ all_modules = ast .literal_eval (all_modules )
494
+ else :
495
+ # If no existing `__all__`, always append in EOF
496
+ insert_position = len (source )
497
+ end_position = - 1
498
+ all_modules = []
499
+
500
+ for modules in marked_unused_module .values ():
501
+ # Get the imported name, `a.b.Foo` -> Foo
502
+ all_modules += [get_imported_name (name ) for name in modules ]
503
+
504
+ new_all_syntax = '__all__ = ' + str (all_modules )
505
+ source = source [:insert_position ] + new_all_syntax + source [end_position :]
506
+ return source
507
+
508
+
509
+ def get_imported_name (module ):
510
+ """
511
+ Return only imported name from pyflakes full module path
512
+
513
+ Example:
514
+ - `a.b.Foo` -> `Foo`
515
+ - `a as b` -> b
516
+ """
517
+ if '.' in module :
518
+ return str (module .split ('.' )[- 1 ])
519
+ elif ' as ' in module :
520
+ return str (module .split (' as ' )[- 1 ])
521
+ # str() to force python 2 to not use unicode
522
+ return str (module )
523
+
481
524
def get_indentation (line ):
482
525
"""Return leading whitespace."""
483
526
if line .strip ():
@@ -497,7 +540,8 @@ def get_line_ending(line):
497
540
498
541
499
542
def fix_code (source , additional_imports = None , expand_star_imports = False ,
500
- remove_all_unused_imports = False , remove_unused_variables = False ):
543
+ remove_all_unused_imports = False , remove_unused_variables = False ,
544
+ populate_all = False ):
501
545
"""Return code with all filtering run on it."""
502
546
if not source :
503
547
return source
@@ -515,9 +559,10 @@ def fix_code(source, additional_imports=None, expand_star_imports=False,
515
559
additional_imports = additional_imports ,
516
560
expand_star_imports = expand_star_imports ,
517
561
remove_all_unused_imports = remove_all_unused_imports ,
518
- remove_unused_variables = remove_unused_variables ))))
562
+ remove_unused_variables = remove_unused_variables ,
563
+ populate_all = populate_all ))))
519
564
520
- if filtered_source == source :
565
+ if filtered_source == source or populate_all :
521
566
break
522
567
source = filtered_source
523
568
@@ -537,7 +582,9 @@ def fix_file(filename, args, standard_out):
537
582
additional_imports = args .imports .split (',' ) if args .imports else None ,
538
583
expand_star_imports = args .expand_star_imports ,
539
584
remove_all_unused_imports = args .remove_all_unused_imports ,
540
- remove_unused_variables = args .remove_unused_variables )
585
+ remove_unused_variables = args .remove_unused_variables ,
586
+ populate_all = args .populate_modules_under_all ,
587
+ )
541
588
542
589
if original_source != filtered_source :
543
590
if args .in_place :
@@ -692,6 +739,9 @@ def _main(argv, standard_out, standard_error):
692
739
'one star import in the file; this is skipped if '
693
740
'there are any uses of `__all__` or `del` in the '
694
741
'file' )
742
+ parser .add_argument ('--populate-modules-under-all' , action = 'store_true' ,
743
+ help = 'populate `__all__` with unused import found in '
744
+ 'the code.' )
695
745
parser .add_argument ('--remove-all-unused-imports' , action = 'store_true' ,
696
746
help = 'remove all unused imports (not just those from '
697
747
'the standard library)' )
0 commit comments