summaryrefslogtreecommitdiff
path: root/venv/Lib/site-packages/pylint/checkers/imports.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/Lib/site-packages/pylint/checkers/imports.py')
-rw-r--r--venv/Lib/site-packages/pylint/checkers/imports.py981
1 files changed, 0 insertions, 981 deletions
diff --git a/venv/Lib/site-packages/pylint/checkers/imports.py b/venv/Lib/site-packages/pylint/checkers/imports.py
deleted file mode 100644
index 42d4362..0000000
--- a/venv/Lib/site-packages/pylint/checkers/imports.py
+++ /dev/null
@@ -1,981 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2012-2014 Google, Inc.
-# Copyright (c) 2013 buck@yelp.com <buck@yelp.com>
-# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015-2016 Moises Lopez <moylop260@vauxoo.com>
-# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
-# Copyright (c) 2015 Cezar <celnazli@bitdefender.com>
-# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
-# Copyright (c) 2015 Noam Yorav-Raphael <noamraph@gmail.com>
-# Copyright (c) 2015 James Morgensen <james.morgensen@gmail.com>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Jared Garst <cultofjared@gmail.com>
-# Copyright (c) 2016 Maik Röder <maikroeder@gmail.com>
-# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
-# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017 Michka Popoff <michkapopoff@gmail.com>
-# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2017 Erik Wright <erik.wright@shopify.com>
-# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
-# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
-# Copyright (c) 2018 Marianna Polatoglou <mpolatoglou@bloomberg.net>
-# Copyright (c) 2019 Paul Renvoise <renvoisepaul@gmail.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
-
-"""imports checkers for Python code"""
-
-import collections
-import copy
-import os
-import sys
-from distutils import sysconfig
-
-import astroid
-import isort
-from astroid import modutils
-from astroid.decorators import cached
-
-from pylint.checkers import BaseChecker
-from pylint.checkers.utils import (
- check_messages,
- is_from_fallback_block,
- node_ignores_exception,
-)
-from pylint.exceptions import EmptyReportError
-from pylint.graph import DotBackend, get_cycles
-from pylint.interfaces import IAstroidChecker
-from pylint.reporters.ureports.nodes import Paragraph, VerbatimText
-from pylint.utils import get_global_option
-
-
-def _qualified_names(modname):
- """Split the names of the given module into subparts
-
- For example,
- _qualified_names('pylint.checkers.ImportsChecker')
- returns
- ['pylint', 'pylint.checkers', 'pylint.checkers.ImportsChecker']
- """
- names = modname.split(".")
- return [".".join(names[0 : i + 1]) for i in range(len(names))]
-
-
-def _get_import_name(importnode, modname):
- """Get a prepared module name from the given import node
-
- In the case of relative imports, this will return the
- absolute qualified module name, which might be useful
- for debugging. Otherwise, the initial module name
- is returned unchanged.
- """
- if isinstance(importnode, astroid.ImportFrom):
- if importnode.level:
- root = importnode.root()
- if isinstance(root, astroid.Module):
- modname = root.relative_to_absolute_name(
- modname, level=importnode.level
- )
- return modname
-
-
-def _get_first_import(node, context, name, base, level, alias):
- """return the node where [base.]<name> is imported or None if not found
- """
- fullname = "%s.%s" % (base, name) if base else name
-
- first = None
- found = False
- for first in context.body:
- if first is node:
- continue
- if first.scope() is node.scope() and first.fromlineno > node.fromlineno:
- continue
- if isinstance(first, astroid.Import):
- if any(fullname == iname[0] for iname in first.names):
- found = True
- break
- elif isinstance(first, astroid.ImportFrom):
- if level == first.level:
- for imported_name, imported_alias in first.names:
- if fullname == "%s.%s" % (first.modname, imported_name):
- found = True
- break
- if (
- name != "*"
- and name == imported_name
- and not (alias or imported_alias)
- ):
- found = True
- break
- if found:
- break
- if found and not astroid.are_exclusive(first, node):
- return first
- return None
-
-
-def _ignore_import_failure(node, modname, ignored_modules):
- for submodule in _qualified_names(modname):
- if submodule in ignored_modules:
- return True
-
- return node_ignores_exception(node, ImportError)
-
-
-# utilities to represents import dependencies as tree and dot graph ###########
-
-
-def _make_tree_defs(mod_files_list):
- """get a list of 2-uple (module, list_of_files_which_import_this_module),
- it will return a dictionary to represent this as a tree
- """
- tree_defs = {}
- for mod, files in mod_files_list:
- node = (tree_defs, ())
- for prefix in mod.split("."):
- node = node[0].setdefault(prefix, [{}, []])
- node[1] += files
- return tree_defs
-
-
-def _repr_tree_defs(data, indent_str=None):
- """return a string which represents imports as a tree"""
- lines = []
- nodes = data.items()
- for i, (mod, (sub, files)) in enumerate(sorted(nodes, key=lambda x: x[0])):
- if not files:
- files = ""
- else:
- files = "(%s)" % ",".join(sorted(files))
- if indent_str is None:
- lines.append("%s %s" % (mod, files))
- sub_indent_str = " "
- else:
- lines.append(r"%s\-%s %s" % (indent_str, mod, files))
- if i == len(nodes) - 1:
- sub_indent_str = "%s " % indent_str
- else:
- sub_indent_str = "%s| " % indent_str
- if sub:
- lines.append(_repr_tree_defs(sub, sub_indent_str))
- return "\n".join(lines)
-
-
-def _dependencies_graph(filename, dep_info):
- """write dependencies as a dot (graphviz) file
- """
- done = {}
- printer = DotBackend(filename[:-4], rankdir="LR")
- printer.emit('URL="." node[shape="box"]')
- for modname, dependencies in sorted(dep_info.items()):
- done[modname] = 1
- printer.emit_node(modname)
- for depmodname in dependencies:
- if depmodname not in done:
- done[depmodname] = 1
- printer.emit_node(depmodname)
- for depmodname, dependencies in sorted(dep_info.items()):
- for modname in dependencies:
- printer.emit_edge(modname, depmodname)
- printer.generate(filename)
-
-
-def _make_graph(filename, dep_info, sect, gtype):
- """generate a dependencies graph and add some information about it in the
- report's section
- """
- _dependencies_graph(filename, dep_info)
- sect.append(Paragraph("%simports graph has been written to %s" % (gtype, filename)))
-
-
-# the import checker itself ###################################################
-
-MSGS = {
- "E0401": (
- "Unable to import %s",
- "import-error",
- "Used when pylint has been unable to import a module.",
- {"old_names": [("F0401", "old-import-error")]},
- ),
- "E0402": (
- "Attempted relative import beyond top-level package",
- "relative-beyond-top-level",
- "Used when a relative import tries to access too many levels "
- "in the current package.",
- ),
- "R0401": (
- "Cyclic import (%s)",
- "cyclic-import",
- "Used when a cyclic import between two or more modules is detected.",
- ),
- "W0401": (
- "Wildcard import %s",
- "wildcard-import",
- "Used when `from module import *` is detected.",
- ),
- "W0402": (
- "Uses of a deprecated module %r",
- "deprecated-module",
- "Used a module marked as deprecated is imported.",
- ),
- "W0404": (
- "Reimport %r (imported line %s)",
- "reimported",
- "Used when a module is reimported multiple times.",
- ),
- "W0406": (
- "Module import itself",
- "import-self",
- "Used when a module is importing itself.",
- ),
- "W0407": (
- "Prefer importing %r instead of %r",
- "preferred-module",
- "Used when a module imported has a preferred replacement module.",
- ),
- "W0410": (
- "__future__ import is not the first non docstring statement",
- "misplaced-future",
- "Python 2.5 and greater require __future__ import to be the "
- "first non docstring statement in the module.",
- ),
- "C0410": (
- "Multiple imports on one line (%s)",
- "multiple-imports",
- "Used when import statement importing multiple modules is detected.",
- ),
- "C0411": (
- "%s should be placed before %s",
- "wrong-import-order",
- "Used when PEP8 import order is not respected (standard imports "
- "first, then third-party libraries, then local imports)",
- ),
- "C0412": (
- "Imports from package %s are not grouped",
- "ungrouped-imports",
- "Used when imports are not grouped by packages",
- ),
- "C0413": (
- 'Import "%s" should be placed at the top of the module',
- "wrong-import-position",
- "Used when code and imports are mixed",
- ),
- "C0414": (
- "Import alias does not rename original package",
- "useless-import-alias",
- "Used when an import alias is same as original package."
- "e.g using import numpy as numpy instead of import numpy as np",
- ),
- "C0415": (
- "Import outside toplevel (%s)",
- "import-outside-toplevel",
- "Used when an import statement is used anywhere other than the module "
- "toplevel. Move this import to the top of the file.",
- ),
-}
-
-
-DEFAULT_STANDARD_LIBRARY = ()
-DEFAULT_KNOWN_THIRD_PARTY = ("enchant",)
-DEFAULT_PREFERRED_MODULES = ()
-
-
-class ImportsChecker(BaseChecker):
- """checks for
- * external modules dependencies
- * relative / wildcard imports
- * cyclic imports
- * uses of deprecated modules
- * uses of modules instead of preferred modules
- """
-
- __implements__ = IAstroidChecker
-
- name = "imports"
- msgs = MSGS
- priority = -2
- deprecated_modules = ("optparse", "tkinter.tix")
-
- options = (
- (
- "deprecated-modules",
- {
- "default": deprecated_modules,
- "type": "csv",
- "metavar": "<modules>",
- "help": "Deprecated modules which should not be used,"
- " separated by a comma.",
- },
- ),
- (
- "preferred-modules",
- {
- "default": DEFAULT_PREFERRED_MODULES,
- "type": "csv",
- "metavar": "<module:preferred-module>",
- "help": "Couples of modules and preferred modules,"
- " separated by a comma.",
- },
- ),
- (
- "import-graph",
- {
- "default": "",
- "type": "string",
- "metavar": "<file.dot>",
- "help": "Create a graph of every (i.e. internal and"
- " external) dependencies in the given file"
- " (report RP0402 must not be disabled).",
- },
- ),
- (
- "ext-import-graph",
- {
- "default": "",
- "type": "string",
- "metavar": "<file.dot>",
- "help": "Create a graph of external dependencies in the"
- " given file (report RP0402 must not be disabled).",
- },
- ),
- (
- "int-import-graph",
- {
- "default": "",
- "type": "string",
- "metavar": "<file.dot>",
- "help": "Create a graph of internal dependencies in the"
- " given file (report RP0402 must not be disabled).",
- },
- ),
- (
- "known-standard-library",
- {
- "default": DEFAULT_STANDARD_LIBRARY,
- "type": "csv",
- "metavar": "<modules>",
- "help": "Force import order to recognize a module as part of "
- "the standard compatibility libraries.",
- },
- ),
- (
- "known-third-party",
- {
- "default": DEFAULT_KNOWN_THIRD_PARTY,
- "type": "csv",
- "metavar": "<modules>",
- "help": "Force import order to recognize a module as part of "
- "a third party library.",
- },
- ),
- (
- "allow-any-import-level",
- {
- "default": (),
- "type": "csv",
- "metavar": "<modules>",
- "help": (
- "List of modules that can be imported at any level, not just "
- "the top level one."
- ),
- },
- ),
- (
- "analyse-fallback-blocks",
- {
- "default": False,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": "Analyse import fallback blocks. This can be used to "
- "support both Python 2 and 3 compatible code, which "
- "means that the block might have code that exists "
- "only in one or another interpreter, leading to false "
- "positives when analysed.",
- },
- ),
- (
- "allow-wildcard-with-all",
- {
- "default": False,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": "Allow wildcard imports from modules that define __all__.",
- },
- ),
- )
-
- def __init__(self, linter=None):
- BaseChecker.__init__(self, linter)
- self.stats = None
- self.import_graph = None
- self._imports_stack = []
- self._first_non_import_node = None
- self._module_pkg = {} # mapping of modules to the pkg they belong in
- self._allow_any_import_level = set()
- self.reports = (
- ("RP0401", "External dependencies", self._report_external_dependencies),
- ("RP0402", "Modules dependencies graph", self._report_dependencies_graph),
- )
-
- self._site_packages = self._compute_site_packages()
-
- @staticmethod
- def _compute_site_packages():
- def _normalized_path(path):
- return os.path.normcase(os.path.abspath(path))
-
- paths = set()
- real_prefix = getattr(sys, "real_prefix", None)
- for prefix in filter(None, (real_prefix, sys.prefix)):
- path = sysconfig.get_python_lib(prefix=prefix)
- path = _normalized_path(path)
- paths.add(path)
-
- # Handle Debian's derivatives /usr/local.
- if os.path.isfile("/etc/debian_version"):
- for prefix in filter(None, (real_prefix, sys.prefix)):
- libpython = os.path.join(
- prefix,
- "local",
- "lib",
- "python" + sysconfig.get_python_version(),
- "dist-packages",
- )
- paths.add(libpython)
- return paths
-
- def open(self):
- """called before visiting project (i.e set of modules)"""
- self.linter.add_stats(dependencies={})
- self.linter.add_stats(cycles=[])
- self.stats = self.linter.stats
- self.import_graph = collections.defaultdict(set)
- self._module_pkg = {} # mapping of modules to the pkg they belong in
- self._excluded_edges = collections.defaultdict(set)
- self._ignored_modules = get_global_option(self, "ignored-modules", default=[])
- # Build a mapping {'module': 'preferred-module'}
- self.preferred_modules = dict(
- module.split(":")
- for module in self.config.preferred_modules
- if ":" in module
- )
- self._allow_any_import_level = set(self.config.allow_any_import_level)
-
- def _import_graph_without_ignored_edges(self):
- filtered_graph = copy.deepcopy(self.import_graph)
- for node in filtered_graph:
- filtered_graph[node].difference_update(self._excluded_edges[node])
- return filtered_graph
-
- def close(self):
- """called before visiting project (i.e set of modules)"""
- if self.linter.is_message_enabled("cyclic-import"):
- graph = self._import_graph_without_ignored_edges()
- vertices = list(graph)
- for cycle in get_cycles(graph, vertices=vertices):
- self.add_message("cyclic-import", args=" -> ".join(cycle))
-
- @check_messages(*MSGS)
- def visit_import(self, node):
- """triggered when an import statement is seen"""
- self._check_reimport(node)
- self._check_import_as_rename(node)
- self._check_toplevel(node)
-
- names = [name for name, _ in node.names]
- if len(names) >= 2:
- self.add_message("multiple-imports", args=", ".join(names), node=node)
-
- for name in names:
- self._check_deprecated_module(node, name)
- self._check_preferred_module(node, name)
- imported_module = self._get_imported_module(node, name)
- if isinstance(node.parent, astroid.Module):
- # Allow imports nested
- self._check_position(node)
- if isinstance(node.scope(), astroid.Module):
- self._record_import(node, imported_module)
-
- if imported_module is None:
- continue
-
- self._add_imported_module(node, imported_module.name)
-
- @check_messages(*MSGS)
- def visit_importfrom(self, node):
- """triggered when a from statement is seen"""
- basename = node.modname
- imported_module = self._get_imported_module(node, basename)
-
- self._check_import_as_rename(node)
- self._check_misplaced_future(node)
- self._check_deprecated_module(node, basename)
- self._check_preferred_module(node, basename)
- self._check_wildcard_imports(node, imported_module)
- self._check_same_line_imports(node)
- self._check_reimport(node, basename=basename, level=node.level)
- self._check_toplevel(node)
-
- if isinstance(node.parent, astroid.Module):
- # Allow imports nested
- self._check_position(node)
- if isinstance(node.scope(), astroid.Module):
- self._record_import(node, imported_module)
- if imported_module is None:
- return
- for name, _ in node.names:
- if name != "*":
- self._add_imported_module(node, "%s.%s" % (imported_module.name, name))
- else:
- self._add_imported_module(node, imported_module.name)
-
- @check_messages(*MSGS)
- def leave_module(self, node):
- # Check imports are grouped by category (standard, 3rd party, local)
- std_imports, ext_imports, loc_imports = self._check_imports_order(node)
-
- # Check that imports are grouped by package within a given category
- met_import = set() #  set for 'import x' style
- met_from = set() #  set for 'from x import y' style
- current_package = None
- for import_node, import_name in std_imports + ext_imports + loc_imports:
- if not self.linter.is_message_enabled(
- "ungrouped-imports", import_node.fromlineno
- ):
- continue
- if isinstance(import_node, astroid.node_classes.ImportFrom):
- met = met_from
- else:
- met = met_import
- package, _, _ = import_name.partition(".")
- if current_package and current_package != package and package in met:
- self.add_message("ungrouped-imports", node=import_node, args=package)
- current_package = package
- met.add(package)
-
- self._imports_stack = []
- self._first_non_import_node = None
-
- def compute_first_non_import_node(self, node):
- if not self.linter.is_message_enabled("wrong-import-position", node.fromlineno):
- return
- # if the node does not contain an import instruction, and if it is the
- # first node of the module, keep a track of it (all the import positions
- # of the module will be compared to the position of this first
- # instruction)
- if self._first_non_import_node:
- return
- if not isinstance(node.parent, astroid.Module):
- return
- nested_allowed = [astroid.TryExcept, astroid.TryFinally]
- is_nested_allowed = [
- allowed for allowed in nested_allowed if isinstance(node, allowed)
- ]
- if is_nested_allowed and any(
- node.nodes_of_class((astroid.Import, astroid.ImportFrom))
- ):
- return
- if isinstance(node, astroid.Assign):
- # Add compatibility for module level dunder names
- # https://www.python.org/dev/peps/pep-0008/#module-level-dunder-names
- valid_targets = [
- isinstance(target, astroid.AssignName)
- and target.name.startswith("__")
- and target.name.endswith("__")
- for target in node.targets
- ]
- if all(valid_targets):
- return
- self._first_non_import_node = node
-
- visit_tryfinally = (
- visit_tryexcept
- ) = (
- visit_assignattr
- ) = (
- visit_assign
- ) = (
- visit_ifexp
- ) = visit_comprehension = visit_expr = visit_if = compute_first_non_import_node
-
- def visit_functiondef(self, node):
- if not self.linter.is_message_enabled("wrong-import-position", node.fromlineno):
- return
- # If it is the first non import instruction of the module, record it.
- if self._first_non_import_node:
- return
-
- # Check if the node belongs to an `If` or a `Try` block. If they
- # contain imports, skip recording this node.
- if not isinstance(node.parent.scope(), astroid.Module):
- return
-
- root = node
- while not isinstance(root.parent, astroid.Module):
- root = root.parent
-
- if isinstance(root, (astroid.If, astroid.TryFinally, astroid.TryExcept)):
- if any(root.nodes_of_class((astroid.Import, astroid.ImportFrom))):
- return
-
- self._first_non_import_node = node
-
- visit_classdef = visit_for = visit_while = visit_functiondef
-
- def _check_misplaced_future(self, node):
- basename = node.modname
- if basename == "__future__":
- # check if this is the first non-docstring statement in the module
- prev = node.previous_sibling()
- if prev:
- # consecutive future statements are possible
- if not (
- isinstance(prev, astroid.ImportFrom)
- and prev.modname == "__future__"
- ):
- self.add_message("misplaced-future", node=node)
- return
-
- def _check_same_line_imports(self, node):
- # Detect duplicate imports on the same line.
- names = (name for name, _ in node.names)
- counter = collections.Counter(names)
- for name, count in counter.items():
- if count > 1:
- self.add_message("reimported", node=node, args=(name, node.fromlineno))
-
- def _check_position(self, node):
- """Check `node` import or importfrom node position is correct
-
- Send a message if `node` comes before another instruction
- """
- # if a first non-import instruction has already been encountered,
- # it means the import comes after it and therefore is not well placed
- if self._first_non_import_node:
- self.add_message("wrong-import-position", node=node, args=node.as_string())
-
- def _record_import(self, node, importedmodnode):
- """Record the package `node` imports from"""
- if isinstance(node, astroid.ImportFrom):
- importedname = node.modname
- else:
- importedname = importedmodnode.name if importedmodnode else None
- if not importedname:
- importedname = node.names[0][0].split(".")[0]
-
- if isinstance(node, astroid.ImportFrom) and (node.level or 0) >= 1:
- # We need the importedname with first point to detect local package
- # Example of node:
- # 'from .my_package1 import MyClass1'
- # the output should be '.my_package1' instead of 'my_package1'
- # Example of node:
- # 'from . import my_package2'
- # the output should be '.my_package2' instead of '{pyfile}'
- importedname = "." + importedname
-
- self._imports_stack.append((node, importedname))
-
- @staticmethod
- def _is_fallback_import(node, imports):
- imports = [import_node for (import_node, _) in imports]
- return any(astroid.are_exclusive(import_node, node) for import_node in imports)
-
- def _check_imports_order(self, _module_node):
- """Checks imports of module `node` are grouped by category
-
- Imports must follow this order: standard, 3rd party, local
- """
- std_imports = []
- third_party_imports = []
- first_party_imports = []
- # need of a list that holds third or first party ordered import
- external_imports = []
- local_imports = []
- third_party_not_ignored = []
- first_party_not_ignored = []
- local_not_ignored = []
- isort_obj = isort.SortImports(
- file_contents="",
- known_third_party=self.config.known_third_party,
- known_standard_library=self.config.known_standard_library,
- )
- for node, modname in self._imports_stack:
- if modname.startswith("."):
- package = "." + modname.split(".")[1]
- else:
- package = modname.split(".")[0]
- nested = not isinstance(node.parent, astroid.Module)
- ignore_for_import_order = not self.linter.is_message_enabled(
- "wrong-import-order", node.fromlineno
- )
- import_category = isort_obj.place_module(package)
- node_and_package_import = (node, package)
- if import_category in ("FUTURE", "STDLIB"):
- std_imports.append(node_and_package_import)
- wrong_import = (
- third_party_not_ignored
- or first_party_not_ignored
- or local_not_ignored
- )
- if self._is_fallback_import(node, wrong_import):
- continue
- if wrong_import and not nested:
- self.add_message(
- "wrong-import-order",
- node=node,
- args=(
- 'standard import "%s"' % node.as_string(),
- '"%s"' % wrong_import[0][0].as_string(),
- ),
- )
- elif import_category == "THIRDPARTY":
- third_party_imports.append(node_and_package_import)
- external_imports.append(node_and_package_import)
- if not nested and not ignore_for_import_order:
- third_party_not_ignored.append(node_and_package_import)
- wrong_import = first_party_not_ignored or local_not_ignored
- if wrong_import and not nested:
- self.add_message(
- "wrong-import-order",
- node=node,
- args=(
- 'third party import "%s"' % node.as_string(),
- '"%s"' % wrong_import[0][0].as_string(),
- ),
- )
- elif import_category == "FIRSTPARTY":
- first_party_imports.append(node_and_package_import)
- external_imports.append(node_and_package_import)
- if not nested and not ignore_for_import_order:
- first_party_not_ignored.append(node_and_package_import)
- wrong_import = local_not_ignored
- if wrong_import and not nested:
- self.add_message(
- "wrong-import-order",
- node=node,
- args=(
- 'first party import "%s"' % node.as_string(),
- '"%s"' % wrong_import[0][0].as_string(),
- ),
- )
- elif import_category == "LOCALFOLDER":
- local_imports.append((node, package))
- if not nested and not ignore_for_import_order:
- local_not_ignored.append((node, package))
- return std_imports, external_imports, local_imports
-
- def _get_imported_module(self, importnode, modname):
- try:
- return importnode.do_import_module(modname)
- except astroid.TooManyLevelsError:
- if _ignore_import_failure(importnode, modname, self._ignored_modules):
- return None
-
- self.add_message("relative-beyond-top-level", node=importnode)
- except astroid.AstroidSyntaxError as exc:
- message = "Cannot import {!r} due to syntax error {!r}".format(
- modname, str(exc.error) # pylint: disable=no-member; false positive
- )
- self.add_message("syntax-error", line=importnode.lineno, args=message)
-
- except astroid.AstroidBuildingException:
- if not self.linter.is_message_enabled("import-error"):
- return None
- if _ignore_import_failure(importnode, modname, self._ignored_modules):
- return None
- if not self.config.analyse_fallback_blocks and is_from_fallback_block(
- importnode
- ):
- return None
-
- dotted_modname = _get_import_name(importnode, modname)
- self.add_message("import-error", args=repr(dotted_modname), node=importnode)
-
- def _add_imported_module(self, node, importedmodname):
- """notify an imported module, used to analyze dependencies"""
- module_file = node.root().file
- context_name = node.root().name
- base = os.path.splitext(os.path.basename(module_file))[0]
-
- try:
- importedmodname = modutils.get_module_part(importedmodname, module_file)
- except ImportError:
- pass
-
- if context_name == importedmodname:
- self.add_message("import-self", node=node)
-
- elif not modutils.is_standard_module(importedmodname):
- # if this is not a package __init__ module
- if base != "__init__" and context_name not in self._module_pkg:
- # record the module's parent, or the module itself if this is
- # a top level module, as the package it belongs to
- self._module_pkg[context_name] = context_name.rsplit(".", 1)[0]
-
- # handle dependencies
- importedmodnames = self.stats["dependencies"].setdefault(
- importedmodname, set()
- )
- if context_name not in importedmodnames:
- importedmodnames.add(context_name)
-
- # update import graph
- self.import_graph[context_name].add(importedmodname)
- if not self.linter.is_message_enabled("cyclic-import", line=node.lineno):
- self._excluded_edges[context_name].add(importedmodname)
-
- def _check_deprecated_module(self, node, mod_path):
- """check if the module is deprecated"""
- for mod_name in self.config.deprecated_modules:
- if mod_path == mod_name or mod_path.startswith(mod_name + "."):
- self.add_message("deprecated-module", node=node, args=mod_path)
-
- def _check_preferred_module(self, node, mod_path):
- """check if the module has a preferred replacement"""
- if mod_path in self.preferred_modules:
- self.add_message(
- "preferred-module",
- node=node,
- args=(self.preferred_modules[mod_path], mod_path),
- )
-
- def _check_import_as_rename(self, node):
- names = node.names
- for name in names:
- if not all(name):
- return
-
- real_name = name[0]
- splitted_packages = real_name.rsplit(".")
- real_name = splitted_packages[-1]
- imported_name = name[1]
- # consider only following cases
- # import x as x
- # and ignore following
- # import x.y.z as z
- if real_name == imported_name and len(splitted_packages) == 1:
- self.add_message("useless-import-alias", node=node)
-
- def _check_reimport(self, node, basename=None, level=None):
- """check if the import is necessary (i.e. not already done)"""
- if not self.linter.is_message_enabled("reimported"):
- return
-
- frame = node.frame()
- root = node.root()
- contexts = [(frame, level)]
- if root is not frame:
- contexts.append((root, None))
-
- for known_context, known_level in contexts:
- for name, alias in node.names:
- first = _get_first_import(
- node, known_context, name, basename, known_level, alias
- )
- if first is not None:
- self.add_message(
- "reimported", node=node, args=(name, first.fromlineno)
- )
-
- def _report_external_dependencies(self, sect, _, _dummy):
- """return a verbatim layout for displaying dependencies"""
- dep_info = _make_tree_defs(self._external_dependencies_info().items())
- if not dep_info:
- raise EmptyReportError()
- tree_str = _repr_tree_defs(dep_info)
- sect.append(VerbatimText(tree_str))
-
- def _report_dependencies_graph(self, sect, _, _dummy):
- """write dependencies as a dot (graphviz) file"""
- dep_info = self.stats["dependencies"]
- if not dep_info or not (
- self.config.import_graph
- or self.config.ext_import_graph
- or self.config.int_import_graph
- ):
- raise EmptyReportError()
- filename = self.config.import_graph
- if filename:
- _make_graph(filename, dep_info, sect, "")
- filename = self.config.ext_import_graph
- if filename:
- _make_graph(filename, self._external_dependencies_info(), sect, "external ")
- filename = self.config.int_import_graph
- if filename:
- _make_graph(filename, self._internal_dependencies_info(), sect, "internal ")
-
- def _filter_dependencies_graph(self, internal):
- """build the internal or the external dependency graph"""
- graph = collections.defaultdict(set)
- for importee, importers in self.stats["dependencies"].items():
- for importer in importers:
- package = self._module_pkg.get(importer, importer)
- is_inside = importee.startswith(package)
- if is_inside and internal or not is_inside and not internal:
- graph[importee].add(importer)
- return graph
-
- @cached
- def _external_dependencies_info(self):
- """return cached external dependencies information or build and
- cache them
- """
- return self._filter_dependencies_graph(internal=False)
-
- @cached
- def _internal_dependencies_info(self):
- """return cached internal dependencies information or build and
- cache them
- """
- return self._filter_dependencies_graph(internal=True)
-
- def _check_wildcard_imports(self, node, imported_module):
- if node.root().package:
- # Skip the check if in __init__.py issue #2026
- return
-
- wildcard_import_is_allowed = self._wildcard_import_is_allowed(imported_module)
- for name, _ in node.names:
- if name == "*" and not wildcard_import_is_allowed:
- self.add_message("wildcard-import", args=node.modname, node=node)
-
- def _wildcard_import_is_allowed(self, imported_module):
- return (
- self.config.allow_wildcard_with_all
- and imported_module is not None
- and "__all__" in imported_module.locals
- )
-
- def _check_toplevel(self, node):
- """Check whether the import is made outside the module toplevel.
- """
- # If the scope of the import is a module, then obviously it is
- # not outside the module toplevel.
- if isinstance(node.scope(), astroid.Module):
- return
-
- if isinstance(node, astroid.ImportFrom):
- module_names = [node.modname]
- else:
- module_names = [name[0] for name in node.names]
-
- # Get the full names of all the imports that are not whitelisted.
- scoped_imports = [
- name for name in module_names if name not in self._allow_any_import_level
- ]
-
- if scoped_imports:
- self.add_message(
- "import-outside-toplevel", args=", ".join(scoped_imports), node=node
- )
-
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(ImportsChecker(linter))