contrib/import-checker.py
changeset 28703 a274c4f9087a
parent 28702 e44f671018e3
child 28704 1fa6fdb72275
equal deleted inserted replaced
28702:e44f671018e3 28703:a274c4f9087a
     1 #!/usr/bin/env python
     1 #!/usr/bin/env python
     2 
     2 
     3 from __future__ import absolute_import
     3 from __future__ import absolute_import, print_function
     4 
     4 
     5 import ast
     5 import ast
     6 import collections
     6 import collections
     7 import os
     7 import os
     8 import sys
     8 import sys
   542 def find_cycles(imports):
   542 def find_cycles(imports):
   543     """Find cycles in an already-loaded import graph.
   543     """Find cycles in an already-loaded import graph.
   544 
   544 
   545     All module names recorded in `imports` should be absolute one.
   545     All module names recorded in `imports` should be absolute one.
   546 
   546 
       
   547     >>> from __future__ import print_function
   547     >>> imports = {'top.foo': ['top.bar', 'os.path', 'top.qux'],
   548     >>> imports = {'top.foo': ['top.bar', 'os.path', 'top.qux'],
   548     ...            'top.bar': ['top.baz', 'sys'],
   549     ...            'top.bar': ['top.baz', 'sys'],
   549     ...            'top.baz': ['top.foo'],
   550     ...            'top.baz': ['top.foo'],
   550     ...            'top.qux': ['top.foo']}
   551     ...            'top.qux': ['top.foo']}
   551     >>> print '\\n'.join(sorted(find_cycles(imports)))
   552     >>> print('\\n'.join(sorted(find_cycles(imports))))
   552     top.bar -> top.baz -> top.foo -> top.bar
   553     top.bar -> top.baz -> top.foo -> top.bar
   553     top.foo -> top.qux -> top.foo
   554     top.foo -> top.qux -> top.foo
   554     """
   555     """
   555     cycles = set()
   556     cycles = set()
   556     for mod in sorted(imports.iterkeys()):
   557     for mod in sorted(imports.iterkeys()):
   564 def _cycle_sortkey(c):
   565 def _cycle_sortkey(c):
   565     return len(c), c
   566     return len(c), c
   566 
   567 
   567 def main(argv):
   568 def main(argv):
   568     if len(argv) < 2 or (argv[1] == '-' and len(argv) > 2):
   569     if len(argv) < 2 or (argv[1] == '-' and len(argv) > 2):
   569         print 'Usage: %s {-|file [file] [file] ...}'
   570         print('Usage: %s {-|file [file] [file] ...}')
   570         return 1
   571         return 1
   571     if argv[1] == '-':
   572     if argv[1] == '-':
   572         argv = argv[:1]
   573         argv = argv[:1]
   573         argv.extend(l.rstrip() for l in sys.stdin.readlines())
   574         argv.extend(l.rstrip() for l in sys.stdin.readlines())
   574     localmods = {}
   575     localmods = {}
   582         src = f.read()
   583         src = f.read()
   583         used_imports[modname] = sorted(
   584         used_imports[modname] = sorted(
   584             imported_modules(src, modname, localmods, ignore_nested=True))
   585             imported_modules(src, modname, localmods, ignore_nested=True))
   585         for error, lineno in verify_import_convention(modname, src, localmods):
   586         for error, lineno in verify_import_convention(modname, src, localmods):
   586             any_errors = True
   587             any_errors = True
   587             print '%s:%d: %s' % (source_path, lineno, error)
   588             print('%s:%d: %s' % (source_path, lineno, error))
   588         f.close()
   589         f.close()
   589     cycles = find_cycles(used_imports)
   590     cycles = find_cycles(used_imports)
   590     if cycles:
   591     if cycles:
   591         firstmods = set()
   592         firstmods = set()
   592         for c in sorted(cycles, key=_cycle_sortkey):
   593         for c in sorted(cycles, key=_cycle_sortkey):
   594             # As a rough cut, ignore any cycle that starts with the
   595             # As a rough cut, ignore any cycle that starts with the
   595             # same module as some other cycle. Otherwise we see lots
   596             # same module as some other cycle. Otherwise we see lots
   596             # of cycles that are effectively duplicates.
   597             # of cycles that are effectively duplicates.
   597             if first in firstmods:
   598             if first in firstmods:
   598                 continue
   599                 continue
   599             print 'Import cycle:', c
   600             print('Import cycle:', c)
   600             firstmods.add(first)
   601             firstmods.add(first)
   601         any_errors = True
   602         any_errors = True
   602     return any_errors != 0
   603     return any_errors != 0
   603 
   604 
   604 if __name__ == '__main__':
   605 if __name__ == '__main__':