@@ -294,7 +294,8 @@ def break_up_import(line):
294294def filter_code (source , additional_imports = None ,
295295 expand_star_imports = False ,
296296 remove_all_unused_imports = False ,
297- remove_unused_variables = False ):
297+ remove_unused_variables = False ,
298+ populate_all = False ):
298299 """Yield code with unused imports removed."""
299300 imports = SAFE_IMPORTS
300301 if additional_imports :
@@ -335,6 +336,10 @@ def filter_code(source, additional_imports=None,
335336 else :
336337 marked_variable_line_numbers = frozenset ()
337338
339+ if populate_all :
340+ marked_import_line_numbers = frozenset ()
341+ source = populate_all_with_modules (source , marked_unused_module )
342+
338343 sio = io .StringIO (source )
339344 previous_line = ''
340345 for line_number , line in enumerate (sio .readlines (), start = 1 ):
@@ -478,6 +483,44 @@ def filter_useless_pass(source):
478483 yield line
479484
480485
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+
481524def get_indentation (line ):
482525 """Return leading whitespace."""
483526 if line .strip ():
@@ -497,7 +540,8 @@ def get_line_ending(line):
497540
498541
499542def 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 ):
501545 """Return code with all filtering run on it."""
502546 if not source :
503547 return source
@@ -515,9 +559,10 @@ def fix_code(source, additional_imports=None, expand_star_imports=False,
515559 additional_imports = additional_imports ,
516560 expand_star_imports = expand_star_imports ,
517561 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 ))))
519564
520- if filtered_source == source :
565+ if filtered_source == source or populate_all :
521566 break
522567 source = filtered_source
523568
@@ -537,7 +582,9 @@ def fix_file(filename, args, standard_out):
537582 additional_imports = args .imports .split (',' ) if args .imports else None ,
538583 expand_star_imports = args .expand_star_imports ,
539584 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+ )
541588
542589 if original_source != filtered_source :
543590 if args .in_place :
@@ -692,6 +739,9 @@ def _main(argv, standard_out, standard_error):
692739 'one star import in the file; this is skipped if '
693740 'there are any uses of `__all__` or `del` in the '
694741 'file' )
742+ parser .add_argument ('--populate-modules-under-all' , action = 'store_true' ,
743+ help = 'populate `__all__` with unused import found in '
744+ 'the code.' )
695745 parser .add_argument ('--remove-all-unused-imports' , action = 'store_true' ,
696746 help = 'remove all unused imports (not just those from '
697747 'the standard library)' )
0 commit comments