blob: b9864582274f535c16dbc692b2a4090c29ca8e89 [file] [log] [blame]
# 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)