| Index: tools/telemetry/third_party/modulegraph/modulegraph/modulegraph.py
|
| diff --git a/tools/telemetry/third_party/modulegraph/modulegraph/modulegraph.py b/tools/telemetry/third_party/modulegraph/modulegraph/modulegraph.py
|
| deleted file mode 100644
|
| index 2795cc416196318324b7db4bfff26950aa1310ba..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/third_party/modulegraph/modulegraph/modulegraph.py
|
| +++ /dev/null
|
| @@ -1,1686 +0,0 @@
|
| -"""
|
| -Find modules used by a script, using bytecode analysis.
|
| -
|
| -Based on the stdlib modulefinder by Thomas Heller and Just van Rossum,
|
| -but uses a graph data structure and 2.3 features
|
| -
|
| -XXX: Verify all calls to import_hook (and variants) to ensure that
|
| -imports are done in the right way.
|
| -"""
|
| -from __future__ import absolute_import, print_function
|
| -
|
| -import pkg_resources
|
| -
|
| -import dis
|
| -import imp
|
| -import marshal
|
| -import os
|
| -import sys
|
| -import struct
|
| -import zipimport
|
| -import re
|
| -from collections import deque, namedtuple
|
| -import ast
|
| -
|
| -from altgraph.ObjectGraph import ObjectGraph
|
| -from altgraph import GraphError
|
| -
|
| -from itertools import count
|
| -
|
| -from modulegraph import util
|
| -from modulegraph import zipio
|
| -
|
| -if sys.version_info[0] == 2:
|
| - from StringIO import StringIO as BytesIO
|
| - from StringIO import StringIO
|
| - from urllib import pathname2url
|
| - def _Bchr(value):
|
| - return chr(value)
|
| -
|
| -else:
|
| - from urllib.request import pathname2url
|
| - from io import BytesIO, StringIO
|
| -
|
| - def _Bchr(value):
|
| - return value
|
| -
|
| -
|
| -# File open mode for reading (univeral newlines)
|
| -if sys.version_info[0] == 2:
|
| - _READ_MODE = "rU"
|
| -else:
|
| - _READ_MODE = "r"
|
| -
|
| -
|
| -
|
| -
|
| -# Modulegraph does a good job at simulating Python's, but it can not
|
| -# handle packagepath modifications packages make at runtime. Therefore there
|
| -# is a mechanism whereby you can register extra paths in this map for a
|
| -# package, and it will be honored.
|
| -#
|
| -# Note this is a mapping is lists of paths.
|
| -_packagePathMap = {}
|
| -
|
| -# Prefix used in magic .pth files used by setuptools to create namespace
|
| -# packages without an __init__.py file.
|
| -#
|
| -# The value is a list of such prefixes as the prefix varies with versions of
|
| -# setuptools.
|
| -_SETUPTOOLS_NAMESPACEPKG_PTHs=(
|
| - "import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('",
|
| - "import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('",
|
| - "import sys, types, os;p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('",
|
| -)
|
| -
|
| -
|
| -def _namespace_package_path(fqname, pathnames, path=None):
|
| - """
|
| - Return the __path__ for the python package in *fqname*.
|
| -
|
| - This function uses setuptools metadata to extract information
|
| - about namespace packages from installed eggs.
|
| - """
|
| - working_set = pkg_resources.WorkingSet(path)
|
| -
|
| - path = list(pathnames)
|
| -
|
| - for dist in working_set:
|
| - if dist.has_metadata('namespace_packages.txt'):
|
| - namespaces = dist.get_metadata(
|
| - 'namespace_packages.txt').splitlines()
|
| - if fqname in namespaces:
|
| - nspath = os.path.join(dist.location, *fqname.split('.'))
|
| - if nspath not in path:
|
| - path.append(nspath)
|
| -
|
| - return path
|
| -
|
| -_strs = re.compile(r'''^\s*["']([A-Za-z0-9_]+)["'],?\s*''') # "<- emacs happy
|
| -
|
| -def _eval_str_tuple(value):
|
| - """
|
| - Input is the repr of a tuple of strings, output
|
| - is that tuple.
|
| -
|
| - This only works with a tuple where the members are
|
| - python identifiers.
|
| - """
|
| - if not (value.startswith('(') and value.endswith(')')):
|
| - raise ValueError(value)
|
| -
|
| - orig_value = value
|
| - value = value[1:-1]
|
| -
|
| - result = []
|
| - while value:
|
| - m = _strs.match(value)
|
| - if m is None:
|
| - raise ValueError(orig_value)
|
| -
|
| - result.append(m.group(1))
|
| - value = value[len(m.group(0)):]
|
| -
|
| - return tuple(result)
|
| -
|
| -def _path_from_importerror(exc, default):
|
| - # This is a hack, but sadly enough the necessary information
|
| - # isn't available otherwise.
|
| - m = re.match('^No module named (\S+)$', str(exc))
|
| - if m is not None:
|
| - return m.group(1)
|
| -
|
| - return default
|
| -
|
| -def os_listdir(path):
|
| - """
|
| - Deprecated name
|
| - """
|
| - warnings.warn("Use zipio.listdir instead of os_listdir",
|
| - DeprecationWarning)
|
| - return zipio.listdir(path)
|
| -
|
| -
|
| -def _code_to_file(co):
|
| - """ Convert code object to a .pyc pseudo-file """
|
| - return BytesIO(
|
| - imp.get_magic() + b'\0\0\0\0' + marshal.dumps(co))
|
| -
|
| -
|
| -def find_module(name, path=None):
|
| - """
|
| - A version of imp.find_module that works with zipped packages.
|
| - """
|
| - if path is None:
|
| - path = sys.path
|
| -
|
| - # Support for the PEP302 importer for normal imports:
|
| - # - Python 2.5 has pkgutil.ImpImporter
|
| - # - In setuptools 0.7 and later there's _pkgutil.ImpImporter
|
| - # - In earlier setuptools versions you pkg_resources.ImpWrapper
|
| - #
|
| - # XXX: This is a bit of a hack, should check if we can just rely on
|
| - # PEP302's get_code() method with all recent versions of pkgutil and/or
|
| - # setuptools (setuptools 0.6.latest, setuptools trunk and python2.[45])
|
| - #
|
| - # For python 3.3 this code should be replaced by code using importlib,
|
| - # for python 3.2 and 2.7 this should be cleaned up a lot.
|
| - try:
|
| - from pkgutil import ImpImporter
|
| - except ImportError:
|
| - try:
|
| - from _pkgutil import ImpImporter
|
| - except ImportError:
|
| - ImpImporter = pkg_resources.ImpWrapper
|
| -
|
| - namespace_path =[]
|
| - fp = None
|
| - for entry in path:
|
| - importer = pkg_resources.get_importer(entry)
|
| - if importer is None:
|
| - continue
|
| -
|
| - if sys.version_info[:2] >= (3,3) and hasattr(importer, 'find_loader'):
|
| - loader, portions = importer.find_loader(name)
|
| -
|
| - else:
|
| - loader = importer.find_module(name)
|
| - portions = []
|
| -
|
| - namespace_path.extend(portions)
|
| -
|
| - if loader is None: continue
|
| -
|
| - if isinstance(importer, ImpImporter):
|
| - filename = loader.filename
|
| - if filename.endswith('.pyc') or filename.endswith('.pyo'):
|
| - fp = open(filename, 'rb')
|
| - description = ('.pyc', 'rb', imp.PY_COMPILED)
|
| - return (fp, filename, description)
|
| -
|
| - elif filename.endswith('.py'):
|
| - if sys.version_info[0] == 2:
|
| - fp = open(filename, _READ_MODE)
|
| - else:
|
| - with open(filename, 'rb') as fp:
|
| - encoding = util.guess_encoding(fp)
|
| -
|
| - fp = open(filename, _READ_MODE, encoding=encoding)
|
| - description = ('.py', _READ_MODE, imp.PY_SOURCE)
|
| - return (fp, filename, description)
|
| -
|
| - else:
|
| - for _sfx, _mode, _type in imp.get_suffixes():
|
| - if _type == imp.C_EXTENSION and filename.endswith(_sfx):
|
| - description = (_sfx, 'rb', imp.C_EXTENSION)
|
| - break
|
| - else:
|
| - description = ('', '', imp.PKG_DIRECTORY)
|
| -
|
| - return (None, filename, description)
|
| -
|
| - if hasattr(loader, 'path'):
|
| - if loader.path.endswith('.pyc') or loader.path.endswith('.pyo'):
|
| - fp = open(loader.path, 'rb')
|
| - description = ('.pyc', 'rb', imp.PY_COMPILED)
|
| - return (fp, loader.path, description)
|
| -
|
| -
|
| - if hasattr(loader, 'get_source'):
|
| - source = loader.get_source(name)
|
| - fp = StringIO(source)
|
| - co = None
|
| -
|
| - else:
|
| - source = None
|
| -
|
| - if source is None:
|
| - if hasattr(loader, 'get_code'):
|
| - co = loader.get_code(name)
|
| - fp = _code_to_file(co)
|
| -
|
| - else:
|
| - fp = None
|
| - co = None
|
| -
|
| - pathname = os.path.join(entry, *name.split('.'))
|
| -
|
| - if isinstance(loader, zipimport.zipimporter):
|
| - # Check if this happens to be a wrapper module introduced by
|
| - # setuptools, if it is we return the actual extension.
|
| - zn = '/'.join(name.split('.'))
|
| - for _sfx, _mode, _type in imp.get_suffixes():
|
| - if _type == imp.C_EXTENSION:
|
| - p = loader.prefix + zn + _sfx
|
| - if loader._files is None:
|
| - loader_files = zipimport._zip_directory_cache[loader.archive]
|
| - else:
|
| - loader_files = loader._files
|
| -
|
| - if p in loader_files:
|
| - description = (_sfx, 'rb', imp.C_EXTENSION)
|
| - return (None, pathname + _sfx, description)
|
| -
|
| - if hasattr(loader, 'is_package') and loader.is_package(name):
|
| - return (None, pathname, ('', '', imp.PKG_DIRECTORY))
|
| -
|
| - if co is None:
|
| - if hasattr(loader, 'path'):
|
| - filename = loader.path
|
| - elif hasattr(loader, 'get_filename'):
|
| - filename = loader.get_filename(name)
|
| - if source is not None:
|
| - if filename.endswith(".pyc") or filename.endswith(".pyo"):
|
| - filename = filename[:-1]
|
| - else:
|
| - filename = None
|
| -
|
| - if filename is not None and (filename.endswith('.py') or filename.endswith('.pyw')):
|
| - return (fp, filename, ('.py', 'rU', imp.PY_SOURCE))
|
| - else:
|
| - if fp is not None:
|
| - fp.close()
|
| - return (None, filename, (os.path.splitext(filename)[-1], 'rb', imp.C_EXTENSION))
|
| -
|
| - else:
|
| - if hasattr(loader, 'path'):
|
| - return (fp, loader.path, ('.pyc', 'rb', imp.PY_COMPILED))
|
| - else:
|
| - return (fp, pathname + '.pyc', ('.pyc', 'rb', imp.PY_COMPILED))
|
| -
|
| - if namespace_path:
|
| - if fp is not None:
|
| - fp.close()
|
| - return (None, namespace_path[0], ('', namespace_path, imp.PKG_DIRECTORY))
|
| -
|
| - raise ImportError(name)
|
| -
|
| -def moduleInfoForPath(path):
|
| - for (ext, readmode, typ) in imp.get_suffixes():
|
| - if path.endswith(ext):
|
| - return os.path.basename(path)[:-len(ext)], readmode, typ
|
| - return None
|
| -
|
| -# A Public interface
|
| -import warnings
|
| -def AddPackagePath(packagename, path):
|
| - warnings.warn("Use addPackagePath instead of AddPackagePath",
|
| - DeprecationWarning)
|
| -
|
| - addPackagePath(packagename, path)
|
| -
|
| -def addPackagePath(packagename, path):
|
| - paths = _packagePathMap.get(packagename, [])
|
| - paths.append(path)
|
| - _packagePathMap[packagename] = paths
|
| -
|
| -_replacePackageMap = {}
|
| -
|
| -# This ReplacePackage mechanism allows modulefinder to work around the
|
| -# way the _xmlplus package injects itself under the name "xml" into
|
| -# sys.modules at runtime by calling ReplacePackage("_xmlplus", "xml")
|
| -# before running ModuleGraph.
|
| -def ReplacePackage(oldname, newname):
|
| - warnings.warn("use replacePackage instead of ReplacePackage",
|
| - DeprecationWarning)
|
| - replacePackage(oldname, newname)
|
| -
|
| -def replacePackage(oldname, newname):
|
| - _replacePackageMap[oldname] = newname
|
| -
|
| -
|
| -class DependencyInfo (namedtuple("DependencyInfo", ["conditional", "function", "tryexcept", "fromlist"])):
|
| - __slots__ = ()
|
| -
|
| - def _merged(self, other):
|
| - if (not self.conditional and not self.function and not self.tryexcept) \
|
| - or (not other.conditional and not other.function and not other.tryexcept):
|
| - return DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=self.fromlist and other.fromlist)
|
| -
|
| - else:
|
| - return DependencyInfo(
|
| - conditional=self.conditional or other.conditional,
|
| - function=self.function or other.function,
|
| - tryexcept=self.tryexcept or other.tryexcept,
|
| - fromlist=self.fromlist and other.fromlist)
|
| -
|
| -
|
| -class Node(object):
|
| - def __init__(self, identifier):
|
| - self.debug = 0
|
| - self.graphident = identifier
|
| - self.identifier = identifier
|
| - self._namespace = {}
|
| - self.filename = None
|
| - self.packagepath = None
|
| - self.code = None
|
| - # The set of global names that are assigned to in the module.
|
| - # This includes those names imported through starimports of
|
| - # Python modules.
|
| - self.globalnames = set()
|
| - # The set of starimports this module did that could not be
|
| - # resolved, ie. a starimport from a non-Python module.
|
| - self.starimports = set()
|
| -
|
| - def __contains__(self, name):
|
| - return name in self._namespace
|
| -
|
| - def __getitem__(self, name):
|
| - return self._namespace[name]
|
| -
|
| - def __setitem__(self, name, value):
|
| - self._namespace[name] = value
|
| -
|
| - def get(self, *args):
|
| - return self._namespace.get(*args)
|
| -
|
| - def __cmp__(self, other):
|
| - try:
|
| - otherIdent = getattr(other, 'graphident')
|
| - except AttributeError:
|
| - return NotImplemented
|
| -
|
| - return cmp(self.graphident, otherIdent)
|
| -
|
| - def __eq__(self, other):
|
| - try:
|
| - otherIdent = getattr(other, 'graphident')
|
| - except AttributeError:
|
| - return False
|
| -
|
| - return self.graphident == otherIdent
|
| -
|
| - def __ne__(self, other):
|
| - try:
|
| - otherIdent = getattr(other, 'graphident')
|
| - except AttributeError:
|
| - return True
|
| -
|
| - return self.graphident != otherIdent
|
| -
|
| - def __lt__(self, other):
|
| - try:
|
| - otherIdent = getattr(other, 'graphident')
|
| - except AttributeError:
|
| - return NotImplemented
|
| -
|
| - return self.graphident < otherIdent
|
| -
|
| - def __le__(self, other):
|
| - try:
|
| - otherIdent = getattr(other, 'graphident')
|
| - except AttributeError:
|
| - return NotImplemented
|
| -
|
| - return self.graphident <= otherIdent
|
| -
|
| - def __gt__(self, other):
|
| - try:
|
| - otherIdent = getattr(other, 'graphident')
|
| - except AttributeError:
|
| - return NotImplemented
|
| -
|
| - return self.graphident > otherIdent
|
| -
|
| - def __ge__(self, other):
|
| - try:
|
| - otherIdent = getattr(other, 'graphident')
|
| - except AttributeError:
|
| - return NotImplemented
|
| -
|
| - return self.graphident >= otherIdent
|
| -
|
| -
|
| - def __hash__(self):
|
| - return hash(self.graphident)
|
| -
|
| - def infoTuple(self):
|
| - return (self.identifier,)
|
| -
|
| - def __repr__(self):
|
| - return '%s%r' % (type(self).__name__, self.infoTuple())
|
| -
|
| -class Alias(str):
|
| - pass
|
| -
|
| -class AliasNode(Node):
|
| - def __init__(self, name, node):
|
| - super(AliasNode, self).__init__(name)
|
| - for k in 'identifier', 'packagepath', '_namespace', 'globalnames', 'starimports':
|
| - setattr(self, k, getattr(node, k, None))
|
| -
|
| - def infoTuple(self):
|
| - return (self.graphident, self.identifier)
|
| -
|
| -class BadModule(Node):
|
| - pass
|
| -
|
| -class ExcludedModule(BadModule):
|
| - pass
|
| -
|
| -class MissingModule(BadModule):
|
| - pass
|
| -
|
| -class Script(Node):
|
| - def __init__(self, filename):
|
| - super(Script, self).__init__(filename)
|
| - self.filename = filename
|
| -
|
| - def infoTuple(self):
|
| - return (self.filename,)
|
| -
|
| -class BaseModule(Node):
|
| - def __init__(self, name, filename=None, path=None):
|
| - super(BaseModule, self).__init__(name)
|
| - self.filename = filename
|
| - self.packagepath = path
|
| -
|
| - def infoTuple(self):
|
| - return tuple(filter(None, (self.identifier, self.filename, self.packagepath)))
|
| -
|
| -class BuiltinModule(BaseModule):
|
| - pass
|
| -
|
| -class SourceModule(BaseModule):
|
| - pass
|
| -
|
| -class InvalidSourceModule(SourceModule):
|
| - pass
|
| -
|
| -class CompiledModule(BaseModule):
|
| - pass
|
| -
|
| -class InvalidCompiledModule(BaseModule):
|
| - pass
|
| -
|
| -class Package(BaseModule):
|
| - pass
|
| -
|
| -class NamespacePackage(Package):
|
| - pass
|
| -
|
| -class Extension(BaseModule):
|
| - pass
|
| -
|
| -class FlatPackage(BaseModule): # nocoverage
|
| - def __init__(self, *args, **kwds):
|
| - warnings.warn("This class will be removed in a future version of modulegraph",
|
| - DeprecationWarning)
|
| - super(FlatPackage, *args, **kwds)
|
| -
|
| -class ArchiveModule(BaseModule): # nocoverage
|
| - def __init__(self, *args, **kwds):
|
| - warnings.warn("This class will be removed in a future version of modulegraph",
|
| - DeprecationWarning)
|
| - super(FlatPackage, *args, **kwds)
|
| -
|
| -# HTML templates for ModuleGraph generator
|
| -header = """\
|
| -<html>
|
| - <head>
|
| - <title>%(TITLE)s</title>
|
| - <style>
|
| - .node { margin:1em 0; }
|
| - </style>
|
| - </head>
|
| - <body>
|
| - <h1>%(TITLE)s</h1>"""
|
| -entry = """
|
| -<div class="node">
|
| - <a name="%(NAME)s" />
|
| - %(CONTENT)s
|
| -</div>"""
|
| -contpl = """<tt>%(NAME)s</tt> %(TYPE)s"""
|
| -contpl_linked = """\
|
| -<a target="code" href="%(URL)s" type="text/plain"><tt>%(NAME)s</tt></a>"""
|
| -imports = """\
|
| - <div class="import">
|
| -%(HEAD)s:
|
| - %(LINKS)s
|
| - </div>
|
| -"""
|
| -footer = """
|
| - </body>
|
| -</html>"""
|
| -
|
| -def _ast_names(names):
|
| - result = []
|
| - for nm in names:
|
| - if isinstance(nm, ast.alias):
|
| - result.append(nm.name)
|
| - else:
|
| - result.append(nm)
|
| - return result
|
| -
|
| -
|
| -if sys.version_info[0] == 2:
|
| - DEFAULT_IMPORT_LEVEL= -1
|
| -else:
|
| - DEFAULT_IMPORT_LEVEL= 0
|
| -
|
| -class _Visitor (ast.NodeVisitor):
|
| - def __init__(self, graph, module):
|
| - self._graph = graph
|
| - self._module = module
|
| - self._level = DEFAULT_IMPORT_LEVEL
|
| - self._in_if = [False]
|
| - self._in_def = [False]
|
| - self._in_tryexcept = [False]
|
| -
|
| - @property
|
| - def in_if(self):
|
| - return self._in_if[-1]
|
| -
|
| - @property
|
| - def in_def(self):
|
| - return self._in_def[-1]
|
| -
|
| - @property
|
| - def in_tryexcept(self):
|
| - return self._in_tryexcept[-1]
|
| -
|
| - def _process_import(self, name, fromlist, level):
|
| -
|
| - if sys.version_info[0] == 2:
|
| - if name == '__future__' and 'absolute_import' in (fromlist or ()):
|
| - self._level = 0
|
| -
|
| - have_star = False
|
| - if fromlist is not None:
|
| - fromlist = set(fromlist)
|
| - if '*' in fromlist:
|
| - fromlist.remove('*')
|
| - have_star = True
|
| -
|
| - imported_module = self._graph._safe_import_hook(name,
|
| - self._module, fromlist, level, attr=DependencyInfo(
|
| - conditional=self.in_if,
|
| - tryexcept=self.in_tryexcept,
|
| - function=self.in_def,
|
| - fromlist=False,
|
| - ))[0]
|
| - if have_star:
|
| - self._module.globalnames.update(imported_module.globalnames)
|
| - self._module.starimports.update(imported_module.starimports)
|
| - if imported_module.code is None:
|
| - self._module.starimports.add(name)
|
| -
|
| -
|
| - def visit_Import(self, node):
|
| - for nm in _ast_names(node.names):
|
| - self._process_import(nm, None, self._level)
|
| -
|
| - def visit_ImportFrom(self, node):
|
| - level = node.level if node.level != 0 else self._level
|
| - self._process_import(node.module or '', _ast_names(node.names), level)
|
| -
|
| - def visit_If(self, node):
|
| - self._in_if.append(True)
|
| - self.generic_visit(node)
|
| - self._in_if.pop()
|
| -
|
| - def visit_FunctionDef(self, node):
|
| - self._in_def.append(True)
|
| - self.generic_visit(node)
|
| - self._in_def.pop()
|
| -
|
| - def visit_Try(self, node):
|
| - self._in_tryexcept.append(True)
|
| - self.generic_visit(node)
|
| - self._in_tryexcept.pop()
|
| -
|
| - def visit_ExceptHandler(self, node):
|
| - self._in_tryexcept.append(True)
|
| - self.generic_visit(node)
|
| - self._in_tryexcept.pop()
|
| -
|
| - def visit_TryExcept(self, node):
|
| - self._in_tryexcept.append(True)
|
| - self.generic_visit(node)
|
| - self._in_tryexcept.pop()
|
| -
|
| - def visit_ExceptHandler(self, node):
|
| - self._in_tryexcept.append(True)
|
| - self.generic_visit(node)
|
| - self._in_tryexcept.pop()
|
| -
|
| - def visit_Expression(self, node):
|
| - # Expression node's cannot contain import statements or
|
| - # other nodes that are relevant for us.
|
| - pass
|
| -
|
| - # Expression isn't actually used as such in AST trees,
|
| - # therefore define visitors for all kinds of expression nodes.
|
| - visit_BoolOp = visit_Expression
|
| - visit_BinOp = visit_Expression
|
| - visit_UnaryOp = visit_Expression
|
| - visit_Lambda = visit_Expression
|
| - visit_IfExp = visit_Expression
|
| - visit_Dict = visit_Expression
|
| - visit_Set = visit_Expression
|
| - visit_ListComp = visit_Expression
|
| - visit_SetComp = visit_Expression
|
| - visit_ListComp = visit_Expression
|
| - visit_GeneratorExp = visit_Expression
|
| - visit_Compare = visit_Expression
|
| - visit_Yield = visit_Expression
|
| - visit_YieldFrom = visit_Expression
|
| - visit_Await = visit_Expression
|
| - visit_Call = visit_Expression
|
| -
|
| -
|
| -
|
| -class ModuleGraph(ObjectGraph):
|
| - def __init__(self, path=None, excludes=(), replace_paths=(), implies=(), graph=None, debug=0):
|
| - super(ModuleGraph, self).__init__(graph=graph, debug=debug)
|
| - if path is None:
|
| - path = sys.path
|
| - self.path = path
|
| - self.lazynodes = {}
|
| - # excludes is stronger than implies
|
| - self.lazynodes.update(dict(implies))
|
| - for m in excludes:
|
| - self.lazynodes[m] = None
|
| - self.replace_paths = replace_paths
|
| -
|
| - self.nspackages = self._calc_setuptools_nspackages()
|
| -
|
| - def _calc_setuptools_nspackages(self):
|
| - # Setuptools has some magic handling for namespace
|
| - # packages when using 'install --single-version-externally-managed'
|
| - # (used by system packagers and also by pip)
|
| - #
|
| - # When this option is used namespace packages are writting to
|
| - # disk *without* an __init__.py file, which means the regular
|
| - # import machinery will not find them.
|
| - #
|
| - # We therefore explicitly look for the hack used by
|
| - # setuptools to get this kind of namespace packages to work.
|
| -
|
| - pkgmap = {}
|
| -
|
| - try:
|
| - from pkgutil import ImpImporter
|
| - except ImportError:
|
| - try:
|
| - from _pkgutil import ImpImporter
|
| - except ImportError:
|
| - ImpImporter = pkg_resources.ImpWrapper
|
| -
|
| - if sys.version_info[:2] >= (3,3):
|
| - import importlib.machinery
|
| - ImpImporter = importlib.machinery.FileFinder
|
| -
|
| - for entry in self.path:
|
| - importer = pkg_resources.get_importer(entry)
|
| -
|
| - if isinstance(importer, ImpImporter):
|
| - try:
|
| - ldir = os.listdir(entry)
|
| - except os.error:
|
| - continue
|
| -
|
| - for fn in ldir:
|
| - if fn.endswith('-nspkg.pth'):
|
| - fp = open(os.path.join(entry, fn), 'rU')
|
| - try:
|
| - for ln in fp:
|
| - for pfx in _SETUPTOOLS_NAMESPACEPKG_PTHs:
|
| - if ln.startswith(pfx):
|
| - try:
|
| - start = len(pfx)-2
|
| - stop = ln.index(')', start)+1
|
| - except ValueError:
|
| - continue
|
| -
|
| - pkg = _eval_str_tuple(ln[start:stop])
|
| - identifier = ".".join(pkg)
|
| - subdir = os.path.join(entry, *pkg)
|
| - if os.path.exists(os.path.join(subdir, '__init__.py')):
|
| - # There is a real __init__.py, ignore the setuptools hack
|
| - continue
|
| -
|
| - if identifier in pkgmap:
|
| - pkgmap[identifier].append(subdir)
|
| - else:
|
| - pkgmap[identifier] = [subdir]
|
| - break
|
| - finally:
|
| - fp.close()
|
| -
|
| - return pkgmap
|
| -
|
| - def implyNodeReference(self, node, other, edge_data=None):
|
| - """
|
| - Imply that one node depends on another.
|
| - other may be a module name or another node.
|
| -
|
| - For use by extension modules and tricky import code
|
| - """
|
| - if isinstance(other, Node):
|
| - self._updateReference(node, other, edge_data)
|
| -
|
| - else:
|
| - if isinstance(other, tuple):
|
| - raise ValueError(other)
|
| -
|
| - others = self._safe_import_hook(other, node, None)
|
| - for other in others:
|
| - self._updateReference(node, other, edge_data)
|
| -
|
| -
|
| - def getReferences(self, fromnode):
|
| - """
|
| - Yield all nodes that 'fromnode' dependes on (that is,
|
| - all modules that 'fromnode' imports.
|
| - """
|
| - node = self.findNode(fromnode)
|
| - out_edges, _ = self.get_edges(node)
|
| - return out_edges
|
| -
|
| - def getReferers(self, tonode, collapse_missing_modules=True):
|
| - node = self.findNode(tonode)
|
| - _, in_edges = self.get_edges(node)
|
| -
|
| - if collapse_missing_modules:
|
| - for n in in_edges:
|
| - if isinstance(n, MissingModule):
|
| - for n in self.getReferers(n, False):
|
| - yield n
|
| -
|
| - else:
|
| - yield n
|
| -
|
| - else:
|
| - for n in in_edges:
|
| - yield n
|
| -
|
| - def hasEdge(self, fromnode, tonode):
|
| - """ Return True iff there is an edge from 'fromnode' to 'tonode' """
|
| - fromnode = self.findNode(fromnode)
|
| - tonode = self.findNode(tonode)
|
| -
|
| - return self.graph.edge_by_node(fromnode, tonode) is not None
|
| -
|
| -
|
| - def foldReferences(self, packagenode):
|
| - """
|
| - Create edges to/from 'packagenode' based on the
|
| - edges to/from modules in package. The module nodes
|
| - are then hidden.
|
| - """
|
| - pkg = self.findNode(packagenode)
|
| -
|
| - for n in self.nodes():
|
| - if not n.identifier.startswith(pkg.identifier + '.'):
|
| - continue
|
| -
|
| - iter_out, iter_inc = n.get_edges()
|
| - for other in iter_out:
|
| - if other.identifier.startswith(pkg.identifier + '.'):
|
| - continue
|
| -
|
| - if not self.hasEdge(pkg, other):
|
| - # Ignore circular dependencies
|
| - self._updateReference(pkg, other, 'pkg-internal-import')
|
| -
|
| - for other in iter_in:
|
| - if other.identifier.startswith(pkg.identifier + '.'):
|
| - # Ignore circular dependencies
|
| - continue
|
| -
|
| - if not self.hasEdge(other, pkg):
|
| - self._updateReference(other, pkg, 'pkg-import')
|
| -
|
| - self.graph.hide_node(n)
|
| -
|
| - # TODO: unfoldReferences(pkg) that restore the submodule nodes and
|
| - # removes 'pkg-import' and 'pkg-internal-import' edges. Care should
|
| - # be taken to ensure that references are correct if multiple packages
|
| - # are folded and then one of them in unfolded
|
| -
|
| -
|
| - def _updateReference(self, fromnode, tonode, edge_data):
|
| - try:
|
| - ed = self.edgeData(fromnode, tonode)
|
| - except (KeyError, GraphError): # XXX: Why 'GraphError'
|
| - return self.createReference(fromnode, tonode, edge_data)
|
| -
|
| - if not (isinstance(ed, DependencyInfo) and isinstance(edge_data, DependencyInfo)):
|
| - self.updateEdgeData(fromnode, tonode, edge_data)
|
| - else:
|
| - self.updateEdgeData(fromnode, tonode, ed._merged(edge_data))
|
| -
|
| -
|
| - def createReference(self, fromnode, tonode, edge_data='direct'):
|
| - """
|
| - Create a reference from fromnode to tonode
|
| - """
|
| - return super(ModuleGraph, self).createReference(fromnode, tonode, edge_data=edge_data)
|
| -
|
| - def findNode(self, name):
|
| - """
|
| - Find a node by identifier. If a node by that identifier exists,
|
| - it will be returned.
|
| -
|
| - If a lazy node exists by that identifier with no dependencies (excluded),
|
| - it will be instantiated and returned.
|
| -
|
| - If a lazy node exists by that identifier with dependencies, it and its
|
| - dependencies will be instantiated and scanned for additional dependencies.
|
| - """
|
| - data = super(ModuleGraph, self).findNode(name)
|
| - if data is not None:
|
| - return data
|
| - if name in self.lazynodes:
|
| - deps = self.lazynodes.pop(name)
|
| - if deps is None:
|
| - # excluded module
|
| - m = self.createNode(ExcludedModule, name)
|
| - elif isinstance(deps, Alias):
|
| - other = self._safe_import_hook(deps, None, None).pop()
|
| - m = self.createNode(AliasNode, name, other)
|
| - self.implyNodeReference(m, other)
|
| -
|
| - else:
|
| - m = self._safe_import_hook(name, None, None).pop()
|
| - for dep in deps:
|
| - self.implyNodeReference(m, dep)
|
| - return m
|
| -
|
| - if name in self.nspackages:
|
| - # name is a --single-version-externally-managed
|
| - # namespace package (setuptools/distribute)
|
| - pathnames = self.nspackages.pop(name)
|
| - m = self.createNode(NamespacePackage, name)
|
| -
|
| - # FIXME: The filename must be set to a string to ensure that py2app
|
| - # works, it is not clear yet why that is. Setting to None would be
|
| - # cleaner.
|
| - m.filename = '-'
|
| - m.packagepath = _namespace_package_path(name, pathnames, self.path)
|
| -
|
| - # As per comment at top of file, simulate runtime packagepath additions.
|
| - m.packagepath = m.packagepath + _packagePathMap.get(name, [])
|
| - return m
|
| -
|
| - return None
|
| -
|
| - def run_script(self, pathname, caller=None):
|
| - """
|
| - Create a node by path (not module name). It is expected to be a Python
|
| - source file, and will be scanned for dependencies.
|
| - """
|
| - self.msg(2, "run_script", pathname)
|
| - pathname = os.path.realpath(pathname)
|
| - m = self.findNode(pathname)
|
| - if m is not None:
|
| - return m
|
| -
|
| - if sys.version_info[0] != 2:
|
| - with open(pathname, 'rb') as fp:
|
| - encoding = util.guess_encoding(fp)
|
| -
|
| - with open(pathname, _READ_MODE, encoding=encoding) as fp:
|
| - contents = fp.read() + '\n'
|
| -
|
| - else:
|
| - with open(pathname, _READ_MODE) as fp:
|
| - contents = fp.read() + '\n'
|
| -
|
| - co = compile(contents, pathname, 'exec', ast.PyCF_ONLY_AST, True)
|
| - m = self.createNode(Script, pathname)
|
| - self._updateReference(caller, m, None)
|
| - self._scan_code(co, m)
|
| - m.code = compile(co, pathname, 'exec', 0, True)
|
| - if self.replace_paths:
|
| - m.code = self._replace_paths_in_code(m.code)
|
| - return m
|
| -
|
| - def import_hook(self, name, caller=None, fromlist=None, level=DEFAULT_IMPORT_LEVEL, attr=None):
|
| - """
|
| - Import a module
|
| -
|
| - Return the set of modules that are imported
|
| - """
|
| - self.msg(3, "import_hook", name, caller, fromlist, level)
|
| - parent = self._determine_parent(caller)
|
| - q, tail = self._find_head_package(parent, name, level)
|
| - m = self._load_tail(q, tail)
|
| - modules = [m]
|
| - if fromlist and m.packagepath:
|
| - for s in self._ensure_fromlist(m, fromlist):
|
| - if s not in modules:
|
| - modules.append(s)
|
| - for m in modules:
|
| - self._updateReference(caller, m, edge_data=attr)
|
| - return modules
|
| -
|
| - def _determine_parent(self, caller):
|
| - """
|
| - Determine the package containing a node
|
| - """
|
| - self.msgin(4, "determine_parent", caller)
|
| - parent = None
|
| - if caller:
|
| - pname = caller.identifier
|
| -
|
| - if isinstance(caller, Package):
|
| - parent = caller
|
| -
|
| - elif '.' in pname:
|
| - pname = pname[:pname.rfind('.')]
|
| - parent = self.findNode(pname)
|
| -
|
| - elif caller.packagepath:
|
| - # XXX: I have no idea why this line
|
| - # is necessary.
|
| - parent = self.findNode(pname)
|
| -
|
| -
|
| - self.msgout(4, "determine_parent ->", parent)
|
| - return parent
|
| -
|
| - def _find_head_package(self, parent, name, level=DEFAULT_IMPORT_LEVEL):
|
| - """
|
| - Given a calling parent package and an import name determine the containing
|
| - package for the name
|
| - """
|
| - self.msgin(4, "find_head_package", parent, name, level)
|
| - if '.' in name:
|
| - head, tail = name.split('.', 1)
|
| - else:
|
| - head, tail = name, ''
|
| -
|
| - if level == -1:
|
| - if parent:
|
| - qname = parent.identifier + '.' + head
|
| - else:
|
| - qname = head
|
| -
|
| - elif level == 0:
|
| - qname = head
|
| -
|
| - # Absolute import, ignore the parent
|
| - parent = None
|
| -
|
| - else:
|
| - if parent is None:
|
| - self.msg(2, "Relative import outside of package")
|
| - raise ImportError("Relative import outside of package (name=%r, parent=%r, level=%r)"%(name, parent, level))
|
| -
|
| - for i in range(level-1):
|
| - if '.' not in parent.identifier:
|
| - self.msg(2, "Relative import outside of package")
|
| - raise ImportError("Relative import outside of package (name=%r, parent=%r, level=%r)"%(name, parent, level))
|
| -
|
| - p_fqdn = parent.identifier.rsplit('.', 1)[0]
|
| - new_parent = self.findNode(p_fqdn)
|
| - if new_parent is None:
|
| - self.msg(2, "Relative import outside of package")
|
| - raise ImportError("Relative import outside of package (name=%r, parent=%r, level=%r)"%(name, parent, level))
|
| -
|
| - assert new_parent is not parent, (new_parent, parent)
|
| - parent = new_parent
|
| -
|
| - if head:
|
| - qname = parent.identifier + '.' + head
|
| - else:
|
| - qname = parent.identifier
|
| -
|
| -
|
| - q = self._import_module(head, qname, parent)
|
| - if q:
|
| - self.msgout(4, "find_head_package ->", (q, tail))
|
| - return q, tail
|
| - if parent:
|
| - qname = head
|
| - parent = None
|
| - q = self._import_module(head, qname, parent)
|
| - if q:
|
| - self.msgout(4, "find_head_package ->", (q, tail))
|
| - return q, tail
|
| - self.msgout(4, "raise ImportError: No module named", qname)
|
| - raise ImportError("No module named " + qname)
|
| -
|
| - def _load_tail(self, mod, tail):
|
| - self.msgin(4, "load_tail", mod, tail)
|
| - result = mod
|
| - while tail:
|
| - i = tail.find('.')
|
| - if i < 0: i = len(tail)
|
| - head, tail = tail[:i], tail[i+1:]
|
| - mname = "%s.%s" % (result.identifier, head)
|
| - result = self._import_module(head, mname, result)
|
| - if result is None:
|
| - result = self.createNode(MissingModule, mname)
|
| - #self.msgout(4, "raise ImportError: No module named", mname)
|
| - #raise ImportError("No module named " + mname)
|
| - self.msgout(4, "load_tail ->", result)
|
| - return result
|
| -
|
| - def _ensure_fromlist(self, m, fromlist):
|
| - fromlist = set(fromlist)
|
| - self.msg(4, "ensure_fromlist", m, fromlist)
|
| - if '*' in fromlist:
|
| - fromlist.update(self._find_all_submodules(m))
|
| - fromlist.remove('*')
|
| - for sub in fromlist:
|
| - submod = m.get(sub)
|
| - if submod is None:
|
| - if sub in m.globalnames:
|
| - # Name is a global in the module
|
| - continue
|
| - # XXX: ^^^ need something simular for names imported
|
| - # by 'm'.
|
| -
|
| - fullname = m.identifier + '.' + sub
|
| - submod = self._import_module(sub, fullname, m)
|
| - if submod is None:
|
| - raise ImportError("No module named " + fullname)
|
| - yield submod
|
| -
|
| - def _find_all_submodules(self, m):
|
| - if not m.packagepath:
|
| - return
|
| - # 'suffixes' used to be a list hardcoded to [".py", ".pyc", ".pyo"].
|
| - # But we must also collect Python extension modules - although
|
| - # we cannot separate normal dlls from Python extensions.
|
| - suffixes = [triple[0] for triple in imp.get_suffixes()]
|
| - for path in m.packagepath:
|
| - try:
|
| - names = zipio.listdir(path)
|
| - except (os.error, IOError):
|
| - self.msg(2, "can't list directory", path)
|
| - continue
|
| - for info in (moduleInfoForPath(p) for p in names):
|
| - if info is None: continue
|
| - if info[0] != '__init__':
|
| - yield info[0]
|
| -
|
| - def _import_module(self, partname, fqname, parent):
|
| - # XXX: Review me for use with absolute imports.
|
| - self.msgin(3, "import_module", partname, fqname, parent)
|
| - m = self.findNode(fqname)
|
| - if m is not None:
|
| - self.msgout(3, "import_module ->", m)
|
| - if parent:
|
| - self._updateReference(m, parent, edge_data=DependencyInfo(
|
| - conditional=False, fromlist=False, function=False, tryexcept=False
|
| - ))
|
| - return m
|
| -
|
| - if parent and parent.packagepath is None:
|
| - self.msgout(3, "import_module -> None")
|
| - return None
|
| -
|
| - try:
|
| - searchpath = None
|
| - if parent is not None and parent.packagepath:
|
| - searchpath = parent.packagepath
|
| -
|
| - fp, pathname, stuff = self._find_module(partname,
|
| - searchpath, parent)
|
| -
|
| - except ImportError:
|
| - self.msgout(3, "import_module ->", None)
|
| - return None
|
| -
|
| - try:
|
| - m = self._load_module(fqname, fp, pathname, stuff)
|
| -
|
| - finally:
|
| - if fp is not None:
|
| - fp.close()
|
| -
|
| - if parent:
|
| - self.msgout(4, "create reference", m, "->", parent)
|
| - self._updateReference(m, parent, edge_data=DependencyInfo(
|
| - conditional=False, fromlist=False, function=False, tryexcept=False
|
| - ))
|
| - parent[partname] = m
|
| -
|
| - self.msgout(3, "import_module ->", m)
|
| - return m
|
| -
|
| - def _load_module(self, fqname, fp, pathname, info):
|
| - suffix, mode, typ = info
|
| - self.msgin(2, "load_module", fqname, fp and "fp", pathname)
|
| -
|
| - if typ == imp.PKG_DIRECTORY:
|
| - if isinstance(mode, (list, tuple)):
|
| - packagepath = mode
|
| - else:
|
| - packagepath = []
|
| -
|
| - m = self._load_package(fqname, pathname, packagepath)
|
| - self.msgout(2, "load_module ->", m)
|
| - return m
|
| -
|
| - if typ == imp.PY_SOURCE:
|
| - contents = fp.read()
|
| - if isinstance(contents, bytes):
|
| - contents += b'\n'
|
| - else:
|
| - contents += '\n'
|
| -
|
| - try:
|
| - co = compile(contents, pathname, 'exec', ast.PyCF_ONLY_AST, True)
|
| - #co = compile(contents, pathname, 'exec', 0, True)
|
| - except SyntaxError:
|
| - co = None
|
| - cls = InvalidSourceModule
|
| -
|
| - else:
|
| - cls = SourceModule
|
| -
|
| - elif typ == imp.PY_COMPILED:
|
| - if fp.read(4) != imp.get_magic():
|
| - self.msgout(2, "raise ImportError: Bad magic number", pathname)
|
| - co = None
|
| - cls = InvalidCompiledModule
|
| -
|
| - else:
|
| - fp.read(4)
|
| - try:
|
| - co = marshal.loads(fp.read())
|
| - cls = CompiledModule
|
| - except Exception:
|
| - co = None
|
| - cls = InvalidCompiledModule
|
| -
|
| - elif typ == imp.C_BUILTIN:
|
| - cls = BuiltinModule
|
| - co = None
|
| -
|
| - else:
|
| - cls = Extension
|
| - co = None
|
| -
|
| - m = self.createNode(cls, fqname)
|
| - m.filename = pathname
|
| - if co is not None:
|
| - self._scan_code(co, m)
|
| -
|
| - if isinstance(co, ast.AST):
|
| - co = compile(co, pathname, 'exec', 0, True)
|
| - if self.replace_paths:
|
| - co = self._replace_paths_in_code(co)
|
| - m.code = co
|
| -
|
| -
|
| - self.msgout(2, "load_module ->", m)
|
| - return m
|
| -
|
| - def _safe_import_hook(self, name, caller, fromlist, level=DEFAULT_IMPORT_LEVEL, attr=None):
|
| - # wrapper for self.import_hook() that won't raise ImportError
|
| - try:
|
| - mods = self.import_hook(name, caller, level=level, attr=attr)
|
| - except ImportError as msg:
|
| - self.msg(2, "ImportError:", str(msg))
|
| - m = self.createNode(MissingModule, _path_from_importerror(msg, name))
|
| - self._updateReference(caller, m, edge_data=attr)
|
| -
|
| - else:
|
| - assert len(mods) == 1
|
| - m = list(mods)[0]
|
| -
|
| - subs = [m]
|
| - if isinstance(attr, DependencyInfo):
|
| - attr = attr._replace(fromlist=True)
|
| - for sub in (fromlist or ()):
|
| - # If this name is in the module namespace already,
|
| - # then add the entry to the list of substitutions
|
| - if sub in m:
|
| - sm = m[sub]
|
| - if sm is not None:
|
| - if sm not in subs:
|
| - self._updateReference(caller, sm, edge_data=attr)
|
| - subs.append(sm)
|
| - continue
|
| -
|
| - elif sub in m.globalnames:
|
| - # Global variable in the module, ignore
|
| - continue
|
| -
|
| -
|
| - # See if we can load it
|
| - # fullname = name + '.' + sub
|
| - fullname = m.identifier + '.' + sub
|
| - #else:
|
| - # print("XXX", repr(name), repr(sub), repr(caller), repr(m))
|
| - sm = self.findNode(fullname)
|
| - if sm is None:
|
| - try:
|
| - sm = self.import_hook(name, caller, fromlist=[sub], level=level, attr=attr)
|
| - except ImportError as msg:
|
| - self.msg(2, "ImportError:", str(msg))
|
| - #sm = self.createNode(MissingModule, _path_from_importerror(msg, fullname))
|
| - sm = self.createNode(MissingModule, fullname)
|
| - else:
|
| - sm = self.findNode(fullname)
|
| - if sm is None:
|
| - sm = self.createNode(MissingModule, fullname)
|
| -
|
| - m[sub] = sm
|
| - if sm is not None:
|
| - self._updateReference(m, sm, edge_data=attr)
|
| - self._updateReference(caller, sm, edge_data=attr)
|
| - if sm not in subs:
|
| - subs.append(sm)
|
| - return subs
|
| -
|
| - def _scan_code(self, co, m):
|
| - if isinstance(co, ast.AST):
|
| - #return self._scan_bytecode(compile(co, '-', 'exec', 0, True), m)
|
| - self._scan_ast(co, m)
|
| - self._scan_bytecode_stores(
|
| - compile(co, '-', 'exec', 0, True), m)
|
| -
|
| - else:
|
| - self._scan_bytecode(co, m)
|
| -
|
| - def _scan_ast(self, co, m):
|
| - visitor = _Visitor(self, m)
|
| - visitor.visit(co)
|
| -
|
| - def _scan_bytecode_stores(self, co, m,
|
| - STORE_NAME=_Bchr(dis.opname.index('STORE_NAME')),
|
| - STORE_GLOBAL=_Bchr(dis.opname.index('STORE_GLOBAL')),
|
| - HAVE_ARGUMENT=_Bchr(dis.HAVE_ARGUMENT),
|
| - unpack=struct.unpack):
|
| -
|
| - extended_import = bool(sys.version_info[:2] >= (2,5))
|
| -
|
| - code = co.co_code
|
| - constants = co.co_consts
|
| - n = len(code)
|
| - i = 0
|
| -
|
| - while i < n:
|
| - c = code[i]
|
| - i += 1
|
| - if c >= HAVE_ARGUMENT:
|
| - i = i+2
|
| -
|
| - if c == STORE_NAME or c == STORE_GLOBAL:
|
| - # keep track of all global names that are assigned to
|
| - oparg = unpack('<H', code[i - 2:i])[0]
|
| - name = co.co_names[oparg]
|
| - m.globalnames.add(name)
|
| -
|
| - cotype = type(co)
|
| - for c in constants:
|
| - if isinstance(c, cotype):
|
| - self._scan_bytecode_stores(c, m)
|
| -
|
| - def _scan_bytecode(self, co, m,
|
| - HAVE_ARGUMENT=_Bchr(dis.HAVE_ARGUMENT),
|
| - LOAD_CONST=_Bchr(dis.opname.index('LOAD_CONST')),
|
| - IMPORT_NAME=_Bchr(dis.opname.index('IMPORT_NAME')),
|
| - IMPORT_FROM=_Bchr(dis.opname.index('IMPORT_FROM')),
|
| - STORE_NAME=_Bchr(dis.opname.index('STORE_NAME')),
|
| - STORE_GLOBAL=_Bchr(dis.opname.index('STORE_GLOBAL')),
|
| - unpack=struct.unpack):
|
| -
|
| - # Python >=2.5: LOAD_CONST flags, LOAD_CONST names, IMPORT_NAME name
|
| - # Python < 2.5: LOAD_CONST names, IMPORT_NAME name
|
| - extended_import = bool(sys.version_info[:2] >= (2,5))
|
| -
|
| - code = co.co_code
|
| - constants = co.co_consts
|
| - n = len(code)
|
| - i = 0
|
| -
|
| - level = None
|
| - fromlist = None
|
| -
|
| - while i < n:
|
| - c = code[i]
|
| - i += 1
|
| - if c >= HAVE_ARGUMENT:
|
| - i = i+2
|
| -
|
| - if c == IMPORT_NAME:
|
| - if extended_import:
|
| - assert code[i-9] == LOAD_CONST
|
| - assert code[i-6] == LOAD_CONST
|
| - arg1, arg2 = unpack('<xHxH', code[i-9:i-3])
|
| - level = co.co_consts[arg1]
|
| - fromlist = co.co_consts[arg2]
|
| - else:
|
| - assert code[-6] == LOAD_CONST
|
| - arg1, = unpack('<xH', code[i-6:i-3])
|
| - level = -1
|
| - fromlist = co.co_consts[arg1]
|
| -
|
| - assert fromlist is None or type(fromlist) is tuple
|
| - oparg, = unpack('<H', code[i - 2:i])
|
| - name = co.co_names[oparg]
|
| - have_star = False
|
| - if fromlist is not None:
|
| - fromlist = set(fromlist)
|
| - if '*' in fromlist:
|
| - fromlist.remove('*')
|
| - have_star = True
|
| -
|
| - #self.msgin(2, "Before import hook", repr(name), repr(m), repr(fromlist), repr(level))
|
| -
|
| - imported_module = self._safe_import_hook(name, m, fromlist, level)[0]
|
| -
|
| - if have_star:
|
| - m.globalnames.update(imported_module.globalnames)
|
| - m.starimports.update(imported_module.starimports)
|
| - if imported_module.code is None:
|
| - m.starimports.add(name)
|
| -
|
| - elif c == STORE_NAME or c == STORE_GLOBAL:
|
| - # keep track of all global names that are assigned to
|
| - oparg = unpack('<H', code[i - 2:i])[0]
|
| - name = co.co_names[oparg]
|
| - m.globalnames.add(name)
|
| -
|
| - cotype = type(co)
|
| - for c in constants:
|
| - if isinstance(c, cotype):
|
| - self._scan_bytecode(c, m)
|
| -
|
| - def _load_package(self, fqname, pathname, pkgpath):
|
| - """
|
| - Called only when an imp.PACKAGE_DIRECTORY is found
|
| - """
|
| - self.msgin(2, "load_package", fqname, pathname, pkgpath)
|
| - newname = _replacePackageMap.get(fqname)
|
| - if newname:
|
| - fqname = newname
|
| -
|
| - ns_pkgpath = _namespace_package_path(fqname, pkgpath or [], self.path)
|
| - if ns_pkgpath or pkgpath:
|
| - # this is a namespace package
|
| - m = self.createNode(NamespacePackage, fqname)
|
| - m.filename = '-'
|
| - m.packagepath = ns_pkgpath
|
| - else:
|
| - m = self.createNode(Package, fqname)
|
| - m.filename = pathname
|
| - m.packagepath = [pathname] + ns_pkgpath
|
| -
|
| - # As per comment at top of file, simulate runtime packagepath additions.
|
| - m.packagepath = m.packagepath + _packagePathMap.get(fqname, [])
|
| -
|
| -
|
| -
|
| - try:
|
| - self.msg(2, "find __init__ for %s"%(m.packagepath,))
|
| - fp, buf, stuff = self._find_module("__init__", m.packagepath, parent=m)
|
| - except ImportError:
|
| - pass
|
| -
|
| - else:
|
| - try:
|
| - self.msg(2, "load __init__ for %s"%(m.packagepath,))
|
| - self._load_module(fqname, fp, buf, stuff)
|
| - finally:
|
| - if fp is not None:
|
| - fp.close()
|
| - self.msgout(2, "load_package ->", m)
|
| - return m
|
| -
|
| - def _find_module(self, name, path, parent=None):
|
| - if parent is not None:
|
| - # assert path is not None
|
| - fullname = parent.identifier + '.' + name
|
| - else:
|
| - fullname = name
|
| -
|
| - node = self.findNode(fullname)
|
| - if node is not None:
|
| - self.msgout(3, "find_module -> already included?", node)
|
| - raise ImportError(name)
|
| -
|
| - if path is None:
|
| - if name in sys.builtin_module_names:
|
| - return (None, None, ("", "", imp.C_BUILTIN))
|
| -
|
| - path = self.path
|
| -
|
| - fp, buf, stuff = find_module(name, path)
|
| - try:
|
| - if buf:
|
| - buf = os.path.realpath(buf)
|
| -
|
| - return (fp, buf, stuff)
|
| - except:
|
| - fp.close()
|
| - raise
|
| -
|
| - def create_xref(self, out=None):
|
| - global header, footer, entry, contpl, contpl_linked, imports
|
| - if out is None:
|
| - out = sys.stdout
|
| - scripts = []
|
| - mods = []
|
| - for mod in self.flatten():
|
| - name = os.path.basename(mod.identifier)
|
| - if isinstance(mod, Script):
|
| - scripts.append((name, mod))
|
| - else:
|
| - mods.append((name, mod))
|
| - scripts.sort()
|
| - mods.sort()
|
| - scriptnames = [name for name, m in scripts]
|
| - scripts.extend(mods)
|
| - mods = scripts
|
| -
|
| - title = "modulegraph cross reference for " + ', '.join(scriptnames)
|
| - print(header % {"TITLE": title}, file=out)
|
| -
|
| - def sorted_namelist(mods):
|
| - lst = [os.path.basename(mod.identifier) for mod in mods if mod]
|
| - lst.sort()
|
| - return lst
|
| - for name, m in mods:
|
| - content = ""
|
| - if isinstance(m, BuiltinModule):
|
| - content = contpl % {"NAME": name,
|
| - "TYPE": "<i>(builtin module)</i>"}
|
| - elif isinstance(m, Extension):
|
| - content = contpl % {"NAME": name,\
|
| - "TYPE": "<tt>%s</tt>" % m.filename}
|
| - else:
|
| - url = pathname2url(m.filename or "")
|
| - content = contpl_linked % {"NAME": name, "URL": url}
|
| - oute, ince = map(sorted_namelist, self.get_edges(m))
|
| - if oute:
|
| - links = ""
|
| - for n in oute:
|
| - links += """ <a href="#%s">%s</a>\n""" % (n, n)
|
| - content += imports % {"HEAD": "imports", "LINKS": links}
|
| - if ince:
|
| - links = ""
|
| - for n in ince:
|
| - links += """ <a href="#%s">%s</a>\n""" % (n, n)
|
| - content += imports % {"HEAD": "imported by", "LINKS": links}
|
| - print(entry % {"NAME": name,"CONTENT": content}, file=out)
|
| - print(footer, file=out)
|
| -
|
| -
|
| - def itergraphreport(self, name='G', flatpackages=()):
|
| - # XXX: Can this be implemented using Dot()?
|
| - nodes = map(self.graph.describe_node, self.graph.iterdfs(self))
|
| - describe_edge = self.graph.describe_edge
|
| - edges = deque()
|
| - packagenodes = set()
|
| - packageidents = {}
|
| - nodetoident = {}
|
| - inpackages = {}
|
| - mainedges = set()
|
| -
|
| - # XXX - implement
|
| - flatpackages = dict(flatpackages)
|
| -
|
| - def nodevisitor(node, data, outgoing, incoming):
|
| - if not isinstance(data, Node):
|
| - return {'label': str(node)}
|
| - #if isinstance(d, (ExcludedModule, MissingModule, BadModule)):
|
| - # return None
|
| - s = '<f0> ' + type(data).__name__
|
| - for i,v in enumerate(data.infoTuple()[:1], 1):
|
| - s += '| <f%d> %s' % (i,v)
|
| - return {'label':s, 'shape':'record'}
|
| -
|
| -
|
| - def edgevisitor(edge, data, head, tail):
|
| - # XXX: This method nonsense, the edge
|
| - # data is never initialized.
|
| - if data == 'orphan':
|
| - return {'style':'dashed'}
|
| - elif data == 'pkgref':
|
| - return {'style':'dotted'}
|
| - return {}
|
| -
|
| - yield 'digraph %s {\n' % (name,)
|
| - attr = dict(rankdir='LR', concentrate='true')
|
| - cpatt = '%s="%s"'
|
| - for item in attr.items():
|
| - yield '\t%s;\n' % (cpatt % item,)
|
| -
|
| - # find all packages (subgraphs)
|
| - for (node, data, outgoing, incoming) in nodes:
|
| - nodetoident[node] = getattr(data, 'identifier', None)
|
| - if isinstance(data, Package):
|
| - packageidents[data.identifier] = node
|
| - inpackages[node] = set([node])
|
| - packagenodes.add(node)
|
| -
|
| -
|
| - # create sets for subgraph, write out descriptions
|
| - for (node, data, outgoing, incoming) in nodes:
|
| - # update edges
|
| - for edge in (describe_edge(e) for e in outgoing):
|
| - edges.append(edge)
|
| -
|
| - # describe node
|
| - yield '\t"%s" [%s];\n' % (
|
| - node,
|
| - ','.join([
|
| - (cpatt % item) for item in
|
| - nodevisitor(node, data, outgoing, incoming).items()
|
| - ]),
|
| - )
|
| -
|
| - inside = inpackages.get(node)
|
| - if inside is None:
|
| - inside = inpackages[node] = set()
|
| - ident = nodetoident[node]
|
| - if ident is None:
|
| - continue
|
| - pkgnode = packageidents.get(ident[:ident.rfind('.')])
|
| - if pkgnode is not None:
|
| - inside.add(pkgnode)
|
| -
|
| -
|
| - graph = []
|
| - subgraphs = {}
|
| - for key in packagenodes:
|
| - subgraphs[key] = []
|
| -
|
| - while edges:
|
| - edge, data, head, tail = edges.popleft()
|
| - if ((head, tail)) in mainedges:
|
| - continue
|
| - mainedges.add((head, tail))
|
| - tailpkgs = inpackages[tail]
|
| - common = inpackages[head] & tailpkgs
|
| - if not common and tailpkgs:
|
| - usepkgs = sorted(tailpkgs)
|
| - if len(usepkgs) != 1 or usepkgs[0] != tail:
|
| - edges.append((edge, data, head, usepkgs[0]))
|
| - edges.append((edge, 'pkgref', usepkgs[-1], tail))
|
| - continue
|
| - if common:
|
| - common = common.pop()
|
| - if tail == common:
|
| - edges.append((edge, data, tail, head))
|
| - elif head == common:
|
| - subgraphs[common].append((edge, 'pkgref', head, tail))
|
| - else:
|
| - edges.append((edge, data, common, head))
|
| - edges.append((edge, data, common, tail))
|
| -
|
| - else:
|
| - graph.append((edge, data, head, tail))
|
| -
|
| - def do_graph(edges, tabs):
|
| - edgestr = tabs + '"%s" -> "%s" [%s];\n'
|
| - # describe edge
|
| - for (edge, data, head, tail) in edges:
|
| - attribs = edgevisitor(edge, data, head, tail)
|
| - yield edgestr % (
|
| - head,
|
| - tail,
|
| - ','.join([(cpatt % item) for item in attribs.items()]),
|
| - )
|
| -
|
| - for g, edges in subgraphs.items():
|
| - yield '\tsubgraph "cluster_%s" {\n' % (g,)
|
| - yield '\t\tlabel="%s";\n' % (nodetoident[g],)
|
| - for s in do_graph(edges, '\t\t'):
|
| - yield s
|
| - yield '\t}\n'
|
| -
|
| - for s in do_graph(graph, '\t'):
|
| - yield s
|
| -
|
| - yield '}\n'
|
| -
|
| -
|
| - def graphreport(self, fileobj=None, flatpackages=()):
|
| - if fileobj is None:
|
| - fileobj = sys.stdout
|
| - fileobj.writelines(self.itergraphreport(flatpackages=flatpackages))
|
| -
|
| - def report(self):
|
| - """Print a report to stdout, listing the found modules with their
|
| - paths, as well as modules that are missing, or seem to be missing.
|
| - """
|
| - print()
|
| - print("%-15s %-25s %s" % ("Class", "Name", "File"))
|
| - print("%-15s %-25s %s" % ("-----", "----", "----"))
|
| - # Print modules found
|
| - sorted = [(os.path.basename(mod.identifier), mod) for mod in self.flatten()]
|
| - sorted.sort()
|
| - for (name, m) in sorted:
|
| - print("%-15s %-25s %s" % (type(m).__name__, name, m.filename or ""))
|
| -
|
| - def _replace_paths_in_code(self, co):
|
| - new_filename = original_filename = os.path.normpath(co.co_filename)
|
| - for f, r in self.replace_paths:
|
| - f = os.path.join(f, '')
|
| - r = os.path.join(r, '')
|
| - if original_filename.startswith(f):
|
| - new_filename = r + original_filename[len(f):]
|
| - break
|
| -
|
| - else:
|
| - return co
|
| -
|
| - consts = list(co.co_consts)
|
| - for i in range(len(consts)):
|
| - if isinstance(consts[i], type(co)):
|
| - consts[i] = self._replace_paths_in_code(consts[i])
|
| -
|
| - code_func = type(co)
|
| -
|
| - if hasattr(co, 'co_kwonlyargcount'):
|
| - return code_func(co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize,
|
| - co.co_flags, co.co_code, tuple(consts), co.co_names,
|
| - co.co_varnames, new_filename, co.co_name,
|
| - co.co_firstlineno, co.co_lnotab,
|
| - co.co_freevars, co.co_cellvars)
|
| - else:
|
| - return code_func(co.co_argcount, co.co_nlocals, co.co_stacksize,
|
| - co.co_flags, co.co_code, tuple(consts), co.co_names,
|
| - co.co_varnames, new_filename, co.co_name,
|
| - co.co_firstlineno, co.co_lnotab,
|
| - co.co_freevars, co.co_cellvars)
|
|
|