summaryrefslogtreecommitdiff
path: root/venv/Lib/site-packages/pylint/pyreverse/inspector.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/Lib/site-packages/pylint/pyreverse/inspector.py')
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/inspector.py357
1 files changed, 357 insertions, 0 deletions
diff --git a/venv/Lib/site-packages/pylint/pyreverse/inspector.py b/venv/Lib/site-packages/pylint/pyreverse/inspector.py
new file mode 100644
index 0000000..702b108
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/inspector.py
@@ -0,0 +1,357 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""
+Visitor doing some postprocessing on the astroid tree.
+Try to resolve definitions (namespace) dictionary, relationship...
+"""
+import collections
+import os
+import traceback
+
+import astroid
+from astroid import bases, exceptions, manager, modutils, node_classes
+
+from pylint.pyreverse import utils
+
+
+def _iface_hdlr(_):
+ """Handler used by interfaces to handle suspicious interface nodes."""
+ return True
+
+
+def _astroid_wrapper(func, modname):
+ print("parsing %s..." % modname)
+ try:
+ return func(modname)
+ except exceptions.AstroidBuildingException as exc:
+ print(exc)
+ except Exception as exc: # pylint: disable=broad-except
+ traceback.print_exc()
+
+
+def interfaces(node, herited=True, handler_func=_iface_hdlr):
+ """Return an iterator on interfaces implemented by the given class node."""
+ try:
+ implements = bases.Instance(node).getattr("__implements__")[0]
+ except exceptions.NotFoundError:
+ return
+ if not herited and implements.frame() is not node:
+ return
+ found = set()
+ missing = False
+ for iface in node_classes.unpack_infer(implements):
+ if iface is astroid.Uninferable:
+ missing = True
+ continue
+ if iface not in found and handler_func(iface):
+ found.add(iface)
+ yield iface
+ if missing:
+ raise exceptions.InferenceError()
+
+
+class IdGeneratorMixIn:
+ """Mixin adding the ability to generate integer uid."""
+
+ def __init__(self, start_value=0):
+ self.id_count = start_value
+
+ def init_counter(self, start_value=0):
+ """init the id counter
+ """
+ self.id_count = start_value
+
+ def generate_id(self):
+ """generate a new identifier
+ """
+ self.id_count += 1
+ return self.id_count
+
+
+class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
+ """Walk on the project tree and resolve relationships.
+
+ According to options the following attributes may be
+ added to visited nodes:
+
+ * uid,
+ a unique identifier for the node (on astroid.Project, astroid.Module,
+ astroid.Class and astroid.locals_type). Only if the linker
+ has been instantiated with tag=True parameter (False by default).
+
+ * Function
+ a mapping from locals names to their bounded value, which may be a
+ constant like a string or an integer, or an astroid node
+ (on astroid.Module, astroid.Class and astroid.Function).
+
+ * instance_attrs_type
+ as locals_type but for klass member attributes (only on astroid.Class)
+
+ * implements,
+ list of implemented interface _objects_ (only on astroid.Class nodes)
+ """
+
+ def __init__(self, project, inherited_interfaces=0, tag=False):
+ IdGeneratorMixIn.__init__(self)
+ utils.LocalsVisitor.__init__(self)
+ # take inherited interface in consideration or not
+ self.inherited_interfaces = inherited_interfaces
+ # tag nodes or not
+ self.tag = tag
+ # visited project
+ self.project = project
+
+ def visit_project(self, node):
+ """visit a pyreverse.utils.Project node
+
+ * optionally tag the node with a unique id
+ """
+ if self.tag:
+ node.uid = self.generate_id()
+ for module in node.modules:
+ self.visit(module)
+
+ def visit_package(self, node):
+ """visit an astroid.Package node
+
+ * optionally tag the node with a unique id
+ """
+ if self.tag:
+ node.uid = self.generate_id()
+ for subelmt in node.values():
+ self.visit(subelmt)
+
+ def visit_module(self, node):
+ """visit an astroid.Module node
+
+ * set the locals_type mapping
+ * set the depends mapping
+ * optionally tag the node with a unique id
+ """
+ if hasattr(node, "locals_type"):
+ return
+ node.locals_type = collections.defaultdict(list)
+ node.depends = []
+ if self.tag:
+ node.uid = self.generate_id()
+
+ def visit_classdef(self, node):
+ """visit an astroid.Class node
+
+ * set the locals_type and instance_attrs_type mappings
+ * set the implements list and build it
+ * optionally tag the node with a unique id
+ """
+ if hasattr(node, "locals_type"):
+ return
+ node.locals_type = collections.defaultdict(list)
+ if self.tag:
+ node.uid = self.generate_id()
+ # resolve ancestors
+ for baseobj in node.ancestors(recurs=False):
+ specializations = getattr(baseobj, "specializations", [])
+ specializations.append(node)
+ baseobj.specializations = specializations
+ # resolve instance attributes
+ node.instance_attrs_type = collections.defaultdict(list)
+ for assignattrs in node.instance_attrs.values():
+ for assignattr in assignattrs:
+ self.handle_assignattr_type(assignattr, node)
+ # resolve implemented interface
+ try:
+ node.implements = list(interfaces(node, self.inherited_interfaces))
+ except astroid.InferenceError:
+ node.implements = ()
+
+ def visit_functiondef(self, node):
+ """visit an astroid.Function node
+
+ * set the locals_type mapping
+ * optionally tag the node with a unique id
+ """
+ if hasattr(node, "locals_type"):
+ return
+ node.locals_type = collections.defaultdict(list)
+ if self.tag:
+ node.uid = self.generate_id()
+
+ link_project = visit_project
+ link_module = visit_module
+ link_class = visit_classdef
+ link_function = visit_functiondef
+
+ def visit_assignname(self, node):
+ """visit an astroid.AssignName node
+
+ handle locals_type
+ """
+ # avoid double parsing done by different Linkers.visit
+ # running over the same project:
+ if hasattr(node, "_handled"):
+ return
+ node._handled = True
+ if node.name in node.frame():
+ frame = node.frame()
+ else:
+ # the name has been defined as 'global' in the frame and belongs
+ # there.
+ frame = node.root()
+ try:
+ if not hasattr(frame, "locals_type"):
+ # If the frame doesn't have a locals_type yet,
+ # it means it wasn't yet visited. Visit it now
+ # to add what's missing from it.
+ if isinstance(frame, astroid.ClassDef):
+ self.visit_classdef(frame)
+ elif isinstance(frame, astroid.FunctionDef):
+ self.visit_functiondef(frame)
+ else:
+ self.visit_module(frame)
+
+ current = frame.locals_type[node.name]
+ values = set(node.infer())
+ frame.locals_type[node.name] = list(set(current) | values)
+ except astroid.InferenceError:
+ pass
+
+ @staticmethod
+ def handle_assignattr_type(node, parent):
+ """handle an astroid.assignattr node
+
+ handle instance_attrs_type
+ """
+ try:
+ values = set(node.infer())
+ current = set(parent.instance_attrs_type[node.attrname])
+ parent.instance_attrs_type[node.attrname] = list(current | values)
+ except astroid.InferenceError:
+ pass
+
+ def visit_import(self, node):
+ """visit an astroid.Import node
+
+ resolve module dependencies
+ """
+ context_file = node.root().file
+ for name in node.names:
+ relative = modutils.is_relative(name[0], context_file)
+ self._imported_module(node, name[0], relative)
+
+ def visit_importfrom(self, node):
+ """visit an astroid.ImportFrom node
+
+ resolve module dependencies
+ """
+ basename = node.modname
+ context_file = node.root().file
+ if context_file is not None:
+ relative = modutils.is_relative(basename, context_file)
+ else:
+ relative = False
+ for name in node.names:
+ if name[0] == "*":
+ continue
+ # analyze dependencies
+ fullname = "%s.%s" % (basename, name[0])
+ if fullname.find(".") > -1:
+ try:
+ fullname = modutils.get_module_part(fullname, context_file)
+ except ImportError:
+ continue
+ if fullname != basename:
+ self._imported_module(node, fullname, relative)
+
+ def compute_module(self, context_name, mod_path):
+ """return true if the module should be added to dependencies"""
+ package_dir = os.path.dirname(self.project.path)
+ if context_name == mod_path:
+ return 0
+ if modutils.is_standard_module(mod_path, (package_dir,)):
+ return 1
+ return 0
+
+ def _imported_module(self, node, mod_path, relative):
+ """Notify an imported module, used to analyze dependencies"""
+ module = node.root()
+ context_name = module.name
+ if relative:
+ mod_path = "%s.%s" % (".".join(context_name.split(".")[:-1]), mod_path)
+ if self.compute_module(context_name, mod_path):
+ # handle dependencies
+ if not hasattr(module, "depends"):
+ module.depends = []
+ mod_paths = module.depends
+ if mod_path not in mod_paths:
+ mod_paths.append(mod_path)
+
+
+class Project:
+ """a project handle a set of modules / packages"""
+
+ def __init__(self, name=""):
+ self.name = name
+ self.path = None
+ self.modules = []
+ self.locals = {}
+ self.__getitem__ = self.locals.__getitem__
+ self.__iter__ = self.locals.__iter__
+ self.values = self.locals.values
+ self.keys = self.locals.keys
+ self.items = self.locals.items
+
+ def add_module(self, node):
+ self.locals[node.name] = node
+ self.modules.append(node)
+
+ def get_module(self, name):
+ return self.locals[name]
+
+ def get_children(self):
+ return self.modules
+
+ def __repr__(self):
+ return "<Project %r at %s (%s modules)>" % (
+ self.name,
+ id(self),
+ len(self.modules),
+ )
+
+
+def project_from_files(
+ files, func_wrapper=_astroid_wrapper, project_name="no name", black_list=("CVS",)
+):
+ """return a Project from a list of files or modules"""
+ # build the project representation
+ astroid_manager = manager.AstroidManager()
+ project = Project(project_name)
+ for something in files:
+ if not os.path.exists(something):
+ fpath = modutils.file_from_modpath(something.split("."))
+ elif os.path.isdir(something):
+ fpath = os.path.join(something, "__init__.py")
+ else:
+ fpath = something
+ ast = func_wrapper(astroid_manager.ast_from_file, fpath)
+ if ast is None:
+ continue
+ project.path = project.path or ast.file
+ project.add_module(ast)
+ base_name = ast.name
+ # recurse in package except if __init__ was explicitly given
+ if ast.package and something.find("__init__") == -1:
+ # recurse on others packages / modules if this is a package
+ for fpath in modutils.get_module_files(
+ os.path.dirname(ast.file), black_list
+ ):
+ ast = func_wrapper(astroid_manager.ast_from_file, fpath)
+ if ast is None or ast.name == base_name:
+ continue
+ project.add_module(ast)
+ return project