diff options
author | wbond <will@wbond.net> | 2019-10-13 07:58:38 -0400 |
---|---|---|
committer | wbond <will@wbond.net> | 2019-10-14 11:33:45 -0400 |
commit | 164235f18dd69a74de30c7395f77c9b191ca3463 (patch) | |
tree | 5c550ec4ee1e936b0d64baf0571a9c96a1667ff4 | |
parent | e6348125558c59a5ded98d1b634f6ca2bda50746 (diff) | |
download | asn1crypto-164235f18dd69a74de30c7395f77c9b191ca3463.tar.gz |
Add asn1crypto.load_order()
-rw-r--r-- | asn1crypto/__init__.py | 38 | ||||
-rw-r--r-- | tests/__init__.py | 2 | ||||
-rw-r--r-- | tests/test_init.py | 137 |
3 files changed, 177 insertions, 0 deletions
diff --git a/asn1crypto/__init__.py b/asn1crypto/__init__.py index afdeb43..ce0831c 100644 --- a/asn1crypto/__init__.py +++ b/asn1crypto/__init__.py @@ -6,4 +6,42 @@ from .version import __version__, __version_info__ __all__ = [ '__version__', '__version_info__', + 'load_order', ] + + +def load_order(): + """ + Returns a list of the module and sub-module names for asn1crypto in + dependency load order, for the sake of live reloading code + + :return: + A list of unicode strings of module names, as they would appear in + sys.modules, ordered by which module should be reloaded first + """ + + return [ + 'asn1crypto._errors', # none + 'asn1crypto._int', # none + 'asn1crypto._ordereddict', # none + 'asn1crypto._teletex_codec', # none + 'asn1crypto._types', # none + 'asn1crypto._inet', # _errors, _types + 'asn1crypto._iri', # _errors, _types + 'asn1crypto.version', # none + 'asn1crypto.pem', # _errors, _types + 'asn1crypto.util', # _errors, _inet, _iri, _ordereddict, _types + 'asn1crypto.parser', # _types, util + 'asn1crypto.core', # _errors, _ordereddict, _teletex_codec, _types, parser, util + 'asn1crypto.algos', # _errors, _int, core, util + 'asn1crypto.keys', # _errors, _types, algos, core, util + 'asn1crypto.x509', # _errors, _iri, _ordereddict, _types, algos, core, keys, util + 'asn1crypto.crl', # algos, core, x509 + 'asn1crypto.csr', # algos, core, keys, x509 + 'asn1crypto.ocsp', # _errors, algos, core, crl, keys, x509 + 'asn1crypto.cms', # algos, core, crl, keys, ocsp, x509 + 'asn1crypto.pdf', # cms, core, crl, ocsp, x509 + 'asn1crypto.pkcs12', # algos, cms, core, keys, x509 + 'asn1crypto.tsp', # algos, cms, core, crl, x509 + 'asn1crypto', # version + ] diff --git a/tests/__init__.py b/tests/__init__.py index fddf30f..40df3c3 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -92,6 +92,7 @@ def test_classes(): from .test_cms import CMSTests from .test_crl import CRLTests from .test_csr import CSRTests + from .test_init import InitTests from .test_keys import KeysTests from .test_ocsp import OCSPTests from .test_pem import PEMTests @@ -107,6 +108,7 @@ def test_classes(): CMSTests, CRLTests, CSRTests, + InitTests, KeysTests, OCSPTests, PEMTests, diff --git a/tests/test_init.py b/tests/test_init.py new file mode 100644 index 0000000..b986458 --- /dev/null +++ b/tests/test_init.py @@ -0,0 +1,137 @@ +# 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) |