| # coding: utf-8 |
| from __future__ import unicode_literals, division, absolute_import, print_function |
| |
| import ast |
| import _ast |
| import unittest |
| import os |
| import sys |
| |
| import asn1crypto as module |
| |
| |
| # This handles situations where an import is importing a function from a |
| # dotted path, e.g. "from . import ident", and ident is a function, not a |
| # submodule |
| MOD_MAP = { |
| } |
| |
| |
| def add_mod(mod_name, imports): |
| """ |
| Maps pre-defined module.function to module import names |
| |
| :param mod_name: |
| A unicode string of a fully-qualified module name being imported |
| |
| :param imports: |
| A set of unicode strings of the modules that are being imported |
| """ |
| |
| imports.add(MOD_MAP.get(mod_name, mod_name)) |
| |
| |
| def walk_ast(parent_node, modname, imports): |
| """ |
| Walks the AST for a module finding any imports and recording them |
| |
| :param parent_node: |
| A node from the _ast module |
| |
| :param modname: |
| A unicode string of the module we are walking the AST of |
| |
| :param imports: |
| A set of unicode strings of the imports that have been found so far |
| """ |
| |
| for node in ast.iter_child_nodes(parent_node): |
| if isinstance(node, _ast.Import): |
| if node.names[0].name.startswith(module.__name__): |
| add_mod(node.names[0].name, imports) |
| |
| elif isinstance(node, _ast.ImportFrom): |
| if node.level > 0: |
| if modname == module.__name__: |
| base_mod = module.__name__ |
| else: |
| base_mod = '.'.join(modname.split('.')[:-node.level]) |
| if node.module: |
| base_mod += '.' + node.module |
| else: |
| base_mod = node.module |
| |
| if not base_mod.startswith(module.__name__): |
| continue |
| |
| if node.level > 0 and not node.module: |
| for n in node.names: |
| add_mod(base_mod + '.' + n.name, imports) |
| else: |
| add_mod(base_mod, imports) |
| |
| elif isinstance(node, _ast.If): |
| for subast in node.body: |
| walk_ast(subast, modname, imports) |
| for subast in node.orelse: |
| walk_ast(subast, modname, imports) |
| |
| elif sys.version_info >= (3, 3) and isinstance(node, _ast.Try): |
| for subast in node.body: |
| walk_ast(subast, modname, imports) |
| for subast in node.orelse: |
| walk_ast(subast, modname, imports) |
| for subast in node.finalbody: |
| walk_ast(subast, modname, imports) |
| |
| elif sys.version_info < (3, 3) and isinstance(node, _ast.TryFinally): |
| for subast in node.body: |
| walk_ast(subast, modname, imports) |
| for subast in node.finalbody: |
| walk_ast(subast, modname, imports) |
| |
| elif sys.version_info < (3, 3) and isinstance(node, _ast.TryExcept): |
| for subast in node.body: |
| walk_ast(subast, modname, imports) |
| for subast in node.orelse: |
| walk_ast(subast, modname, imports) |
| |
| |
| class InitTests(unittest.TestCase): |
| |
| def test_load_order(self): |
| deps = {} |
| |
| mod_root = os.path.abspath(os.path.dirname(module.__file__)) |
| files = [] |
| for root, dnames, fnames in os.walk(mod_root): |
| for f in fnames: |
| if f.endswith('.py'): |
| full_path = os.path.join(root, f) |
| rel_path = full_path.replace(mod_root + os.sep, '') |
| files.append((full_path, rel_path)) |
| |
| for full_path, rel_path in sorted(files): |
| with open(full_path, 'rb') as f: |
| full_code = f.read() |
| if sys.version_info >= (3,): |
| full_code = full_code.decode('utf-8') |
| |
| modname = rel_path.replace('.py', '').replace(os.sep, '.') |
| if modname == '__init__': |
| modname = module.__name__ |
| else: |
| modname = '%s.%s' % (module.__name__, modname) |
| |
| imports = set([]) |
| module_node = ast.parse(full_code, filename=full_path) |
| walk_ast(module_node, modname, imports) |
| |
| deps[modname] = imports |
| |
| load_order = module.load_order() |
| prev = set([]) |
| for mod in load_order: |
| self.assertEqual(True, mod in deps) |
| self.assertEqual((mod, set([])), (mod, deps[mod] - prev)) |
| prev.add(mod) |