aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwbond <will@wbond.net>2019-10-13 07:58:38 -0400
committerwbond <will@wbond.net>2019-10-14 11:33:45 -0400
commit164235f18dd69a74de30c7395f77c9b191ca3463 (patch)
tree5c550ec4ee1e936b0d64baf0571a9c96a1667ff4
parente6348125558c59a5ded98d1b634f6ca2bda50746 (diff)
downloadasn1crypto-164235f18dd69a74de30c7395f77c9b191ca3463.tar.gz
Add asn1crypto.load_order()
-rw-r--r--asn1crypto/__init__.py38
-rw-r--r--tests/__init__.py2
-rw-r--r--tests/test_init.py137
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)