Guest User

Find indirect imports

a guest
Jul 20th, 2014
313
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.82 KB | None | 0 0
  1. '''
  2. Copyright © 2014 Nikolaus Rath <Nikolaus.org>
  3.  
  4. This program can be distributed under the same terms as Python,
  5. https://www.python.org/download/releases/3.4.1/license
  6. '''
  7.  
  8. import ast
  9.  
  10. def get_definitions(path):
  11.     '''Yield all objects defined directly in *path*
  12.  
  13.    This does not include imported objects, or objects defined
  14.    dynamically (e.g. by using eval, or modifying globals()).
  15.    '''
  16.  
  17.     names = set()
  18.     for node in ast.parse(open(path, 'rb').read()).body:
  19.         for name in _iter_definitions(node):
  20.             names.add(name)
  21.     return names
  22.  
  23. def _iter_definitions(node):
  24.     if isinstance(node, ast.Assign):
  25.         for node in node.targets:
  26.             while isinstance(node, ast.Attribute):
  27.                 node = node.value
  28.             assert isinstance(node, ast.Name)
  29.             yield node.id
  30.     elif isinstance(node, (ast.FunctionDef, ast.ClassDef)):
  31.         yield node.name
  32.     elif isinstance(node, ast.If):
  33.         for snode in node.body:
  34.             yield from _iter_definitions(snode)
  35.         for snode in node.orelse:
  36.             yield from _iter_definitions(snode)
  37.     elif isinstance(node, ast.Try):
  38.         for snode in (node.body, node.finalbody, node.orelse):
  39.             for ssnode in snode:
  40.                 yield from _iter_definitions(ssnode)
  41.         for snode in node.handlers:
  42.             assert isinstance(snode, ast.ExceptHandler)
  43.             for ssnode in snode.body:
  44.                 yield from _iter_definitions(ssnode)
  45.  
  46. def iter_imports(path):
  47.     '''Yield imports in *path*'''
  48.  
  49.     for node in ast.parse(open(path, 'rb').read()).body:
  50.         if isinstance(node, ast.ImportFrom):
  51.             if node.module is None:
  52.                 prefix = ()
  53.             else:
  54.                 prefix = tuple(node.module.split('.'))
  55.             for snode in node.names:
  56.                 yield (node.level, prefix + (snode.name,))
  57.         elif isinstance(node, ast.Import):
  58.             for node in node.names:
  59.                 yield (0, tuple(node.name.split('.')))
  60.  
  61. def yield_modules(path):
  62.     '''Yield all Python modules underneath *path*'''
  63.  
  64.     for (dpath, dnames, fnames) in os.walk(path):
  65.         module = tuple(dpath.split('/')[1:])
  66.         for fname in fnames:
  67.             if not fname.endswith('.py'):
  68.                 continue
  69.             fpath = os.path.join(dpath, fname)
  70.             if fname == '__init__.py':
  71.                 yield (fpath, module)
  72.             else:
  73.                 yield (fpath, module + (fname[:-3],))
  74.  
  75.         dnames[:] = [ x for x in dnames
  76.                       if os.path.exists(os.path.join(dpath, x, '__init__.py')) ]
  77.  
  78. def check_imports(path):
  79.     '''Check if all imports within *path* are direct'''
  80.  
  81.     # Memorize where objects are defined
  82.     definitions = dict()
  83.     for (fpath, modname) in yield_modules(path):
  84.         definitions[modname] = get_definitions(fpath)
  85.  
  86.     # Check if imports are direct
  87.     found_problems = False
  88.     for (fpath, modname) in yield_modules(path):
  89.         for (lvl, name) in iter_imports(fpath):
  90.             if lvl:
  91.                 if lvl and fpath.endswith('/__init__.py'):
  92.                     lvl += 1
  93.                 name = modname[:len(modname)-lvl] + name
  94.             if name in definitions:
  95.                 # Import of entire module
  96.                 continue
  97.  
  98.             mod_idx = len(name)-1
  99.             while mod_idx > 0:
  100.                 if name[:mod_idx] in definitions:
  101.                     break
  102.                 mod_idx -= 1
  103.             else:
  104.                 # No definitions on record
  105.                 continue
  106.  
  107.             if name[mod_idx] not in definitions[name[:mod_idx]]:
  108.                 print('%s imports %s from %s, but is defined elsewhere'
  109.                       % (fpath, name[mod_idx], '.'.join(name[:mod_idx])))
  110.                 found_problems = True
  111.  
  112.     return found_problems
Advertisement
Add Comment
Please, Sign In to add comment