diff options
Diffstat (limited to 'venv/Lib/site-packages/pylint/pyreverse/inspector.py')
-rw-r--r-- | venv/Lib/site-packages/pylint/pyreverse/inspector.py | 357 |
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 |