summaryrefslogtreecommitdiff
path: root/venv/Lib/site-packages/pylint/checkers
diff options
context:
space:
mode:
Diffstat (limited to 'venv/Lib/site-packages/pylint/checkers')
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__init__.py64
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/__init__.cpython-37.pycbin1580 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/async.cpython-37.pycbin2722 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/base.cpython-37.pycbin61785 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/base_checker.cpython-37.pycbin6481 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/classes.cpython-37.pycbin44537 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-37.pycbin11667 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/exceptions.cpython-37.pycbin15668 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/format.cpython-37.pycbin31580 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/imports.cpython-37.pycbin25427 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/logging.cpython-37.pycbin10919 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/misc.cpython-37.pycbin4597 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/newstyle.cpython-37.pycbin2422 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/python3.cpython-37.pycbin34941 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-37.pycbin3254 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/refactoring.cpython-37.pycbin45321 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/similar.cpython-37.pycbin12304 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/spelling.cpython-37.pycbin9755 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/stdlib.cpython-37.pycbin12738 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/strings.cpython-37.pycbin17427 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/typecheck.cpython-37.pycbin40274 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/utils.cpython-37.pycbin31460 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/variables.cpython-37.pycbin44587 -> 0 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/async.py89
-rw-r--r--venv/Lib/site-packages/pylint/checkers/base.py2333
-rw-r--r--venv/Lib/site-packages/pylint/checkers/base_checker.py187
-rw-r--r--venv/Lib/site-packages/pylint/checkers/classes.py1844
-rw-r--r--venv/Lib/site-packages/pylint/checkers/design_analysis.py496
-rw-r--r--venv/Lib/site-packages/pylint/checkers/exceptions.py546
-rw-r--r--venv/Lib/site-packages/pylint/checkers/format.py1332
-rw-r--r--venv/Lib/site-packages/pylint/checkers/imports.py981
-rw-r--r--venv/Lib/site-packages/pylint/checkers/logging.py384
-rw-r--r--venv/Lib/site-packages/pylint/checkers/misc.py171
-rw-r--r--venv/Lib/site-packages/pylint/checkers/newstyle.py127
-rw-r--r--venv/Lib/site-packages/pylint/checkers/python3.py1398
-rw-r--r--venv/Lib/site-packages/pylint/checkers/raw_metrics.py119
-rw-r--r--venv/Lib/site-packages/pylint/checkers/refactoring.py1510
-rw-r--r--venv/Lib/site-packages/pylint/checkers/similar.py452
-rw-r--r--venv/Lib/site-packages/pylint/checkers/spelling.py411
-rw-r--r--venv/Lib/site-packages/pylint/checkers/stdlib.py452
-rw-r--r--venv/Lib/site-packages/pylint/checkers/strings.py755
-rw-r--r--venv/Lib/site-packages/pylint/checkers/typecheck.py1770
-rw-r--r--venv/Lib/site-packages/pylint/checkers/utils.py1253
-rw-r--r--venv/Lib/site-packages/pylint/checkers/variables.py1987
44 files changed, 0 insertions, 18661 deletions
diff --git a/venv/Lib/site-packages/pylint/checkers/__init__.py b/venv/Lib/site-packages/pylint/checkers/__init__.py
deleted file mode 100644
index 9c6306f..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__init__.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2013-2014 Google, Inc.
-# Copyright (c) 2013 buck@yelp.com <buck@yelp.com>
-# Copyright (c) 2014-2017 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
-# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@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
-
-"""utilities methods and classes for checkers
-
-Base id of standard checkers (used in msg and report ids):
-01: base
-02: classes
-03: format
-04: import
-05: misc
-06: variables
-07: exceptions
-08: similar
-09: design_analysis
-10: newstyle
-11: typecheck
-12: logging
-13: string_format
-14: string_constant
-15: stdlib
-16: python3
-17: refactoring
-18-50: not yet used: reserved for future internal checkers.
-51-99: perhaps used: reserved for external checkers
-
-The raw_metrics checker has no number associated since it doesn't emit any
-messages nor reports. XXX not true, emit a 07 report !
-
-"""
-
-from pylint.checkers.base_checker import BaseChecker, BaseTokenChecker
-from pylint.utils import register_plugins
-
-
-def table_lines_from_stats(stats, _, columns):
- """get values listed in <columns> from <stats> and <old_stats>,
- and return a formated list of values, designed to be given to a
- ureport.Table object
- """
- lines = []
- for m_type in columns:
- new = stats[m_type]
- new = "%.3f" % new if isinstance(new, float) else str(new)
- lines += (m_type.replace("_", " "), new, "NC", "NC")
- return lines
-
-
-def initialize(linter):
- """initialize linter with checkers in this package """
- register_plugins(linter, __path__[0])
-
-
-__all__ = ("BaseChecker", "BaseTokenChecker", "initialize")
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/__init__.cpython-37.pyc
deleted file mode 100644
index 3782086..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/__init__.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/async.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/async.cpython-37.pyc
deleted file mode 100644
index ea14658..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/async.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/base.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/base.cpython-37.pyc
deleted file mode 100644
index aaa3e51..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/base.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/base_checker.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/base_checker.cpython-37.pyc
deleted file mode 100644
index e4f8221..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/base_checker.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/classes.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/classes.cpython-37.pyc
deleted file mode 100644
index d0f58b4..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/classes.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-37.pyc
deleted file mode 100644
index 647b5aa..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/exceptions.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/exceptions.cpython-37.pyc
deleted file mode 100644
index 5371c29..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/exceptions.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/format.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/format.cpython-37.pyc
deleted file mode 100644
index 8a6a0c0..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/format.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/imports.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/imports.cpython-37.pyc
deleted file mode 100644
index f8b924d..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/imports.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/logging.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/logging.cpython-37.pyc
deleted file mode 100644
index 90cc06e..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/logging.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/misc.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/misc.cpython-37.pyc
deleted file mode 100644
index 9f449d4..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/misc.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/newstyle.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/newstyle.cpython-37.pyc
deleted file mode 100644
index e409591..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/newstyle.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/python3.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/python3.cpython-37.pyc
deleted file mode 100644
index b405dd3..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/python3.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-37.pyc
deleted file mode 100644
index fdf16f6..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/refactoring.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/refactoring.cpython-37.pyc
deleted file mode 100644
index f65c6b5..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/refactoring.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/similar.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/similar.cpython-37.pyc
deleted file mode 100644
index 09b77e5..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/similar.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/spelling.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/spelling.cpython-37.pyc
deleted file mode 100644
index dbf748c..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/spelling.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/stdlib.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/stdlib.cpython-37.pyc
deleted file mode 100644
index 97576df..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/stdlib.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/strings.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/strings.cpython-37.pyc
deleted file mode 100644
index 0aab77c..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/strings.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/typecheck.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/typecheck.cpython-37.pyc
deleted file mode 100644
index cc0c9b4..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/typecheck.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/utils.cpython-37.pyc
deleted file mode 100644
index 90e8ff1..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/utils.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/variables.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/variables.cpython-37.pyc
deleted file mode 100644
index 943ffbd..0000000
--- a/venv/Lib/site-packages/pylint/checkers/__pycache__/variables.cpython-37.pyc
+++ /dev/null
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/async.py b/venv/Lib/site-packages/pylint/checkers/async.py
deleted file mode 100644
index c33071e..0000000
--- a/venv/Lib/site-packages/pylint/checkers/async.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2017 Derek Gustafson <degustaf@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
-
-"""Checker for anything related to the async protocol (PEP 492)."""
-
-import sys
-
-import astroid
-from astroid import bases, exceptions
-
-from pylint import checkers, interfaces, utils
-from pylint.checkers import utils as checker_utils
-from pylint.checkers.utils import decorated_with
-
-
-class AsyncChecker(checkers.BaseChecker):
- __implements__ = interfaces.IAstroidChecker
- name = "async"
- msgs = {
- "E1700": (
- "Yield inside async function",
- "yield-inside-async-function",
- "Used when an `yield` or `yield from` statement is "
- "found inside an async function.",
- {"minversion": (3, 5)},
- ),
- "E1701": (
- "Async context manager '%s' doesn't implement __aenter__ and __aexit__.",
- "not-async-context-manager",
- "Used when an async context manager is used with an object "
- "that does not implement the async context management protocol.",
- {"minversion": (3, 5)},
- ),
- }
-
- def open(self):
- self._ignore_mixin_members = utils.get_global_option(
- self, "ignore-mixin-members"
- )
- self._async_generators = ["contextlib.asynccontextmanager"]
-
- @checker_utils.check_messages("yield-inside-async-function")
- def visit_asyncfunctiondef(self, node):
- for child in node.nodes_of_class(astroid.Yield):
- if child.scope() is node and (
- sys.version_info[:2] == (3, 5) or isinstance(child, astroid.YieldFrom)
- ):
- self.add_message("yield-inside-async-function", node=child)
-
- @checker_utils.check_messages("not-async-context-manager")
- def visit_asyncwith(self, node):
- for ctx_mgr, _ in node.items:
- inferred = checker_utils.safe_infer(ctx_mgr)
- if inferred is None or inferred is astroid.Uninferable:
- continue
-
- if isinstance(inferred, bases.AsyncGenerator):
- # Check if we are dealing with a function decorated
- # with contextlib.asynccontextmanager.
- if decorated_with(inferred.parent, self._async_generators):
- continue
- else:
- try:
- inferred.getattr("__aenter__")
- inferred.getattr("__aexit__")
- except exceptions.NotFoundError:
- if isinstance(inferred, astroid.Instance):
- # If we do not know the bases of this class,
- # just skip it.
- if not checker_utils.has_known_bases(inferred):
- continue
- # Just ignore mixin classes.
- if self._ignore_mixin_members:
- if inferred.name[-5:].lower() == "mixin":
- continue
- else:
- continue
-
- self.add_message(
- "not-async-context-manager", node=node, args=(inferred.name,)
- )
-
-
-def register(linter):
- """required method to auto register this checker"""
- linter.register_checker(AsyncChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/base.py b/venv/Lib/site-packages/pylint/checkers/base.py
deleted file mode 100644
index c94676e..0000000
--- a/venv/Lib/site-packages/pylint/checkers/base.py
+++ /dev/null
@@ -1,2333 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006-2016 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
-# Copyright (c) 2012-2014 Google, Inc.
-# Copyright (c) 2013-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 Nick Bastin <nick.bastin@gmail.com>
-# Copyright (c) 2015 Michael Kefeder <oss@multiwave.ch>
-# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
-# Copyright (c) 2015 Stephane Wirtel <stephane@wirtel.be>
-# Copyright (c) 2015 Cosmin Poieana <cmin@ropython.org>
-# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
-# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016, 2018 Jakub Wilk <jwilk@jwilk.net>
-# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
-# Copyright (c) 2016 Elias Dorneles <eliasdorneles@gmail.com>
-# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2016 Yannack <yannack@users.noreply.github.com>
-# Copyright (c) 2016 Alex Jurkiewicz <alex@jurkiewi.cz>
-# Copyright (c) 2017 Jacques Kvam <jwkvam@gmail.com>
-# Copyright (c) 2017 ttenhoeve-aa <ttenhoeve@appannie.com>
-# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
-# Copyright (c) 2018 Steven M. Vascellaro <svascellaro@gmail.com>
-# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
-# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
-# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
-# Copyright (c) 2018 Chris Lamb <chris@chris-lamb.co.uk>
-# Copyright (c) 2018 glmdgrielson <32415403+glmdgrielson@users.noreply.github.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
-
-"""basic checker for Python code"""
-
-import builtins
-import collections
-import itertools
-import re
-import sys
-from typing import Pattern
-
-import astroid
-import astroid.bases
-import astroid.scoped_nodes
-from astroid.arguments import CallSite
-
-import pylint.utils as lint_utils
-from pylint import checkers, exceptions, interfaces
-from pylint.checkers import utils
-from pylint.checkers.utils import is_property_setter_or_deleter
-from pylint.reporters.ureports import nodes as reporter_nodes
-
-
-class NamingStyle:
- # It may seem counterintuitive that single naming style
- # has multiple "accepted" forms of regular expressions,
- # but we need to special-case stuff like dunder names
- # in method names.
- CLASS_NAME_RGX = None # type: Pattern[str]
- MOD_NAME_RGX = None # type: Pattern[str]
- CONST_NAME_RGX = None # type: Pattern[str]
- COMP_VAR_RGX = None # type: Pattern[str]
- DEFAULT_NAME_RGX = None # type: Pattern[str]
- CLASS_ATTRIBUTE_RGX = None # type: Pattern[str]
-
- @classmethod
- def get_regex(cls, name_type):
- return {
- "module": cls.MOD_NAME_RGX,
- "const": cls.CONST_NAME_RGX,
- "class": cls.CLASS_NAME_RGX,
- "function": cls.DEFAULT_NAME_RGX,
- "method": cls.DEFAULT_NAME_RGX,
- "attr": cls.DEFAULT_NAME_RGX,
- "argument": cls.DEFAULT_NAME_RGX,
- "variable": cls.DEFAULT_NAME_RGX,
- "class_attribute": cls.CLASS_ATTRIBUTE_RGX,
- "inlinevar": cls.COMP_VAR_RGX,
- }[name_type]
-
-
-class SnakeCaseStyle(NamingStyle):
- """Regex rules for snake_case naming style."""
-
- CLASS_NAME_RGX = re.compile("[a-z_][a-z0-9_]+$")
- MOD_NAME_RGX = re.compile("([a-z_][a-z0-9_]*)$")
- CONST_NAME_RGX = re.compile("(([a-z_][a-z0-9_]*)|(__.*__))$")
- COMP_VAR_RGX = re.compile("[a-z_][a-z0-9_]*$")
- DEFAULT_NAME_RGX = re.compile(
- "(([a-z_][a-z0-9_]{2,})|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$"
- )
- CLASS_ATTRIBUTE_RGX = re.compile(r"(([a-z_][a-z0-9_]{2,}|(__.*__)))$")
-
-
-class CamelCaseStyle(NamingStyle):
- """Regex rules for camelCase naming style."""
-
- CLASS_NAME_RGX = re.compile("[a-z_][a-zA-Z0-9]+$")
- MOD_NAME_RGX = re.compile("([a-z_][a-zA-Z0-9]*)$")
- CONST_NAME_RGX = re.compile("(([a-z_][A-Za-z0-9]*)|(__.*__))$")
- COMP_VAR_RGX = re.compile("[a-z_][A-Za-z0-9]*$")
- DEFAULT_NAME_RGX = re.compile("(([a-z_][a-zA-Z0-9]{2,})|(__[a-z][a-zA-Z0-9_]+__))$")
- CLASS_ATTRIBUTE_RGX = re.compile(r"([a-z_][A-Za-z0-9]{2,}|(__.*__))$")
-
-
-class PascalCaseStyle(NamingStyle):
- """Regex rules for PascalCase naming style."""
-
- CLASS_NAME_RGX = re.compile("[A-Z_][a-zA-Z0-9]+$")
- MOD_NAME_RGX = re.compile("[A-Z_][a-zA-Z0-9]+$")
- CONST_NAME_RGX = re.compile("(([A-Z_][A-Za-z0-9]*)|(__.*__))$")
- COMP_VAR_RGX = re.compile("[A-Z_][a-zA-Z0-9]+$")
- DEFAULT_NAME_RGX = re.compile("[A-Z_][a-zA-Z0-9]{2,}$|(__[a-z][a-zA-Z0-9_]+__)$")
- CLASS_ATTRIBUTE_RGX = re.compile("[A-Z_][a-zA-Z0-9]{2,}$")
-
-
-class UpperCaseStyle(NamingStyle):
- """Regex rules for UPPER_CASE naming style."""
-
- CLASS_NAME_RGX = re.compile("[A-Z_][A-Z0-9_]+$")
- MOD_NAME_RGX = re.compile("[A-Z_][A-Z0-9_]+$")
- CONST_NAME_RGX = re.compile("(([A-Z_][A-Z0-9_]*)|(__.*__))$")
- COMP_VAR_RGX = re.compile("[A-Z_][A-Z0-9_]+$")
- DEFAULT_NAME_RGX = re.compile("([A-Z_][A-Z0-9_]{2,})|(__[a-z][a-zA-Z0-9_]+__)$")
- CLASS_ATTRIBUTE_RGX = re.compile("[A-Z_][A-Z0-9_]{2,}$")
-
-
-class AnyStyle(NamingStyle):
- @classmethod
- def get_regex(cls, name_type):
- return re.compile(".*")
-
-
-NAMING_STYLES = {
- "snake_case": SnakeCaseStyle,
- "camelCase": CamelCaseStyle,
- "PascalCase": PascalCaseStyle,
- "UPPER_CASE": UpperCaseStyle,
- "any": AnyStyle,
-}
-
-# do not require a doc string on private/system methods
-NO_REQUIRED_DOC_RGX = re.compile("^_")
-REVERSED_PROTOCOL_METHOD = "__reversed__"
-SEQUENCE_PROTOCOL_METHODS = ("__getitem__", "__len__")
-REVERSED_METHODS = (SEQUENCE_PROTOCOL_METHODS, (REVERSED_PROTOCOL_METHOD,))
-TYPECHECK_COMPARISON_OPERATORS = frozenset(("is", "is not", "==", "!=", "in", "not in"))
-LITERAL_NODE_TYPES = (astroid.Const, astroid.Dict, astroid.List, astroid.Set)
-UNITTEST_CASE = "unittest.case"
-BUILTINS = builtins.__name__
-TYPE_QNAME = "%s.type" % BUILTINS
-ABC_METACLASSES = {"_py_abc.ABCMeta", "abc.ABCMeta"} # Python 3.7+,
-
-# Name categories that are always consistent with all naming conventions.
-EXEMPT_NAME_CATEGORIES = {"exempt", "ignore"}
-
-# A mapping from builtin-qname -> symbol, to be used when generating messages
-# about dangerous default values as arguments
-DEFAULT_ARGUMENT_SYMBOLS = dict(
- zip(
- [".".join([BUILTINS, x]) for x in ("set", "dict", "list")],
- ["set()", "{}", "[]"],
- )
-)
-REVERSED_COMPS = {"<": ">", "<=": ">=", ">": "<", ">=": "<="}
-COMPARISON_OPERATORS = frozenset(("==", "!=", "<", ">", "<=", ">="))
-# List of methods which can be redefined
-REDEFINABLE_METHODS = frozenset(("__module__",))
-TYPING_FORWARD_REF_QNAME = "typing.ForwardRef"
-
-
-def _redefines_import(node):
- """ Detect that the given node (AssignName) is inside an
- exception handler and redefines an import from the tryexcept body.
- Returns True if the node redefines an import, False otherwise.
- """
- current = node
- while current and not isinstance(current.parent, astroid.ExceptHandler):
- current = current.parent
- if not current or not utils.error_of_type(current.parent, ImportError):
- return False
- try_block = current.parent.parent
- for import_node in try_block.nodes_of_class((astroid.ImportFrom, astroid.Import)):
- for name, alias in import_node.names:
- if alias:
- if alias == node.name:
- return True
- elif name == node.name:
- return True
- return False
-
-
-def in_loop(node):
- """return True if the node is inside a kind of for loop"""
- parent = node.parent
- while parent is not None:
- if isinstance(
- parent,
- (
- astroid.For,
- astroid.ListComp,
- astroid.SetComp,
- astroid.DictComp,
- astroid.GeneratorExp,
- ),
- ):
- return True
- parent = parent.parent
- return False
-
-
-def in_nested_list(nested_list, obj):
- """return true if the object is an element of <nested_list> or of a nested
- list
- """
- for elmt in nested_list:
- if isinstance(elmt, (list, tuple)):
- if in_nested_list(elmt, obj):
- return True
- elif elmt == obj:
- return True
- return False
-
-
-def _get_break_loop_node(break_node):
- """
- Returns the loop node that holds the break node in arguments.
-
- Args:
- break_node (astroid.Break): the break node of interest.
-
- Returns:
- astroid.For or astroid.While: the loop node holding the break node.
- """
- loop_nodes = (astroid.For, astroid.While)
- parent = break_node.parent
- while not isinstance(parent, loop_nodes) or break_node in getattr(
- parent, "orelse", []
- ):
- break_node = parent
- parent = parent.parent
- if parent is None:
- break
- return parent
-
-
-def _loop_exits_early(loop):
- """
- Returns true if a loop may ends up in a break statement.
-
- Args:
- loop (astroid.For, astroid.While): the loop node inspected.
-
- Returns:
- bool: True if the loop may ends up in a break statement, False otherwise.
- """
- loop_nodes = (astroid.For, astroid.While)
- definition_nodes = (astroid.FunctionDef, astroid.ClassDef)
- inner_loop_nodes = [
- _node
- for _node in loop.nodes_of_class(loop_nodes, skip_klass=definition_nodes)
- if _node != loop
- ]
- return any(
- _node
- for _node in loop.nodes_of_class(astroid.Break, skip_klass=definition_nodes)
- if _get_break_loop_node(_node) not in inner_loop_nodes
- )
-
-
-def _is_multi_naming_match(match, node_type, confidence):
- return (
- match is not None
- and match.lastgroup is not None
- and match.lastgroup not in EXEMPT_NAME_CATEGORIES
- and (node_type != "method" or confidence != interfaces.INFERENCE_FAILURE)
- )
-
-
-BUILTIN_PROPERTY = "builtins.property"
-
-
-def _get_properties(config):
- """Returns a tuple of property classes and names.
-
- Property classes are fully qualified, such as 'abc.abstractproperty' and
- property names are the actual names, such as 'abstract_property'.
- """
- property_classes = {BUILTIN_PROPERTY}
- property_names = set() # Not returning 'property', it has its own check.
- if config is not None:
- property_classes.update(config.property_classes)
- property_names.update(
- (prop.rsplit(".", 1)[-1] for prop in config.property_classes)
- )
- return property_classes, property_names
-
-
-def _determine_function_name_type(node, config=None):
- """Determine the name type whose regex the a function's name should match.
-
- :param node: A function node.
- :type node: astroid.node_classes.NodeNG
- :param config: Configuration from which to pull additional property classes.
- :type config: :class:`optparse.Values`
-
- :returns: One of ('function', 'method', 'attr')
- :rtype: str
- """
- property_classes, property_names = _get_properties(config)
- if not node.is_method():
- return "function"
-
- if is_property_setter_or_deleter(node):
- # If the function is decorated using the prop_method.{setter,getter}
- # form, treat it like an attribute as well.
- return "attr"
-
- if node.decorators:
- decorators = node.decorators.nodes
- else:
- decorators = []
- for decorator in decorators:
- # If the function is a property (decorated with @property
- # or @abc.abstractproperty), the name type is 'attr'.
- if isinstance(decorator, astroid.Name) or (
- isinstance(decorator, astroid.Attribute)
- and decorator.attrname in property_names
- ):
- inferred = utils.safe_infer(decorator)
- if inferred and inferred.qname() in property_classes:
- return "attr"
- return "method"
-
-
-def _has_abstract_methods(node):
- """
- Determine if the given `node` has abstract methods.
-
- The methods should be made abstract by decorating them
- with `abc` decorators.
- """
- return len(utils.unimplemented_abstract_methods(node)) > 0
-
-
-def report_by_type_stats(sect, stats, _):
- """make a report of
-
- * percentage of different types documented
- * percentage of different types with a bad name
- """
- # percentage of different types documented and/or with a bad name
- nice_stats = {}
- for node_type in ("module", "class", "method", "function"):
- try:
- total = stats[node_type]
- except KeyError:
- raise exceptions.EmptyReportError()
- nice_stats[node_type] = {}
- if total != 0:
- try:
- documented = total - stats["undocumented_" + node_type]
- percent = (documented * 100.0) / total
- nice_stats[node_type]["percent_documented"] = "%.2f" % percent
- except KeyError:
- nice_stats[node_type]["percent_documented"] = "NC"
- try:
- percent = (stats["badname_" + node_type] * 100.0) / total
- nice_stats[node_type]["percent_badname"] = "%.2f" % percent
- except KeyError:
- nice_stats[node_type]["percent_badname"] = "NC"
- lines = ("type", "number", "old number", "difference", "%documented", "%badname")
- for node_type in ("module", "class", "method", "function"):
- new = stats[node_type]
- lines += (
- node_type,
- str(new),
- "NC",
- "NC",
- nice_stats[node_type].get("percent_documented", "0"),
- nice_stats[node_type].get("percent_badname", "0"),
- )
- sect.append(reporter_nodes.Table(children=lines, cols=6, rheaders=1))
-
-
-def redefined_by_decorator(node):
- """return True if the object is a method redefined via decorator.
-
- For example:
- @property
- def x(self): return self._x
- @x.setter
- def x(self, value): self._x = value
- """
- if node.decorators:
- for decorator in node.decorators.nodes:
- if (
- isinstance(decorator, astroid.Attribute)
- and getattr(decorator.expr, "name", None) == node.name
- ):
- return True
- return False
-
-
-class _BasicChecker(checkers.BaseChecker):
- __implements__ = interfaces.IAstroidChecker
- name = "basic"
-
-
-class BasicErrorChecker(_BasicChecker):
- msgs = {
- "E0100": (
- "__init__ method is a generator",
- "init-is-generator",
- "Used when the special class method __init__ is turned into a "
- "generator by a yield in its body.",
- ),
- "E0101": (
- "Explicit return in __init__",
- "return-in-init",
- "Used when the special class method __init__ has an explicit "
- "return value.",
- ),
- "E0102": (
- "%s already defined line %s",
- "function-redefined",
- "Used when a function / class / method is redefined.",
- ),
- "E0103": (
- "%r not properly in loop",
- "not-in-loop",
- "Used when break or continue keywords are used outside a loop.",
- ),
- "E0104": (
- "Return outside function",
- "return-outside-function",
- 'Used when a "return" statement is found outside a function or method.',
- ),
- "E0105": (
- "Yield outside function",
- "yield-outside-function",
- 'Used when a "yield" statement is found outside a function or method.',
- ),
- "E0106": (
- "Return with argument inside generator",
- "return-arg-in-generator",
- 'Used when a "return" statement with an argument is found '
- "outside in a generator function or method (e.g. with some "
- '"yield" statements).',
- {"maxversion": (3, 3)},
- ),
- "E0107": (
- "Use of the non-existent %s operator",
- "nonexistent-operator",
- "Used when you attempt to use the C-style pre-increment or "
- "pre-decrement operator -- and ++, which doesn't exist in Python.",
- ),
- "E0108": (
- "Duplicate argument name %s in function definition",
- "duplicate-argument-name",
- "Duplicate argument names in function definitions are syntax errors.",
- ),
- "E0110": (
- "Abstract class %r with abstract methods instantiated",
- "abstract-class-instantiated",
- "Used when an abstract class with `abc.ABCMeta` as metaclass "
- "has abstract methods and is instantiated.",
- ),
- "W0120": (
- "Else clause on loop without a break statement",
- "useless-else-on-loop",
- "Loops should only have an else clause if they can exit early "
- "with a break statement, otherwise the statements under else "
- "should be on the same scope as the loop itself.",
- ),
- "E0112": (
- "More than one starred expression in assignment",
- "too-many-star-expressions",
- "Emitted when there are more than one starred "
- "expressions (`*x`) in an assignment. This is a SyntaxError.",
- ),
- "E0113": (
- "Starred assignment target must be in a list or tuple",
- "invalid-star-assignment-target",
- "Emitted when a star expression is used as a starred assignment target.",
- ),
- "E0114": (
- "Can use starred expression only in assignment target",
- "star-needs-assignment-target",
- "Emitted when a star expression is not used in an assignment target.",
- ),
- "E0115": (
- "Name %r is nonlocal and global",
- "nonlocal-and-global",
- "Emitted when a name is both nonlocal and global.",
- ),
- "E0116": (
- "'continue' not supported inside 'finally' clause",
- "continue-in-finally",
- "Emitted when the `continue` keyword is found "
- "inside a finally clause, which is a SyntaxError.",
- ),
- "E0117": (
- "nonlocal name %s found without binding",
- "nonlocal-without-binding",
- "Emitted when a nonlocal variable does not have an attached "
- "name somewhere in the parent scopes",
- ),
- "E0118": (
- "Name %r is used prior to global declaration",
- "used-prior-global-declaration",
- "Emitted when a name is used prior a global declaration, "
- "which results in an error since Python 3.6.",
- {"minversion": (3, 6)},
- ),
- }
-
- @utils.check_messages("function-redefined")
- def visit_classdef(self, node):
- self._check_redefinition("class", node)
-
- def _too_many_starred_for_tuple(self, assign_tuple):
- starred_count = 0
- for elem in assign_tuple.itered():
- if isinstance(elem, astroid.Tuple):
- return self._too_many_starred_for_tuple(elem)
- if isinstance(elem, astroid.Starred):
- starred_count += 1
- return starred_count > 1
-
- @utils.check_messages("too-many-star-expressions", "invalid-star-assignment-target")
- def visit_assign(self, node):
- # Check *a, *b = ...
- assign_target = node.targets[0]
- # Check *a = b
- if isinstance(node.targets[0], astroid.Starred):
- self.add_message("invalid-star-assignment-target", node=node)
-
- if not isinstance(assign_target, astroid.Tuple):
- return
- if self._too_many_starred_for_tuple(assign_target):
- self.add_message("too-many-star-expressions", node=node)
-
- @utils.check_messages("star-needs-assignment-target")
- def visit_starred(self, node):
- """Check that a Starred expression is used in an assignment target."""
- if isinstance(node.parent, astroid.Call):
- # f(*args) is converted to Call(args=[Starred]), so ignore
- # them for this check.
- return
- if isinstance(
- node.parent, (astroid.List, astroid.Tuple, astroid.Set, astroid.Dict)
- ):
- # PEP 448 unpacking.
- return
-
- stmt = node.statement()
- if not isinstance(stmt, astroid.Assign):
- return
-
- if stmt.value is node or stmt.value.parent_of(node):
- self.add_message("star-needs-assignment-target", node=node)
-
- @utils.check_messages(
- "init-is-generator",
- "return-in-init",
- "function-redefined",
- "return-arg-in-generator",
- "duplicate-argument-name",
- "nonlocal-and-global",
- "used-prior-global-declaration",
- )
- def visit_functiondef(self, node):
- self._check_nonlocal_and_global(node)
- self._check_name_used_prior_global(node)
- if not redefined_by_decorator(
- node
- ) and not utils.is_registered_in_singledispatch_function(node):
- self._check_redefinition(node.is_method() and "method" or "function", node)
- # checks for max returns, branch, return in __init__
- returns = node.nodes_of_class(
- astroid.Return, skip_klass=(astroid.FunctionDef, astroid.ClassDef)
- )
- if node.is_method() and node.name == "__init__":
- if node.is_generator():
- self.add_message("init-is-generator", node=node)
- else:
- values = [r.value for r in returns]
- # Are we returning anything but None from constructors
- if any(v for v in values if not utils.is_none(v)):
- self.add_message("return-in-init", node=node)
- # Check for duplicate names by clustering args with same name for detailed report
- arg_clusters = collections.defaultdict(list)
- arguments = filter(None, [node.args.args, node.args.kwonlyargs])
-
- for arg in itertools.chain.from_iterable(arguments):
- arg_clusters[arg.name].append(arg)
-
- # provide detailed report about each repeated argument
- for argument_duplicates in arg_clusters.values():
- if len(argument_duplicates) != 1:
- for argument in argument_duplicates:
- self.add_message(
- "duplicate-argument-name",
- line=argument.lineno,
- node=argument,
- args=(argument.name,),
- )
-
- visit_asyncfunctiondef = visit_functiondef
-
- def _check_name_used_prior_global(self, node):
-
- scope_globals = {
- name: child
- for child in node.nodes_of_class(astroid.Global)
- for name in child.names
- if child.scope() is node
- }
-
- if not scope_globals:
- return
-
- for node_name in node.nodes_of_class(astroid.Name):
- if node_name.scope() is not node:
- continue
-
- name = node_name.name
- corresponding_global = scope_globals.get(name)
- if not corresponding_global:
- continue
-
- global_lineno = corresponding_global.fromlineno
- if global_lineno and global_lineno > node_name.fromlineno:
- self.add_message(
- "used-prior-global-declaration", node=node_name, args=(name,)
- )
-
- def _check_nonlocal_and_global(self, node):
- """Check that a name is both nonlocal and global."""
-
- def same_scope(current):
- return current.scope() is node
-
- from_iter = itertools.chain.from_iterable
- nonlocals = set(
- from_iter(
- child.names
- for child in node.nodes_of_class(astroid.Nonlocal)
- if same_scope(child)
- )
- )
-
- if not nonlocals:
- return
-
- global_vars = set(
- from_iter(
- child.names
- for child in node.nodes_of_class(astroid.Global)
- if same_scope(child)
- )
- )
- for name in nonlocals.intersection(global_vars):
- self.add_message("nonlocal-and-global", args=(name,), node=node)
-
- @utils.check_messages("return-outside-function")
- def visit_return(self, node):
- if not isinstance(node.frame(), astroid.FunctionDef):
- self.add_message("return-outside-function", node=node)
-
- @utils.check_messages("yield-outside-function")
- def visit_yield(self, node):
- self._check_yield_outside_func(node)
-
- @utils.check_messages("yield-outside-function")
- def visit_yieldfrom(self, node):
- self._check_yield_outside_func(node)
-
- @utils.check_messages("not-in-loop", "continue-in-finally")
- def visit_continue(self, node):
- self._check_in_loop(node, "continue")
-
- @utils.check_messages("not-in-loop")
- def visit_break(self, node):
- self._check_in_loop(node, "break")
-
- @utils.check_messages("useless-else-on-loop")
- def visit_for(self, node):
- self._check_else_on_loop(node)
-
- @utils.check_messages("useless-else-on-loop")
- def visit_while(self, node):
- self._check_else_on_loop(node)
-
- @utils.check_messages("nonexistent-operator")
- def visit_unaryop(self, node):
- """check use of the non-existent ++ and -- operator operator"""
- if (
- (node.op in "+-")
- and isinstance(node.operand, astroid.UnaryOp)
- and (node.operand.op == node.op)
- ):
- self.add_message("nonexistent-operator", node=node, args=node.op * 2)
-
- def _check_nonlocal_without_binding(self, node, name):
- current_scope = node.scope()
- while True:
- if current_scope.parent is None:
- break
-
- if not isinstance(current_scope, (astroid.ClassDef, astroid.FunctionDef)):
- self.add_message("nonlocal-without-binding", args=(name,), node=node)
- return
-
- if name not in current_scope.locals:
- current_scope = current_scope.parent.scope()
- continue
-
- # Okay, found it.
- return
-
- if not isinstance(current_scope, astroid.FunctionDef):
- self.add_message("nonlocal-without-binding", args=(name,), node=node)
-
- @utils.check_messages("nonlocal-without-binding")
- def visit_nonlocal(self, node):
- for name in node.names:
- self._check_nonlocal_without_binding(node, name)
-
- @utils.check_messages("abstract-class-instantiated")
- def visit_call(self, node):
- """ Check instantiating abstract class with
- abc.ABCMeta as metaclass.
- """
- try:
- for inferred in node.func.infer():
- self._check_inferred_class_is_abstract(inferred, node)
- except astroid.InferenceError:
- return
-
- def _check_inferred_class_is_abstract(self, inferred, node):
- if not isinstance(inferred, astroid.ClassDef):
- return
-
- klass = utils.node_frame_class(node)
- if klass is inferred:
- # Don't emit the warning if the class is instantiated
- # in its own body or if the call is not an instance
- # creation. If the class is instantiated into its own
- # body, we're expecting that it knows what it is doing.
- return
-
- # __init__ was called
- abstract_methods = _has_abstract_methods(inferred)
-
- if not abstract_methods:
- return
-
- metaclass = inferred.metaclass()
-
- if metaclass is None:
- # Python 3.4 has `abc.ABC`, which won't be detected
- # by ClassNode.metaclass()
- for ancestor in inferred.ancestors():
- if ancestor.qname() == "abc.ABC":
- self.add_message(
- "abstract-class-instantiated", args=(inferred.name,), node=node
- )
- break
-
- return
-
- if metaclass.qname() in ABC_METACLASSES:
- self.add_message(
- "abstract-class-instantiated", args=(inferred.name,), node=node
- )
-
- def _check_yield_outside_func(self, node):
- if not isinstance(node.frame(), (astroid.FunctionDef, astroid.Lambda)):
- self.add_message("yield-outside-function", node=node)
-
- def _check_else_on_loop(self, node):
- """Check that any loop with an else clause has a break statement."""
- if node.orelse and not _loop_exits_early(node):
- self.add_message(
- "useless-else-on-loop",
- node=node,
- # This is not optimal, but the line previous
- # to the first statement in the else clause
- # will usually be the one that contains the else:.
- line=node.orelse[0].lineno - 1,
- )
-
- def _check_in_loop(self, node, node_name):
- """check that a node is inside a for or while loop"""
- _node = node.parent
- while _node:
- if isinstance(_node, (astroid.For, astroid.While)):
- if node not in _node.orelse:
- return
-
- if isinstance(_node, (astroid.ClassDef, astroid.FunctionDef)):
- break
- if (
- isinstance(_node, astroid.TryFinally)
- and node in _node.finalbody
- and isinstance(node, astroid.Continue)
- ):
- self.add_message("continue-in-finally", node=node)
-
- _node = _node.parent
-
- self.add_message("not-in-loop", node=node, args=node_name)
-
- def _check_redefinition(self, redeftype, node):
- """check for redefinition of a function / method / class name"""
- parent_frame = node.parent.frame()
-
- # Ignore function stubs created for type information
- redefinitions = parent_frame.locals[node.name]
- defined_self = next(
- (local for local in redefinitions if not utils.is_overload_stub(local)),
- node,
- )
- if defined_self is not node and not astroid.are_exclusive(node, defined_self):
-
- # Additional checks for methods which are not considered
- # redefined, since they are already part of the base API.
- if (
- isinstance(parent_frame, astroid.ClassDef)
- and node.name in REDEFINABLE_METHODS
- ):
- return
-
- if utils.is_overload_stub(node):
- return
-
- # Check if we have forward references for this node.
- try:
- redefinition_index = redefinitions.index(node)
- except ValueError:
- pass
- else:
- for redefinition in redefinitions[:redefinition_index]:
- inferred = utils.safe_infer(redefinition)
- if (
- inferred
- and isinstance(inferred, astroid.Instance)
- and inferred.qname() == TYPING_FORWARD_REF_QNAME
- ):
- return
-
- dummy_variables_rgx = lint_utils.get_global_option(
- self, "dummy-variables-rgx", default=None
- )
- if dummy_variables_rgx and dummy_variables_rgx.match(node.name):
- return
- self.add_message(
- "function-redefined",
- node=node,
- args=(redeftype, defined_self.fromlineno),
- )
-
-
-class BasicChecker(_BasicChecker):
- """checks for :
- * doc strings
- * number of arguments, local variables, branches, returns and statements in
- functions, methods
- * required module attributes
- * dangerous default values as arguments
- * redefinition of function / method / class
- * uses of the global statement
- """
-
- __implements__ = interfaces.IAstroidChecker
-
- name = "basic"
- msgs = {
- "W0101": (
- "Unreachable code",
- "unreachable",
- 'Used when there is some code behind a "return" or "raise" '
- "statement, which will never be accessed.",
- ),
- "W0102": (
- "Dangerous default value %s as argument",
- "dangerous-default-value",
- "Used when a mutable value as list or dictionary is detected in "
- "a default value for an argument.",
- ),
- "W0104": (
- "Statement seems to have no effect",
- "pointless-statement",
- "Used when a statement doesn't have (or at least seems to) any effect.",
- ),
- "W0105": (
- "String statement has no effect",
- "pointless-string-statement",
- "Used when a string is used as a statement (which of course "
- "has no effect). This is a particular case of W0104 with its "
- "own message so you can easily disable it if you're using "
- "those strings as documentation, instead of comments.",
- ),
- "W0106": (
- 'Expression "%s" is assigned to nothing',
- "expression-not-assigned",
- "Used when an expression that is not a function call is assigned "
- "to nothing. Probably something else was intended.",
- ),
- "W0108": (
- "Lambda may not be necessary",
- "unnecessary-lambda",
- "Used when the body of a lambda expression is a function call "
- "on the same argument list as the lambda itself; such lambda "
- "expressions are in all but a few cases replaceable with the "
- "function being called in the body of the lambda.",
- ),
- "W0109": (
- "Duplicate key %r in dictionary",
- "duplicate-key",
- "Used when a dictionary expression binds the same key multiple times.",
- ),
- "W0122": (
- "Use of exec",
- "exec-used",
- 'Used when you use the "exec" statement (function for Python '
- "3), to discourage its usage. That doesn't "
- "mean you cannot use it !",
- ),
- "W0123": (
- "Use of eval",
- "eval-used",
- 'Used when you use the "eval" function, to discourage its '
- "usage. Consider using `ast.literal_eval` for safely evaluating "
- "strings containing Python expressions "
- "from untrusted sources. ",
- ),
- "W0150": (
- "%s statement in finally block may swallow exception",
- "lost-exception",
- "Used when a break or a return statement is found inside the "
- "finally clause of a try...finally block: the exceptions raised "
- "in the try clause will be silently swallowed instead of being "
- "re-raised.",
- ),
- "W0199": (
- "Assert called on a 2-item-tuple. Did you mean 'assert x,y'?",
- "assert-on-tuple",
- "A call of assert on a tuple will always evaluate to true if "
- "the tuple is not empty, and will always evaluate to false if "
- "it is.",
- ),
- "W0124": (
- 'Following "as" with another context manager looks like a tuple.',
- "confusing-with-statement",
- "Emitted when a `with` statement component returns multiple values "
- "and uses name binding with `as` only for a part of those values, "
- "as in with ctx() as a, b. This can be misleading, since it's not "
- "clear if the context manager returns a tuple or if the node without "
- "a name binding is another context manager.",
- ),
- "W0125": (
- "Using a conditional statement with a constant value",
- "using-constant-test",
- "Emitted when a conditional statement (If or ternary if) "
- "uses a constant value for its test. This might not be what "
- "the user intended to do.",
- ),
- "W0126": (
- "Using a conditional statement with potentially wrong function or method call due to missing parentheses",
- "missing-parentheses-for-call-in-test",
- "Emitted when a conditional statement (If or ternary if) "
- "seems to wrongly call a function due to missing parentheses",
- ),
- "W0127": (
- "Assigning the same variable %r to itself",
- "self-assigning-variable",
- "Emitted when we detect that a variable is assigned to itself",
- ),
- "W0128": (
- "Redeclared variable %r in assignment",
- "redeclared-assigned-name",
- "Emitted when we detect that a variable was redeclared in the same assignment.",
- ),
- "E0111": (
- "The first reversed() argument is not a sequence",
- "bad-reversed-sequence",
- "Used when the first argument to reversed() builtin "
- "isn't a sequence (does not implement __reversed__, "
- "nor __getitem__ and __len__",
- ),
- "E0119": (
- "format function is not called on str",
- "misplaced-format-function",
- "Emitted when format function is not called on str object. "
- 'e.g doing print("value: {}").format(123) instead of '
- 'print("value: {}".format(123)). This might not be what the user '
- "intended to do.",
- ),
- }
-
- reports = (("RP0101", "Statistics by type", report_by_type_stats),)
-
- def __init__(self, linter):
- _BasicChecker.__init__(self, linter)
- self.stats = None
- self._tryfinallys = None
-
- def open(self):
- """initialize visit variables and statistics
- """
- self._tryfinallys = []
- self.stats = self.linter.add_stats(module=0, function=0, method=0, class_=0)
-
- @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test")
- def visit_if(self, node):
- self._check_using_constant_test(node, node.test)
-
- @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test")
- def visit_ifexp(self, node):
- self._check_using_constant_test(node, node.test)
-
- @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test")
- def visit_comprehension(self, node):
- if node.ifs:
- for if_test in node.ifs:
- self._check_using_constant_test(node, if_test)
-
- def _check_using_constant_test(self, node, test):
- const_nodes = (
- astroid.Module,
- astroid.scoped_nodes.GeneratorExp,
- astroid.Lambda,
- astroid.FunctionDef,
- astroid.ClassDef,
- astroid.bases.Generator,
- astroid.UnboundMethod,
- astroid.BoundMethod,
- astroid.Module,
- )
- structs = (astroid.Dict, astroid.Tuple, astroid.Set)
-
- # These nodes are excepted, since they are not constant
- # values, requiring a computation to happen.
- except_nodes = (
- astroid.Call,
- astroid.BinOp,
- astroid.BoolOp,
- astroid.UnaryOp,
- astroid.Subscript,
- )
- inferred = None
- emit = isinstance(test, (astroid.Const,) + structs + const_nodes)
- if not isinstance(test, except_nodes):
- inferred = utils.safe_infer(test)
-
- if emit:
- self.add_message("using-constant-test", node=node)
- elif isinstance(inferred, const_nodes):
- # If the constant node is a FunctionDef or Lambda then
- #  it may be a illicit function call due to missing parentheses
- call_inferred = None
- if isinstance(inferred, astroid.FunctionDef):
- call_inferred = inferred.infer_call_result()
- elif isinstance(inferred, astroid.Lambda):
- call_inferred = inferred.infer_call_result(node)
- if call_inferred:
- try:
- for inf_call in call_inferred:
- if inf_call != astroid.Uninferable:
- self.add_message(
- "missing-parentheses-for-call-in-test", node=node
- )
- break
- except astroid.InferenceError:
- pass
- self.add_message("using-constant-test", node=node)
-
- def visit_module(self, _):
- """check module name, docstring and required arguments
- """
- self.stats["module"] += 1
-
- def visit_classdef(self, node): # pylint: disable=unused-argument
- """check module name, docstring and redefinition
- increment branch counter
- """
- self.stats["class"] += 1
-
- @utils.check_messages(
- "pointless-statement", "pointless-string-statement", "expression-not-assigned"
- )
- def visit_expr(self, node):
- """Check for various kind of statements without effect"""
- expr = node.value
- if isinstance(expr, astroid.Const) and isinstance(expr.value, str):
- # treat string statement in a separated message
- # Handle PEP-257 attribute docstrings.
- # An attribute docstring is defined as being a string right after
- # an assignment at the module level, class level or __init__ level.
- scope = expr.scope()
- if isinstance(
- scope, (astroid.ClassDef, astroid.Module, astroid.FunctionDef)
- ):
- if isinstance(scope, astroid.FunctionDef) and scope.name != "__init__":
- pass
- else:
- sibling = expr.previous_sibling()
- if (
- sibling is not None
- and sibling.scope() is scope
- and isinstance(sibling, (astroid.Assign, astroid.AnnAssign))
- ):
- return
- self.add_message("pointless-string-statement", node=node)
- return
-
- # Ignore if this is :
- # * a direct function call
- # * the unique child of a try/except body
- # * a yield statement
- # * an ellipsis (which can be used on Python 3 instead of pass)
- # warn W0106 if we have any underlying function call (we can't predict
- # side effects), else pointless-statement
- if (
- isinstance(
- expr, (astroid.Yield, astroid.Await, astroid.Ellipsis, astroid.Call)
- )
- or (
- isinstance(node.parent, astroid.TryExcept)
- and node.parent.body == [node]
- )
- or (isinstance(expr, astroid.Const) and expr.value is Ellipsis)
- ):
- return
- if any(expr.nodes_of_class(astroid.Call)):
- self.add_message(
- "expression-not-assigned", node=node, args=expr.as_string()
- )
- else:
- self.add_message("pointless-statement", node=node)
-
- @staticmethod
- def _filter_vararg(node, call_args):
- # Return the arguments for the given call which are
- # not passed as vararg.
- for arg in call_args:
- if isinstance(arg, astroid.Starred):
- if (
- isinstance(arg.value, astroid.Name)
- and arg.value.name != node.args.vararg
- ):
- yield arg
- else:
- yield arg
-
- @staticmethod
- def _has_variadic_argument(args, variadic_name):
- if not args:
- return True
- for arg in args:
- if isinstance(arg.value, astroid.Name):
- if arg.value.name != variadic_name:
- return True
- else:
- return True
- return False
-
- @utils.check_messages("unnecessary-lambda")
- def visit_lambda(self, node):
- """check whether or not the lambda is suspicious
- """
- # if the body of the lambda is a call expression with the same
- # argument list as the lambda itself, then the lambda is
- # possibly unnecessary and at least suspicious.
- if node.args.defaults:
- # If the arguments of the lambda include defaults, then a
- # judgment cannot be made because there is no way to check
- # that the defaults defined by the lambda are the same as
- # the defaults defined by the function called in the body
- # of the lambda.
- return
- call = node.body
- if not isinstance(call, astroid.Call):
- # The body of the lambda must be a function call expression
- # for the lambda to be unnecessary.
- return
- if isinstance(node.body.func, astroid.Attribute) and isinstance(
- node.body.func.expr, astroid.Call
- ):
- # Chained call, the intermediate call might
- # return something else (but we don't check that, yet).
- return
-
- call_site = CallSite.from_call(call)
- ordinary_args = list(node.args.args)
- new_call_args = list(self._filter_vararg(node, call.args))
- if node.args.kwarg:
- if self._has_variadic_argument(call.kwargs, node.args.kwarg):
- return
-
- if node.args.vararg:
- if self._has_variadic_argument(call.starargs, node.args.vararg):
- return
- elif call.starargs:
- return
-
- if call.keywords:
- # Look for additional keyword arguments that are not part
- # of the lambda's signature
- lambda_kwargs = {keyword.name for keyword in node.args.defaults}
- if len(lambda_kwargs) != len(call_site.keyword_arguments):
- # Different lengths, so probably not identical
- return
- if set(call_site.keyword_arguments).difference(lambda_kwargs):
- return
-
- # The "ordinary" arguments must be in a correspondence such that:
- # ordinary_args[i].name == call.args[i].name.
- if len(ordinary_args) != len(new_call_args):
- return
- for arg, passed_arg in zip(ordinary_args, new_call_args):
- if not isinstance(passed_arg, astroid.Name):
- return
- if arg.name != passed_arg.name:
- return
-
- self.add_message("unnecessary-lambda", line=node.fromlineno, node=node)
-
- @utils.check_messages("dangerous-default-value")
- def visit_functiondef(self, node):
- """check function name, docstring, arguments, redefinition,
- variable names, max locals
- """
- self.stats["method" if node.is_method() else "function"] += 1
- self._check_dangerous_default(node)
-
- visit_asyncfunctiondef = visit_functiondef
-
- def _check_dangerous_default(self, node):
- # check for dangerous default values as arguments
- is_iterable = lambda n: isinstance(n, (astroid.List, astroid.Set, astroid.Dict))
- for default in node.args.defaults:
- try:
- value = next(default.infer())
- except astroid.InferenceError:
- continue
-
- if (
- isinstance(value, astroid.Instance)
- and value.qname() in DEFAULT_ARGUMENT_SYMBOLS
- ):
-
- if value is default:
- msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()]
- elif isinstance(value, astroid.Instance) or is_iterable(value):
- # We are here in the following situation(s):
- # * a dict/set/list/tuple call which wasn't inferred
- # to a syntax node ({}, () etc.). This can happen
- # when the arguments are invalid or unknown to
- # the inference.
- # * a variable from somewhere else, which turns out to be a list
- # or a dict.
- if is_iterable(default):
- msg = value.pytype()
- elif isinstance(default, astroid.Call):
- msg = "%s() (%s)" % (value.name, value.qname())
- else:
- msg = "%s (%s)" % (default.as_string(), value.qname())
- else:
- # this argument is a name
- msg = "%s (%s)" % (
- default.as_string(),
- DEFAULT_ARGUMENT_SYMBOLS[value.qname()],
- )
- self.add_message("dangerous-default-value", node=node, args=(msg,))
-
- @utils.check_messages("unreachable", "lost-exception")
- def visit_return(self, node):
- """1 - check is the node has a right sibling (if so, that's some
- unreachable code)
- 2 - check is the node is inside the finally clause of a try...finally
- block
- """
- self._check_unreachable(node)
- # Is it inside final body of a try...finally bloc ?
- self._check_not_in_finally(node, "return", (astroid.FunctionDef,))
-
- @utils.check_messages("unreachable")
- def visit_continue(self, node):
- """check is the node has a right sibling (if so, that's some unreachable
- code)
- """
- self._check_unreachable(node)
-
- @utils.check_messages("unreachable", "lost-exception")
- def visit_break(self, node):
- """1 - check is the node has a right sibling (if so, that's some
- unreachable code)
- 2 - check is the node is inside the finally clause of a try...finally
- block
- """
- # 1 - Is it right sibling ?
- self._check_unreachable(node)
- # 2 - Is it inside final body of a try...finally bloc ?
- self._check_not_in_finally(node, "break", (astroid.For, astroid.While))
-
- @utils.check_messages("unreachable")
- def visit_raise(self, node):
- """check if the node has a right sibling (if so, that's some unreachable
- code)
- """
- self._check_unreachable(node)
-
- @utils.check_messages("exec-used")
- def visit_exec(self, node):
- """just print a warning on exec statements"""
- self.add_message("exec-used", node=node)
-
- def _check_misplaced_format_function(self, call_node):
- if not isinstance(call_node.func, astroid.Attribute):
- return
- if call_node.func.attrname != "format":
- return
-
- expr = utils.safe_infer(call_node.func.expr)
- if expr is astroid.Uninferable:
- return
- if not expr:
- # we are doubtful on inferred type of node, so here just check if format
- # was called on print()
- call_expr = call_node.func.expr
- if not isinstance(call_expr, astroid.Call):
- return
- if (
- isinstance(call_expr.func, astroid.Name)
- and call_expr.func.name == "print"
- ):
- self.add_message("misplaced-format-function", node=call_node)
-
- @utils.check_messages(
- "eval-used", "exec-used", "bad-reversed-sequence", "misplaced-format-function"
- )
- def visit_call(self, node):
- """visit a Call node -> check if this is not a blacklisted builtin
- call and check for * or ** use
- """
- self._check_misplaced_format_function(node)
- if isinstance(node.func, astroid.Name):
- name = node.func.name
- # ignore the name if it's not a builtin (i.e. not defined in the
- # locals nor globals scope)
- if not (name in node.frame() or name in node.root()):
- if name == "exec":
- self.add_message("exec-used", node=node)
- elif name == "reversed":
- self._check_reversed(node)
- elif name == "eval":
- self.add_message("eval-used", node=node)
-
- @utils.check_messages("assert-on-tuple")
- def visit_assert(self, node):
- """check the use of an assert statement on a tuple."""
- if (
- node.fail is None
- and isinstance(node.test, astroid.Tuple)
- and len(node.test.elts) == 2
- ):
- self.add_message("assert-on-tuple", node=node)
-
- @utils.check_messages("duplicate-key")
- def visit_dict(self, node):
- """check duplicate key in dictionary"""
- keys = set()
- for k, _ in node.items:
- if isinstance(k, astroid.Const):
- key = k.value
- if key in keys:
- self.add_message("duplicate-key", node=node, args=key)
- keys.add(key)
-
- def visit_tryfinally(self, node):
- """update try...finally flag"""
- self._tryfinallys.append(node)
-
- def leave_tryfinally(self, node): # pylint: disable=unused-argument
- """update try...finally flag"""
- self._tryfinallys.pop()
-
- def _check_unreachable(self, node):
- """check unreachable code"""
- unreach_stmt = node.next_sibling()
- if unreach_stmt is not None:
- self.add_message("unreachable", node=unreach_stmt)
-
- def _check_not_in_finally(self, node, node_name, breaker_classes=()):
- """check that a node is not inside a finally clause of a
- try...finally statement.
- If we found before a try...finally bloc a parent which its type is
- in breaker_classes, we skip the whole check."""
- # if self._tryfinallys is empty, we're not an in try...finally block
- if not self._tryfinallys:
- return
- # the node could be a grand-grand...-children of the try...finally
- _parent = node.parent
- _node = node
- while _parent and not isinstance(_parent, breaker_classes):
- if hasattr(_parent, "finalbody") and _node in _parent.finalbody:
- self.add_message("lost-exception", node=node, args=node_name)
- return
- _node = _parent
- _parent = _node.parent
-
- def _check_reversed(self, node):
- """ check that the argument to `reversed` is a sequence """
- try:
- argument = utils.safe_infer(utils.get_argument_from_call(node, position=0))
- except utils.NoSuchArgumentError:
- pass
- else:
- if argument is astroid.Uninferable:
- return
- if argument is None:
- # Nothing was inferred.
- # Try to see if we have iter().
- if isinstance(node.args[0], astroid.Call):
- try:
- func = next(node.args[0].func.infer())
- except astroid.InferenceError:
- return
- if getattr(
- func, "name", None
- ) == "iter" and utils.is_builtin_object(func):
- self.add_message("bad-reversed-sequence", node=node)
- return
-
- if isinstance(argument, (astroid.List, astroid.Tuple)):
- return
-
- if isinstance(argument, astroid.Instance):
- if argument._proxied.name == "dict" and utils.is_builtin_object(
- argument._proxied
- ):
- self.add_message("bad-reversed-sequence", node=node)
- return
- if any(
- ancestor.name == "dict" and utils.is_builtin_object(ancestor)
- for ancestor in argument._proxied.ancestors()
- ):
- # Mappings aren't accepted by reversed(), unless
- # they provide explicitly a __reversed__ method.
- try:
- argument.locals[REVERSED_PROTOCOL_METHOD]
- except KeyError:
- self.add_message("bad-reversed-sequence", node=node)
- return
-
- if hasattr(argument, "getattr"):
- # everything else is not a proper sequence for reversed()
- for methods in REVERSED_METHODS:
- for meth in methods:
- try:
- argument.getattr(meth)
- except astroid.NotFoundError:
- break
- else:
- break
- else:
- self.add_message("bad-reversed-sequence", node=node)
- else:
- self.add_message("bad-reversed-sequence", node=node)
-
- @utils.check_messages("confusing-with-statement")
- def visit_with(self, node):
- # a "with" statement with multiple managers coresponds
- # to one AST "With" node with multiple items
- pairs = node.items
- if pairs:
- for prev_pair, pair in zip(pairs, pairs[1:]):
- if isinstance(prev_pair[1], astroid.AssignName) and (
- pair[1] is None and not isinstance(pair[0], astroid.Call)
- ):
- # Don't emit a message if the second is a function call
- # there's no way that can be mistaken for a name assignment.
- # If the line number doesn't match
- # we assume it's a nested "with".
- self.add_message("confusing-with-statement", node=node)
-
- def _check_self_assigning_variable(self, node):
- # Detect assigning to the same variable.
-
- scope = node.scope()
- scope_locals = scope.locals
-
- rhs_names = []
- targets = node.targets
- if isinstance(targets[0], astroid.Tuple):
- if len(targets) != 1:
- # A complex assignment, so bail out early.
- return
- targets = targets[0].elts
-
- if isinstance(node.value, astroid.Name):
- if len(targets) != 1:
- return
- rhs_names = [node.value]
- elif isinstance(node.value, astroid.Tuple):
- rhs_count = len(node.value.elts)
- if len(targets) != rhs_count or rhs_count == 1:
- return
- rhs_names = node.value.elts
-
- for target, lhs_name in zip(targets, rhs_names):
- if not isinstance(lhs_name, astroid.Name):
- continue
- if not isinstance(target, astroid.AssignName):
- continue
- if isinstance(scope, astroid.ClassDef) and target.name in scope_locals:
- # Check that the scope is different than a class level, which is usually
- # a pattern to expose module level attributes as class level ones.
- continue
- if target.name == lhs_name.name:
- self.add_message(
- "self-assigning-variable", args=(target.name,), node=target
- )
-
- def _check_redeclared_assign_name(self, targets):
- for target in targets:
- if not isinstance(target, astroid.Tuple):
- continue
-
- found_names = []
- for element in target.elts:
- if isinstance(element, astroid.Tuple):
- self._check_redeclared_assign_name([element])
- elif isinstance(element, astroid.AssignName) and element.name != "_":
- found_names.append(element.name)
-
- names = collections.Counter(found_names)
- for name, count in names.most_common():
- if count > 1:
- self.add_message(
- "redeclared-assigned-name", args=(name,), node=target
- )
-
- @utils.check_messages("self-assigning-variable", "redeclared-assigned-name")
- def visit_assign(self, node):
- self._check_self_assigning_variable(node)
- self._check_redeclared_assign_name(node.targets)
-
- @utils.check_messages("redeclared-assigned-name")
- def visit_for(self, node):
- self._check_redeclared_assign_name([node.target])
-
-
-KNOWN_NAME_TYPES = {
- "module",
- "const",
- "class",
- "function",
- "method",
- "attr",
- "argument",
- "variable",
- "class_attribute",
- "inlinevar",
-}
-
-
-HUMAN_READABLE_TYPES = {
- "module": "module",
- "const": "constant",
- "class": "class",
- "function": "function",
- "method": "method",
- "attr": "attribute",
- "argument": "argument",
- "variable": "variable",
- "class_attribute": "class attribute",
- "inlinevar": "inline iteration",
-}
-
-DEFAULT_NAMING_STYLES = {
- "module": "snake_case",
- "const": "UPPER_CASE",
- "class": "PascalCase",
- "function": "snake_case",
- "method": "snake_case",
- "attr": "snake_case",
- "argument": "snake_case",
- "variable": "snake_case",
- "class_attribute": "any",
- "inlinevar": "any",
-}
-
-
-def _create_naming_options():
- name_options = []
- for name_type in sorted(KNOWN_NAME_TYPES):
- human_readable_name = HUMAN_READABLE_TYPES[name_type]
- default_style = DEFAULT_NAMING_STYLES[name_type]
- name_type = name_type.replace("_", "-")
- name_options.append(
- (
- "%s-naming-style" % (name_type,),
- {
- "default": default_style,
- "type": "choice",
- "choices": list(NAMING_STYLES.keys()),
- "metavar": "<style>",
- "help": "Naming style matching correct %s names."
- % (human_readable_name,),
- },
- )
- )
- name_options.append(
- (
- "%s-rgx" % (name_type,),
- {
- "default": None,
- "type": "regexp",
- "metavar": "<regexp>",
- "help": "Regular expression matching correct %s names. Overrides %s-naming-style."
- % (human_readable_name, name_type),
- },
- )
- )
- return tuple(name_options)
-
-
-class NameChecker(_BasicChecker):
-
- msgs = {
- "C0102": (
- 'Black listed name "%s"',
- "blacklisted-name",
- "Used when the name is listed in the black list (unauthorized names).",
- ),
- "C0103": (
- '%s name "%s" doesn\'t conform to %s',
- "invalid-name",
- "Used when the name doesn't conform to naming rules "
- "associated to its type (constant, variable, class...).",
- ),
- "W0111": (
- "Name %s will become a keyword in Python %s",
- "assign-to-new-keyword",
- "Used when assignment will become invalid in future "
- "Python release due to introducing new keyword.",
- ),
- }
-
- options = (
- (
- "good-names",
- {
- "default": ("i", "j", "k", "ex", "Run", "_"),
- "type": "csv",
- "metavar": "<names>",
- "help": "Good variable names which should always be accepted,"
- " separated by a comma.",
- },
- ),
- (
- "bad-names",
- {
- "default": ("foo", "bar", "baz", "toto", "tutu", "tata"),
- "type": "csv",
- "metavar": "<names>",
- "help": "Bad variable names which should always be refused, "
- "separated by a comma.",
- },
- ),
- (
- "name-group",
- {
- "default": (),
- "type": "csv",
- "metavar": "<name1:name2>",
- "help": (
- "Colon-delimited sets of names that determine each"
- " other's naming style when the name regexes"
- " allow several styles."
- ),
- },
- ),
- (
- "include-naming-hint",
- {
- "default": False,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": "Include a hint for the correct naming format with invalid-name.",
- },
- ),
- (
- "property-classes",
- {
- "default": ("abc.abstractproperty",),
- "type": "csv",
- "metavar": "<decorator names>",
- "help": "List of decorators that produce properties, such as "
- "abc.abstractproperty. Add to this list to register "
- "other decorators that produce valid properties. "
- "These decorators are taken in consideration only for invalid-name.",
- },
- ),
- ) + _create_naming_options()
-
- KEYWORD_ONSET = {(3, 7): {"async", "await"}}
-
- def __init__(self, linter):
- _BasicChecker.__init__(self, linter)
- self._name_category = {}
- self._name_group = {}
- self._bad_names = {}
- self._name_regexps = {}
- self._name_hints = {}
-
- def open(self):
- self.stats = self.linter.add_stats(
- badname_module=0,
- badname_class=0,
- badname_function=0,
- badname_method=0,
- badname_attr=0,
- badname_const=0,
- badname_variable=0,
- badname_inlinevar=0,
- badname_argument=0,
- badname_class_attribute=0,
- )
- for group in self.config.name_group:
- for name_type in group.split(":"):
- self._name_group[name_type] = "group_%s" % (group,)
-
- regexps, hints = self._create_naming_rules()
- self._name_regexps = regexps
- self._name_hints = hints
-
- def _create_naming_rules(self):
- regexps = {}
- hints = {}
-
- for name_type in KNOWN_NAME_TYPES:
- naming_style_option_name = "%s_naming_style" % (name_type,)
- naming_style_name = getattr(self.config, naming_style_option_name)
-
- regexps[name_type] = NAMING_STYLES[naming_style_name].get_regex(name_type)
-
- custom_regex_setting_name = "%s_rgx" % (name_type,)
- custom_regex = getattr(self.config, custom_regex_setting_name, None)
- if custom_regex is not None:
- regexps[name_type] = custom_regex
-
- if custom_regex is not None:
- hints[name_type] = "%r pattern" % custom_regex.pattern
- else:
- hints[name_type] = "%s naming style" % naming_style_name
-
- return regexps, hints
-
- @utils.check_messages("blacklisted-name", "invalid-name")
- def visit_module(self, node):
- self._check_name("module", node.name.split(".")[-1], node)
- self._bad_names = {}
-
- def leave_module(self, node): # pylint: disable=unused-argument
- for all_groups in self._bad_names.values():
- if len(all_groups) < 2:
- continue
- groups = collections.defaultdict(list)
- min_warnings = sys.maxsize
- for group in all_groups.values():
- groups[len(group)].append(group)
- min_warnings = min(len(group), min_warnings)
- if len(groups[min_warnings]) > 1:
- by_line = sorted(
- groups[min_warnings],
- key=lambda group: min(warning[0].lineno for warning in group),
- )
- warnings = itertools.chain(*by_line[1:])
- else:
- warnings = groups[min_warnings][0]
- for args in warnings:
- self._raise_name_warning(*args)
-
- @utils.check_messages("blacklisted-name", "invalid-name", "assign-to-new-keyword")
- def visit_classdef(self, node):
- self._check_assign_to_new_keyword_violation(node.name, node)
- self._check_name("class", node.name, node)
- for attr, anodes in node.instance_attrs.items():
- if not any(node.instance_attr_ancestors(attr)):
- self._check_name("attr", attr, anodes[0])
-
- @utils.check_messages("blacklisted-name", "invalid-name", "assign-to-new-keyword")
- def visit_functiondef(self, node):
- # Do not emit any warnings if the method is just an implementation
- # of a base class method.
- self._check_assign_to_new_keyword_violation(node.name, node)
- confidence = interfaces.HIGH
- if node.is_method():
- if utils.overrides_a_method(node.parent.frame(), node.name):
- return
- confidence = (
- interfaces.INFERENCE
- if utils.has_known_bases(node.parent.frame())
- else interfaces.INFERENCE_FAILURE
- )
-
- self._check_name(
- _determine_function_name_type(node, config=self.config),
- node.name,
- node,
- confidence,
- )
- # Check argument names
- args = node.args.args
- if args is not None:
- self._recursive_check_names(args, node)
-
- visit_asyncfunctiondef = visit_functiondef
-
- @utils.check_messages("blacklisted-name", "invalid-name")
- def visit_global(self, node):
- for name in node.names:
- self._check_name("const", name, node)
-
- @utils.check_messages("blacklisted-name", "invalid-name", "assign-to-new-keyword")
- def visit_assignname(self, node):
- """check module level assigned names"""
- self._check_assign_to_new_keyword_violation(node.name, node)
- frame = node.frame()
- assign_type = node.assign_type()
- if isinstance(assign_type, astroid.Comprehension):
- self._check_name("inlinevar", node.name, node)
- elif isinstance(frame, astroid.Module):
- if isinstance(assign_type, astroid.Assign) and not in_loop(assign_type):
- if isinstance(utils.safe_infer(assign_type.value), astroid.ClassDef):
- self._check_name("class", node.name, node)
- else:
- if not _redefines_import(node):
- # Don't emit if the name redefines an import
- # in an ImportError except handler.
- self._check_name("const", node.name, node)
- elif isinstance(assign_type, astroid.ExceptHandler):
- self._check_name("variable", node.name, node)
- elif isinstance(frame, astroid.FunctionDef):
- # global introduced variable aren't in the function locals
- if node.name in frame and node.name not in frame.argnames():
- if not _redefines_import(node):
- self._check_name("variable", node.name, node)
- elif isinstance(frame, astroid.ClassDef):
- if not list(frame.local_attr_ancestors(node.name)):
- self._check_name("class_attribute", node.name, node)
-
- def _recursive_check_names(self, args, node):
- """check names in a possibly recursive list <arg>"""
- for arg in args:
- if isinstance(arg, astroid.AssignName):
- self._check_name("argument", arg.name, node)
- else:
- self._recursive_check_names(arg.elts, node)
-
- def _find_name_group(self, node_type):
- return self._name_group.get(node_type, node_type)
-
- def _raise_name_warning(self, node, node_type, name, confidence):
- type_label = HUMAN_READABLE_TYPES[node_type]
- hint = self._name_hints[node_type]
- if self.config.include_naming_hint:
- hint += " (%r pattern)" % self._name_regexps[node_type].pattern
- args = (type_label.capitalize(), name, hint)
-
- self.add_message("invalid-name", node=node, args=args, confidence=confidence)
- self.stats["badname_" + node_type] += 1
-
- def _check_name(self, node_type, name, node, confidence=interfaces.HIGH):
- """check for a name using the type's regexp"""
-
- def _should_exempt_from_invalid_name(node):
- if node_type == "variable":
- inferred = utils.safe_infer(node)
- if isinstance(inferred, astroid.ClassDef):
- return True
- return False
-
- if utils.is_inside_except(node):
- clobbering, _ = utils.clobber_in_except(node)
- if clobbering:
- return
- if name in self.config.good_names:
- return
- if name in self.config.bad_names:
- self.stats["badname_" + node_type] += 1
- self.add_message("blacklisted-name", node=node, args=name)
- return
- regexp = self._name_regexps[node_type]
- match = regexp.match(name)
-
- if _is_multi_naming_match(match, node_type, confidence):
- name_group = self._find_name_group(node_type)
- bad_name_group = self._bad_names.setdefault(name_group, {})
- warnings = bad_name_group.setdefault(match.lastgroup, [])
- warnings.append((node, node_type, name, confidence))
-
- if match is None and not _should_exempt_from_invalid_name(node):
- self._raise_name_warning(node, node_type, name, confidence)
-
- def _check_assign_to_new_keyword_violation(self, name, node):
- keyword_first_version = self._name_became_keyword_in_version(
- name, self.KEYWORD_ONSET
- )
- if keyword_first_version is not None:
- self.add_message(
- "assign-to-new-keyword",
- node=node,
- args=(name, keyword_first_version),
- confidence=interfaces.HIGH,
- )
-
- @staticmethod
- def _name_became_keyword_in_version(name, rules):
- for version, keywords in rules.items():
- if name in keywords and sys.version_info < version:
- return ".".join(map(str, version))
- return None
-
-
-class DocStringChecker(_BasicChecker):
- msgs = {
- "C0112": (
- "Empty %s docstring",
- "empty-docstring",
- "Used when a module, function, class or method has an empty "
- "docstring (it would be too easy ;).",
- {"old_names": [("W0132", "old-empty-docstring")]},
- ),
- "C0114": (
- "Missing module docstring",
- "missing-module-docstring",
- "Used when a module has no docstring."
- "Empty modules do not require a docstring.",
- {"old_names": [("C0111", "missing-docstring")]},
- ),
- "C0115": (
- "Missing class docstring",
- "missing-class-docstring",
- "Used when a class has no docstring."
- "Even an empty class must have a docstring.",
- {"old_names": [("C0111", "missing-docstring")]},
- ),
- "C0116": (
- "Missing function or method docstring",
- "missing-function-docstring",
- "Used when a function or method has no docstring."
- "Some special methods like __init__ do not require a "
- "docstring.",
- {"old_names": [("C0111", "missing-docstring")]},
- ),
- }
- options = (
- (
- "no-docstring-rgx",
- {
- "default": NO_REQUIRED_DOC_RGX,
- "type": "regexp",
- "metavar": "<regexp>",
- "help": "Regular expression which should only match "
- "function or class names that do not require a "
- "docstring.",
- },
- ),
- (
- "docstring-min-length",
- {
- "default": -1,
- "type": "int",
- "metavar": "<int>",
- "help": (
- "Minimum line length for functions/classes that"
- " require docstrings, shorter ones are exempt."
- ),
- },
- ),
- )
-
- def open(self):
- self.stats = self.linter.add_stats(
- undocumented_module=0,
- undocumented_function=0,
- undocumented_method=0,
- undocumented_class=0,
- )
-
- @utils.check_messages("missing-docstring", "empty-docstring")
- def visit_module(self, node):
- self._check_docstring("module", node)
-
- @utils.check_messages("missing-docstring", "empty-docstring")
- def visit_classdef(self, node):
- if self.config.no_docstring_rgx.match(node.name) is None:
- self._check_docstring("class", node)
-
- @utils.check_messages("missing-docstring", "empty-docstring")
- def visit_functiondef(self, node):
- if self.config.no_docstring_rgx.match(node.name) is None:
- ftype = "method" if node.is_method() else "function"
- if is_property_setter_or_deleter(node):
- return
-
- if isinstance(node.parent.frame(), astroid.ClassDef):
- overridden = False
- confidence = (
- interfaces.INFERENCE
- if utils.has_known_bases(node.parent.frame())
- else interfaces.INFERENCE_FAILURE
- )
- # check if node is from a method overridden by its ancestor
- for ancestor in node.parent.frame().ancestors():
- if node.name in ancestor and isinstance(
- ancestor[node.name], astroid.FunctionDef
- ):
- overridden = True
- break
- self._check_docstring(
- ftype, node, report_missing=not overridden, confidence=confidence
- )
- elif isinstance(node.parent.frame(), astroid.Module):
- self._check_docstring(ftype, node)
- else:
- return
-
- visit_asyncfunctiondef = visit_functiondef
-
- def _check_docstring(
- self, node_type, node, report_missing=True, confidence=interfaces.HIGH
- ):
- """check the node has a non empty docstring"""
- docstring = node.doc
- if docstring is None:
- if not report_missing:
- return
- lines = utils.get_node_last_lineno(node) - node.lineno
-
- if node_type == "module" and not lines:
- # If the module has no body, there's no reason
- # to require a docstring.
- return
- max_lines = self.config.docstring_min_length
-
- if node_type != "module" and max_lines > -1 and lines < max_lines:
- return
- self.stats["undocumented_" + node_type] += 1
- if (
- node.body
- and isinstance(node.body[0], astroid.Expr)
- and isinstance(node.body[0].value, astroid.Call)
- ):
- # Most likely a string with a format call. Let's see.
- func = utils.safe_infer(node.body[0].value.func)
- if isinstance(func, astroid.BoundMethod) and isinstance(
- func.bound, astroid.Instance
- ):
- # Strings.
- if func.bound.name == "str":
- return
- if func.bound.name in ("str", "unicode", "bytes"):
- return
- if node_type == "module":
- message = "missing-module-docstring"
- elif node_type == "class":
- message = "missing-class-docstring"
- else:
- message = "missing-function-docstring"
- self.add_message(message, node=node, confidence=confidence)
- elif not docstring.strip():
- self.stats["undocumented_" + node_type] += 1
- self.add_message(
- "empty-docstring", node=node, args=(node_type,), confidence=confidence
- )
-
-
-class PassChecker(_BasicChecker):
- """check if the pass statement is really necessary"""
-
- msgs = {
- "W0107": (
- "Unnecessary pass statement",
- "unnecessary-pass",
- 'Used when a "pass" statement that can be avoided is encountered.',
- )
- }
-
- @utils.check_messages("unnecessary-pass")
- def visit_pass(self, node):
- if len(node.parent.child_sequence(node)) > 1 or (
- isinstance(node.parent, (astroid.ClassDef, astroid.FunctionDef))
- and (node.parent.doc is not None)
- ):
- self.add_message("unnecessary-pass", node=node)
-
-
-def _is_one_arg_pos_call(call):
- """Is this a call with exactly 1 argument,
- where that argument is positional?
- """
- return isinstance(call, astroid.Call) and len(call.args) == 1 and not call.keywords
-
-
-class ComparisonChecker(_BasicChecker):
- """Checks for comparisons
-
- - singleton comparison: 'expr == True', 'expr == False' and 'expr == None'
- - yoda condition: 'const "comp" right' where comp can be '==', '!=', '<',
- '<=', '>' or '>=', and right can be a variable, an attribute, a method or
- a function
- """
-
- msgs = {
- "C0121": (
- "Comparison to %s should be %s",
- "singleton-comparison",
- "Used when an expression is compared to singleton "
- "values like True, False or None.",
- ),
- "C0122": (
- "Comparison should be %s",
- "misplaced-comparison-constant",
- "Used when the constant is placed on the left side "
- "of a comparison. It is usually clearer in intent to "
- "place it in the right hand side of the comparison.",
- ),
- "C0123": (
- "Using type() instead of isinstance() for a typecheck.",
- "unidiomatic-typecheck",
- "The idiomatic way to perform an explicit typecheck in "
- "Python is to use isinstance(x, Y) rather than "
- "type(x) == Y, type(x) is Y. Though there are unusual "
- "situations where these give different results.",
- {"old_names": [("W0154", "old-unidiomatic-typecheck")]},
- ),
- "R0123": (
- "Comparison to literal",
- "literal-comparison",
- "Used when comparing an object to a literal, which is usually "
- "what you do not want to do, since you can compare to a different "
- "literal than what was expected altogether.",
- ),
- "R0124": (
- "Redundant comparison - %s",
- "comparison-with-itself",
- "Used when something is compared against itself.",
- ),
- "W0143": (
- "Comparing against a callable, did you omit the parenthesis?",
- "comparison-with-callable",
- "This message is emitted when pylint detects that a comparison with a "
- "callable was made, which might suggest that some parenthesis were omitted, "
- "resulting in potential unwanted behaviour.",
- ),
- }
-
- def _check_singleton_comparison(self, singleton, root_node, negative_check=False):
- if singleton.value is True:
- if not negative_check:
- suggestion = "just 'expr'"
- else:
- suggestion = "just 'not expr'"
- self.add_message(
- "singleton-comparison", node=root_node, args=(True, suggestion)
- )
- elif singleton.value is False:
- if not negative_check:
- suggestion = "'not expr'"
- else:
- suggestion = "'expr'"
- self.add_message(
- "singleton-comparison", node=root_node, args=(False, suggestion)
- )
- elif singleton.value is None:
- if not negative_check:
- suggestion = "'expr is None'"
- else:
- suggestion = "'expr is not None'"
- self.add_message(
- "singleton-comparison", node=root_node, args=(None, suggestion)
- )
-
- def _check_literal_comparison(self, literal, node):
- """Check if we compare to a literal, which is usually what we do not want to do."""
- nodes = (astroid.List, astroid.Tuple, astroid.Dict, astroid.Set)
- is_other_literal = isinstance(literal, nodes)
- is_const = False
- if isinstance(literal, astroid.Const):
- if isinstance(literal.value, bool) or literal.value is None:
- # Not interested in this values.
- return
- is_const = isinstance(literal.value, (bytes, str, int, float))
-
- if is_const or is_other_literal:
- self.add_message("literal-comparison", node=node)
-
- def _check_misplaced_constant(self, node, left, right, operator):
- if isinstance(right, astroid.Const):
- return
- operator = REVERSED_COMPS.get(operator, operator)
- suggestion = "%s %s %r" % (right.as_string(), operator, left.value)
- self.add_message("misplaced-comparison-constant", node=node, args=(suggestion,))
-
- def _check_logical_tautology(self, node):
- """Check if identifier is compared against itself.
- :param node: Compare node
- :type node: astroid.node_classes.Compare
- :Example:
- val = 786
- if val == val: # [comparison-with-itself]
- pass
- """
- left_operand = node.left
- right_operand = node.ops[0][1]
- operator = node.ops[0][0]
- if isinstance(left_operand, astroid.Const) and isinstance(
- right_operand, astroid.Const
- ):
- left_operand = left_operand.value
- right_operand = right_operand.value
- elif isinstance(left_operand, astroid.Name) and isinstance(
- right_operand, astroid.Name
- ):
- left_operand = left_operand.name
- right_operand = right_operand.name
-
- if left_operand == right_operand:
- suggestion = "%s %s %s" % (left_operand, operator, right_operand)
- self.add_message("comparison-with-itself", node=node, args=(suggestion,))
-
- def _check_callable_comparison(self, node):
- operator = node.ops[0][0]
- if operator not in COMPARISON_OPERATORS:
- return
-
- bare_callables = (astroid.FunctionDef, astroid.BoundMethod)
- left_operand, right_operand = node.left, node.ops[0][1]
- # this message should be emitted only when there is comparison of bare callable
- # with non bare callable.
- if (
- sum(
- 1
- for operand in (left_operand, right_operand)
- if isinstance(utils.safe_infer(operand), bare_callables)
- )
- == 1
- ):
- self.add_message("comparison-with-callable", node=node)
-
- @utils.check_messages(
- "singleton-comparison",
- "misplaced-comparison-constant",
- "unidiomatic-typecheck",
- "literal-comparison",
- "comparison-with-itself",
- "comparison-with-callable",
- )
- def visit_compare(self, node):
- self._check_callable_comparison(node)
- self._check_logical_tautology(node)
- self._check_unidiomatic_typecheck(node)
- # NOTE: this checker only works with binary comparisons like 'x == 42'
- # but not 'x == y == 42'
- if len(node.ops) != 1:
- return
-
- left = node.left
- operator, right = node.ops[0]
- if operator in COMPARISON_OPERATORS and isinstance(left, astroid.Const):
- self._check_misplaced_constant(node, left, right, operator)
-
- if operator == "==":
- if isinstance(left, astroid.Const):
- self._check_singleton_comparison(left, node)
- elif isinstance(right, astroid.Const):
- self._check_singleton_comparison(right, node)
- if operator == "!=":
- if isinstance(right, astroid.Const):
- self._check_singleton_comparison(right, node, negative_check=True)
- if operator in ("is", "is not"):
- self._check_literal_comparison(right, node)
-
- def _check_unidiomatic_typecheck(self, node):
- operator, right = node.ops[0]
- if operator in TYPECHECK_COMPARISON_OPERATORS:
- left = node.left
- if _is_one_arg_pos_call(left):
- self._check_type_x_is_y(node, left, operator, right)
-
- def _check_type_x_is_y(self, node, left, operator, right):
- """Check for expressions like type(x) == Y."""
- left_func = utils.safe_infer(left.func)
- if not (
- isinstance(left_func, astroid.ClassDef) and left_func.qname() == TYPE_QNAME
- ):
- return
-
- if operator in ("is", "is not") and _is_one_arg_pos_call(right):
- right_func = utils.safe_infer(right.func)
- if (
- isinstance(right_func, astroid.ClassDef)
- and right_func.qname() == TYPE_QNAME
- ):
- # type(x) == type(a)
- right_arg = utils.safe_infer(right.args[0])
- if not isinstance(right_arg, LITERAL_NODE_TYPES):
- # not e.g. type(x) == type([])
- return
- self.add_message("unidiomatic-typecheck", node=node)
-
-
-def register(linter):
- """required method to auto register this checker"""
- linter.register_checker(BasicErrorChecker(linter))
- linter.register_checker(BasicChecker(linter))
- linter.register_checker(NameChecker(linter))
- linter.register_checker(DocStringChecker(linter))
- linter.register_checker(PassChecker(linter))
- linter.register_checker(ComparisonChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/base_checker.py b/venv/Lib/site-packages/pylint/checkers/base_checker.py
deleted file mode 100644
index f2ae4e5..0000000
--- a/venv/Lib/site-packages/pylint/checkers/base_checker.py
+++ /dev/null
@@ -1,187 +0,0 @@
-# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2013-2014 Google, Inc.
-# Copyright (c) 2013 buck@yelp.com <buck@yelp.com>
-# Copyright (c) 2014-2017 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
-# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@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
-
-from inspect import cleandoc
-from typing import Any
-
-from pylint.config import OptionsProviderMixIn
-from pylint.constants import _MSG_ORDER, WarningScope
-from pylint.exceptions import InvalidMessageError
-from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements
-from pylint.message.message_definition import MessageDefinition
-from pylint.utils import get_rst_section, get_rst_title
-
-
-class BaseChecker(OptionsProviderMixIn):
-
- # checker name (you may reuse an existing one)
- name = None # type: str
- # options level (0 will be displaying in --help, 1 in --long-help)
- level = 1
- # ordered list of options to control the checker behaviour
- options = () # type: Any
- # messages issued by this checker
- msgs = {} # type: Any
- # reports issued by this checker
- reports = () # type: Any
- # mark this checker as enabled or not.
- enabled = True
-
- def __init__(self, linter=None):
- """checker instances should have the linter as argument
-
- :param ILinter linter: is an object implementing ILinter."""
- if self.name is not None:
- self.name = self.name.lower()
- OptionsProviderMixIn.__init__(self)
- self.linter = linter
-
- def __gt__(self, other):
- """Permit to sort a list of Checker by name."""
- return "{}{}".format(self.name, self.msgs).__gt__(
- "{}{}".format(other.name, other.msgs)
- )
-
- def __repr__(self):
- status = "Checker" if self.enabled else "Disabled checker"
- return "{} '{}' (responsible for '{}')".format(
- status, self.name, "', '".join(self.msgs.keys())
- )
-
- def __str__(self):
- """This might be incomplete because multiple class inheriting BaseChecker
- can have the same name. Cf MessageHandlerMixIn.get_full_documentation()"""
- return self.get_full_documentation(
- msgs=self.msgs, options=self.options_and_values(), reports=self.reports
- )
-
- def get_full_documentation(self, msgs, options, reports, doc=None, module=None):
- result = ""
- checker_title = "%s checker" % (self.name.replace("_", " ").title())
- if module:
- # Provide anchor to link against
- result += ".. _%s:\n\n" % module
- result += "%s\n" % get_rst_title(checker_title, "~")
- if module:
- result += "This checker is provided by ``%s``.\n" % module
- result += "Verbatim name of the checker is ``%s``.\n\n" % self.name
- if doc:
- # Provide anchor to link against
- result += get_rst_title("{} Documentation".format(checker_title), "^")
- result += "%s\n\n" % cleandoc(doc)
- # options might be an empty generator and not be False when casted to boolean
- options = list(options)
- if options:
- result += get_rst_title("{} Options".format(checker_title), "^")
- result += "%s\n" % get_rst_section(None, options)
- if msgs:
- result += get_rst_title("{} Messages".format(checker_title), "^")
- for msgid, msg in sorted(
- msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1])
- ):
- msg = self.create_message_definition_from_tuple(msgid, msg)
- result += "%s\n" % msg.format_help(checkerref=False)
- result += "\n"
- if reports:
- result += get_rst_title("{} Reports".format(checker_title), "^")
- for report in reports:
- result += ":%s: %s\n" % report[:2]
- result += "\n"
- result += "\n"
- return result
-
- def add_message(
- self, msgid, line=None, node=None, args=None, confidence=None, col_offset=None
- ):
- if not confidence:
- confidence = UNDEFINED
- self.linter.add_message(msgid, line, node, args, confidence, col_offset)
-
- def check_consistency(self):
- """Check the consistency of msgid.
-
- msg ids for a checker should be a string of len 4, where the two first
- characters are the checker id and the two last the msg id in this
- checker.
-
- :raises InvalidMessageError: If the checker id in the messages are not
- always the same. """
- checker_id = None
- existing_ids = []
- for message in self.messages:
- if checker_id is not None and checker_id != message.msgid[1:3]:
- error_msg = "Inconsistent checker part in message id "
- error_msg += "'{}' (expected 'x{checker_id}xx' ".format(
- message.msgid, checker_id=checker_id
- )
- error_msg += "because we already had {existing_ids}).".format(
- existing_ids=existing_ids
- )
- raise InvalidMessageError(error_msg)
- checker_id = message.msgid[1:3]
- existing_ids.append(message.msgid)
-
- def create_message_definition_from_tuple(self, msgid, msg_tuple):
- if implements(self, (IRawChecker, ITokenChecker)):
- default_scope = WarningScope.LINE
- else:
- default_scope = WarningScope.NODE
- options = {}
- if len(msg_tuple) > 3:
- (msg, symbol, descr, options) = msg_tuple
- elif len(msg_tuple) > 2:
- (msg, symbol, descr) = msg_tuple
- else:
- error_msg = """Messages should have a msgid and a symbol. Something like this :
-
-"W1234": (
- "message",
- "message-symbol",
- "Message description with detail.",
- ...
-),
-"""
- raise InvalidMessageError(error_msg)
- options.setdefault("scope", default_scope)
- return MessageDefinition(self, msgid, msg, descr, symbol, **options)
-
- @property
- def messages(self) -> list:
- return [
- self.create_message_definition_from_tuple(msgid, msg_tuple)
- for msgid, msg_tuple in sorted(self.msgs.items())
- ]
-
- # dummy methods implementing the IChecker interface
-
- def get_message_definition(self, msgid):
- for message_definition in self.messages:
- if message_definition.msgid == msgid:
- return message_definition
- error_msg = "MessageDefinition for '{}' does not exists. ".format(msgid)
- error_msg += "Choose from {}.".format([m.msgid for m in self.messages])
- raise InvalidMessageError(error_msg)
-
- def open(self):
- """called before visiting project (i.e set of modules)"""
-
- def close(self):
- """called after visiting project (i.e set of modules)"""
-
-
-class BaseTokenChecker(BaseChecker):
- """Base class for checkers that want to have access to the token stream."""
-
- def process_tokens(self, tokens):
- """Should be overridden by subclasses."""
- raise NotImplementedError()
diff --git a/venv/Lib/site-packages/pylint/checkers/classes.py b/venv/Lib/site-packages/pylint/checkers/classes.py
deleted file mode 100644
index 9f5d099..0000000
--- a/venv/Lib/site-packages/pylint/checkers/classes.py
+++ /dev/null
@@ -1,1844 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006-2016 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2010 Maarten ter Huurne <maarten@treewalker.org>
-# Copyright (c) 2012-2014 Google, Inc.
-# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
-# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2014 David Pursehouse <david.pursehouse@gmail.com>
-# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
-# Copyright (c) 2016 Anthony Foglia <afoglia@users.noreply.github.com>
-# Copyright (c) 2016 Florian Bruhin <me@the-compiler.org>
-# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
-# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
-# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
-# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
-# Copyright (c) 2018 Ben Green <benhgreen@icloud.com>
-# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
-# Copyright (c) 2018 Nick Drozd <nicholasdrozd@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
-
-"""classes checker for Python code
-"""
-import collections
-from itertools import chain, zip_longest
-
-import astroid
-from astroid import decorators, objects
-from astroid.bases import BUILTINS, Generator
-from astroid.exceptions import DuplicateBasesError, InconsistentMroError
-from astroid.scoped_nodes import function_to_method
-
-from pylint.checkers import BaseChecker
-from pylint.checkers.utils import (
- PYMETHODS,
- SPECIAL_METHODS_PARAMS,
- check_messages,
- class_is_abstract,
- decorated_with,
- decorated_with_property,
- has_known_bases,
- is_attr_private,
- is_attr_protected,
- is_builtin_object,
- is_comprehension,
- is_iterable,
- is_property_setter,
- is_property_setter_or_deleter,
- is_protocol_class,
- node_frame_class,
- overrides_a_method,
- safe_infer,
- unimplemented_abstract_methods,
-)
-from pylint.interfaces import IAstroidChecker
-from pylint.utils import get_global_option
-
-NEXT_METHOD = "__next__"
-INVALID_BASE_CLASSES = {"bool", "range", "slice", "memoryview"}
-BUILTIN_DECORATORS = {"builtins.property", "builtins.classmethod"}
-
-# Dealing with useless override detection, with regard
-# to parameters vs arguments
-
-_CallSignature = collections.namedtuple(
- "_CallSignature", "args kws starred_args starred_kws"
-)
-_ParameterSignature = collections.namedtuple(
- "_ParameterSignature", "args kwonlyargs varargs kwargs"
-)
-
-
-def _signature_from_call(call):
- kws = {}
- args = []
- starred_kws = []
- starred_args = []
- for keyword in call.keywords or []:
- arg, value = keyword.arg, keyword.value
- if arg is None and isinstance(value, astroid.Name):
- # Starred node and we are interested only in names,
- # otherwise some transformation might occur for the parameter.
- starred_kws.append(value.name)
- elif isinstance(value, astroid.Name):
- kws[arg] = value.name
- else:
- kws[arg] = None
-
- for arg in call.args:
- if isinstance(arg, astroid.Starred) and isinstance(arg.value, astroid.Name):
- # Positional variadic and a name, otherwise some transformation
- # might have occurred.
- starred_args.append(arg.value.name)
- elif isinstance(arg, astroid.Name):
- args.append(arg.name)
- else:
- args.append(None)
-
- return _CallSignature(args, kws, starred_args, starred_kws)
-
-
-def _signature_from_arguments(arguments):
- kwarg = arguments.kwarg
- vararg = arguments.vararg
- args = [arg.name for arg in arguments.args if arg.name != "self"]
- kwonlyargs = [arg.name for arg in arguments.kwonlyargs]
- return _ParameterSignature(args, kwonlyargs, vararg, kwarg)
-
-
-def _definition_equivalent_to_call(definition, call):
- """Check if a definition signature is equivalent to a call."""
- if definition.kwargs:
- same_kw_variadics = definition.kwargs in call.starred_kws
- else:
- same_kw_variadics = not call.starred_kws
- if definition.varargs:
- same_args_variadics = definition.varargs in call.starred_args
- else:
- same_args_variadics = not call.starred_args
- same_kwonlyargs = all(kw in call.kws for kw in definition.kwonlyargs)
- same_args = definition.args == call.args
-
- no_additional_kwarg_arguments = True
- if call.kws:
- for keyword in call.kws:
- is_arg = keyword in call.args
- is_kwonly = keyword in definition.kwonlyargs
- if not is_arg and not is_kwonly:
- # Maybe this argument goes into **kwargs,
- # or it is an extraneous argument.
- # In any case, the signature is different than
- # the call site, which stops our search.
- no_additional_kwarg_arguments = False
- break
-
- return all(
- (
- same_args,
- same_kwonlyargs,
- same_args_variadics,
- same_kw_variadics,
- no_additional_kwarg_arguments,
- )
- )
-
-
-# Deal with parameters overridding in two methods.
-
-
-def _positional_parameters(method):
- positional = method.args.args
- if method.type in ("classmethod", "method"):
- positional = positional[1:]
- return positional
-
-
-def _get_node_type(node, potential_types):
- """
- Return the type of the node if it exists in potential_types.
-
- Args:
- node (astroid.node): node to get the type of.
- potential_types (tuple): potential types of the node.
-
- Returns:
- type: type of the node or None.
- """
- for potential_type in potential_types:
- if isinstance(node, potential_type):
- return potential_type
- return None
-
-
-def _check_arg_equality(node_a, node_b, attr_name):
- """
- Check equality of nodes based on the comparison of their attributes named attr_name.
-
- Args:
- node_a (astroid.node): first node to compare.
- node_b (astroid.node): second node to compare.
- attr_name (str): name of the nodes attribute to use for comparison.
-
- Returns:
- bool: True if node_a.attr_name == node_b.attr_name, False otherwise.
- """
- return getattr(node_a, attr_name) == getattr(node_b, attr_name)
-
-
-def _has_different_parameters_default_value(original, overridden):
- """
- Check if original and overridden methods arguments have different default values
-
- Return True if one of the overridden arguments has a default
- value different from the default value of the original argument
- If one of the method doesn't have argument (.args is None)
- return False
- """
- if original.args is None or overridden.args is None:
- return False
-
- all_args = chain(original.args, original.kwonlyargs)
- original_param_names = [param.name for param in all_args]
- default_missing = object()
- for param_name in original_param_names:
- try:
- original_default = original.default_value(param_name)
- except astroid.exceptions.NoDefault:
- original_default = default_missing
- try:
- overridden_default = overridden.default_value(param_name)
- except astroid.exceptions.NoDefault:
- overridden_default = default_missing
-
- default_list = [
- arg == default_missing for arg in (original_default, overridden_default)
- ]
- if any(default_list) and not all(default_list):
- # Only one arg has no default value
- return True
-
- astroid_type_compared_attr = {
- astroid.Const: "value",
- astroid.ClassDef: "name",
- astroid.Tuple: "elts",
- astroid.List: "elts",
- }
- handled_types = tuple(
- astroid_type for astroid_type in astroid_type_compared_attr
- )
- original_type = _get_node_type(original_default, handled_types)
- if original_type:
- #  We handle only astroid types that are inside the dict astroid_type_compared_attr
- if not isinstance(overridden_default, original_type):
- #  Two args with same name but different types
- return True
- if not _check_arg_equality(
- original_default,
- overridden_default,
- astroid_type_compared_attr[original_type],
- ):
- # Two args with same type but different values
- return True
- return False
-
-
-def _has_different_parameters(original, overridden, dummy_parameter_regex):
- zipped = zip_longest(original, overridden)
- for original_param, overridden_param in zipped:
- params = (original_param, overridden_param)
- if not all(params):
- return True
-
- names = [param.name for param in params]
- if any(map(dummy_parameter_regex.match, names)):
- continue
- if original_param.name != overridden_param.name:
- return True
- return False
-
-
-def _different_parameters(original, overridden, dummy_parameter_regex):
- """Determine if the two methods have different parameters
-
- They are considered to have different parameters if:
-
- * they have different positional parameters, including different names
-
- * one of the methods is having variadics, while the other is not
-
- * they have different keyword only parameters.
-
- """
- original_parameters = _positional_parameters(original)
- overridden_parameters = _positional_parameters(overridden)
-
- different_positional = _has_different_parameters(
- original_parameters, overridden_parameters, dummy_parameter_regex
- )
- different_kwonly = _has_different_parameters(
- original.args.kwonlyargs, overridden.args.kwonlyargs, dummy_parameter_regex
- )
- if original.name in PYMETHODS:
- # Ignore the difference for special methods. If the parameter
- # numbers are different, then that is going to be caught by
- # unexpected-special-method-signature.
- # If the names are different, it doesn't matter, since they can't
- # be used as keyword arguments anyway.
- different_positional = different_kwonly = False
-
- # Both or none should have extra variadics, otherwise the method
- # loses or gains capabilities that are not reflected into the parent method,
- # leading to potential inconsistencies in the code.
- different_kwarg = (
- sum(1 for param in (original.args.kwarg, overridden.args.kwarg) if not param)
- == 1
- )
- different_vararg = (
- sum(1 for param in (original.args.vararg, overridden.args.vararg) if not param)
- == 1
- )
-
- return any(
- (different_positional, different_kwarg, different_vararg, different_kwonly)
- )
-
-
-def _is_invalid_base_class(cls):
- return cls.name in INVALID_BASE_CLASSES and is_builtin_object(cls)
-
-
-def _has_data_descriptor(cls, attr):
- attributes = cls.getattr(attr)
- for attribute in attributes:
- try:
- for inferred in attribute.infer():
- if isinstance(inferred, astroid.Instance):
- try:
- inferred.getattr("__get__")
- inferred.getattr("__set__")
- except astroid.NotFoundError:
- continue
- else:
- return True
- except astroid.InferenceError:
- # Can't infer, avoid emitting a false positive in this case.
- return True
- return False
-
-
-def _called_in_methods(func, klass, methods):
- """ Check if the func was called in any of the given methods,
- belonging to the *klass*. Returns True if so, False otherwise.
- """
- if not isinstance(func, astroid.FunctionDef):
- return False
- for method in methods:
- try:
- inferred = klass.getattr(method)
- except astroid.NotFoundError:
- continue
- for infer_method in inferred:
- for call in infer_method.nodes_of_class(astroid.Call):
- try:
- bound = next(call.func.infer())
- except (astroid.InferenceError, StopIteration):
- continue
- if not isinstance(bound, astroid.BoundMethod):
- continue
- func_obj = bound._proxied
- if isinstance(func_obj, astroid.UnboundMethod):
- func_obj = func_obj._proxied
- if func_obj.name == func.name:
- return True
- return False
-
-
-def _is_attribute_property(name, klass):
- """ Check if the given attribute *name* is a property
- in the given *klass*.
-
- It will look for `property` calls or for functions
- with the given name, decorated by `property` or `property`
- subclasses.
- Returns ``True`` if the name is a property in the given klass,
- ``False`` otherwise.
- """
-
- try:
- attributes = klass.getattr(name)
- except astroid.NotFoundError:
- return False
- property_name = "{}.property".format(BUILTINS)
- for attr in attributes:
- if attr is astroid.Uninferable:
- continue
- try:
- inferred = next(attr.infer())
- except astroid.InferenceError:
- continue
- if isinstance(inferred, astroid.FunctionDef) and decorated_with_property(
- inferred
- ):
- return True
- if inferred.pytype() == property_name:
- return True
- return False
-
-
-def _has_bare_super_call(fundef_node):
- for call in fundef_node.nodes_of_class(astroid.Call):
- func = call.func
- if isinstance(func, astroid.Name) and func.name == "super" and not call.args:
- return True
- return False
-
-
-def _safe_infer_call_result(node, caller, context=None):
- """
- Safely infer the return value of a function.
-
- Returns None if inference failed or if there is some ambiguity (more than
- one node has been inferred). Otherwise returns inferred value.
- """
- try:
- inferit = node.infer_call_result(caller, context=context)
- value = next(inferit)
- except astroid.InferenceError:
- return None # inference failed
- except StopIteration:
- return None # no values inferred
- try:
- next(inferit)
- return None # there is ambiguity on the inferred node
- except astroid.InferenceError:
- return None # there is some kind of ambiguity
- except StopIteration:
- return value
-
-
-def _has_same_layout_slots(slots, assigned_value):
- inferred = next(assigned_value.infer())
- if isinstance(inferred, astroid.ClassDef):
- other_slots = inferred.slots()
- if all(
- first_slot and second_slot and first_slot.value == second_slot.value
- for (first_slot, second_slot) in zip_longest(slots, other_slots)
- ):
- return True
- return False
-
-
-MSGS = {
- "F0202": (
- "Unable to check methods signature (%s / %s)",
- "method-check-failed",
- "Used when Pylint has been unable to check methods signature "
- "compatibility for an unexpected reason. Please report this kind "
- "if you don't make sense of it.",
- ),
- "E0202": (
- "An attribute defined in %s line %s hides this method",
- "method-hidden",
- "Used when a class defines a method which is hidden by an "
- "instance attribute from an ancestor class or set by some "
- "client code.",
- ),
- "E0203": (
- "Access to member %r before its definition line %s",
- "access-member-before-definition",
- "Used when an instance member is accessed before it's actually assigned.",
- ),
- "W0201": (
- "Attribute %r defined outside __init__",
- "attribute-defined-outside-init",
- "Used when an instance attribute is defined outside the __init__ method.",
- ),
- "W0212": (
- "Access to a protected member %s of a client class", # E0214
- "protected-access",
- "Used when a protected member (i.e. class member with a name "
- "beginning with an underscore) is access outside the class or a "
- "descendant of the class where it's defined.",
- ),
- "E0211": (
- "Method has no argument",
- "no-method-argument",
- "Used when a method which should have the bound instance as "
- "first argument has no argument defined.",
- ),
- "E0213": (
- 'Method should have "self" as first argument',
- "no-self-argument",
- 'Used when a method has an attribute different the "self" as '
- "first argument. This is considered as an error since this is "
- "a so common convention that you shouldn't break it!",
- ),
- "C0202": (
- "Class method %s should have %s as first argument",
- "bad-classmethod-argument",
- "Used when a class method has a first argument named differently "
- "than the value specified in valid-classmethod-first-arg option "
- '(default to "cls"), recommended to easily differentiate them '
- "from regular instance methods.",
- ),
- "C0203": (
- "Metaclass method %s should have %s as first argument",
- "bad-mcs-method-argument",
- "Used when a metaclass method has a first argument named "
- "differently than the value specified in valid-classmethod-first"
- '-arg option (default to "cls"), recommended to easily '
- "differentiate them from regular instance methods.",
- ),
- "C0204": (
- "Metaclass class method %s should have %s as first argument",
- "bad-mcs-classmethod-argument",
- "Used when a metaclass class method has a first argument named "
- "differently than the value specified in valid-metaclass-"
- 'classmethod-first-arg option (default to "mcs"), recommended to '
- "easily differentiate them from regular instance methods.",
- ),
- "W0211": (
- "Static method with %r as first argument",
- "bad-staticmethod-argument",
- 'Used when a static method has "self" or a value specified in '
- "valid-classmethod-first-arg option or "
- "valid-metaclass-classmethod-first-arg option as first argument.",
- ),
- "R0201": (
- "Method could be a function",
- "no-self-use",
- "Used when a method doesn't use its bound instance, and so could "
- "be written as a function.",
- ),
- "W0221": (
- "Parameters differ from %s %r method",
- "arguments-differ",
- "Used when a method has a different number of arguments than in "
- "the implemented interface or in an overridden method.",
- ),
- "W0222": (
- "Signature differs from %s %r method",
- "signature-differs",
- "Used when a method signature is different than in the "
- "implemented interface or in an overridden method.",
- ),
- "W0223": (
- "Method %r is abstract in class %r but is not overridden",
- "abstract-method",
- "Used when an abstract method (i.e. raise NotImplementedError) is "
- "not overridden in concrete class.",
- ),
- "W0231": (
- "__init__ method from base class %r is not called",
- "super-init-not-called",
- "Used when an ancestor class method has an __init__ method "
- "which is not called by a derived class.",
- ),
- "W0232": (
- "Class has no __init__ method",
- "no-init",
- "Used when a class has no __init__ method, neither its parent classes.",
- ),
- "W0233": (
- "__init__ method from a non direct base class %r is called",
- "non-parent-init-called",
- "Used when an __init__ method is called on a class which is not "
- "in the direct ancestors for the analysed class.",
- ),
- "W0235": (
- "Useless super delegation in method %r",
- "useless-super-delegation",
- "Used whenever we can detect that an overridden method is useless, "
- "relying on super() delegation to do the same thing as another method "
- "from the MRO.",
- ),
- "W0236": (
- "Method %r was expected to be %r, found it instead as %r",
- "invalid-overridden-method",
- "Used when we detect that a method was overridden as a property "
- "or the other way around, which could result in potential bugs at "
- "runtime.",
- ),
- "E0236": (
- "Invalid object %r in __slots__, must contain only non empty strings",
- "invalid-slots-object",
- "Used when an invalid (non-string) object occurs in __slots__.",
- ),
- "E0237": (
- "Assigning to attribute %r not defined in class slots",
- "assigning-non-slot",
- "Used when assigning to an attribute not defined in the class slots.",
- ),
- "E0238": (
- "Invalid __slots__ object",
- "invalid-slots",
- "Used when an invalid __slots__ is found in class. "
- "Only a string, an iterable or a sequence is permitted.",
- ),
- "E0239": (
- "Inheriting %r, which is not a class.",
- "inherit-non-class",
- "Used when a class inherits from something which is not a class.",
- ),
- "E0240": (
- "Inconsistent method resolution order for class %r",
- "inconsistent-mro",
- "Used when a class has an inconsistent method resolution order.",
- ),
- "E0241": (
- "Duplicate bases for class %r",
- "duplicate-bases",
- "Used when a class has duplicate bases.",
- ),
- "E0242": (
- "Value %r in slots conflicts with class variable",
- "class-variable-slots-conflict",
- "Used when a value in __slots__ conflicts with a class variable, property or method.",
- ),
- "R0202": (
- "Consider using a decorator instead of calling classmethod",
- "no-classmethod-decorator",
- "Used when a class method is defined without using the decorator syntax.",
- ),
- "R0203": (
- "Consider using a decorator instead of calling staticmethod",
- "no-staticmethod-decorator",
- "Used when a static method is defined without using the decorator syntax.",
- ),
- "C0205": (
- "Class __slots__ should be a non-string iterable",
- "single-string-used-for-slots",
- "Used when a class __slots__ is a simple string, rather than an iterable.",
- ),
- "R0205": (
- "Class %r inherits from object, can be safely removed from bases in python3",
- "useless-object-inheritance",
- "Used when a class inherit from object, which under python3 is implicit, "
- "hence can be safely removed from bases.",
- ),
- "R0206": (
- "Cannot have defined parameters for properties",
- "property-with-parameters",
- "Used when we detect that a property also has parameters, which are useless, "
- "given that properties cannot be called with additional arguments.",
- ),
-}
-
-
-class ScopeAccessMap:
- """Store the accessed variables per scope."""
-
- def __init__(self):
- self._scopes = collections.defaultdict(lambda: collections.defaultdict(list))
-
- def set_accessed(self, node):
- """Set the given node as accessed."""
-
- frame = node_frame_class(node)
- if frame is None:
- # The node does not live in a class.
- return
- self._scopes[frame][node.attrname].append(node)
-
- def accessed(self, scope):
- """Get the accessed variables for the given scope."""
- return self._scopes.get(scope, {})
-
-
-class ClassChecker(BaseChecker):
- """checks for :
- * methods without self as first argument
- * overridden methods signature
- * access only to existent members via self
- * attributes not defined in the __init__ method
- * unreachable code
- """
-
- __implements__ = (IAstroidChecker,)
-
- # configuration section name
- name = "classes"
- # messages
- msgs = MSGS
- priority = -2
- # configuration options
- options = (
- (
- "defining-attr-methods",
- {
- "default": ("__init__", "__new__", "setUp", "__post_init__"),
- "type": "csv",
- "metavar": "<method names>",
- "help": "List of method names used to declare (i.e. assign) \
-instance attributes.",
- },
- ),
- (
- "valid-classmethod-first-arg",
- {
- "default": ("cls",),
- "type": "csv",
- "metavar": "<argument names>",
- "help": "List of valid names for the first argument in \
-a class method.",
- },
- ),
- (
- "valid-metaclass-classmethod-first-arg",
- {
- "default": ("cls",),
- "type": "csv",
- "metavar": "<argument names>",
- "help": "List of valid names for the first argument in \
-a metaclass class method.",
- },
- ),
- (
- "exclude-protected",
- {
- "default": (
- # namedtuple public API.
- "_asdict",
- "_fields",
- "_replace",
- "_source",
- "_make",
- ),
- "type": "csv",
- "metavar": "<protected access exclusions>",
- "help": (
- "List of member names, which should be excluded "
- "from the protected access warning."
- ),
- },
- ),
- )
-
- def __init__(self, linter=None):
- BaseChecker.__init__(self, linter)
- self._accessed = ScopeAccessMap()
- self._first_attrs = []
- self._meth_could_be_func = None
-
- @decorators.cachedproperty
- def _dummy_rgx(self):
- return get_global_option(self, "dummy-variables-rgx", default=None)
-
- @decorators.cachedproperty
- def _ignore_mixin(self):
- return get_global_option(self, "ignore-mixin-members", default=True)
-
- @check_messages(
- "abstract-method",
- "no-init",
- "invalid-slots",
- "single-string-used-for-slots",
- "invalid-slots-object",
- "class-variable-slots-conflict",
- "inherit-non-class",
- "useless-object-inheritance",
- "inconsistent-mro",
- "duplicate-bases",
- )
- def visit_classdef(self, node):
- """init visit variable _accessed
- """
- self._check_bases_classes(node)
- # if not an exception or a metaclass
- if node.type == "class" and has_known_bases(node):
- try:
- node.local_attr("__init__")
- except astroid.NotFoundError:
- self.add_message("no-init", args=node, node=node)
- self._check_slots(node)
- self._check_proper_bases(node)
- self._check_consistent_mro(node)
-
- def _check_consistent_mro(self, node):
- """Detect that a class has a consistent mro or duplicate bases."""
- try:
- node.mro()
- except InconsistentMroError:
- self.add_message("inconsistent-mro", args=node.name, node=node)
- except DuplicateBasesError:
- self.add_message("duplicate-bases", args=node.name, node=node)
- except NotImplementedError:
- # Old style class, there's no mro so don't do anything.
- pass
-
- def _check_proper_bases(self, node):
- """
- Detect that a class inherits something which is not
- a class or a type.
- """
- for base in node.bases:
- ancestor = safe_infer(base)
- if ancestor in (astroid.Uninferable, None):
- continue
- if isinstance(ancestor, astroid.Instance) and ancestor.is_subtype_of(
- "%s.type" % (BUILTINS,)
- ):
- continue
-
- if not isinstance(ancestor, astroid.ClassDef) or _is_invalid_base_class(
- ancestor
- ):
- self.add_message("inherit-non-class", args=base.as_string(), node=node)
-
- if ancestor.name == object.__name__:
- self.add_message(
- "useless-object-inheritance", args=node.name, node=node
- )
-
- def leave_classdef(self, cnode):
- """close a class node:
- check that instance attributes are defined in __init__ and check
- access to existent members
- """
- # check access to existent members on non metaclass classes
- if self._ignore_mixin and cnode.name[-5:].lower() == "mixin":
- # We are in a mixin class. No need to try to figure out if
- # something is missing, since it is most likely that it will
- # miss.
- return
-
- accessed = self._accessed.accessed(cnode)
- if cnode.type != "metaclass":
- self._check_accessed_members(cnode, accessed)
- # checks attributes are defined in an allowed method such as __init__
- if not self.linter.is_message_enabled("attribute-defined-outside-init"):
- return
- defining_methods = self.config.defining_attr_methods
- current_module = cnode.root()
- for attr, nodes in cnode.instance_attrs.items():
- # Exclude `__dict__` as it is already defined.
- if attr == "__dict__":
- continue
-
- # Skip nodes which are not in the current module and it may screw up
- # the output, while it's not worth it
- nodes = [
- n
- for n in nodes
- if not isinstance(n.statement(), (astroid.Delete, astroid.AugAssign))
- and n.root() is current_module
- ]
- if not nodes:
- continue # error detected by typechecking
-
- # Check if any method attr is defined in is a defining method
- # or if we have the attribute defined in a setter.
- frames = (node.frame() for node in nodes)
- if any(
- frame.name in defining_methods or is_property_setter(frame)
- for frame in frames
- ):
- continue
-
- # check attribute is defined in a parent's __init__
- for parent in cnode.instance_attr_ancestors(attr):
- attr_defined = False
- # check if any parent method attr is defined in is a defining method
- for node in parent.instance_attrs[attr]:
- if node.frame().name in defining_methods:
- attr_defined = True
- if attr_defined:
- # we're done :)
- break
- else:
- # check attribute is defined as a class attribute
- try:
- cnode.local_attr(attr)
- except astroid.NotFoundError:
- for node in nodes:
- if node.frame().name not in defining_methods:
- # If the attribute was set by a call in any
- # of the defining methods, then don't emit
- # the warning.
- if _called_in_methods(
- node.frame(), cnode, defining_methods
- ):
- continue
- self.add_message(
- "attribute-defined-outside-init", args=attr, node=node
- )
-
- def visit_functiondef(self, node):
- """check method arguments, overriding"""
- # ignore actual functions
- if not node.is_method():
- return
-
- self._check_useless_super_delegation(node)
- self._check_property_with_parameters(node)
-
- klass = node.parent.frame()
- self._meth_could_be_func = True
- # check first argument is self if this is actually a method
- self._check_first_arg_for_type(node, klass.type == "metaclass")
- if node.name == "__init__":
- self._check_init(node)
- return
- # check signature if the method overloads inherited method
- for overridden in klass.local_attr_ancestors(node.name):
- # get astroid for the searched method
- try:
- parent_function = overridden[node.name]
- except KeyError:
- # we have found the method but it's not in the local
- # dictionary.
- # This may happen with astroid build from living objects
- continue
- if not isinstance(parent_function, astroid.FunctionDef):
- continue
- self._check_signature(node, parent_function, "overridden", klass)
- self._check_invalid_overridden_method(node, parent_function)
- break
-
- if node.decorators:
- for decorator in node.decorators.nodes:
- if isinstance(decorator, astroid.Attribute) and decorator.attrname in (
- "getter",
- "setter",
- "deleter",
- ):
- # attribute affectation will call this method, not hiding it
- return
- if isinstance(decorator, astroid.Name):
- if decorator.name == "property":
- # attribute affectation will either call a setter or raise
- # an attribute error, anyway not hiding the function
- return
-
- # Infer the decorator and see if it returns something useful
- inferred = safe_infer(decorator)
- if not inferred:
- return
- if isinstance(inferred, astroid.FunctionDef):
- # Okay, it's a decorator, let's see what it can infer.
- try:
- inferred = next(inferred.infer_call_result(inferred))
- except astroid.InferenceError:
- return
- try:
- if (
- isinstance(inferred, (astroid.Instance, astroid.ClassDef))
- and inferred.getattr("__get__")
- and inferred.getattr("__set__")
- ):
- return
- except astroid.AttributeInferenceError:
- pass
-
- # check if the method is hidden by an attribute
- try:
- overridden = klass.instance_attr(node.name)[0]
- overridden_frame = overridden.frame()
- if (
- isinstance(overridden_frame, astroid.FunctionDef)
- and overridden_frame.type == "method"
- ):
- overridden_frame = overridden_frame.parent.frame()
- if isinstance(overridden_frame, astroid.ClassDef) and klass.is_subtype_of(
- overridden_frame.qname()
- ):
- args = (overridden.root().name, overridden.fromlineno)
- self.add_message("method-hidden", args=args, node=node)
- except astroid.NotFoundError:
- pass
-
- visit_asyncfunctiondef = visit_functiondef
-
- def _check_useless_super_delegation(self, function):
- """Check if the given function node is an useless method override
-
- We consider it *useless* if it uses the super() builtin, but having
- nothing additional whatsoever than not implementing the method at all.
- If the method uses super() to delegate an operation to the rest of the MRO,
- and if the method called is the same as the current one, the arguments
- passed to super() are the same as the parameters that were passed to
- this method, then the method could be removed altogether, by letting
- other implementation to take precedence.
- """
-
- if (
- not function.is_method()
- # With decorators is a change of use
- or function.decorators
- ):
- return
-
- body = function.body
- if len(body) != 1:
- # Multiple statements, which means this overridden method
- # could do multiple things we are not aware of.
- return
-
- statement = body[0]
- if not isinstance(statement, (astroid.Expr, astroid.Return)):
- # Doing something else than what we are interested into.
- return
-
- call = statement.value
- if (
- not isinstance(call, astroid.Call)
- # Not a super() attribute access.
- or not isinstance(call.func, astroid.Attribute)
- ):
- return
-
- # Should be a super call.
- try:
- super_call = next(call.func.expr.infer())
- except astroid.InferenceError:
- return
- else:
- if not isinstance(super_call, objects.Super):
- return
-
- # The name should be the same.
- if call.func.attrname != function.name:
- return
-
- # Should be a super call with the MRO pointer being the
- # current class and the type being the current instance.
- current_scope = function.parent.scope()
- if (
- super_call.mro_pointer != current_scope
- or not isinstance(super_call.type, astroid.Instance)
- or super_call.type.name != current_scope.name
- ):
- return
-
- #  Check values of default args
- klass = function.parent.frame()
- meth_node = None
- for overridden in klass.local_attr_ancestors(function.name):
- # get astroid for the searched method
- try:
- meth_node = overridden[function.name]
- except KeyError:
- # we have found the method but it's not in the local
- # dictionary.
- # This may happen with astroid build from living objects
- continue
- if (
- not isinstance(meth_node, astroid.FunctionDef)
- # If the method have an ancestor which is not a
- # function then it is legitimate to redefine it
- or _has_different_parameters_default_value(
- meth_node.args, function.args
- )
- ):
- return
- break
-
- # Detect if the parameters are the same as the call's arguments.
- params = _signature_from_arguments(function.args)
- args = _signature_from_call(call)
-
- if meth_node is not None:
-
- def form_annotations(annotations):
- return [
- annotation.as_string() for annotation in filter(None, annotations)
- ]
-
- called_annotations = form_annotations(function.args.annotations)
- overridden_annotations = form_annotations(meth_node.args.annotations)
- if called_annotations and overridden_annotations:
- if called_annotations != overridden_annotations:
- return
-
- if _definition_equivalent_to_call(params, args):
- self.add_message(
- "useless-super-delegation", node=function, args=(function.name,)
- )
-
- def _check_property_with_parameters(self, node):
- if node.args.args and len(node.args.args) > 1 and decorated_with_property(node):
- self.add_message("property-with-parameters", node=node)
-
- def _check_invalid_overridden_method(self, function_node, parent_function_node):
- parent_is_property = decorated_with_property(
- parent_function_node
- ) or is_property_setter_or_deleter(parent_function_node)
- current_is_property = decorated_with_property(
- function_node
- ) or is_property_setter_or_deleter(function_node)
- if parent_is_property and not current_is_property:
- self.add_message(
- "invalid-overridden-method",
- args=(function_node.name, "property", function_node.type),
- node=function_node,
- )
- elif not parent_is_property and current_is_property:
- self.add_message(
- "invalid-overridden-method",
- args=(function_node.name, "method", "property"),
- node=function_node,
- )
-
- def _check_slots(self, node):
- if "__slots__" not in node.locals:
- return
- for slots in node.igetattr("__slots__"):
- # check if __slots__ is a valid type
- if slots is astroid.Uninferable:
- continue
- if not is_iterable(slots) and not is_comprehension(slots):
- self.add_message("invalid-slots", node=node)
- continue
-
- if isinstance(slots, astroid.Const):
- # a string, ignore the following checks
- self.add_message("single-string-used-for-slots", node=node)
- continue
- if not hasattr(slots, "itered"):
- # we can't obtain the values, maybe a .deque?
- continue
-
- if isinstance(slots, astroid.Dict):
- values = [item[0] for item in slots.items]
- else:
- values = slots.itered()
- if values is astroid.Uninferable:
- return
- for elt in values:
- try:
- self._check_slots_elt(elt, node)
- except astroid.InferenceError:
- continue
-
- def _check_slots_elt(self, elt, node):
- for inferred in elt.infer():
- if inferred is astroid.Uninferable:
- continue
- if not isinstance(inferred, astroid.Const) or not isinstance(
- inferred.value, str
- ):
- self.add_message(
- "invalid-slots-object", args=inferred.as_string(), node=elt
- )
- continue
- if not inferred.value:
- self.add_message(
- "invalid-slots-object", args=inferred.as_string(), node=elt
- )
-
- # Check if we have a conflict with a class variable.
- class_variable = node.locals.get(inferred.value)
- if class_variable:
- # Skip annotated assignments which don't conflict at all with slots.
- if len(class_variable) == 1:
- parent = class_variable[0].parent
- if isinstance(parent, astroid.AnnAssign) and parent.value is None:
- return
- self.add_message(
- "class-variable-slots-conflict", args=(inferred.value,), node=elt
- )
-
- def leave_functiondef(self, node):
- """on method node, check if this method couldn't be a function
-
- ignore class, static and abstract methods, initializer,
- methods overridden from a parent class.
- """
- if node.is_method():
- if node.args.args is not None:
- self._first_attrs.pop()
- if not self.linter.is_message_enabled("no-self-use"):
- return
- class_node = node.parent.frame()
- if (
- self._meth_could_be_func
- and node.type == "method"
- and node.name not in PYMETHODS
- and not (
- node.is_abstract()
- or overrides_a_method(class_node, node.name)
- or decorated_with_property(node)
- or _has_bare_super_call(node)
- or is_protocol_class(class_node)
- )
- ):
- self.add_message("no-self-use", node=node)
-
- def visit_attribute(self, node):
- """check if the getattr is an access to a class member
- if so, register it. Also check for access to protected
- class member from outside its class (but ignore __special__
- methods)
- """
- # Check self
- if self._uses_mandatory_method_param(node):
- self._accessed.set_accessed(node)
- return
- if not self.linter.is_message_enabled("protected-access"):
- return
-
- self._check_protected_attribute_access(node)
-
- def visit_assignattr(self, node):
- if isinstance(
- node.assign_type(), astroid.AugAssign
- ) and self._uses_mandatory_method_param(node):
- self._accessed.set_accessed(node)
- self._check_in_slots(node)
-
- def _check_in_slots(self, node):
- """ Check that the given AssignAttr node
- is defined in the class slots.
- """
- inferred = safe_infer(node.expr)
- if not isinstance(inferred, astroid.Instance):
- return
-
- klass = inferred._proxied
- if not has_known_bases(klass):
- return
- if "__slots__" not in klass.locals or not klass.newstyle:
- return
-
- slots = klass.slots()
- if slots is None:
- return
- # If any ancestor doesn't use slots, the slots
- # defined for this class are superfluous.
- if any(
- "__slots__" not in ancestor.locals and ancestor.name != "object"
- for ancestor in klass.ancestors()
- ):
- return
-
- if not any(slot.value == node.attrname for slot in slots):
- # If we have a '__dict__' in slots, then
- # assigning any name is valid.
- if not any(slot.value == "__dict__" for slot in slots):
- if _is_attribute_property(node.attrname, klass):
- # Properties circumvent the slots mechanism,
- # so we should not emit a warning for them.
- return
- if node.attrname in klass.locals and _has_data_descriptor(
- klass, node.attrname
- ):
- # Descriptors circumvent the slots mechanism as well.
- return
- if node.attrname == "__class__" and _has_same_layout_slots(
- slots, node.parent.value
- ):
- return
- self.add_message("assigning-non-slot", args=(node.attrname,), node=node)
-
- @check_messages(
- "protected-access", "no-classmethod-decorator", "no-staticmethod-decorator"
- )
- def visit_assign(self, assign_node):
- self._check_classmethod_declaration(assign_node)
- node = assign_node.targets[0]
- if not isinstance(node, astroid.AssignAttr):
- return
-
- if self._uses_mandatory_method_param(node):
- return
- self._check_protected_attribute_access(node)
-
- def _check_classmethod_declaration(self, node):
- """Checks for uses of classmethod() or staticmethod()
-
- When a @classmethod or @staticmethod decorator should be used instead.
- A message will be emitted only if the assignment is at a class scope
- and only if the classmethod's argument belongs to the class where it
- is defined.
- `node` is an assign node.
- """
- if not isinstance(node.value, astroid.Call):
- return
-
- # check the function called is "classmethod" or "staticmethod"
- func = node.value.func
- if not isinstance(func, astroid.Name) or func.name not in (
- "classmethod",
- "staticmethod",
- ):
- return
-
- msg = (
- "no-classmethod-decorator"
- if func.name == "classmethod"
- else "no-staticmethod-decorator"
- )
- # assignment must be at a class scope
- parent_class = node.scope()
- if not isinstance(parent_class, astroid.ClassDef):
- return
-
- # Check if the arg passed to classmethod is a class member
- classmeth_arg = node.value.args[0]
- if not isinstance(classmeth_arg, astroid.Name):
- return
-
- method_name = classmeth_arg.name
- if any(method_name == member.name for member in parent_class.mymethods()):
- self.add_message(msg, node=node.targets[0])
-
- def _check_protected_attribute_access(self, node):
- """Given an attribute access node (set or get), check if attribute
- access is legitimate. Call _check_first_attr with node before calling
- this method. Valid cases are:
- * self._attr in a method or cls._attr in a classmethod. Checked by
- _check_first_attr.
- * Klass._attr inside "Klass" class.
- * Klass2._attr inside "Klass" class when Klass2 is a base class of
- Klass.
- """
- attrname = node.attrname
-
- if (
- is_attr_protected(attrname)
- and attrname not in self.config.exclude_protected
- ):
-
- klass = node_frame_class(node)
-
- # In classes, check we are not getting a parent method
- # through the class object or through super
- callee = node.expr.as_string()
-
- # We are not in a class, no remaining valid case
- if klass is None:
- self.add_message("protected-access", node=node, args=attrname)
- return
-
- # If the expression begins with a call to super, that's ok.
- if (
- isinstance(node.expr, astroid.Call)
- and isinstance(node.expr.func, astroid.Name)
- and node.expr.func.name == "super"
- ):
- return
-
- # If the expression begins with a call to type(self), that's ok.
- if self._is_type_self_call(node.expr):
- return
-
- # We are in a class, one remaining valid cases, Klass._attr inside
- # Klass
- if not (callee == klass.name or callee in klass.basenames):
- # Detect property assignments in the body of the class.
- # This is acceptable:
- #
- # class A:
- # b = property(lambda: self._b)
-
- stmt = node.parent.statement()
- if (
- isinstance(stmt, astroid.Assign)
- and len(stmt.targets) == 1
- and isinstance(stmt.targets[0], astroid.AssignName)
- ):
- name = stmt.targets[0].name
- if _is_attribute_property(name, klass):
- return
-
- #  A licit use of protected member is inside a special method
- if not attrname.startswith(
- "__"
- ) and self._is_called_inside_special_method(node):
- return
-
- self.add_message("protected-access", node=node, args=attrname)
-
- @staticmethod
- def _is_called_inside_special_method(node: astroid.node_classes.NodeNG) -> bool:
- """
- Returns true if the node is located inside a special (aka dunder) method
- """
- try:
- frame_name = node.frame().name
- except AttributeError:
- return False
- return frame_name and frame_name in PYMETHODS
-
- def _is_type_self_call(self, expr):
- return (
- isinstance(expr, astroid.Call)
- and isinstance(expr.func, astroid.Name)
- and expr.func.name == "type"
- and len(expr.args) == 1
- and self._is_mandatory_method_param(expr.args[0])
- )
-
- def visit_name(self, node):
- """check if the name handle an access to a class member
- if so, register it
- """
- if self._first_attrs and (
- node.name == self._first_attrs[-1] or not self._first_attrs[-1]
- ):
- self._meth_could_be_func = False
-
- def _check_accessed_members(self, node, accessed):
- """check that accessed members are defined"""
- excs = ("AttributeError", "Exception", "BaseException")
- for attr, nodes in accessed.items():
- try:
- # is it a class attribute ?
- node.local_attr(attr)
- # yes, stop here
- continue
- except astroid.NotFoundError:
- pass
- # is it an instance attribute of a parent class ?
- try:
- next(node.instance_attr_ancestors(attr))
- # yes, stop here
- continue
- except StopIteration:
- pass
- # is it an instance attribute ?
- try:
- defstmts = node.instance_attr(attr)
- except astroid.NotFoundError:
- pass
- else:
- # filter out augment assignment nodes
- defstmts = [stmt for stmt in defstmts if stmt not in nodes]
- if not defstmts:
- # only augment assignment for this node, no-member should be
- # triggered by the typecheck checker
- continue
- # filter defstmts to only pick the first one when there are
- # several assignments in the same scope
- scope = defstmts[0].scope()
- defstmts = [
- stmt
- for i, stmt in enumerate(defstmts)
- if i == 0 or stmt.scope() is not scope
- ]
- # if there are still more than one, don't attempt to be smarter
- # than we can be
- if len(defstmts) == 1:
- defstmt = defstmts[0]
- # check that if the node is accessed in the same method as
- # it's defined, it's accessed after the initial assignment
- frame = defstmt.frame()
- lno = defstmt.fromlineno
- for _node in nodes:
- if (
- _node.frame() is frame
- and _node.fromlineno < lno
- and not astroid.are_exclusive(
- _node.statement(), defstmt, excs
- )
- ):
- self.add_message(
- "access-member-before-definition",
- node=_node,
- args=(attr, lno),
- )
-
- def _check_first_arg_for_type(self, node, metaclass=0):
- """check the name of first argument, expect:
-
- * 'self' for a regular method
- * 'cls' for a class method or a metaclass regular method (actually
- valid-classmethod-first-arg value)
- * 'mcs' for a metaclass class method (actually
- valid-metaclass-classmethod-first-arg)
- * not one of the above for a static method
- """
- # don't care about functions with unknown argument (builtins)
- if node.args.args is None:
- return
- if node.args.args:
- first_arg = node.argnames()[0]
- elif node.args.posonlyargs:
- first_arg = node.args.posonlyargs[0].name
- else:
- first_arg = None
- self._first_attrs.append(first_arg)
- first = self._first_attrs[-1]
- # static method
- if node.type == "staticmethod":
- if (
- first_arg == "self"
- or first_arg in self.config.valid_classmethod_first_arg
- or first_arg in self.config.valid_metaclass_classmethod_first_arg
- ):
- self.add_message("bad-staticmethod-argument", args=first, node=node)
- return
- self._first_attrs[-1] = None
- # class / regular method with no args
- elif not node.args.args and not node.args.posonlyargs:
- self.add_message("no-method-argument", node=node)
- # metaclass
- elif metaclass:
- # metaclass __new__ or classmethod
- if node.type == "classmethod":
- self._check_first_arg_config(
- first,
- self.config.valid_metaclass_classmethod_first_arg,
- node,
- "bad-mcs-classmethod-argument",
- node.name,
- )
- # metaclass regular method
- else:
- self._check_first_arg_config(
- first,
- self.config.valid_classmethod_first_arg,
- node,
- "bad-mcs-method-argument",
- node.name,
- )
- # regular class
- else:
- # class method
- if node.type == "classmethod" or node.name == "__class_getitem__":
- self._check_first_arg_config(
- first,
- self.config.valid_classmethod_first_arg,
- node,
- "bad-classmethod-argument",
- node.name,
- )
- # regular method without self as argument
- elif first != "self":
- self.add_message("no-self-argument", node=node)
-
- def _check_first_arg_config(self, first, config, node, message, method_name):
- if first not in config:
- if len(config) == 1:
- valid = repr(config[0])
- else:
- valid = ", ".join(repr(v) for v in config[:-1])
- valid = "%s or %r" % (valid, config[-1])
- self.add_message(message, args=(method_name, valid), node=node)
-
- def _check_bases_classes(self, node):
- """check that the given class node implements abstract methods from
- base classes
- """
-
- def is_abstract(method):
- return method.is_abstract(pass_is_abstract=False)
-
- # check if this class abstract
- if class_is_abstract(node):
- return
-
- methods = sorted(
- unimplemented_abstract_methods(node, is_abstract).items(),
- key=lambda item: item[0],
- )
- for name, method in methods:
- owner = method.parent.frame()
- if owner is node:
- continue
- # owner is not this class, it must be a parent class
- # check that the ancestor's method is not abstract
- if name in node.locals:
- # it is redefined as an attribute or with a descriptor
- continue
- self.add_message("abstract-method", node=node, args=(name, owner.name))
-
- def _check_init(self, node):
- """check that the __init__ method call super or ancestors'__init__
- method (unless it is used for type hinting with `typing.overload`)
- """
- if not self.linter.is_message_enabled(
- "super-init-not-called"
- ) and not self.linter.is_message_enabled("non-parent-init-called"):
- return
- klass_node = node.parent.frame()
- to_call = _ancestors_to_call(klass_node)
- not_called_yet = dict(to_call)
- for stmt in node.nodes_of_class(astroid.Call):
- expr = stmt.func
- if not isinstance(expr, astroid.Attribute) or expr.attrname != "__init__":
- continue
- # skip the test if using super
- if (
- isinstance(expr.expr, astroid.Call)
- and isinstance(expr.expr.func, astroid.Name)
- and expr.expr.func.name == "super"
- ):
- return
- try:
- for klass in expr.expr.infer():
- if klass is astroid.Uninferable:
- continue
- # The inferred klass can be super(), which was
- # assigned to a variable and the `__init__`
- # was called later.
- #
- # base = super()
- # base.__init__(...)
-
- if (
- isinstance(klass, astroid.Instance)
- and isinstance(klass._proxied, astroid.ClassDef)
- and is_builtin_object(klass._proxied)
- and klass._proxied.name == "super"
- ):
- return
- if isinstance(klass, objects.Super):
- return
- try:
- del not_called_yet[klass]
- except KeyError:
- if klass not in to_call:
- self.add_message(
- "non-parent-init-called", node=expr, args=klass.name
- )
- except astroid.InferenceError:
- continue
- for klass, method in not_called_yet.items():
- if decorated_with(node, ["typing.overload"]):
- continue
- cls = node_frame_class(method)
- if klass.name == "object" or (cls and cls.name == "object"):
- continue
- self.add_message("super-init-not-called", args=klass.name, node=node)
-
- def _check_signature(self, method1, refmethod, class_type, cls):
- """check that the signature of the two given methods match
- """
- if not (
- isinstance(method1, astroid.FunctionDef)
- and isinstance(refmethod, astroid.FunctionDef)
- ):
- self.add_message(
- "method-check-failed", args=(method1, refmethod), node=method1
- )
- return
-
- instance = cls.instantiate_class()
- method1 = function_to_method(method1, instance)
- refmethod = function_to_method(refmethod, instance)
-
- # Don't care about functions with unknown argument (builtins).
- if method1.args.args is None or refmethod.args.args is None:
- return
-
- # Ignore private to class methods.
- if is_attr_private(method1.name):
- return
- # Ignore setters, they have an implicit extra argument,
- # which shouldn't be taken in consideration.
- if is_property_setter(method1):
- return
-
- if _different_parameters(
- refmethod, method1, dummy_parameter_regex=self._dummy_rgx
- ):
- self.add_message(
- "arguments-differ", args=(class_type, method1.name), node=method1
- )
- elif len(method1.args.defaults) < len(refmethod.args.defaults):
- self.add_message(
- "signature-differs", args=(class_type, method1.name), node=method1
- )
-
- def _uses_mandatory_method_param(self, node):
- """Check that attribute lookup name use first attribute variable name
-
- Name is `self` for method, `cls` for classmethod and `mcs` for metaclass.
- """
- return self._is_mandatory_method_param(node.expr)
-
- def _is_mandatory_method_param(self, node):
- """Check if astroid.Name corresponds to first attribute variable name
-
- Name is `self` for method, `cls` for classmethod and `mcs` for metaclass.
- """
- return (
- self._first_attrs
- and isinstance(node, astroid.Name)
- and node.name == self._first_attrs[-1]
- )
-
-
-class SpecialMethodsChecker(BaseChecker):
- """Checker which verifies that special methods
- are implemented correctly.
- """
-
- __implements__ = (IAstroidChecker,)
- name = "classes"
- msgs = {
- "E0301": (
- "__iter__ returns non-iterator",
- "non-iterator-returned",
- "Used when an __iter__ method returns something which is not an "
- "iterable (i.e. has no `%s` method)" % NEXT_METHOD,
- {
- "old_names": [
- ("W0234", "old-non-iterator-returned-1"),
- ("E0234", "old-non-iterator-returned-2"),
- ]
- },
- ),
- "E0302": (
- "The special method %r expects %s param(s), %d %s given",
- "unexpected-special-method-signature",
- "Emitted when a special method was defined with an "
- "invalid number of parameters. If it has too few or "
- "too many, it might not work at all.",
- {"old_names": [("E0235", "bad-context-manager")]},
- ),
- "E0303": (
- "__len__ does not return non-negative integer",
- "invalid-length-returned",
- "Used when a __len__ method returns something which is not a "
- "non-negative integer",
- {},
- ),
- }
- priority = -2
-
- @check_messages(
- "unexpected-special-method-signature",
- "non-iterator-returned",
- "invalid-length-returned",
- )
- def visit_functiondef(self, node):
- if not node.is_method():
- return
- if node.name == "__iter__":
- self._check_iter(node)
- if node.name == "__len__":
- self._check_len(node)
- if node.name in PYMETHODS:
- self._check_unexpected_method_signature(node)
-
- visit_asyncfunctiondef = visit_functiondef
-
- def _check_unexpected_method_signature(self, node):
- expected_params = SPECIAL_METHODS_PARAMS[node.name]
-
- if expected_params is None:
- # This can support a variable number of parameters.
- return
- if not node.args.args and not node.args.vararg:
- # Method has no parameter, will be caught
- # by no-method-argument.
- return
-
- if decorated_with(node, [BUILTINS + ".staticmethod"]):
- # We expect to not take in consideration self.
- all_args = node.args.args
- else:
- all_args = node.args.args[1:]
- mandatory = len(all_args) - len(node.args.defaults)
- optional = len(node.args.defaults)
- current_params = mandatory + optional
-
- if isinstance(expected_params, tuple):
- # The expected number of parameters can be any value from this
- # tuple, although the user should implement the method
- # to take all of them in consideration.
- emit = mandatory not in expected_params
- expected_params = "between %d or %d" % expected_params
- else:
- # If the number of mandatory parameters doesn't
- # suffice, the expected parameters for this
- # function will be deduced from the optional
- # parameters.
- rest = expected_params - mandatory
- if rest == 0:
- emit = False
- elif rest < 0:
- emit = True
- elif rest > 0:
- emit = not ((optional - rest) >= 0 or node.args.vararg)
-
- if emit:
- verb = "was" if current_params <= 1 else "were"
- self.add_message(
- "unexpected-special-method-signature",
- args=(node.name, expected_params, current_params, verb),
- node=node,
- )
-
- @staticmethod
- def _is_iterator(node):
- if node is astroid.Uninferable:
- # Just ignore Uninferable objects.
- return True
- if isinstance(node, Generator):
- # Generators can be itered.
- return True
-
- if isinstance(node, astroid.Instance):
- try:
- node.local_attr(NEXT_METHOD)
- return True
- except astroid.NotFoundError:
- pass
- elif isinstance(node, astroid.ClassDef):
- metaclass = node.metaclass()
- if metaclass and isinstance(metaclass, astroid.ClassDef):
- try:
- metaclass.local_attr(NEXT_METHOD)
- return True
- except astroid.NotFoundError:
- pass
- return False
-
- def _check_iter(self, node):
- inferred = _safe_infer_call_result(node, node)
- if inferred is not None:
- if not self._is_iterator(inferred):
- self.add_message("non-iterator-returned", node=node)
-
- def _check_len(self, node):
- inferred = _safe_infer_call_result(node, node)
- if not inferred or inferred is astroid.Uninferable:
- return
-
- if (
- isinstance(inferred, astroid.Instance)
- and inferred.name == "int"
- and not isinstance(inferred, astroid.Const)
- ):
- # Assume it's good enough, since the int() call might wrap
- # something that's uninferable for us
- return
-
- if not isinstance(inferred, astroid.Const):
- self.add_message("invalid-length-returned", node=node)
- return
-
- value = inferred.value
- if not isinstance(value, int) or value < 0:
- self.add_message("invalid-length-returned", node=node)
-
-
-def _ancestors_to_call(klass_node, method="__init__"):
- """return a dictionary where keys are the list of base classes providing
- the queried method, and so that should/may be called from the method node
- """
- to_call = {}
- for base_node in klass_node.ancestors(recurs=False):
- try:
- to_call[base_node] = next(base_node.igetattr(method))
- except astroid.InferenceError:
- continue
- return to_call
-
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(ClassChecker(linter))
- linter.register_checker(SpecialMethodsChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/design_analysis.py b/venv/Lib/site-packages/pylint/checkers/design_analysis.py
deleted file mode 100644
index 50d8eaa..0000000
--- a/venv/Lib/site-packages/pylint/checkers/design_analysis.py
+++ /dev/null
@@ -1,496 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006, 2009-2010, 2012-2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2012, 2014 Google, Inc.
-# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2017 ahirnish <ahirnish@gmail.com>
-# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
-# Copyright (c) 2018 Mark Miller <725mrm@gmail.com>
-# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
-# Copyright (c) 2018 Jakub Wilk <jwilk@jwilk.net>
-
-# 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
-
-"""check for signs of poor design"""
-
-import re
-from collections import defaultdict
-
-import astroid
-from astroid import BoolOp, If, decorators
-
-from pylint import utils
-from pylint.checkers import BaseChecker
-from pylint.checkers.utils import check_messages
-from pylint.interfaces import IAstroidChecker
-
-MSGS = {
- "R0901": (
- "Too many ancestors (%s/%s)",
- "too-many-ancestors",
- "Used when class has too many parent classes, try to reduce "
- "this to get a simpler (and so easier to use) class.",
- ),
- "R0902": (
- "Too many instance attributes (%s/%s)",
- "too-many-instance-attributes",
- "Used when class has too many instance attributes, try to reduce "
- "this to get a simpler (and so easier to use) class.",
- ),
- "R0903": (
- "Too few public methods (%s/%s)",
- "too-few-public-methods",
- "Used when class has too few public methods, so be sure it's "
- "really worth it.",
- ),
- "R0904": (
- "Too many public methods (%s/%s)",
- "too-many-public-methods",
- "Used when class has too many public methods, try to reduce "
- "this to get a simpler (and so easier to use) class.",
- ),
- "R0911": (
- "Too many return statements (%s/%s)",
- "too-many-return-statements",
- "Used when a function or method has too many return statement, "
- "making it hard to follow.",
- ),
- "R0912": (
- "Too many branches (%s/%s)",
- "too-many-branches",
- "Used when a function or method has too many branches, "
- "making it hard to follow.",
- ),
- "R0913": (
- "Too many arguments (%s/%s)",
- "too-many-arguments",
- "Used when a function or method takes too many arguments.",
- ),
- "R0914": (
- "Too many local variables (%s/%s)",
- "too-many-locals",
- "Used when a function or method has too many local variables.",
- ),
- "R0915": (
- "Too many statements (%s/%s)",
- "too-many-statements",
- "Used when a function or method has too many statements. You "
- "should then split it in smaller functions / methods.",
- ),
- "R0916": (
- "Too many boolean expressions in if statement (%s/%s)",
- "too-many-boolean-expressions",
- "Used when an if statement contains too many boolean expressions.",
- ),
-}
-SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
-DATACLASSES_DECORATORS = frozenset({"dataclass", "attrs"})
-DATACLASS_IMPORT = "dataclasses"
-TYPING_NAMEDTUPLE = "typing.NamedTuple"
-
-
-def _is_exempt_from_public_methods(node: astroid.ClassDef) -> bool:
- """Check if a class is exempt from too-few-public-methods"""
-
- # If it's a typing.Namedtuple or an Enum
- for ancestor in node.ancestors():
- if ancestor.name == "Enum" and ancestor.root().name == "enum":
- return True
- if ancestor.qname() == TYPING_NAMEDTUPLE:
- return True
-
- # Or if it's a dataclass
- if not node.decorators:
- return False
-
- root_locals = set(node.root().locals)
- for decorator in node.decorators.nodes:
- if isinstance(decorator, astroid.Call):
- decorator = decorator.func
- if not isinstance(decorator, (astroid.Name, astroid.Attribute)):
- continue
- if isinstance(decorator, astroid.Name):
- name = decorator.name
- else:
- name = decorator.attrname
- if name in DATACLASSES_DECORATORS and (
- root_locals.intersection(DATACLASSES_DECORATORS)
- or DATACLASS_IMPORT in root_locals
- ):
- return True
- return False
-
-
-def _count_boolean_expressions(bool_op):
- """Counts the number of boolean expressions in BoolOp `bool_op` (recursive)
-
- example: a and (b or c or (d and e)) ==> 5 boolean expressions
- """
- nb_bool_expr = 0
- for bool_expr in bool_op.get_children():
- if isinstance(bool_expr, BoolOp):
- nb_bool_expr += _count_boolean_expressions(bool_expr)
- else:
- nb_bool_expr += 1
- return nb_bool_expr
-
-
-def _count_methods_in_class(node):
- all_methods = sum(1 for method in node.methods() if not method.name.startswith("_"))
- # Special methods count towards the number of public methods,
- # but don't count towards there being too many methods.
- for method in node.mymethods():
- if SPECIAL_OBJ.search(method.name) and method.name != "__init__":
- all_methods += 1
- return all_methods
-
-
-class MisdesignChecker(BaseChecker):
- """checks for sign of poor/misdesign:
- * number of methods, attributes, local variables...
- * size, complexity of functions, methods
- """
-
- __implements__ = (IAstroidChecker,)
-
- # configuration section name
- name = "design"
- # messages
- msgs = MSGS
- priority = -2
- # configuration options
- options = (
- (
- "max-args",
- {
- "default": 5,
- "type": "int",
- "metavar": "<int>",
- "help": "Maximum number of arguments for function / method.",
- },
- ),
- (
- "max-locals",
- {
- "default": 15,
- "type": "int",
- "metavar": "<int>",
- "help": "Maximum number of locals for function / method body.",
- },
- ),
- (
- "max-returns",
- {
- "default": 6,
- "type": "int",
- "metavar": "<int>",
- "help": "Maximum number of return / yield for function / "
- "method body.",
- },
- ),
- (
- "max-branches",
- {
- "default": 12,
- "type": "int",
- "metavar": "<int>",
- "help": "Maximum number of branch for function / method body.",
- },
- ),
- (
- "max-statements",
- {
- "default": 50,
- "type": "int",
- "metavar": "<int>",
- "help": "Maximum number of statements in function / method " "body.",
- },
- ),
- (
- "max-parents",
- {
- "default": 7,
- "type": "int",
- "metavar": "<num>",
- "help": "Maximum number of parents for a class (see R0901).",
- },
- ),
- (
- "max-attributes",
- {
- "default": 7,
- "type": "int",
- "metavar": "<num>",
- "help": "Maximum number of attributes for a class \
-(see R0902).",
- },
- ),
- (
- "min-public-methods",
- {
- "default": 2,
- "type": "int",
- "metavar": "<num>",
- "help": "Minimum number of public methods for a class \
-(see R0903).",
- },
- ),
- (
- "max-public-methods",
- {
- "default": 20,
- "type": "int",
- "metavar": "<num>",
- "help": "Maximum number of public methods for a class \
-(see R0904).",
- },
- ),
- (
- "max-bool-expr",
- {
- "default": 5,
- "type": "int",
- "metavar": "<num>",
- "help": "Maximum number of boolean expressions in an if "
- "statement (see R0916).",
- },
- ),
- )
-
- def __init__(self, linter=None):
- BaseChecker.__init__(self, linter)
- self.stats = None
- self._returns = None
- self._branches = None
- self._stmts = None
-
- def open(self):
- """initialize visit variables"""
- self.stats = self.linter.add_stats()
- self._returns = []
- self._branches = defaultdict(int)
- self._stmts = []
-
- def _inc_all_stmts(self, amount):
- for i in range(len(self._stmts)):
- self._stmts[i] += amount
-
- @decorators.cachedproperty
- def _ignored_argument_names(self):
- return utils.get_global_option(self, "ignored-argument-names", default=None)
-
- @check_messages(
- "too-many-ancestors",
- "too-many-instance-attributes",
- "too-few-public-methods",
- "too-many-public-methods",
- )
- def visit_classdef(self, node):
- """check size of inheritance hierarchy and number of instance attributes
- """
- nb_parents = len(list(node.ancestors()))
- if nb_parents > self.config.max_parents:
- self.add_message(
- "too-many-ancestors",
- node=node,
- args=(nb_parents, self.config.max_parents),
- )
-
- if len(node.instance_attrs) > self.config.max_attributes:
- self.add_message(
- "too-many-instance-attributes",
- node=node,
- args=(len(node.instance_attrs), self.config.max_attributes),
- )
-
- @check_messages("too-few-public-methods", "too-many-public-methods")
- def leave_classdef(self, node):
- """check number of public methods"""
- my_methods = sum(
- 1 for method in node.mymethods() if not method.name.startswith("_")
- )
-
- # Does the class contain less than n public methods ?
- # This checks only the methods defined in the current class,
- # since the user might not have control over the classes
- # from the ancestors. It avoids some false positives
- # for classes such as unittest.TestCase, which provides
- # a lot of assert methods. It doesn't make sense to warn
- # when the user subclasses TestCase to add his own tests.
- if my_methods > self.config.max_public_methods:
- self.add_message(
- "too-many-public-methods",
- node=node,
- args=(my_methods, self.config.max_public_methods),
- )
-
- # Stop here for exception, metaclass, interface classes and other
- # classes for which we don't need to count the methods.
- if node.type != "class" or _is_exempt_from_public_methods(node):
- return
-
- # Does the class contain more than n public methods ?
- # This checks all the methods defined by ancestors and
- # by the current class.
- all_methods = _count_methods_in_class(node)
- if all_methods < self.config.min_public_methods:
- self.add_message(
- "too-few-public-methods",
- node=node,
- args=(all_methods, self.config.min_public_methods),
- )
-
- @check_messages(
- "too-many-return-statements",
- "too-many-branches",
- "too-many-arguments",
- "too-many-locals",
- "too-many-statements",
- "keyword-arg-before-vararg",
- )
- def visit_functiondef(self, node):
- """check function name, docstring, arguments, redefinition,
- variable names, max locals
- """
- # init branch and returns counters
- self._returns.append(0)
- # check number of arguments
- args = node.args.args
- ignored_argument_names = self._ignored_argument_names
- if args is not None:
- ignored_args_num = 0
- if ignored_argument_names:
- ignored_args_num = sum(
- 1 for arg in args if ignored_argument_names.match(arg.name)
- )
-
- argnum = len(args) - ignored_args_num
- if argnum > self.config.max_args:
- self.add_message(
- "too-many-arguments",
- node=node,
- args=(len(args), self.config.max_args),
- )
- else:
- ignored_args_num = 0
- # check number of local variables
- locnum = len(node.locals) - ignored_args_num
- if locnum > self.config.max_locals:
- self.add_message(
- "too-many-locals", node=node, args=(locnum, self.config.max_locals)
- )
- # init new statements counter
- self._stmts.append(1)
-
- visit_asyncfunctiondef = visit_functiondef
-
- @check_messages(
- "too-many-return-statements",
- "too-many-branches",
- "too-many-arguments",
- "too-many-locals",
- "too-many-statements",
- )
- def leave_functiondef(self, node):
- """most of the work is done here on close:
- checks for max returns, branch, return in __init__
- """
- returns = self._returns.pop()
- if returns > self.config.max_returns:
- self.add_message(
- "too-many-return-statements",
- node=node,
- args=(returns, self.config.max_returns),
- )
- branches = self._branches[node]
- if branches > self.config.max_branches:
- self.add_message(
- "too-many-branches",
- node=node,
- args=(branches, self.config.max_branches),
- )
- # check number of statements
- stmts = self._stmts.pop()
- if stmts > self.config.max_statements:
- self.add_message(
- "too-many-statements",
- node=node,
- args=(stmts, self.config.max_statements),
- )
-
- leave_asyncfunctiondef = leave_functiondef
-
- def visit_return(self, _):
- """count number of returns"""
- if not self._returns:
- return # return outside function, reported by the base checker
- self._returns[-1] += 1
-
- def visit_default(self, node):
- """default visit method -> increments the statements counter if
- necessary
- """
- if node.is_statement:
- self._inc_all_stmts(1)
-
- def visit_tryexcept(self, node):
- """increments the branches counter"""
- branches = len(node.handlers)
- if node.orelse:
- branches += 1
- self._inc_branch(node, branches)
- self._inc_all_stmts(branches)
-
- def visit_tryfinally(self, node):
- """increments the branches counter"""
- self._inc_branch(node, 2)
- self._inc_all_stmts(2)
-
- @check_messages("too-many-boolean-expressions")
- def visit_if(self, node):
- """increments the branches counter and checks boolean expressions"""
- self._check_boolean_expressions(node)
- branches = 1
- # don't double count If nodes coming from some 'elif'
- if node.orelse and (len(node.orelse) > 1 or not isinstance(node.orelse[0], If)):
- branches += 1
- self._inc_branch(node, branches)
- self._inc_all_stmts(branches)
-
- def _check_boolean_expressions(self, node):
- """Go through "if" node `node` and counts its boolean expressions
-
- if the "if" node test is a BoolOp node
- """
- condition = node.test
- if not isinstance(condition, BoolOp):
- return
- nb_bool_expr = _count_boolean_expressions(condition)
- if nb_bool_expr > self.config.max_bool_expr:
- self.add_message(
- "too-many-boolean-expressions",
- node=condition,
- args=(nb_bool_expr, self.config.max_bool_expr),
- )
-
- def visit_while(self, node):
- """increments the branches counter"""
- branches = 1
- if node.orelse:
- branches += 1
- self._inc_branch(node, branches)
-
- visit_for = visit_while
-
- def _inc_branch(self, node, branchesnum=1):
- """increments the branches counter"""
- self._branches[node.scope()] += branchesnum
-
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(MisdesignChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/exceptions.py b/venv/Lib/site-packages/pylint/checkers/exceptions.py
deleted file mode 100644
index 360e1d1..0000000
--- a/venv/Lib/site-packages/pylint/checkers/exceptions.py
+++ /dev/null
@@ -1,546 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2011-2014 Google, Inc.
-# Copyright (c) 2012 Tim Hatch <tim@timhatch.com>
-# Copyright (c) 2013-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 Rene Zhang <rz99@cornell.edu>
-# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
-# Copyright (c) 2015 Steven Myint <hg@stevenmyint.com>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Erik <erik.eriksson@yahoo.com>
-# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
-# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2017 Martin von Gagern <gagern@google.com>
-# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
-# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
-# Copyright (c) 2018 Alexander Todorov <atodorov@otb.bg>
-# 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
-
-"""Checks for various exception related errors."""
-import builtins
-import inspect
-import typing
-
-import astroid
-from astroid.node_classes import NodeNG
-
-from pylint import checkers, interfaces
-from pylint.checkers import utils
-
-
-def _builtin_exceptions():
- def predicate(obj):
- return isinstance(obj, type) and issubclass(obj, BaseException)
-
- members = inspect.getmembers(builtins, predicate)
- return {exc.__name__ for (_, exc) in members}
-
-
-def _annotated_unpack_infer(stmt, context=None):
- """
- Recursively generate nodes inferred by the given statement.
- If the inferred value is a list or a tuple, recurse on the elements.
- Returns an iterator which yields tuples in the format
- ('original node', 'inferred node').
- """
- if isinstance(stmt, (astroid.List, astroid.Tuple)):
- for elt in stmt.elts:
- inferred = utils.safe_infer(elt)
- if inferred and inferred is not astroid.Uninferable:
- yield elt, inferred
- return
- for inferred in stmt.infer(context):
- if inferred is astroid.Uninferable:
- continue
- yield stmt, inferred
-
-
-def _is_raising(body: typing.List) -> bool:
- """Return true if the given statement node raise an exception"""
- for node in body:
- if isinstance(node, astroid.Raise):
- return True
- return False
-
-
-OVERGENERAL_EXCEPTIONS = ("BaseException", "Exception")
-BUILTINS_NAME = builtins.__name__
-
-MSGS = {
- "E0701": (
- "Bad except clauses order (%s)",
- "bad-except-order",
- "Used when except clauses are not in the correct order (from the "
- "more specific to the more generic). If you don't fix the order, "
- "some exceptions may not be caught by the most specific handler.",
- ),
- "E0702": (
- "Raising %s while only classes or instances are allowed",
- "raising-bad-type",
- "Used when something which is neither a class, an instance or a "
- "string is raised (i.e. a `TypeError` will be raised).",
- ),
- "E0703": (
- "Exception context set to something which is not an exception, nor None",
- "bad-exception-context",
- 'Used when using the syntax "raise ... from ...", '
- "where the exception context is not an exception, "
- "nor None.",
- ),
- "E0704": (
- "The raise statement is not inside an except clause",
- "misplaced-bare-raise",
- "Used when a bare raise is not used inside an except clause. "
- "This generates an error, since there are no active exceptions "
- "to be reraised. An exception to this rule is represented by "
- "a bare raise inside a finally clause, which might work, as long "
- "as an exception is raised inside the try block, but it is "
- "nevertheless a code smell that must not be relied upon.",
- ),
- "E0710": (
- "Raising a new style class which doesn't inherit from BaseException",
- "raising-non-exception",
- "Used when a new style class which doesn't inherit from "
- "BaseException is raised.",
- ),
- "E0711": (
- "NotImplemented raised - should raise NotImplementedError",
- "notimplemented-raised",
- "Used when NotImplemented is raised instead of NotImplementedError",
- ),
- "E0712": (
- "Catching an exception which doesn't inherit from Exception: %s",
- "catching-non-exception",
- "Used when a class which doesn't inherit from "
- "Exception is used as an exception in an except clause.",
- ),
- "W0702": (
- "No exception type(s) specified",
- "bare-except",
- "Used when an except clause doesn't specify exceptions type to catch.",
- ),
- "W0703": (
- "Catching too general exception %s",
- "broad-except",
- "Used when an except catches a too general exception, "
- "possibly burying unrelated errors.",
- ),
- "W0705": (
- "Catching previously caught exception type %s",
- "duplicate-except",
- "Used when an except catches a type that was already caught by "
- "a previous handler.",
- ),
- "W0706": (
- "The except handler raises immediately",
- "try-except-raise",
- "Used when an except handler uses raise as its first or only "
- "operator. This is useless because it raises back the exception "
- "immediately. Remove the raise operator or the entire "
- "try-except-raise block!",
- ),
- "W0711": (
- 'Exception to catch is the result of a binary "%s" operation',
- "binary-op-exception",
- "Used when the exception to catch is of the form "
- '"except A or B:". If intending to catch multiple, '
- 'rewrite as "except (A, B):"',
- ),
- "W0715": (
- "Exception arguments suggest string formatting might be intended",
- "raising-format-tuple",
- "Used when passing multiple arguments to an exception "
- "constructor, the first of them a string literal containing what "
- "appears to be placeholders intended for formatting",
- ),
- "W0716": (
- "Invalid exception operation. %s",
- "wrong-exception-operation",
- "Used when an operation is done against an exception, but the operation "
- "is not valid for the exception in question. Usually emitted when having "
- "binary operations between exceptions in except handlers.",
- ),
-}
-
-
-class BaseVisitor:
- """Base class for visitors defined in this module."""
-
- def __init__(self, checker, node):
- self._checker = checker
- self._node = node
-
- def visit(self, node):
- name = node.__class__.__name__.lower()
- dispatch_meth = getattr(self, "visit_" + name, None)
- if dispatch_meth:
- dispatch_meth(node)
- else:
- self.visit_default(node)
-
- def visit_default(self, node): # pylint: disable=unused-argument
- """Default implementation for all the nodes."""
-
-
-class ExceptionRaiseRefVisitor(BaseVisitor):
- """Visit references (anything that is not an AST leaf)."""
-
- def visit_name(self, name):
- if name.name == "NotImplemented":
- self._checker.add_message("notimplemented-raised", node=self._node)
-
- def visit_call(self, call):
- if isinstance(call.func, astroid.Name):
- self.visit_name(call.func)
- if (
- len(call.args) > 1
- and isinstance(call.args[0], astroid.Const)
- and isinstance(call.args[0].value, str)
- ):
- msg = call.args[0].value
- if "%" in msg or ("{" in msg and "}" in msg):
- self._checker.add_message("raising-format-tuple", node=self._node)
-
-
-class ExceptionRaiseLeafVisitor(BaseVisitor):
- """Visitor for handling leaf kinds of a raise value."""
-
- def visit_const(self, const):
- if not isinstance(const.value, str):
- # raising-string will be emitted from python3 porting checker.
- self._checker.add_message(
- "raising-bad-type", node=self._node, args=const.value.__class__.__name__
- )
-
- def visit_instance(self, instance):
- # pylint: disable=protected-access
- cls = instance._proxied
- self.visit_classdef(cls)
-
- # Exception instances have a particular class type
- visit_exceptioninstance = visit_instance
-
- def visit_classdef(self, cls):
- if not utils.inherit_from_std_ex(cls) and utils.has_known_bases(cls):
- if cls.newstyle:
- self._checker.add_message("raising-non-exception", node=self._node)
-
- def visit_tuple(self, _):
- self._checker.add_message("raising-bad-type", node=self._node, args="tuple")
-
- def visit_default(self, node):
- name = getattr(node, "name", node.__class__.__name__)
- self._checker.add_message("raising-bad-type", node=self._node, args=name)
-
-
-class ExceptionsChecker(checkers.BaseChecker):
- """Exception related checks."""
-
- __implements__ = interfaces.IAstroidChecker
-
- name = "exceptions"
- msgs = MSGS
- priority = -4
- options = (
- (
- "overgeneral-exceptions",
- {
- "default": OVERGENERAL_EXCEPTIONS,
- "type": "csv",
- "metavar": "<comma-separated class names>",
- "help": "Exceptions that will emit a warning "
- 'when being caught. Defaults to "%s".'
- % (", ".join(OVERGENERAL_EXCEPTIONS),),
- },
- ),
- )
-
- def open(self):
- self._builtin_exceptions = _builtin_exceptions()
- super(ExceptionsChecker, self).open()
-
- @utils.check_messages(
- "misplaced-bare-raise",
- "raising-bad-type",
- "raising-non-exception",
- "notimplemented-raised",
- "bad-exception-context",
- "raising-format-tuple",
- )
- def visit_raise(self, node):
- if node.exc is None:
- self._check_misplaced_bare_raise(node)
- return
-
- if node.cause:
- self._check_bad_exception_context(node)
-
- expr = node.exc
- ExceptionRaiseRefVisitor(self, node).visit(expr)
-
- try:
- inferred_value = expr.inferred()[-1]
- except astroid.InferenceError:
- pass
- else:
- if inferred_value:
- ExceptionRaiseLeafVisitor(self, node).visit(inferred_value)
-
- def _check_misplaced_bare_raise(self, node):
- # Filter out if it's present in __exit__.
- scope = node.scope()
- if (
- isinstance(scope, astroid.FunctionDef)
- and scope.is_method()
- and scope.name == "__exit__"
- ):
- return
-
- current = node
- # Stop when a new scope is generated or when the raise
- # statement is found inside a TryFinally.
- ignores = (astroid.ExceptHandler, astroid.FunctionDef)
- while current and not isinstance(current.parent, ignores):
- current = current.parent
-
- expected = (astroid.ExceptHandler,)
- if not current or not isinstance(current.parent, expected):
- self.add_message("misplaced-bare-raise", node=node)
-
- def _check_bad_exception_context(self, node):
- """Verify that the exception context is properly set.
-
- An exception context can be only `None` or an exception.
- """
- cause = utils.safe_infer(node.cause)
- if cause in (astroid.Uninferable, None):
- return
-
- if isinstance(cause, astroid.Const):
- if cause.value is not None:
- self.add_message("bad-exception-context", node=node)
- elif not isinstance(cause, astroid.ClassDef) and not utils.inherit_from_std_ex(
- cause
- ):
- self.add_message("bad-exception-context", node=node)
-
- def _check_catching_non_exception(self, handler, exc, part):
- if isinstance(exc, astroid.Tuple):
- # Check if it is a tuple of exceptions.
- inferred = [utils.safe_infer(elt) for elt in exc.elts]
- if any(node is astroid.Uninferable for node in inferred):
- # Don't emit if we don't know every component.
- return
- if all(
- node
- and (utils.inherit_from_std_ex(node) or not utils.has_known_bases(node))
- for node in inferred
- ):
- return
-
- if not isinstance(exc, astroid.ClassDef):
- # Don't emit the warning if the inferred stmt
- # is None, but the exception handler is something else,
- # maybe it was redefined.
- if isinstance(exc, astroid.Const) and exc.value is None:
- if (
- isinstance(handler.type, astroid.Const)
- and handler.type.value is None
- ) or handler.type.parent_of(exc):
- # If the exception handler catches None or
- # the exception component, which is None, is
- # defined by the entire exception handler, then
- # emit a warning.
- self.add_message(
- "catching-non-exception",
- node=handler.type,
- args=(part.as_string(),),
- )
- else:
- self.add_message(
- "catching-non-exception",
- node=handler.type,
- args=(part.as_string(),),
- )
- return
-
- if (
- not utils.inherit_from_std_ex(exc)
- and exc.name not in self._builtin_exceptions
- ):
- if utils.has_known_bases(exc):
- self.add_message(
- "catching-non-exception", node=handler.type, args=(exc.name,)
- )
-
- def _check_try_except_raise(self, node):
- def gather_exceptions_from_handler(
- handler
- ) -> typing.Optional[typing.List[NodeNG]]:
- exceptions = [] # type: typing.List[NodeNG]
- if handler.type:
- exceptions_in_handler = utils.safe_infer(handler.type)
- if isinstance(exceptions_in_handler, astroid.Tuple):
- exceptions = list(
- {
- exception
- for exception in exceptions_in_handler.elts
- if isinstance(exception, astroid.Name)
- }
- )
- elif exceptions_in_handler:
- exceptions = [exceptions_in_handler]
- else:
- # Break when we cannot infer anything reliably.
- return None
- return exceptions
-
- bare_raise = False
- handler_having_bare_raise = None
- excs_in_bare_handler = []
- for handler in node.handlers:
- if bare_raise:
- # check that subsequent handler is not parent of handler which had bare raise.
- # since utils.safe_infer can fail for bare except, check it before.
- # also break early if bare except is followed by bare except.
-
- excs_in_current_handler = gather_exceptions_from_handler(handler)
-
- if not excs_in_current_handler:
- bare_raise = False
- break
- if excs_in_bare_handler is None:
- # It can be `None` when the inference failed
- break
-
- for exc_in_current_handler in excs_in_current_handler:
- inferred_current = utils.safe_infer(exc_in_current_handler)
- if any(
- utils.is_subclass_of(
- utils.safe_infer(exc_in_bare_handler), inferred_current
- )
- for exc_in_bare_handler in excs_in_bare_handler
- ):
- bare_raise = False
- break
-
- # `raise` as the first operator inside the except handler
- if _is_raising([handler.body[0]]):
- # flags when there is a bare raise
- if handler.body[0].exc is None:
- bare_raise = True
- handler_having_bare_raise = handler
- excs_in_bare_handler = gather_exceptions_from_handler(handler)
- else:
- if bare_raise:
- self.add_message("try-except-raise", node=handler_having_bare_raise)
-
- @utils.check_messages("wrong-exception-operation")
- def visit_binop(self, node):
- if isinstance(node.parent, astroid.ExceptHandler):
- # except (V | A)
- suggestion = "Did you mean '(%s, %s)' instead?" % (
- node.left.as_string(),
- node.right.as_string(),
- )
- self.add_message("wrong-exception-operation", node=node, args=(suggestion,))
-
- @utils.check_messages("wrong-exception-operation")
- def visit_compare(self, node):
- if isinstance(node.parent, astroid.ExceptHandler):
- # except (V < A)
- suggestion = "Did you mean '(%s, %s)' instead?" % (
- node.left.as_string(),
- ", ".join(operand.as_string() for _, operand in node.ops),
- )
- self.add_message("wrong-exception-operation", node=node, args=(suggestion,))
-
- @utils.check_messages(
- "bare-except",
- "broad-except",
- "try-except-raise",
- "binary-op-exception",
- "bad-except-order",
- "catching-non-exception",
- "duplicate-except",
- )
- def visit_tryexcept(self, node):
- """check for empty except"""
- self._check_try_except_raise(node)
- exceptions_classes = []
- nb_handlers = len(node.handlers)
- for index, handler in enumerate(node.handlers):
- if handler.type is None:
- if not _is_raising(handler.body):
- self.add_message("bare-except", node=handler)
-
- # check if an "except:" is followed by some other
- # except
- if index < (nb_handlers - 1):
- msg = "empty except clause should always appear last"
- self.add_message("bad-except-order", node=node, args=msg)
-
- elif isinstance(handler.type, astroid.BoolOp):
- self.add_message(
- "binary-op-exception", node=handler, args=handler.type.op
- )
- else:
- try:
- excs = list(_annotated_unpack_infer(handler.type))
- except astroid.InferenceError:
- continue
-
- for part, exc in excs:
- if exc is astroid.Uninferable:
- continue
- if isinstance(exc, astroid.Instance) and utils.inherit_from_std_ex(
- exc
- ):
- # pylint: disable=protected-access
- exc = exc._proxied
-
- self._check_catching_non_exception(handler, exc, part)
-
- if not isinstance(exc, astroid.ClassDef):
- continue
-
- exc_ancestors = [
- anc
- for anc in exc.ancestors()
- if isinstance(anc, astroid.ClassDef)
- ]
-
- for previous_exc in exceptions_classes:
- if previous_exc in exc_ancestors:
- msg = "%s is an ancestor class of %s" % (
- previous_exc.name,
- exc.name,
- )
- self.add_message(
- "bad-except-order", node=handler.type, args=msg
- )
- if (
- exc.name in self.config.overgeneral_exceptions
- and exc.root().name == utils.EXCEPTIONS_MODULE
- and not _is_raising(handler.body)
- ):
- self.add_message(
- "broad-except", args=exc.name, node=handler.type
- )
-
- if exc in exceptions_classes:
- self.add_message(
- "duplicate-except", args=exc.name, node=handler.type
- )
-
- exceptions_classes += [exc for _, exc in excs]
-
-
-def register(linter):
- """required method to auto register this checker"""
- linter.register_checker(ExceptionsChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/format.py b/venv/Lib/site-packages/pylint/checkers/format.py
deleted file mode 100644
index c4cad31..0000000
--- a/venv/Lib/site-packages/pylint/checkers/format.py
+++ /dev/null
@@ -1,1332 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2012-2015 Google, Inc.
-# Copyright (c) 2013 moxian <aleftmail@inbox.ru>
-# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 frost-nzcr4 <frost.nzcr4@jagmort.com>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Mike Frysinger <vapier@gentoo.org>
-# Copyright (c) 2015 Fabio Natali <me@fabionatali.com>
-# Copyright (c) 2015 Harut <yes@harutune.name>
-# Copyright (c) 2015 Mihai Balint <balint.mihai@gmail.com>
-# Copyright (c) 2015 Pavel Roskin <proski@gnu.org>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Petr Pulc <petrpulc@gmail.com>
-# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
-# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
-# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017 Krzysztof Czapla <k.czapla68@gmail.com>
-# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2017 James M. Allen <james.m.allen@gmail.com>
-# Copyright (c) 2017 vinnyrose <vinnyrose@users.noreply.github.com>
-# Copyright (c) 2018 Bryce Guinta <bryce.guinta@protonmail.com>
-# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
-# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
-# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
-# Copyright (c) 2018 Fureigh <rhys.fureigh@gsa.gov>
-# Copyright (c) 2018 Pierre Sassoulas <pierre.sassoulas@wisebim.fr>
-# Copyright (c) 2018 Andreas Freimuth <andreas.freimuth@united-bits.de>
-# Copyright (c) 2018 Jakub Wilk <jwilk@jwilk.net>
-
-# 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
-
-"""Python code format's checker.
-
-By default try to follow Guido's style guide :
-
-https://www.python.org/doc/essays/styleguide/
-
-Some parts of the process_token method is based from The Tab Nanny std module.
-"""
-
-import keyword
-import tokenize
-from functools import reduce # pylint: disable=redefined-builtin
-
-from astroid import nodes
-
-from pylint.checkers import BaseTokenChecker
-from pylint.checkers.utils import check_messages
-from pylint.constants import OPTION_RGX, WarningScope
-from pylint.interfaces import IAstroidChecker, IRawChecker, ITokenChecker
-
-_ASYNC_TOKEN = "async"
-_CONTINUATION_BLOCK_OPENERS = [
- "elif",
- "except",
- "for",
- "if",
- "while",
- "def",
- "class",
- "with",
-]
-_KEYWORD_TOKENS = [
- "assert",
- "del",
- "elif",
- "except",
- "for",
- "if",
- "in",
- "not",
- "raise",
- "return",
- "while",
- "yield",
- "with",
-]
-
-_SPACED_OPERATORS = [
- "==",
- "<",
- ">",
- "!=",
- "<>",
- "<=",
- ">=",
- "+=",
- "-=",
- "*=",
- "**=",
- "/=",
- "//=",
- "&=",
- "|=",
- "^=",
- "%=",
- ">>=",
- "<<=",
-]
-_OPENING_BRACKETS = ["(", "[", "{"]
-_CLOSING_BRACKETS = [")", "]", "}"]
-_TAB_LENGTH = 8
-
-_EOL = frozenset([tokenize.NEWLINE, tokenize.NL, tokenize.COMMENT])
-_JUNK_TOKENS = (tokenize.COMMENT, tokenize.NL)
-
-# Whitespace checking policy constants
-_MUST = 0
-_MUST_NOT = 1
-_IGNORE = 2
-
-# Whitespace checking config constants
-_DICT_SEPARATOR = "dict-separator"
-_TRAILING_COMMA = "trailing-comma"
-_EMPTY_LINE = "empty-line"
-_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR, _EMPTY_LINE]
-_DEFAULT_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR]
-
-MSGS = {
- "C0301": (
- "Line too long (%s/%s)",
- "line-too-long",
- "Used when a line is longer than a given number of characters.",
- ),
- "C0302": (
- "Too many lines in module (%s/%s)", # was W0302
- "too-many-lines",
- "Used when a module has too many lines, reducing its readability.",
- ),
- "C0303": (
- "Trailing whitespace",
- "trailing-whitespace",
- "Used when there is whitespace between the end of a line and the newline.",
- ),
- "C0304": (
- "Final newline missing",
- "missing-final-newline",
- "Used when the last line in a file is missing a newline.",
- ),
- "C0305": (
- "Trailing newlines",
- "trailing-newlines",
- "Used when there are trailing blank lines in a file.",
- ),
- "W0311": (
- "Bad indentation. Found %s %s, expected %s",
- "bad-indentation",
- "Used when an unexpected number of indentation's tabulations or "
- "spaces has been found.",
- ),
- "C0330": ("Wrong %s indentation%s%s.\n%s%s", "bad-continuation", "TODO"),
- "W0312": (
- "Found indentation with %ss instead of %ss",
- "mixed-indentation",
- "Used when there are some mixed tabs and spaces in a module.",
- ),
- "W0301": (
- "Unnecessary semicolon", # was W0106
- "unnecessary-semicolon",
- 'Used when a statement is ended by a semi-colon (";"), which '
- "isn't necessary (that's python, not C ;).",
- ),
- "C0321": (
- "More than one statement on a single line",
- "multiple-statements",
- "Used when more than on statement are found on the same line.",
- {"scope": WarningScope.NODE},
- ),
- "C0325": (
- "Unnecessary parens after %r keyword",
- "superfluous-parens",
- "Used when a single item in parentheses follows an if, for, or "
- "other keyword.",
- ),
- "C0326": (
- "%s space %s %s %s\n%s",
- "bad-whitespace",
- (
- "Used when a wrong number of spaces is used around an operator, "
- "bracket or block opener."
- ),
- {
- "old_names": [
- ("C0323", "no-space-after-operator"),
- ("C0324", "no-space-after-comma"),
- ("C0322", "no-space-before-operator"),
- ]
- },
- ),
- "C0327": (
- "Mixed line endings LF and CRLF",
- "mixed-line-endings",
- "Used when there are mixed (LF and CRLF) newline signs in a file.",
- ),
- "C0328": (
- "Unexpected line ending format. There is '%s' while it should be '%s'.",
- "unexpected-line-ending-format",
- "Used when there is different newline than expected.",
- ),
-}
-
-
-def _underline_token(token):
- length = token[3][1] - token[2][1]
- offset = token[2][1]
- referenced_line = token[4]
- # If the referenced line does not end with a newline char, fix it
- if referenced_line[-1] != "\n":
- referenced_line += "\n"
- return referenced_line + (" " * offset) + ("^" * length)
-
-
-def _column_distance(token1, token2):
- if token1 == token2:
- return 0
- if token2[3] < token1[3]:
- token1, token2 = token2, token1
- if token1[3][0] != token2[2][0]:
- return None
- return token2[2][1] - token1[3][1]
-
-
-def _last_token_on_line_is(tokens, line_end, token):
- return (
- line_end > 0
- and tokens.token(line_end - 1) == token
- or line_end > 1
- and tokens.token(line_end - 2) == token
- and tokens.type(line_end - 1) == tokenize.COMMENT
- )
-
-
-def _token_followed_by_eol(tokens, position):
- return (
- tokens.type(position + 1) == tokenize.NL
- or tokens.type(position + 1) == tokenize.COMMENT
- and tokens.type(position + 2) == tokenize.NL
- )
-
-
-def _get_indent_string(line):
- """Return the indention string of the given line."""
- result = ""
- for char in line:
- if char in " \t":
- result += char
- else:
- break
- return result
-
-
-def _get_indent_length(line):
- """Return the length of the indentation on the given token's line."""
- result = 0
- for char in line:
- if char == " ":
- result += 1
- elif char == "\t":
- result += _TAB_LENGTH
- else:
- break
- return result
-
-
-def _get_indent_hint_line(bar_positions, bad_position):
- """Return a line with |s for each of the positions in the given lists."""
- if not bar_positions:
- return "", ""
-
- bar_positions = [_get_indent_length(indent) for indent in bar_positions]
- bad_position = _get_indent_length(bad_position)
- delta_message = ""
- markers = [(pos, "|") for pos in bar_positions]
- if len(markers) == 1:
- # if we have only one marker we'll provide an extra hint on how to fix
- expected_position = markers[0][0]
- delta = abs(expected_position - bad_position)
- direction = "add" if expected_position > bad_position else "remove"
- delta_message = _CONTINUATION_HINT_MESSAGE % (
- direction,
- delta,
- "s" if delta > 1 else "",
- )
- markers.append((bad_position, "^"))
- markers.sort()
- line = [" "] * (markers[-1][0] + 1)
- for position, marker in markers:
- line[position] = marker
- return "".join(line), delta_message
-
-
-class _ContinuedIndent:
- __slots__ = (
- "valid_outdent_strings",
- "valid_continuation_strings",
- "context_type",
- "token",
- "position",
- )
-
- def __init__(
- self,
- context_type,
- token,
- position,
- valid_outdent_strings,
- valid_continuation_strings,
- ):
- self.valid_outdent_strings = valid_outdent_strings
- self.valid_continuation_strings = valid_continuation_strings
- self.context_type = context_type
- self.position = position
- self.token = token
-
-
-# The contexts for hanging indents.
-# A hanging indented dictionary value after :
-HANGING_DICT_VALUE = "dict-value"
-# Hanging indentation in an expression.
-HANGING = "hanging"
-# Hanging indentation in a block header.
-HANGING_BLOCK = "hanging-block"
-# Continued indentation inside an expression.
-CONTINUED = "continued"
-# Continued indentation in a block header.
-CONTINUED_BLOCK = "continued-block"
-
-SINGLE_LINE = "single"
-WITH_BODY = "multi"
-
-_CONTINUATION_MSG_PARTS = {
- HANGING_DICT_VALUE: ("hanging", " in dict value"),
- HANGING: ("hanging", ""),
- HANGING_BLOCK: ("hanging", " before block"),
- CONTINUED: ("continued", ""),
- CONTINUED_BLOCK: ("continued", " before block"),
-}
-
-_CONTINUATION_HINT_MESSAGE = " (%s %d space%s)" # Ex: (remove 2 spaces)
-
-
-def _Indentations(*args):
- """Valid indentation strings for a continued line."""
- return {a: None for a in args}
-
-
-def _BeforeBlockIndentations(single, with_body):
- """Valid alternative indentation strings for continued lines before blocks.
-
- :param int single: Valid indentation string for statements on a single logical line.
- :param int with_body: Valid indentation string for statements on several lines.
-
- :returns: A dictionary mapping indent offsets to a string representing
- whether the indent if for a line or block.
- :rtype: dict
- """
- return {single: SINGLE_LINE, with_body: WITH_BODY}
-
-
-class TokenWrapper:
- """A wrapper for readable access to token information."""
-
- def __init__(self, tokens):
- self._tokens = tokens
-
- def token(self, idx):
- return self._tokens[idx][1]
-
- def type(self, idx):
- return self._tokens[idx][0]
-
- def start_line(self, idx):
- return self._tokens[idx][2][0]
-
- def start_col(self, idx):
- return self._tokens[idx][2][1]
-
- def line(self, idx):
- return self._tokens[idx][4]
-
- def line_indent(self, idx):
- """Get the string of TABs and Spaces used for indentation of the line of this token"""
- return _get_indent_string(self.line(idx))
-
- def token_indent(self, idx):
- """Get an indentation string for hanging indentation, consisting of the line-indent plus
- a number of spaces to fill up to the column of this token.
-
- e.g. the token indent for foo
- in "<TAB><TAB>print(foo)"
- is "<TAB><TAB> "
- """
- line_indent = self.line_indent(idx)
- return line_indent + " " * (self.start_col(idx) - len(line_indent))
-
-
-class ContinuedLineState:
- """Tracker for continued indentation inside a logical line."""
-
- def __init__(self, tokens, config):
- self._line_start = -1
- self._cont_stack = []
- self._is_block_opener = False
- self.retained_warnings = []
- self._config = config
- self._tokens = TokenWrapper(tokens)
-
- @property
- def has_content(self):
- return bool(self._cont_stack)
-
- @property
- def _block_indent_string(self):
- return self._config.indent_string.replace("\\t", "\t")
-
- @property
- def _continuation_string(self):
- return self._block_indent_string[0] * self._config.indent_after_paren
-
- @property
- def _continuation_size(self):
- return self._config.indent_after_paren
-
- def handle_line_start(self, pos):
- """Record the first non-junk token at the start of a line."""
- if self._line_start > -1:
- return
-
- check_token_position = pos
- if self._tokens.token(pos) == _ASYNC_TOKEN:
- check_token_position += 1
- self._is_block_opener = (
- self._tokens.token(check_token_position) in _CONTINUATION_BLOCK_OPENERS
- )
- self._line_start = pos
-
- def next_physical_line(self):
- """Prepares the tracker for a new physical line (NL)."""
- self._line_start = -1
- self._is_block_opener = False
-
- def next_logical_line(self):
- """Prepares the tracker for a new logical line (NEWLINE).
-
- A new logical line only starts with block indentation.
- """
- self.next_physical_line()
- self.retained_warnings = []
- self._cont_stack = []
-
- def add_block_warning(self, token_position, state, valid_indentations):
- self.retained_warnings.append((token_position, state, valid_indentations))
-
- def get_valid_indentations(self, idx):
- """Returns the valid offsets for the token at the given position."""
- # The closing brace on a dict or the 'for' in a dict comprehension may
- # reset two indent levels because the dict value is ended implicitly
- stack_top = -1
- if (
- self._tokens.token(idx) in ("}", "for")
- and self._cont_stack[-1].token == ":"
- ):
- stack_top = -2
- indent = self._cont_stack[stack_top]
- if self._tokens.token(idx) in _CLOSING_BRACKETS:
- valid_indentations = indent.valid_outdent_strings
- else:
- valid_indentations = indent.valid_continuation_strings
- return indent, valid_indentations.copy()
-
- def _hanging_indent_after_bracket(self, bracket, position):
- """Extracts indentation information for a hanging indent
-
- Case of hanging indent after a bracket (including parenthesis)
-
- :param str bracket: bracket in question
- :param int position: Position of bracket in self._tokens
-
- :returns: the state and valid positions for hanging indentation
- :rtype: _ContinuedIndent
- """
- indentation = self._tokens.line_indent(position)
- if (
- self._is_block_opener
- and self._continuation_string == self._block_indent_string
- ):
- return _ContinuedIndent(
- HANGING_BLOCK,
- bracket,
- position,
- _Indentations(indentation + self._continuation_string, indentation),
- _BeforeBlockIndentations(
- indentation + self._continuation_string,
- indentation + self._continuation_string * 2,
- ),
- )
- if bracket == ":":
- # If the dict key was on the same line as the open brace, the new
- # correct indent should be relative to the key instead of the
- # current indent level
- paren_align = self._cont_stack[-1].valid_outdent_strings
- next_align = self._cont_stack[-1].valid_continuation_strings.copy()
- next_align_keys = list(next_align.keys())
- next_align[next_align_keys[0] + self._continuation_string] = True
- # Note that the continuation of
- # d = {
- # 'a': 'b'
- # 'c'
- # }
- # is handled by the special-casing for hanging continued string indents.
- return _ContinuedIndent(
- HANGING_DICT_VALUE, bracket, position, paren_align, next_align
- )
- return _ContinuedIndent(
- HANGING,
- bracket,
- position,
- _Indentations(indentation, indentation + self._continuation_string),
- _Indentations(indentation + self._continuation_string),
- )
-
- def _continuation_inside_bracket(self, bracket, position):
- """Extracts indentation information for a continued indent."""
- indentation = self._tokens.line_indent(position)
- token_indent = self._tokens.token_indent(position)
- next_token_indent = self._tokens.token_indent(position + 1)
- if (
- self._is_block_opener
- and next_token_indent == indentation + self._block_indent_string
- ):
- return _ContinuedIndent(
- CONTINUED_BLOCK,
- bracket,
- position,
- _Indentations(token_indent),
- _BeforeBlockIndentations(
- next_token_indent, next_token_indent + self._continuation_string
- ),
- )
- return _ContinuedIndent(
- CONTINUED,
- bracket,
- position,
- _Indentations(token_indent, next_token_indent),
- _Indentations(next_token_indent),
- )
-
- def pop_token(self):
- self._cont_stack.pop()
-
- def push_token(self, token, position):
- """Pushes a new token for continued indentation on the stack.
-
- Tokens that can modify continued indentation offsets are:
- * opening brackets
- * 'lambda'
- * : inside dictionaries
-
- push_token relies on the caller to filter out those
- interesting tokens.
-
- :param int token: The concrete token
- :param int position: The position of the token in the stream.
- """
- if _token_followed_by_eol(self._tokens, position):
- self._cont_stack.append(self._hanging_indent_after_bracket(token, position))
- else:
- self._cont_stack.append(self._continuation_inside_bracket(token, position))
-
-
-class FormatChecker(BaseTokenChecker):
- """checks for :
- * unauthorized constructions
- * strict indentation
- * line length
- """
-
- __implements__ = (ITokenChecker, IAstroidChecker, IRawChecker)
-
- # configuration section name
- name = "format"
- # messages
- msgs = MSGS
- # configuration options
- # for available dict keys/values see the optik parser 'add_option' method
- options = (
- (
- "max-line-length",
- {
- "default": 100,
- "type": "int",
- "metavar": "<int>",
- "help": "Maximum number of characters on a single line.",
- },
- ),
- (
- "ignore-long-lines",
- {
- "type": "regexp",
- "metavar": "<regexp>",
- "default": r"^\s*(# )?<?https?://\S+>?$",
- "help": (
- "Regexp for a line that is allowed to be longer than " "the limit."
- ),
- },
- ),
- (
- "single-line-if-stmt",
- {
- "default": False,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": (
- "Allow the body of an if to be on the same "
- "line as the test if there is no else."
- ),
- },
- ),
- (
- "single-line-class-stmt",
- {
- "default": False,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": (
- "Allow the body of a class to be on the same "
- "line as the declaration if body contains "
- "single statement."
- ),
- },
- ),
- (
- "no-space-check",
- {
- "default": ",".join(_DEFAULT_NO_SPACE_CHECK_CHOICES),
- "metavar": ",".join(_NO_SPACE_CHECK_CHOICES),
- "type": "multiple_choice",
- "choices": _NO_SPACE_CHECK_CHOICES,
- "help": (
- "List of optional constructs for which whitespace "
- "checking is disabled. "
- "`" + _DICT_SEPARATOR + "` is used to allow tabulation "
- "in dicts, etc.: {1 : 1,\\n222: 2}. "
- "`" + _TRAILING_COMMA + "` allows a space between comma "
- "and closing bracket: (a, ). "
- "`" + _EMPTY_LINE + "` allows space-only lines."
- ),
- },
- ),
- (
- "max-module-lines",
- {
- "default": 1000,
- "type": "int",
- "metavar": "<int>",
- "help": "Maximum number of lines in a module.",
- },
- ),
- (
- "indent-string",
- {
- "default": " ",
- "type": "non_empty_string",
- "metavar": "<string>",
- "help": "String used as indentation unit. This is usually "
- '" " (4 spaces) or "\\t" (1 tab).',
- },
- ),
- (
- "indent-after-paren",
- {
- "type": "int",
- "metavar": "<int>",
- "default": 4,
- "help": "Number of spaces of indent required inside a hanging "
- "or continued line.",
- },
- ),
- (
- "expected-line-ending-format",
- {
- "type": "choice",
- "metavar": "<empty or LF or CRLF>",
- "default": "",
- "choices": ["", "LF", "CRLF"],
- "help": (
- "Expected format of line ending, "
- "e.g. empty (any line ending), LF or CRLF."
- ),
- },
- ),
- )
-
- def __init__(self, linter=None):
- BaseTokenChecker.__init__(self, linter)
- self._lines = None
- self._visited_lines = None
- self._bracket_stack = [None]
-
- def _pop_token(self):
- self._bracket_stack.pop()
- self._current_line.pop_token()
-
- def _push_token(self, token, idx):
- self._bracket_stack.append(token)
- self._current_line.push_token(token, idx)
-
- def new_line(self, tokens, line_end, line_start):
- """a new line has been encountered, process it if necessary"""
- if _last_token_on_line_is(tokens, line_end, ";"):
- self.add_message("unnecessary-semicolon", line=tokens.start_line(line_end))
-
- line_num = tokens.start_line(line_start)
- line = tokens.line(line_start)
- if tokens.type(line_start) not in _JUNK_TOKENS:
- self._lines[line_num] = line.split("\n")[0]
- self.check_lines(line, line_num)
-
- def process_module(self, _module):
- self._keywords_with_parens = set()
-
- def _check_keyword_parentheses(self, tokens, start):
- """Check that there are not unnecessary parens after a keyword.
-
- Parens are unnecessary if there is exactly one balanced outer pair on a
- line, and it is followed by a colon, and contains no commas (i.e. is not a
- tuple).
-
- Args:
- tokens: list of Tokens; the entire list of Tokens.
- start: int; the position of the keyword in the token list.
- """
- # If the next token is not a paren, we're fine.
- if self._inside_brackets(":") and tokens[start][1] == "for":
- self._pop_token()
- if tokens[start + 1][1] != "(":
- return
-
- found_and_or = False
- depth = 0
- keyword_token = str(tokens[start][1])
- line_num = tokens[start][2][0]
-
- for i in range(start, len(tokens) - 1):
- token = tokens[i]
-
- # If we hit a newline, then assume any parens were for continuation.
- if token[0] == tokenize.NL:
- return
-
- if token[1] == "(":
- depth += 1
- elif token[1] == ")":
- depth -= 1
- if depth:
- continue
- # ')' can't happen after if (foo), since it would be a syntax error.
- if tokens[i + 1][1] in (":", ")", "]", "}", "in") or tokens[i + 1][
- 0
- ] in (tokenize.NEWLINE, tokenize.ENDMARKER, tokenize.COMMENT):
- # The empty tuple () is always accepted.
- if i == start + 2:
- return
- if keyword_token == "not":
- if not found_and_or:
- self.add_message(
- "superfluous-parens", line=line_num, args=keyword_token
- )
- elif keyword_token in ("return", "yield"):
- self.add_message(
- "superfluous-parens", line=line_num, args=keyword_token
- )
- elif keyword_token not in self._keywords_with_parens:
- if not found_and_or:
- self.add_message(
- "superfluous-parens", line=line_num, args=keyword_token
- )
- return
- elif depth == 1:
- # This is a tuple, which is always acceptable.
- if token[1] == ",":
- return
- # 'and' and 'or' are the only boolean operators with lower precedence
- # than 'not', so parens are only required when they are found.
- if token[1] in ("and", "or"):
- found_and_or = True
- # A yield inside an expression must always be in parentheses,
- # quit early without error.
- elif token[1] == "yield":
- return
- # A generator expression always has a 'for' token in it, and
- # the 'for' token is only legal inside parens when it is in a
- # generator expression. The parens are necessary here, so bail
- # without an error.
- elif token[1] == "for":
- return
-
- def _opening_bracket(self, tokens, i):
- self._push_token(tokens[i][1], i)
- # Special case: ignore slices
- if tokens[i][1] == "[" and tokens[i + 1][1] == ":":
- return
-
- if i > 0 and (
- tokens[i - 1][0] == tokenize.NAME
- and not (keyword.iskeyword(tokens[i - 1][1]))
- or tokens[i - 1][1] in _CLOSING_BRACKETS
- ):
- self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT))
- else:
- self._check_space(tokens, i, (_IGNORE, _MUST_NOT))
-
- def _closing_bracket(self, tokens, i):
- if self._inside_brackets(":"):
- self._pop_token()
- self._pop_token()
- # Special case: ignore slices
- if tokens[i - 1][1] == ":" and tokens[i][1] == "]":
- return
- policy_before = _MUST_NOT
- if tokens[i][1] in _CLOSING_BRACKETS and tokens[i - 1][1] == ",":
- if _TRAILING_COMMA in self.config.no_space_check:
- policy_before = _IGNORE
-
- self._check_space(tokens, i, (policy_before, _IGNORE))
-
- def _has_valid_type_annotation(self, tokens, i):
- """Extended check of PEP-484 type hint presence"""
- if not self._inside_brackets("("):
- return False
- # token_info
- # type string start end line
- # 0 1 2 3 4
- bracket_level = 0
- for token in tokens[i - 1 :: -1]:
- if token[1] == ":":
- return True
- if token[1] == "(":
- return False
- if token[1] == "]":
- bracket_level += 1
- elif token[1] == "[":
- bracket_level -= 1
- elif token[1] == ",":
- if not bracket_level:
- return False
- elif token[1] in (".", "..."):
- continue
- elif token[0] not in (tokenize.NAME, tokenize.STRING, tokenize.NL):
- return False
- return False
-
- def _check_equals_spacing(self, tokens, i):
- """Check the spacing of a single equals sign."""
- if self._has_valid_type_annotation(tokens, i):
- self._check_space(tokens, i, (_MUST, _MUST))
- elif self._inside_brackets("(") or self._inside_brackets("lambda"):
- self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT))
- else:
- self._check_space(tokens, i, (_MUST, _MUST))
-
- def _open_lambda(self, tokens, i): # pylint:disable=unused-argument
- self._push_token("lambda", i)
-
- def _handle_colon(self, tokens, i):
- # Special case: ignore slices
- if self._inside_brackets("["):
- return
- if self._inside_brackets("{") and _DICT_SEPARATOR in self.config.no_space_check:
- policy = (_IGNORE, _IGNORE)
- else:
- policy = (_MUST_NOT, _MUST)
- self._check_space(tokens, i, policy)
-
- if self._inside_brackets("lambda"):
- self._pop_token()
- elif self._inside_brackets("{"):
- self._push_token(":", i)
-
- def _handle_comma(self, tokens, i):
- # Only require a following whitespace if this is
- # not a hanging comma before a closing bracket.
- if tokens[i + 1][1] in _CLOSING_BRACKETS:
- self._check_space(tokens, i, (_MUST_NOT, _IGNORE))
- else:
- self._check_space(tokens, i, (_MUST_NOT, _MUST))
- if self._inside_brackets(":"):
- self._pop_token()
-
- def _check_surrounded_by_space(self, tokens, i):
- """Check that a binary operator is surrounded by exactly one space."""
- self._check_space(tokens, i, (_MUST, _MUST))
-
- def _check_space(self, tokens, i, policies):
- def _policy_string(policy):
- if policy == _MUST:
- return "Exactly one", "required"
- return "No", "allowed"
-
- def _name_construct(token):
- if token[1] == ",":
- return "comma"
- if token[1] == ":":
- return ":"
- if token[1] in "()[]{}":
- return "bracket"
- if token[1] in ("<", ">", "<=", ">=", "!=", "=="):
- return "comparison"
- if self._inside_brackets("("):
- return "keyword argument assignment"
- return "assignment"
-
- good_space = [True, True]
- token = tokens[i]
- pairs = [(tokens[i - 1], token), (token, tokens[i + 1])]
-
- for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)):
- if token_pair[other_idx][0] in _EOL or policy == _IGNORE:
- continue
-
- distance = _column_distance(*token_pair)
- if distance is None:
- continue
- good_space[other_idx] = (policy == _MUST and distance == 1) or (
- policy == _MUST_NOT and distance == 0
- )
-
- warnings = []
- if not any(good_space) and policies[0] == policies[1]:
- warnings.append((policies[0], "around"))
- else:
- for ok, policy, position in zip(good_space, policies, ("before", "after")):
- if not ok:
- warnings.append((policy, position))
- for policy, position in warnings:
- construct = _name_construct(token)
- count, state = _policy_string(policy)
- self.add_message(
- "bad-whitespace",
- line=token[2][0],
- args=(count, state, position, construct, _underline_token(token)),
- col_offset=token[2][1],
- )
-
- def _inside_brackets(self, left):
- return self._bracket_stack[-1] == left
-
- def _prepare_token_dispatcher(self):
- raw = [
- (_KEYWORD_TOKENS, self._check_keyword_parentheses),
- (_OPENING_BRACKETS, self._opening_bracket),
- (_CLOSING_BRACKETS, self._closing_bracket),
- (["="], self._check_equals_spacing),
- (_SPACED_OPERATORS, self._check_surrounded_by_space),
- ([","], self._handle_comma),
- ([":"], self._handle_colon),
- (["lambda"], self._open_lambda),
- ]
-
- dispatch = {}
- for tokens, handler in raw:
- for token in tokens:
- dispatch[token] = handler
- return dispatch
-
- def process_tokens(self, tokens):
- """process tokens and search for :
-
- _ non strict indentation (i.e. not always using the <indent> parameter as
- indent unit)
- _ too long lines (i.e. longer than <max_chars>)
- _ optionally bad construct (if given, bad_construct must be a compiled
- regular expression).
- """
- self._bracket_stack = [None]
- indents = [0]
- check_equal = False
- line_num = 0
- self._lines = {}
- self._visited_lines = {}
- token_handlers = self._prepare_token_dispatcher()
- self._last_line_ending = None
- last_blank_line_num = 0
-
- self._current_line = ContinuedLineState(tokens, self.config)
- for idx, (tok_type, token, start, _, line) in enumerate(tokens):
- if start[0] != line_num:
- line_num = start[0]
- # A tokenizer oddity: if an indented line contains a multi-line
- # docstring, the line member of the INDENT token does not contain
- # the full line; therefore we check the next token on the line.
- if tok_type == tokenize.INDENT:
- self.new_line(TokenWrapper(tokens), idx - 1, idx + 1)
- else:
- self.new_line(TokenWrapper(tokens), idx - 1, idx)
-
- if tok_type == tokenize.NEWLINE:
- # a program statement, or ENDMARKER, will eventually follow,
- # after some (possibly empty) run of tokens of the form
- # (NL | COMMENT)* (INDENT | DEDENT+)?
- # If an INDENT appears, setting check_equal is wrong, and will
- # be undone when we see the INDENT.
- check_equal = True
- self._process_retained_warnings(TokenWrapper(tokens), idx)
- self._current_line.next_logical_line()
- self._check_line_ending(token, line_num)
- elif tok_type == tokenize.INDENT:
- check_equal = False
- self.check_indent_level(token, indents[-1] + 1, line_num)
- indents.append(indents[-1] + 1)
- elif tok_type == tokenize.DEDENT:
- # there's nothing we need to check here! what's important is
- # that when the run of DEDENTs ends, the indentation of the
- # program statement (or ENDMARKER) that triggered the run is
- # equal to what's left at the top of the indents stack
- check_equal = True
- if len(indents) > 1:
- del indents[-1]
- elif tok_type == tokenize.NL:
- if not line.strip("\r\n"):
- last_blank_line_num = line_num
- self._check_continued_indentation(TokenWrapper(tokens), idx + 1)
- self._current_line.next_physical_line()
- elif tok_type not in (tokenize.COMMENT, tokenize.ENCODING):
- self._current_line.handle_line_start(idx)
- # This is the first concrete token following a NEWLINE, so it
- # must be the first token of the next program statement, or an
- # ENDMARKER; the "line" argument exposes the leading whitespace
- # for this statement; in the case of ENDMARKER, line is an empty
- # string, so will properly match the empty string with which the
- # "indents" stack was seeded
- if check_equal:
- check_equal = False
- self.check_indent_level(line, indents[-1], line_num)
-
- if tok_type == tokenize.NUMBER and token.endswith("l"):
- self.add_message("lowercase-l-suffix", line=line_num)
-
- try:
- handler = token_handlers[token]
- except KeyError:
- pass
- else:
- handler(tokens, idx)
-
- line_num -= 1 # to be ok with "wc -l"
- if line_num > self.config.max_module_lines:
- # Get the line where the too-many-lines (or its message id)
- # was disabled or default to 1.
- message_definition = self.linter.msgs_store.get_message_definitions(
- "too-many-lines"
- )[0]
- names = (message_definition.msgid, "too-many-lines")
- line = next(filter(None, map(self.linter._pragma_lineno.get, names)), 1)
- self.add_message(
- "too-many-lines",
- args=(line_num, self.config.max_module_lines),
- line=line,
- )
-
- # See if there are any trailing lines. Do not complain about empty
- # files like __init__.py markers.
- if line_num == last_blank_line_num and line_num > 0:
- self.add_message("trailing-newlines", line=line_num)
-
- def _check_line_ending(self, line_ending, line_num):
- # check if line endings are mixed
- if self._last_line_ending is not None:
- # line_ending == "" indicates a synthetic newline added at
- # the end of a file that does not, in fact, end with a
- # newline.
- if line_ending and line_ending != self._last_line_ending:
- self.add_message("mixed-line-endings", line=line_num)
-
- self._last_line_ending = line_ending
-
- # check if line ending is as expected
- expected = self.config.expected_line_ending_format
- if expected:
- # reduce multiple \n\n\n\n to one \n
- line_ending = reduce(lambda x, y: x + y if x != y else x, line_ending, "")
- line_ending = "LF" if line_ending == "\n" else "CRLF"
- if line_ending != expected:
- self.add_message(
- "unexpected-line-ending-format",
- args=(line_ending, expected),
- line=line_num,
- )
-
- def _process_retained_warnings(self, tokens, current_pos):
- single_line_block_stmt = not _last_token_on_line_is(tokens, current_pos, ":")
-
- for indent_pos, state, indentations in self._current_line.retained_warnings:
- block_type = indentations[tokens.token_indent(indent_pos)]
- hints = {k: v for k, v in indentations.items() if v != block_type}
- if single_line_block_stmt and block_type == WITH_BODY:
- self._add_continuation_message(state, hints, tokens, indent_pos)
- elif not single_line_block_stmt and block_type == SINGLE_LINE:
- self._add_continuation_message(state, hints, tokens, indent_pos)
-
- def _check_continued_indentation(self, tokens, next_idx):
- def same_token_around_nl(token_type):
- return (
- tokens.type(next_idx) == token_type
- and tokens.type(next_idx - 2) == token_type
- )
-
- # Do not issue any warnings if the next line is empty.
- if not self._current_line.has_content or tokens.type(next_idx) == tokenize.NL:
- return
-
- state, valid_indentations = self._current_line.get_valid_indentations(next_idx)
- # Special handling for hanging comments and strings. If the last line ended
- # with a comment (string) and the new line contains only a comment, the line
- # may also be indented to the start of the previous token.
- if same_token_around_nl(tokenize.COMMENT) or same_token_around_nl(
- tokenize.STRING
- ):
- valid_indentations[tokens.token_indent(next_idx - 2)] = True
-
- # We can only decide if the indentation of a continued line before opening
- # a new block is valid once we know of the body of the block is on the
- # same line as the block opener. Since the token processing is single-pass,
- # emitting those warnings is delayed until the block opener is processed.
- if (
- state.context_type in (HANGING_BLOCK, CONTINUED_BLOCK)
- and tokens.token_indent(next_idx) in valid_indentations
- ):
- self._current_line.add_block_warning(next_idx, state, valid_indentations)
- elif tokens.token_indent(next_idx) not in valid_indentations:
- length_indentation = len(tokens.token_indent(next_idx))
- if not any(
- length_indentation == 2 * len(indentation)
- for indentation in valid_indentations
- ):
- self._add_continuation_message(
- state, valid_indentations, tokens, next_idx
- )
-
- def _add_continuation_message(self, state, indentations, tokens, position):
- readable_type, readable_position = _CONTINUATION_MSG_PARTS[state.context_type]
- hint_line, delta_message = _get_indent_hint_line(
- indentations, tokens.token_indent(position)
- )
- self.add_message(
- "bad-continuation",
- line=tokens.start_line(position),
- args=(
- readable_type,
- readable_position,
- delta_message,
- tokens.line(position),
- hint_line,
- ),
- )
-
- @check_messages("multiple-statements")
- def visit_default(self, node):
- """check the node line number and check it if not yet done"""
- if not node.is_statement:
- return
- if not node.root().pure_python:
- return
- prev_sibl = node.previous_sibling()
- if prev_sibl is not None:
- prev_line = prev_sibl.fromlineno
- else:
- # The line on which a finally: occurs in a try/finally
- # is not directly represented in the AST. We infer it
- # by taking the last line of the body and adding 1, which
- # should be the line of finally:
- if (
- isinstance(node.parent, nodes.TryFinally)
- and node in node.parent.finalbody
- ):
- prev_line = node.parent.body[0].tolineno + 1
- else:
- prev_line = node.parent.statement().fromlineno
- line = node.fromlineno
- assert line, node
- if prev_line == line and self._visited_lines.get(line) != 2:
- self._check_multi_statement_line(node, line)
- return
- if line in self._visited_lines:
- return
- try:
- tolineno = node.blockstart_tolineno
- except AttributeError:
- tolineno = node.tolineno
- assert tolineno, node
- lines = []
- for line in range(line, tolineno + 1):
- self._visited_lines[line] = 1
- try:
- lines.append(self._lines[line].rstrip())
- except KeyError:
- lines.append("")
-
- def _check_multi_statement_line(self, node, line):
- """Check for lines containing multiple statements."""
- # Do not warn about multiple nested context managers
- # in with statements.
- if isinstance(node, nodes.With):
- return
- # For try... except... finally..., the two nodes
- # appear to be on the same line due to how the AST is built.
- if isinstance(node, nodes.TryExcept) and isinstance(
- node.parent, nodes.TryFinally
- ):
- return
- if (
- isinstance(node.parent, nodes.If)
- and not node.parent.orelse
- and self.config.single_line_if_stmt
- ):
- return
- if (
- isinstance(node.parent, nodes.ClassDef)
- and len(node.parent.body) == 1
- and self.config.single_line_class_stmt
- ):
- return
- self.add_message("multiple-statements", node=node)
- self._visited_lines[line] = 2
-
- def check_lines(self, lines, i):
- """check lines have less than a maximum number of characters
- """
- max_chars = self.config.max_line_length
- ignore_long_line = self.config.ignore_long_lines
-
- def check_line(line, i):
- if not line.endswith("\n"):
- self.add_message("missing-final-newline", line=i)
- else:
- # exclude \f (formfeed) from the rstrip
- stripped_line = line.rstrip("\t\n\r\v ")
- if not stripped_line and _EMPTY_LINE in self.config.no_space_check:
- # allow empty lines
- pass
- elif line[len(stripped_line) :] not in ("\n", "\r\n"):
- self.add_message(
- "trailing-whitespace", line=i, col_offset=len(stripped_line)
- )
- # Don't count excess whitespace in the line length.
- line = stripped_line
- mobj = OPTION_RGX.search(line)
- if mobj and "=" in line:
- front_of_equal, _, back_of_equal = mobj.group(1).partition("=")
- if front_of_equal.strip() == "disable":
- if "line-too-long" in {
- _msg_id.strip() for _msg_id in back_of_equal.split(",")
- }:
- return None
- line = line.rsplit("#", 1)[0].rstrip()
-
- if len(line) > max_chars and not ignore_long_line.search(line):
- self.add_message("line-too-long", line=i, args=(len(line), max_chars))
- return i + 1
-
- unsplit_ends = {
- "\v",
- "\x0b",
- "\f",
- "\x0c",
- "\x1c",
- "\x1d",
- "\x1e",
- "\x85",
- "\u2028",
- "\u2029",
- }
- unsplit = []
- for line in lines.splitlines(True):
- if line[-1] in unsplit_ends:
- unsplit.append(line)
- continue
-
- if unsplit:
- unsplit.append(line)
- line = "".join(unsplit)
- unsplit = []
-
- i = check_line(line, i)
- if i is None:
- break
-
- if unsplit:
- check_line("".join(unsplit), i)
-
- def check_indent_level(self, string, expected, line_num):
- """return the indent level of the string
- """
- indent = self.config.indent_string
- if indent == "\\t": # \t is not interpreted in the configuration file
- indent = "\t"
- level = 0
- unit_size = len(indent)
- while string[:unit_size] == indent:
- string = string[unit_size:]
- level += 1
- suppl = ""
- while string and string[0] in " \t":
- if string[0] != indent[0]:
- if string[0] == "\t":
- args = ("tab", "space")
- else:
- args = ("space", "tab")
- self.add_message("mixed-indentation", args=args, line=line_num)
- return level
- suppl += string[0]
- string = string[1:]
- if level != expected or suppl:
- i_type = "spaces"
- if indent[0] == "\t":
- i_type = "tabs"
- self.add_message(
- "bad-indentation",
- line=line_num,
- args=(level * unit_size + len(suppl), i_type, expected * unit_size),
- )
- return None
-
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(FormatChecker(linter))
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))
diff --git a/venv/Lib/site-packages/pylint/checkers/logging.py b/venv/Lib/site-packages/pylint/checkers/logging.py
deleted file mode 100644
index 5ad0e76..0000000
--- a/venv/Lib/site-packages/pylint/checkers/logging.py
+++ /dev/null
@@ -1,384 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2009, 2012, 2014 Google, Inc.
-# Copyright (c) 2012 Mike Bryant <leachim@leachim.info>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Chris Murray <chris@chrismurray.scot>
-# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2017 guillaume2 <guillaume.peillex@gmail.col>
-# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
-# Copyright (c) 2018 Mariatta Wijaya <mariatta@python.org>
-# 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
-
-"""checker for use of Python logging
-"""
-import string
-
-import astroid
-
-from pylint import checkers, interfaces
-from pylint.checkers import utils
-from pylint.checkers.utils import check_messages
-
-MSGS = {
- "W1201": (
- "Specify string format arguments as logging function parameters",
- "logging-not-lazy",
- "Used when a logging statement has a call form of "
- '"logging.<logging method>(format_string % (format_args...))". '
- "Such calls should leave string interpolation to the logging "
- "method itself and be written "
- '"logging.<logging method>(format_string, format_args...)" '
- "so that the program may avoid incurring the cost of the "
- "interpolation in those cases in which no message will be "
- "logged. For more, see "
- "http://www.python.org/dev/peps/pep-0282/.",
- ),
- "W1202": (
- "Use %s formatting in logging functions%s",
- "logging-format-interpolation",
- "Used when a logging statement has a call form of "
- '"logging.<logging method>(<string formatting>)".'
- " with invalid string formatting. "
- "Use another way for format the string instead.",
- ),
- "E1200": (
- "Unsupported logging format character %r (%#02x) at index %d",
- "logging-unsupported-format",
- "Used when an unsupported format character is used in a logging "
- "statement format string.",
- ),
- "E1201": (
- "Logging format string ends in middle of conversion specifier",
- "logging-format-truncated",
- "Used when a logging statement format string terminates before "
- "the end of a conversion specifier.",
- ),
- "E1205": (
- "Too many arguments for logging format string",
- "logging-too-many-args",
- "Used when a logging format string is given too many arguments.",
- ),
- "E1206": (
- "Not enough arguments for logging format string",
- "logging-too-few-args",
- "Used when a logging format string is given too few arguments.",
- ),
-}
-
-
-CHECKED_CONVENIENCE_FUNCTIONS = {
- "critical",
- "debug",
- "error",
- "exception",
- "fatal",
- "info",
- "warn",
- "warning",
-}
-
-
-def is_method_call(func, types=(), methods=()):
- """Determines if a BoundMethod node represents a method call.
-
- Args:
- func (astroid.BoundMethod): The BoundMethod AST node to check.
- types (Optional[String]): Optional sequence of caller type names to restrict check.
- methods (Optional[String]): Optional sequence of method names to restrict check.
-
- Returns:
- bool: true if the node represents a method call for the given type and
- method names, False otherwise.
- """
- return (
- isinstance(func, astroid.BoundMethod)
- and isinstance(func.bound, astroid.Instance)
- and (func.bound.name in types if types else True)
- and (func.name in methods if methods else True)
- )
-
-
-class LoggingChecker(checkers.BaseChecker):
- """Checks use of the logging module."""
-
- __implements__ = interfaces.IAstroidChecker
- name = "logging"
- msgs = MSGS
-
- options = (
- (
- "logging-modules",
- {
- "default": ("logging",),
- "type": "csv",
- "metavar": "<comma separated list>",
- "help": "Logging modules to check that the string format "
- "arguments are in logging function parameter format.",
- },
- ),
- (
- "logging-format-style",
- {
- "default": "old",
- "type": "choice",
- "metavar": "<old (%) or new ({) or fstr (f'')>",
- "choices": ["old", "new", "fstr"],
- "help": "Format style used to check logging format string. "
- "`old` means using % formatting, `new` is for `{}` formatting,"
- "and `fstr` is for f-strings.",
- },
- ),
- )
-
- def visit_module(self, node): # pylint: disable=unused-argument
- """Clears any state left in this checker from last module checked."""
- # The code being checked can just as easily "import logging as foo",
- # so it is necessary to process the imports and store in this field
- # what name the logging module is actually given.
- self._logging_names = set()
- logging_mods = self.config.logging_modules
-
- self._format_style = self.config.logging_format_style
- format_styles = {"old": "%", "new": "{", "fstr": "f-string"}
- format_style_help = ""
- if self._format_style == "old":
- format_style_help = " and pass the % parameters as arguments"
-
- self._format_style_args = (format_styles[self._format_style], format_style_help)
-
- self._logging_modules = set(logging_mods)
- self._from_imports = {}
- for logging_mod in logging_mods:
- parts = logging_mod.rsplit(".", 1)
- if len(parts) > 1:
- self._from_imports[parts[0]] = parts[1]
-
- def visit_importfrom(self, node):
- """Checks to see if a module uses a non-Python logging module."""
- try:
- logging_name = self._from_imports[node.modname]
- for module, as_name in node.names:
- if module == logging_name:
- self._logging_names.add(as_name or module)
- except KeyError:
- pass
-
- def visit_import(self, node):
- """Checks to see if this module uses Python's built-in logging."""
- for module, as_name in node.names:
- if module in self._logging_modules:
- self._logging_names.add(as_name or module)
-
- @check_messages(*MSGS)
- def visit_call(self, node):
- """Checks calls to logging methods."""
-
- def is_logging_name():
- return (
- isinstance(node.func, astroid.Attribute)
- and isinstance(node.func.expr, astroid.Name)
- and node.func.expr.name in self._logging_names
- )
-
- def is_logger_class():
- try:
- for inferred in node.func.infer():
- if isinstance(inferred, astroid.BoundMethod):
- parent = inferred._proxied.parent
- if isinstance(parent, astroid.ClassDef) and (
- parent.qname() == "logging.Logger"
- or any(
- ancestor.qname() == "logging.Logger"
- for ancestor in parent.ancestors()
- )
- ):
- return True, inferred._proxied.name
- except astroid.exceptions.InferenceError:
- pass
- return False, None
-
- if is_logging_name():
- name = node.func.attrname
- else:
- result, name = is_logger_class()
- if not result:
- return
- self._check_log_method(node, name)
-
- def _check_log_method(self, node, name):
- """Checks calls to logging.log(level, format, *format_args)."""
- if name == "log":
- if node.starargs or node.kwargs or len(node.args) < 2:
- # Either a malformed call, star args, or double-star args. Beyond
- # the scope of this checker.
- return
- format_pos = 1
- elif name in CHECKED_CONVENIENCE_FUNCTIONS:
- if node.starargs or node.kwargs or not node.args:
- # Either no args, star args, or double-star args. Beyond the
- # scope of this checker.
- return
- format_pos = 0
- else:
- return
-
- if isinstance(node.args[format_pos], astroid.BinOp):
- binop = node.args[format_pos]
- emit = binop.op == "%"
- if binop.op == "+":
- total_number_of_strings = sum(
- 1
- for operand in (binop.left, binop.right)
- if self._is_operand_literal_str(utils.safe_infer(operand))
- )
- emit = total_number_of_strings > 0
- if emit:
- self.add_message("logging-not-lazy", node=node)
- elif isinstance(node.args[format_pos], astroid.Call):
- self._check_call_func(node.args[format_pos])
- elif isinstance(node.args[format_pos], astroid.Const):
- self._check_format_string(node, format_pos)
- elif isinstance(
- node.args[format_pos], (astroid.FormattedValue, astroid.JoinedStr)
- ):
- if self._format_style != "fstr":
- self.add_message(
- "logging-format-interpolation",
- node=node,
- args=self._format_style_args,
- )
-
- @staticmethod
- def _is_operand_literal_str(operand):
- """
- Return True if the operand in argument is a literal string
- """
- return isinstance(operand, astroid.Const) and operand.name == "str"
-
- def _check_call_func(self, node):
- """Checks that function call is not format_string.format().
-
- Args:
- node (astroid.node_classes.Call):
- Call AST node to be checked.
- """
- func = utils.safe_infer(node.func)
- types = ("str", "unicode")
- methods = ("format",)
- if is_method_call(func, types, methods) and not is_complex_format_str(
- func.bound
- ):
- self.add_message(
- "logging-format-interpolation", node=node, args=self._format_style_args
- )
-
- def _check_format_string(self, node, format_arg):
- """Checks that format string tokens match the supplied arguments.
-
- Args:
- node (astroid.node_classes.NodeNG): AST node to be checked.
- format_arg (int): Index of the format string in the node arguments.
- """
- num_args = _count_supplied_tokens(node.args[format_arg + 1 :])
- if not num_args:
- # If no args were supplied the string is not interpolated and can contain
- # formatting characters - it's used verbatim. Don't check any further.
- return
-
- format_string = node.args[format_arg].value
- required_num_args = 0
- if isinstance(format_string, bytes):
- format_string = format_string.decode()
- if isinstance(format_string, str):
- try:
- if self._format_style == "old":
- keyword_args, required_num_args, _, _ = utils.parse_format_string(
- format_string
- )
- if keyword_args:
- # Keyword checking on logging strings is complicated by
- # special keywords - out of scope.
- return
- elif self._format_style == "new":
- keyword_arguments, implicit_pos_args, explicit_pos_args = utils.parse_format_method_string(
- format_string
- )
-
- keyword_args_cnt = len(
- set(k for k, l in keyword_arguments if not isinstance(k, int))
- )
- required_num_args = (
- keyword_args_cnt + implicit_pos_args + explicit_pos_args
- )
- else:
- self.add_message(
- "logging-format-interpolation",
- node=node,
- args=self._format_style_args,
- )
- except utils.UnsupportedFormatCharacter as ex:
- char = format_string[ex.index]
- self.add_message(
- "logging-unsupported-format",
- node=node,
- args=(char, ord(char), ex.index),
- )
- return
- except utils.IncompleteFormatString:
- self.add_message("logging-format-truncated", node=node)
- return
- if num_args > required_num_args:
- self.add_message("logging-too-many-args", node=node)
- elif num_args < required_num_args:
- self.add_message("logging-too-few-args", node=node)
-
-
-def is_complex_format_str(node):
- """Checks if node represents a string with complex formatting specs.
-
- Args:
- node (astroid.node_classes.NodeNG): AST node to check
- Returns:
- bool: True if inferred string uses complex formatting, False otherwise
- """
- inferred = utils.safe_infer(node)
- if inferred is None or not (
- isinstance(inferred, astroid.Const) and isinstance(inferred.value, str)
- ):
- return True
- try:
- parsed = list(string.Formatter().parse(inferred.value))
- except ValueError:
- # This format string is invalid
- return False
- for _, _, format_spec, _ in parsed:
- if format_spec:
- return True
- return False
-
-
-def _count_supplied_tokens(args):
- """Counts the number of tokens in an args list.
-
- The Python log functions allow for special keyword arguments: func,
- exc_info and extra. To handle these cases correctly, we only count
- arguments that aren't keywords.
-
- Args:
- args (list): AST nodes that are arguments for a log format string.
-
- Returns:
- int: Number of AST nodes that aren't keywords.
- """
- return sum(1 for arg in args if not isinstance(arg, astroid.Keyword))
-
-
-def register(linter):
- """Required method to auto-register this checker."""
- linter.register_checker(LoggingChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/misc.py b/venv/Lib/site-packages/pylint/checkers/misc.py
deleted file mode 100644
index dcf7a3e..0000000
--- a/venv/Lib/site-packages/pylint/checkers/misc.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006, 2009-2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2012-2014 Google, Inc.
-# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Alexandru Coman <fcoman@bitdefender.com>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2016 glegoux <gilles.legoux@gmail.com>
-# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017 Mikhail Fesenko <proggga@gmail.com>
-# Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi>
-
-# 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
-
-
-"""Check source code is ascii only or has an encoding declaration (PEP 263)"""
-
-import re
-import tokenize
-
-from pylint.checkers import BaseChecker
-from pylint.constants import OPTION_RGX
-from pylint.interfaces import IRawChecker, ITokenChecker
-from pylint.message import MessagesHandlerMixIn
-
-
-class ByIdManagedMessagesChecker(BaseChecker):
-
- """checks for messages that are enabled or disabled by id instead of symbol."""
-
- __implements__ = IRawChecker
-
- # configuration section name
- name = "miscellaneous"
- msgs = {
- "I0023": (
- "%s",
- "use-symbolic-message-instead",
- "Used when a message is enabled or disabled by id.",
- )
- }
-
- options = ()
-
- def process_module(self, module):
- """inspect the source file to find messages activated or deactivated by id."""
- managed_msgs = MessagesHandlerMixIn.get_by_id_managed_msgs()
- for (mod_name, msg_id, msg_symbol, lineno, is_disabled) in managed_msgs:
- if mod_name == module.name:
- if is_disabled:
- txt = "Id '{ident}' is used to disable '{symbol}' message emission".format(
- ident=msg_id, symbol=msg_symbol
- )
- else:
- txt = "Id '{ident}' is used to enable '{symbol}' message emission".format(
- ident=msg_id, symbol=msg_symbol
- )
- self.add_message("use-symbolic-message-instead", line=lineno, args=txt)
- MessagesHandlerMixIn.clear_by_id_managed_msgs()
-
-
-class EncodingChecker(BaseChecker):
-
- """checks for:
- * warning notes in the code like FIXME, XXX
- * encoding issues.
- """
-
- __implements__ = (IRawChecker, ITokenChecker)
-
- # configuration section name
- name = "miscellaneous"
- msgs = {
- "W0511": (
- "%s",
- "fixme",
- "Used when a warning note as FIXME or XXX is detected.",
- )
- }
-
- options = (
- (
- "notes",
- {
- "type": "csv",
- "metavar": "<comma separated values>",
- "default": ("FIXME", "XXX", "TODO"),
- "help": (
- "List of note tags to take in consideration, "
- "separated by a comma."
- ),
- },
- ),
- )
-
- def open(self):
- super().open()
- self._fixme_pattern = re.compile(
- r"#\s*(%s)\b" % "|".join(map(re.escape, self.config.notes)), re.I
- )
-
- def _check_encoding(self, lineno, line, file_encoding):
- try:
- return line.decode(file_encoding)
- except UnicodeDecodeError:
- pass
- except LookupError:
- if line.startswith("#") and "coding" in line and file_encoding in line:
- self.add_message(
- "syntax-error",
- line=lineno,
- args='Cannot decode using encoding "{}",'
- " bad encoding".format(file_encoding),
- )
-
- def process_module(self, module):
- """inspect the source file to find encoding problem"""
- if module.file_encoding:
- encoding = module.file_encoding
- else:
- encoding = "ascii"
-
- with module.stream() as stream:
- for lineno, line in enumerate(stream):
- self._check_encoding(lineno + 1, line, encoding)
-
- def process_tokens(self, tokens):
- """inspect the source to find fixme problems"""
- if not self.config.notes:
- return
- comments = (
- token_info for token_info in tokens if token_info.type == tokenize.COMMENT
- )
- for comment in comments:
- comment_text = comment.string[1:].lstrip() # trim '#' and whitespaces
-
- # handle pylint disable clauses
- disable_option_match = OPTION_RGX.search(comment_text)
- if disable_option_match:
- try:
- _, value = disable_option_match.group(1).split("=", 1)
- values = [_val.strip().upper() for _val in value.split(",")]
- if set(values) & set(self.config.notes):
- continue
- except ValueError:
- self.add_message(
- "bad-inline-option",
- args=disable_option_match.group(1).strip(),
- line=comment.start[0],
- )
- continue
-
- # emit warnings if necessary
- match = self._fixme_pattern.search("#" + comment_text.lower())
- if match:
- note = match.group(1)
- self.add_message(
- "fixme",
- col_offset=comment.string.lower().index(note.lower()),
- args=comment_text,
- line=comment.start[0],
- )
-
-
-def register(linter):
- """required method to auto register this checker"""
- linter.register_checker(EncodingChecker(linter))
- linter.register_checker(ByIdManagedMessagesChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/newstyle.py b/venv/Lib/site-packages/pylint/checkers/newstyle.py
deleted file mode 100644
index 46f4e4e..0000000
--- a/venv/Lib/site-packages/pylint/checkers/newstyle.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# Copyright (c) 2006, 2008-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2012-2014 Google, Inc.
-# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
-# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
-
-# 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
-
-"""check for new / old style related problems
-"""
-import astroid
-
-from pylint.checkers import BaseChecker
-from pylint.checkers.utils import check_messages, has_known_bases, node_frame_class
-from pylint.interfaces import IAstroidChecker
-
-MSGS = {
- "E1003": (
- "Bad first argument %r given to super()",
- "bad-super-call",
- "Used when another argument than the current class is given as "
- "first argument of the super builtin.",
- )
-}
-
-
-class NewStyleConflictChecker(BaseChecker):
- """checks for usage of new style capabilities on old style classes and
- other new/old styles conflicts problems
- * use of property, __slots__, super
- * "super" usage
- """
-
- __implements__ = (IAstroidChecker,)
-
- # configuration section name
- name = "newstyle"
- # messages
- msgs = MSGS
- priority = -2
- # configuration options
- options = ()
-
- @check_messages("bad-super-call")
- def visit_functiondef(self, node):
- """check use of super"""
- # ignore actual functions or method within a new style class
- if not node.is_method():
- return
- klass = node.parent.frame()
- for stmt in node.nodes_of_class(astroid.Call):
- if node_frame_class(stmt) != node_frame_class(node):
- # Don't look down in other scopes.
- continue
-
- expr = stmt.func
- if not isinstance(expr, astroid.Attribute):
- continue
-
- call = expr.expr
- # skip the test if using super
- if not (
- isinstance(call, astroid.Call)
- and isinstance(call.func, astroid.Name)
- and call.func.name == "super"
- ):
- continue
-
- # super should not be used on an old style class
- if klass.newstyle or not has_known_bases(klass):
- # super first arg should not be the class
- if not call.args:
- continue
-
- # calling super(type(self), self) can lead to recursion loop
- # in derived classes
- arg0 = call.args[0]
- if (
- isinstance(arg0, astroid.Call)
- and isinstance(arg0.func, astroid.Name)
- and arg0.func.name == "type"
- ):
- self.add_message("bad-super-call", node=call, args=("type",))
- continue
-
- # calling super(self.__class__, self) can lead to recursion loop
- # in derived classes
- if (
- len(call.args) >= 2
- and isinstance(call.args[1], astroid.Name)
- and call.args[1].name == "self"
- and isinstance(arg0, astroid.Attribute)
- and arg0.attrname == "__class__"
- ):
- self.add_message(
- "bad-super-call", node=call, args=("self.__class__",)
- )
- continue
-
- try:
- supcls = call.args and next(call.args[0].infer(), None)
- except astroid.InferenceError:
- continue
-
- if klass is not supcls:
- name = None
- # if supcls is not Uninferable, then supcls was inferred
- # and use its name. Otherwise, try to look
- # for call.args[0].name
- if supcls:
- name = supcls.name
- elif call.args and hasattr(call.args[0], "name"):
- name = call.args[0].name
- if name:
- self.add_message("bad-super-call", node=call, args=(name,))
-
- visit_asyncfunctiondef = visit_functiondef
-
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(NewStyleConflictChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/python3.py b/venv/Lib/site-packages/pylint/checkers/python3.py
deleted file mode 100644
index 583b1c2..0000000
--- a/venv/Lib/site-packages/pylint/checkers/python3.py
+++ /dev/null
@@ -1,1398 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014-2015 Brett Cannon <brett@python.org>
-# Copyright (c) 2015 Simu Toni <simutoni@gmail.com>
-# Copyright (c) 2015 Pavel Roskin <proski@gnu.org>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2015 Cosmin Poieana <cmin@ropython.org>
-# Copyright (c) 2015 Viorel Stirbu <viorels@gmail.com>
-# Copyright (c) 2016, 2018 Jakub Wilk <jwilk@jwilk.net>
-# Copyright (c) 2016-2017 Roy Williams <roy.williams.iii@gmail.com>
-# Copyright (c) 2016 Roy Williams <rwilliams@lyft.com>
-# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2016 Erik <erik.eriksson@yahoo.com>
-# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
-# Copyright (c) 2017 Daniel Miller <millerdev@gmail.com>
-# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017 ahirnish <ahirnish@gmail.com>
-# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
-# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
-# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
-# Copyright (c) 2018 gaurikholkar <f2013002@goa.bits-pilani.ac.in>
-# 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
-
-"""Check Python 2 code for Python 2/3 source-compatible issues."""
-import re
-import tokenize
-from collections import namedtuple
-
-import astroid
-from astroid import bases
-
-from pylint import checkers, interfaces
-from pylint.checkers import utils
-from pylint.checkers.utils import find_try_except_wrapper_node, node_ignores_exception
-from pylint.constants import WarningScope
-from pylint.interfaces import INFERENCE, INFERENCE_FAILURE
-
-_ZERO = re.compile("^0+$")
-
-
-def _is_old_octal(literal):
- if _ZERO.match(literal):
- return False
- if re.match(r"0\d+", literal):
- try:
- int(literal, 8)
- except ValueError:
- return False
- return True
- return None
-
-
-def _inferred_value_is_dict(value):
- if isinstance(value, astroid.Dict):
- return True
- return isinstance(value, astroid.Instance) and "dict" in value.basenames
-
-
-def _is_builtin(node):
- return getattr(node, "name", None) in ("__builtin__", "builtins")
-
-
-_ACCEPTS_ITERATOR = {
- "iter",
- "list",
- "tuple",
- "sorted",
- "set",
- "sum",
- "any",
- "all",
- "enumerate",
- "dict",
- "filter",
- "reversed",
- "max",
- "min",
- "frozenset",
- "OrderedDict",
-}
-ATTRIBUTES_ACCEPTS_ITERATOR = {"join", "from_iterable"}
-_BUILTIN_METHOD_ACCEPTS_ITERATOR = {
- "builtins.list.extend",
- "builtins.dict.update",
- "builtins.set.update",
-}
-DICT_METHODS = {"items", "keys", "values"}
-
-
-def _in_iterating_context(node):
- """Check if the node is being used as an iterator.
-
- Definition is taken from lib2to3.fixer_util.in_special_context().
- """
- parent = node.parent
- # Since a call can't be the loop variant we only need to know if the node's
- # parent is a 'for' loop to know it's being used as the iterator for the
- # loop.
- if isinstance(parent, astroid.For):
- return True
- # Need to make sure the use of the node is in the iterator part of the
- # comprehension.
- if isinstance(parent, astroid.Comprehension):
- if parent.iter == node:
- return True
- # Various built-ins can take in an iterable or list and lead to the same
- # value.
- elif isinstance(parent, astroid.Call):
- if isinstance(parent.func, astroid.Name):
- if parent.func.name in _ACCEPTS_ITERATOR:
- return True
- elif isinstance(parent.func, astroid.Attribute):
- if parent.func.attrname in ATTRIBUTES_ACCEPTS_ITERATOR:
- return True
-
- inferred = utils.safe_infer(parent.func)
- if inferred:
- if inferred.qname() in _BUILTIN_METHOD_ACCEPTS_ITERATOR:
- return True
- root = inferred.root()
- if root and root.name == "itertools":
- return True
- # If the call is in an unpacking, there's no need to warn,
- # since it can be considered iterating.
- elif isinstance(parent, astroid.Assign) and isinstance(
- parent.targets[0], (astroid.List, astroid.Tuple)
- ):
- if len(parent.targets[0].elts) > 1:
- return True
- # If the call is in a containment check, we consider that to
- # be an iterating context
- elif (
- isinstance(parent, astroid.Compare)
- and len(parent.ops) == 1
- and parent.ops[0][0] == "in"
- ):
- return True
- # Also if it's an `yield from`, that's fair
- elif isinstance(parent, astroid.YieldFrom):
- return True
- if isinstance(parent, astroid.Starred):
- return True
- return False
-
-
-def _is_conditional_import(node):
- """Checks if an import node is in the context of a conditional.
- """
- parent = node.parent
- return isinstance(
- parent, (astroid.TryExcept, astroid.ExceptHandler, astroid.If, astroid.IfExp)
- )
-
-
-Branch = namedtuple("Branch", ["node", "is_py2_only"])
-
-
-class Python3Checker(checkers.BaseChecker):
-
- __implements__ = interfaces.IAstroidChecker
- enabled = False
- name = "python3"
-
- msgs = {
- # Errors for what will syntactically break in Python 3, warnings for
- # everything else.
- "E1601": (
- "print statement used",
- "print-statement",
- "Used when a print statement is used "
- "(`print` is a function in Python 3)",
- ),
- "E1602": (
- "Parameter unpacking specified",
- "parameter-unpacking",
- "Used when parameter unpacking is specified for a function"
- "(Python 3 doesn't allow it)",
- ),
- "E1603": (
- "Implicit unpacking of exceptions is not supported in Python 3",
- "unpacking-in-except",
- "Python3 will not allow implicit unpacking of "
- "exceptions in except clauses. "
- "See http://www.python.org/dev/peps/pep-3110/",
- {"old_names": [("W0712", "old-unpacking-in-except")]},
- ),
- "E1604": (
- "Use raise ErrorClass(args) instead of raise ErrorClass, args.",
- "old-raise-syntax",
- "Used when the alternate raise syntax "
- "'raise foo, bar' is used "
- "instead of 'raise foo(bar)'.",
- {"old_names": [("W0121", "old-old-raise-syntax")]},
- ),
- "E1605": (
- "Use of the `` operator",
- "backtick",
- 'Used when the deprecated "``" (backtick) operator is used '
- "instead of the str() function.",
- {"scope": WarningScope.NODE, "old_names": [("W0333", "old-backtick")]},
- ),
- "E1609": (
- "Import * only allowed at module level",
- "import-star-module-level",
- "Used when the import star syntax is used somewhere "
- "else than the module level.",
- {"maxversion": (3, 0)},
- ),
- "W1601": (
- "apply built-in referenced",
- "apply-builtin",
- "Used when the apply built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1602": (
- "basestring built-in referenced",
- "basestring-builtin",
- "Used when the basestring built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1603": (
- "buffer built-in referenced",
- "buffer-builtin",
- "Used when the buffer built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1604": (
- "cmp built-in referenced",
- "cmp-builtin",
- "Used when the cmp built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1605": (
- "coerce built-in referenced",
- "coerce-builtin",
- "Used when the coerce built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1606": (
- "execfile built-in referenced",
- "execfile-builtin",
- "Used when the execfile built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1607": (
- "file built-in referenced",
- "file-builtin",
- "Used when the file built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1608": (
- "long built-in referenced",
- "long-builtin",
- "Used when the long built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1609": (
- "raw_input built-in referenced",
- "raw_input-builtin",
- "Used when the raw_input built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1610": (
- "reduce built-in referenced",
- "reduce-builtin",
- "Used when the reduce built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1611": (
- "StandardError built-in referenced",
- "standarderror-builtin",
- "Used when the StandardError built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1612": (
- "unicode built-in referenced",
- "unicode-builtin",
- "Used when the unicode built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1613": (
- "xrange built-in referenced",
- "xrange-builtin",
- "Used when the xrange built-in function is referenced "
- "(missing from Python 3)",
- ),
- "W1614": (
- "__coerce__ method defined",
- "coerce-method",
- "Used when a __coerce__ method is defined "
- "(method is not used by Python 3)",
- ),
- "W1615": (
- "__delslice__ method defined",
- "delslice-method",
- "Used when a __delslice__ method is defined "
- "(method is not used by Python 3)",
- ),
- "W1616": (
- "__getslice__ method defined",
- "getslice-method",
- "Used when a __getslice__ method is defined "
- "(method is not used by Python 3)",
- ),
- "W1617": (
- "__setslice__ method defined",
- "setslice-method",
- "Used when a __setslice__ method is defined "
- "(method is not used by Python 3)",
- ),
- "W1618": (
- "import missing `from __future__ import absolute_import`",
- "no-absolute-import",
- "Used when an import is not accompanied by "
- "``from __future__ import absolute_import`` "
- "(default behaviour in Python 3)",
- ),
- "W1619": (
- "division w/o __future__ statement",
- "old-division",
- "Used for non-floor division w/o a float literal or "
- "``from __future__ import division`` "
- "(Python 3 returns a float for int division unconditionally)",
- ),
- "W1620": (
- "Calling a dict.iter*() method",
- "dict-iter-method",
- "Used for calls to dict.iterkeys(), itervalues() or iteritems() "
- "(Python 3 lacks these methods)",
- ),
- "W1621": (
- "Calling a dict.view*() method",
- "dict-view-method",
- "Used for calls to dict.viewkeys(), viewvalues() or viewitems() "
- "(Python 3 lacks these methods)",
- ),
- "W1622": (
- "Called a next() method on an object",
- "next-method-called",
- "Used when an object's next() method is called "
- "(Python 3 uses the next() built-in function)",
- ),
- "W1623": (
- "Assigning to a class's __metaclass__ attribute",
- "metaclass-assignment",
- "Used when a metaclass is specified by assigning to __metaclass__ "
- "(Python 3 specifies the metaclass as a class statement argument)",
- ),
- "W1624": (
- "Indexing exceptions will not work on Python 3",
- "indexing-exception",
- "Indexing exceptions will not work on Python 3. Use "
- "`exception.args[index]` instead.",
- {"old_names": [("W0713", "old-indexing-exception")]},
- ),
- "W1625": (
- "Raising a string exception",
- "raising-string",
- "Used when a string exception is raised. This will not "
- "work on Python 3.",
- {"old_names": [("W0701", "old-raising-string")]},
- ),
- "W1626": (
- "reload built-in referenced",
- "reload-builtin",
- "Used when the reload built-in function is referenced "
- "(missing from Python 3). You can use instead imp.reload "
- "or importlib.reload.",
- ),
- "W1627": (
- "__oct__ method defined",
- "oct-method",
- "Used when an __oct__ method is defined "
- "(method is not used by Python 3)",
- ),
- "W1628": (
- "__hex__ method defined",
- "hex-method",
- "Used when a __hex__ method is defined (method is not used by Python 3)",
- ),
- "W1629": (
- "__nonzero__ method defined",
- "nonzero-method",
- "Used when a __nonzero__ method is defined "
- "(method is not used by Python 3)",
- ),
- "W1630": (
- "__cmp__ method defined",
- "cmp-method",
- "Used when a __cmp__ method is defined (method is not used by Python 3)",
- ),
- # 'W1631': replaced by W1636
- "W1632": (
- "input built-in referenced",
- "input-builtin",
- "Used when the input built-in is referenced "
- "(backwards-incompatible semantics in Python 3)",
- ),
- "W1633": (
- "round built-in referenced",
- "round-builtin",
- "Used when the round built-in is referenced "
- "(backwards-incompatible semantics in Python 3)",
- ),
- "W1634": (
- "intern built-in referenced",
- "intern-builtin",
- "Used when the intern built-in is referenced "
- "(Moved to sys.intern in Python 3)",
- ),
- "W1635": (
- "unichr built-in referenced",
- "unichr-builtin",
- "Used when the unichr built-in is referenced (Use chr in Python 3)",
- ),
- "W1636": (
- "map built-in referenced when not iterating",
- "map-builtin-not-iterating",
- "Used when the map built-in is referenced in a non-iterating "
- "context (returns an iterator in Python 3)",
- {"old_names": [("W1631", "implicit-map-evaluation")]},
- ),
- "W1637": (
- "zip built-in referenced when not iterating",
- "zip-builtin-not-iterating",
- "Used when the zip built-in is referenced in a non-iterating "
- "context (returns an iterator in Python 3)",
- ),
- "W1638": (
- "range built-in referenced when not iterating",
- "range-builtin-not-iterating",
- "Used when the range built-in is referenced in a non-iterating "
- "context (returns a range in Python 3)",
- ),
- "W1639": (
- "filter built-in referenced when not iterating",
- "filter-builtin-not-iterating",
- "Used when the filter built-in is referenced in a non-iterating "
- "context (returns an iterator in Python 3)",
- ),
- "W1640": (
- "Using the cmp argument for list.sort / sorted",
- "using-cmp-argument",
- "Using the cmp argument for list.sort or the sorted "
- "builtin should be avoided, since it was removed in "
- "Python 3. Using either `key` or `functools.cmp_to_key` "
- "should be preferred.",
- ),
- "W1641": (
- "Implementing __eq__ without also implementing __hash__",
- "eq-without-hash",
- "Used when a class implements __eq__ but not __hash__. In Python 2, objects "
- "get object.__hash__ as the default implementation, in Python 3 objects get "
- "None as their default __hash__ implementation if they also implement __eq__.",
- ),
- "W1642": (
- "__div__ method defined",
- "div-method",
- "Used when a __div__ method is defined. Using `__truediv__` and setting"
- "__div__ = __truediv__ should be preferred."
- "(method is not used by Python 3)",
- ),
- "W1643": (
- "__idiv__ method defined",
- "idiv-method",
- "Used when an __idiv__ method is defined. Using `__itruediv__` and setting"
- "__idiv__ = __itruediv__ should be preferred."
- "(method is not used by Python 3)",
- ),
- "W1644": (
- "__rdiv__ method defined",
- "rdiv-method",
- "Used when a __rdiv__ method is defined. Using `__rtruediv__` and setting"
- "__rdiv__ = __rtruediv__ should be preferred."
- "(method is not used by Python 3)",
- ),
- "W1645": (
- "Exception.message removed in Python 3",
- "exception-message-attribute",
- "Used when the message attribute is accessed on an Exception. Use "
- "str(exception) instead.",
- ),
- "W1646": (
- "non-text encoding used in str.decode",
- "invalid-str-codec",
- "Used when using str.encode or str.decode with a non-text encoding. Use "
- "codecs module to handle arbitrary codecs.",
- ),
- "W1647": (
- "sys.maxint removed in Python 3",
- "sys-max-int",
- "Used when accessing sys.maxint. Use sys.maxsize instead.",
- ),
- "W1648": (
- "Module moved in Python 3",
- "bad-python3-import",
- "Used when importing a module that no longer exists in Python 3.",
- ),
- "W1649": (
- "Accessing a deprecated function on the string module",
- "deprecated-string-function",
- "Used when accessing a string function that has been deprecated in Python 3.",
- ),
- "W1650": (
- "Using str.translate with deprecated deletechars parameters",
- "deprecated-str-translate-call",
- "Used when using the deprecated deletechars parameters from str.translate. Use "
- "re.sub to remove the desired characters ",
- ),
- "W1651": (
- "Accessing a deprecated function on the itertools module",
- "deprecated-itertools-function",
- "Used when accessing a function on itertools that has been removed in Python 3.",
- ),
- "W1652": (
- "Accessing a deprecated fields on the types module",
- "deprecated-types-field",
- "Used when accessing a field on types that has been removed in Python 3.",
- ),
- "W1653": (
- "next method defined",
- "next-method-defined",
- "Used when a next method is defined that would be an iterator in Python 2 but "
- "is treated as a normal function in Python 3.",
- ),
- "W1654": (
- "dict.items referenced when not iterating",
- "dict-items-not-iterating",
- "Used when dict.items is referenced in a non-iterating "
- "context (returns an iterator in Python 3)",
- ),
- "W1655": (
- "dict.keys referenced when not iterating",
- "dict-keys-not-iterating",
- "Used when dict.keys is referenced in a non-iterating "
- "context (returns an iterator in Python 3)",
- ),
- "W1656": (
- "dict.values referenced when not iterating",
- "dict-values-not-iterating",
- "Used when dict.values is referenced in a non-iterating "
- "context (returns an iterator in Python 3)",
- ),
- "W1657": (
- "Accessing a removed attribute on the operator module",
- "deprecated-operator-function",
- "Used when accessing a field on operator module that has been "
- "removed in Python 3.",
- ),
- "W1658": (
- "Accessing a removed attribute on the urllib module",
- "deprecated-urllib-function",
- "Used when accessing a field on urllib module that has been "
- "removed or moved in Python 3.",
- ),
- "W1659": (
- "Accessing a removed xreadlines attribute",
- "xreadlines-attribute",
- "Used when accessing the xreadlines() function on a file stream, "
- "removed in Python 3.",
- ),
- "W1660": (
- "Accessing a removed attribute on the sys module",
- "deprecated-sys-function",
- "Used when accessing a field on sys module that has been "
- "removed in Python 3.",
- ),
- "W1661": (
- "Using an exception object that was bound by an except handler",
- "exception-escape",
- "Emitted when using an exception, that was bound in an except "
- "handler, outside of the except handler. On Python 3 these "
- "exceptions will be deleted once they get out "
- "of the except handler.",
- ),
- "W1662": (
- "Using a variable that was bound inside a comprehension",
- "comprehension-escape",
- "Emitted when using a variable, that was bound in a comprehension "
- "handler, outside of the comprehension itself. On Python 3 these "
- "variables will be deleted outside of the "
- "comprehension.",
- ),
- }
-
- _bad_builtins = frozenset(
- [
- "apply",
- "basestring",
- "buffer",
- "cmp",
- "coerce",
- "execfile",
- "file",
- "input", # Not missing, but incompatible semantics
- "intern",
- "long",
- "raw_input",
- "reduce",
- "round", # Not missing, but incompatible semantics
- "StandardError",
- "unichr",
- "unicode",
- "xrange",
- "reload",
- ]
- )
-
- _unused_magic_methods = frozenset(
- [
- "__coerce__",
- "__delslice__",
- "__getslice__",
- "__setslice__",
- "__oct__",
- "__hex__",
- "__nonzero__",
- "__cmp__",
- "__div__",
- "__idiv__",
- "__rdiv__",
- ]
- )
-
- _invalid_encodings = frozenset(
- [
- "base64_codec",
- "base64",
- "base_64",
- "bz2_codec",
- "bz2",
- "hex_codec",
- "hex",
- "quopri_codec",
- "quopri",
- "quotedprintable",
- "quoted_printable",
- "uu_codec",
- "uu",
- "zlib_codec",
- "zlib",
- "zip",
- "rot13",
- "rot_13",
- ]
- )
-
- _bad_python3_module_map = {
- "sys-max-int": {"sys": frozenset(["maxint"])},
- "deprecated-itertools-function": {
- "itertools": frozenset(
- ["izip", "ifilter", "imap", "izip_longest", "ifilterfalse"]
- )
- },
- "deprecated-types-field": {
- "types": frozenset(
- [
- "EllipsisType",
- "XRangeType",
- "ComplexType",
- "StringType",
- "TypeType",
- "LongType",
- "UnicodeType",
- "ClassType",
- "BufferType",
- "StringTypes",
- "NotImplementedType",
- "NoneType",
- "InstanceType",
- "FloatType",
- "SliceType",
- "UnboundMethodType",
- "ObjectType",
- "IntType",
- "TupleType",
- "ListType",
- "DictType",
- "FileType",
- "DictionaryType",
- "BooleanType",
- "DictProxyType",
- ]
- )
- },
- "bad-python3-import": frozenset(
- [
- "anydbm",
- "BaseHTTPServer",
- "__builtin__",
- "CGIHTTPServer",
- "ConfigParser",
- "copy_reg",
- "cPickle",
- "cStringIO",
- "Cookie",
- "cookielib",
- "dbhash",
- "dumbdbm",
- "dumbdb",
- "Dialog",
- "DocXMLRPCServer",
- "FileDialog",
- "FixTk",
- "gdbm",
- "htmlentitydefs",
- "HTMLParser",
- "httplib",
- "markupbase",
- "Queue",
- "repr",
- "robotparser",
- "ScrolledText",
- "SimpleDialog",
- "SimpleHTTPServer",
- "SimpleXMLRPCServer",
- "StringIO",
- "dummy_thread",
- "SocketServer",
- "test.test_support",
- "Tkinter",
- "Tix",
- "Tkconstants",
- "tkColorChooser",
- "tkCommonDialog",
- "Tkdnd",
- "tkFileDialog",
- "tkFont",
- "tkMessageBox",
- "tkSimpleDialog",
- "UserList",
- "UserString",
- "whichdb",
- "_winreg",
- "xmlrpclib",
- "audiodev",
- "Bastion",
- "bsddb185",
- "bsddb3",
- "Canvas",
- "cfmfile",
- "cl",
- "commands",
- "compiler",
- "dircache",
- "dl",
- "exception",
- "fpformat",
- "htmllib",
- "ihooks",
- "imageop",
- "imputil",
- "linuxaudiodev",
- "md5",
- "mhlib",
- "mimetools",
- "MimeWriter",
- "mimify",
- "multifile",
- "mutex",
- "new",
- "popen2",
- "posixfile",
- "pure",
- "rexec",
- "rfc822",
- "sets",
- "sha",
- "sgmllib",
- "sre",
- "stringold",
- "sunaudio",
- "sv",
- "test.testall",
- "thread",
- "timing",
- "toaiff",
- "user",
- "urllib2",
- "urlparse",
- ]
- ),
- "deprecated-string-function": {
- "string": frozenset(
- [
- "maketrans",
- "atof",
- "atoi",
- "atol",
- "capitalize",
- "expandtabs",
- "find",
- "rfind",
- "index",
- "rindex",
- "count",
- "lower",
- "letters",
- "split",
- "rsplit",
- "splitfields",
- "join",
- "joinfields",
- "lstrip",
- "rstrip",
- "strip",
- "swapcase",
- "translate",
- "upper",
- "ljust",
- "rjust",
- "center",
- "zfill",
- "replace",
- "lowercase",
- "letters",
- "uppercase",
- "atol_error",
- "atof_error",
- "atoi_error",
- "index_error",
- ]
- )
- },
- "deprecated-operator-function": {"operator": frozenset({"div"})},
- "deprecated-urllib-function": {
- "urllib": frozenset(
- {
- "addbase",
- "addclosehook",
- "addinfo",
- "addinfourl",
- "always_safe",
- "basejoin",
- "ftpcache",
- "ftperrors",
- "ftpwrapper",
- "getproxies",
- "getproxies_environment",
- "getproxies_macosx_sysconf",
- "main",
- "noheaders",
- "pathname2url",
- "proxy_bypass",
- "proxy_bypass_environment",
- "proxy_bypass_macosx_sysconf",
- "quote",
- "quote_plus",
- "reporthook",
- "splitattr",
- "splithost",
- "splitnport",
- "splitpasswd",
- "splitport",
- "splitquery",
- "splittag",
- "splittype",
- "splituser",
- "splitvalue",
- "unquote",
- "unquote_plus",
- "unwrap",
- "url2pathname",
- "urlcleanup",
- "urlencode",
- "urlopen",
- "urlretrieve",
- }
- )
- },
- "deprecated-sys-function": {"sys": frozenset({"exc_clear"})},
- }
-
- _python_2_tests = frozenset(
- [
- astroid.extract_node(x).repr_tree()
- for x in [
- "sys.version_info[0] == 2",
- "sys.version_info[0] < 3",
- "sys.version_info == (2, 7)",
- "sys.version_info <= (2, 7)",
- "sys.version_info < (3, 0)",
- ]
- ]
- )
-
- def __init__(self, *args, **kwargs):
- self._future_division = False
- self._future_absolute_import = False
- self._modules_warned_about = set()
- self._branch_stack = []
- super(Python3Checker, self).__init__(*args, **kwargs)
-
- # pylint: disable=keyword-arg-before-vararg, arguments-differ
- def add_message(self, msg_id, always_warn=False, *args, **kwargs):
- if always_warn or not (
- self._branch_stack and self._branch_stack[-1].is_py2_only
- ):
- super(Python3Checker, self).add_message(msg_id, *args, **kwargs)
-
- def _is_py2_test(self, node):
- if isinstance(node.test, astroid.Attribute) and isinstance(
- node.test.expr, astroid.Name
- ):
- if node.test.expr.name == "six" and node.test.attrname == "PY2":
- return True
- elif (
- isinstance(node.test, astroid.Compare)
- and node.test.repr_tree() in self._python_2_tests
- ):
- return True
- return False
-
- def visit_if(self, node):
- self._branch_stack.append(Branch(node, self._is_py2_test(node)))
-
- def leave_if(self, node):
- assert self._branch_stack.pop().node == node
-
- def visit_ifexp(self, node):
- self._branch_stack.append(Branch(node, self._is_py2_test(node)))
-
- def leave_ifexp(self, node):
- assert self._branch_stack.pop().node == node
-
- def visit_module(self, node): # pylint: disable=unused-argument
- """Clear checker state after previous module."""
- self._future_division = False
- self._future_absolute_import = False
-
- def visit_functiondef(self, node):
- if node.is_method():
- if node.name in self._unused_magic_methods:
- method_name = node.name
- if node.name.startswith("__"):
- method_name = node.name[2:-2]
- self.add_message(method_name + "-method", node=node)
- elif node.name == "next":
- # If there is a method named `next` declared, if it is invokable
- # with zero arguments then it implements the Iterator protocol.
- # This means if the method is an instance method or a
- # classmethod 1 argument should cause a failure, if it is a
- # staticmethod 0 arguments should cause a failure.
- failing_arg_count = 1
- if utils.decorated_with(node, [bases.BUILTINS + ".staticmethod"]):
- failing_arg_count = 0
- if len(node.args.args) == failing_arg_count:
- self.add_message("next-method-defined", node=node)
-
- @utils.check_messages("parameter-unpacking")
- def visit_arguments(self, node):
- for arg in node.args:
- if isinstance(arg, astroid.Tuple):
- self.add_message("parameter-unpacking", node=arg)
-
- @utils.check_messages("comprehension-escape")
- def visit_listcomp(self, node):
- names = {
- generator.target.name
- for generator in node.generators
- if isinstance(generator.target, astroid.AssignName)
- }
- scope = node.parent.scope()
- scope_names = scope.nodes_of_class(astroid.Name, skip_klass=astroid.FunctionDef)
- has_redefined_assign_name = any(
- assign_name
- for assign_name in scope.nodes_of_class(
- astroid.AssignName, skip_klass=astroid.FunctionDef
- )
- if assign_name.name in names and assign_name.lineno > node.lineno
- )
- if has_redefined_assign_name:
- return
-
- emitted_for_names = set()
- scope_names = list(scope_names)
- for scope_name in scope_names:
- if (
- scope_name.name not in names
- or scope_name.lineno <= node.lineno
- or scope_name.name in emitted_for_names
- or scope_name.scope() == node
- ):
- continue
-
- emitted_for_names.add(scope_name.name)
- self.add_message("comprehension-escape", node=scope_name)
-
- def visit_name(self, node):
- """Detect when a "bad" built-in is referenced."""
- found_node, _ = node.lookup(node.name)
- if not _is_builtin(found_node):
- return
- if node.name not in self._bad_builtins:
- return
- if node_ignores_exception(node) or isinstance(
- find_try_except_wrapper_node(node), astroid.ExceptHandler
- ):
- return
-
- message = node.name.lower() + "-builtin"
- self.add_message(message, node=node)
-
- @utils.check_messages("print-statement")
- def visit_print(self, node):
- self.add_message("print-statement", node=node, always_warn=True)
-
- def _warn_if_deprecated(self, node, module, attributes, report_on_modules=True):
- for message, module_map in self._bad_python3_module_map.items():
- if module in module_map and module not in self._modules_warned_about:
- if isinstance(module_map, frozenset):
- if report_on_modules:
- self._modules_warned_about.add(module)
- self.add_message(message, node=node)
- elif attributes and module_map[module].intersection(attributes):
- self.add_message(message, node=node)
-
- def visit_importfrom(self, node):
- if node.modname == "__future__":
- for name, _ in node.names:
- if name == "division":
- self._future_division = True
- elif name == "absolute_import":
- self._future_absolute_import = True
- else:
- if not self._future_absolute_import:
- if self.linter.is_message_enabled("no-absolute-import"):
- self.add_message("no-absolute-import", node=node)
- self._future_absolute_import = True
- if not _is_conditional_import(node) and not node.level:
- self._warn_if_deprecated(node, node.modname, {x[0] for x in node.names})
-
- if node.names[0][0] == "*":
- if self.linter.is_message_enabled("import-star-module-level"):
- if not isinstance(node.scope(), astroid.Module):
- self.add_message("import-star-module-level", node=node)
-
- def visit_import(self, node):
- if not self._future_absolute_import:
- if self.linter.is_message_enabled("no-absolute-import"):
- self.add_message("no-absolute-import", node=node)
- self._future_absolute_import = True
- if not _is_conditional_import(node):
- for name, _ in node.names:
- self._warn_if_deprecated(node, name, None)
-
- @utils.check_messages("metaclass-assignment")
- def visit_classdef(self, node):
- if "__metaclass__" in node.locals:
- self.add_message("metaclass-assignment", node=node)
- locals_and_methods = set(node.locals).union(x.name for x in node.mymethods())
- if "__eq__" in locals_and_methods and "__hash__" not in locals_and_methods:
- self.add_message("eq-without-hash", node=node)
-
- @utils.check_messages("old-division")
- def visit_binop(self, node):
- if not self._future_division and node.op == "/":
- for arg in (node.left, node.right):
- inferred = utils.safe_infer(arg)
- # If we can infer the object and that object is not an int, bail out.
- if inferred and not (
- (
- isinstance(inferred, astroid.Const)
- and isinstance(inferred.value, int)
- )
- or (
- isinstance(inferred, astroid.Instance)
- and inferred.name == "int"
- )
- ):
- break
- else:
- self.add_message("old-division", node=node)
-
- def _check_cmp_argument(self, node):
- # Check that the `cmp` argument is used
- kwargs = []
- if isinstance(node.func, astroid.Attribute) and node.func.attrname == "sort":
- inferred = utils.safe_infer(node.func.expr)
- if not inferred:
- return
-
- builtins_list = "{}.list".format(bases.BUILTINS)
- if isinstance(inferred, astroid.List) or inferred.qname() == builtins_list:
- kwargs = node.keywords
-
- elif isinstance(node.func, astroid.Name) and node.func.name == "sorted":
- inferred = utils.safe_infer(node.func)
- if not inferred:
- return
-
- builtins_sorted = "{}.sorted".format(bases.BUILTINS)
- if inferred.qname() == builtins_sorted:
- kwargs = node.keywords
-
- for kwarg in kwargs or []:
- if kwarg.arg == "cmp":
- self.add_message("using-cmp-argument", node=node)
- return
-
- @staticmethod
- def _is_constant_string_or_name(node):
- if isinstance(node, astroid.Const):
- return isinstance(node.value, str)
- return isinstance(node, astroid.Name)
-
- @staticmethod
- def _is_none(node):
- return isinstance(node, astroid.Const) and node.value is None
-
- @staticmethod
- def _has_only_n_positional_args(node, number_of_args):
- return len(node.args) == number_of_args and all(node.args) and not node.keywords
-
- @staticmethod
- def _could_be_string(inferred_types):
- confidence = INFERENCE if inferred_types else INFERENCE_FAILURE
- for inferred_type in inferred_types:
- if inferred_type is astroid.Uninferable:
- confidence = INFERENCE_FAILURE
- elif not (
- isinstance(inferred_type, astroid.Const)
- and isinstance(inferred_type.value, str)
- ):
- return None
- return confidence
-
- def visit_call(self, node):
- self._check_cmp_argument(node)
-
- if isinstance(node.func, astroid.Attribute):
- inferred_types = set()
- try:
- for inferred_receiver in node.func.expr.infer():
- if inferred_receiver is astroid.Uninferable:
- continue
- inferred_types.add(inferred_receiver)
- if isinstance(inferred_receiver, astroid.Module):
- self._warn_if_deprecated(
- node,
- inferred_receiver.name,
- {node.func.attrname},
- report_on_modules=False,
- )
- if (
- _inferred_value_is_dict(inferred_receiver)
- and node.func.attrname in DICT_METHODS
- ):
- if not _in_iterating_context(node):
- checker = "dict-{}-not-iterating".format(node.func.attrname)
- self.add_message(checker, node=node)
- except astroid.InferenceError:
- pass
- if node.args:
- is_str_confidence = self._could_be_string(inferred_types)
- if is_str_confidence:
- if (
- node.func.attrname in ("encode", "decode")
- and len(node.args) >= 1
- and node.args[0]
- ):
- first_arg = node.args[0]
- self._validate_encoding(first_arg, node)
- if (
- node.func.attrname == "translate"
- and self._has_only_n_positional_args(node, 2)
- and self._is_none(node.args[0])
- and self._is_constant_string_or_name(node.args[1])
- ):
- # The above statement looking for calls of the form:
- #
- # foo.translate(None, 'abc123')
- #
- # or
- #
- # foo.translate(None, some_variable)
- #
- # This check is somewhat broad and _may_ have some false positives, but
- # after checking several large codebases it did not have any false
- # positives while finding several real issues. This call pattern seems
- # rare enough that the trade off is worth it.
- self.add_message(
- "deprecated-str-translate-call",
- node=node,
- confidence=is_str_confidence,
- )
- return
- if node.keywords:
- return
- if node.func.attrname == "next":
- self.add_message("next-method-called", node=node)
- else:
- if node.func.attrname in ("iterkeys", "itervalues", "iteritems"):
- self.add_message("dict-iter-method", node=node)
- elif node.func.attrname in ("viewkeys", "viewvalues", "viewitems"):
- self.add_message("dict-view-method", node=node)
- elif isinstance(node.func, astroid.Name):
- found_node = node.func.lookup(node.func.name)[0]
- if _is_builtin(found_node):
- if node.func.name in ("filter", "map", "range", "zip"):
- if not _in_iterating_context(node):
- checker = "{}-builtin-not-iterating".format(node.func.name)
- self.add_message(checker, node=node)
- if node.func.name == "open" and node.keywords:
- kwargs = node.keywords
- for kwarg in kwargs or []:
- if kwarg.arg == "encoding":
- self._validate_encoding(kwarg.value, node)
- break
-
- def _validate_encoding(self, encoding, node):
- if isinstance(encoding, astroid.Const):
- value = encoding.value
- if value in self._invalid_encodings:
- self.add_message("invalid-str-codec", node=node)
-
- @utils.check_messages("indexing-exception")
- def visit_subscript(self, node):
- """ Look for indexing exceptions. """
- try:
- for inferred in node.value.infer():
- if not isinstance(inferred, astroid.Instance):
- continue
- if utils.inherit_from_std_ex(inferred):
- self.add_message("indexing-exception", node=node)
- except astroid.InferenceError:
- return
-
- def visit_assignattr(self, node):
- if isinstance(node.assign_type(), astroid.AugAssign):
- self.visit_attribute(node)
-
- def visit_delattr(self, node):
- self.visit_attribute(node)
-
- @utils.check_messages("exception-message-attribute", "xreadlines-attribute")
- def visit_attribute(self, node):
- """Look for removed attributes"""
- if node.attrname == "xreadlines":
- self.add_message("xreadlines-attribute", node=node)
- return
-
- exception_message = "message"
- try:
- for inferred in node.expr.infer():
- if isinstance(inferred, astroid.Instance) and utils.inherit_from_std_ex(
- inferred
- ):
- if node.attrname == exception_message:
-
- # Exceptions with .message clearly defined are an exception
- if exception_message in inferred.instance_attrs:
- continue
- self.add_message("exception-message-attribute", node=node)
- if isinstance(inferred, astroid.Module):
- self._warn_if_deprecated(
- node, inferred.name, {node.attrname}, report_on_modules=False
- )
- except astroid.InferenceError:
- return
-
- @utils.check_messages("unpacking-in-except", "comprehension-escape")
- def visit_excepthandler(self, node):
- """Visit an except handler block and check for exception unpacking."""
-
- def _is_used_in_except_block(node):
- scope = node.scope()
- current = node
- while (
- current
- and current != scope
- and not isinstance(current, astroid.ExceptHandler)
- ):
- current = current.parent
- return isinstance(current, astroid.ExceptHandler) and current.type != node
-
- if isinstance(node.name, (astroid.Tuple, astroid.List)):
- self.add_message("unpacking-in-except", node=node)
- return
-
- if not node.name:
- return
-
- # Find any names
- scope = node.parent.scope()
- scope_names = scope.nodes_of_class(astroid.Name, skip_klass=astroid.FunctionDef)
- scope_names = list(scope_names)
- potential_leaked_names = [
- scope_name
- for scope_name in scope_names
- if scope_name.name == node.name.name
- and scope_name.lineno > node.lineno
- and not _is_used_in_except_block(scope_name)
- ]
- reassignments_for_same_name = {
- assign_name.lineno
- for assign_name in scope.nodes_of_class(
- astroid.AssignName, skip_klass=astroid.FunctionDef
- )
- if assign_name.name == node.name.name
- }
- for leaked_name in potential_leaked_names:
- if any(
- node.lineno < elem < leaked_name.lineno
- for elem in reassignments_for_same_name
- ):
- continue
- self.add_message("exception-escape", node=leaked_name)
-
- @utils.check_messages("backtick")
- def visit_repr(self, node):
- self.add_message("backtick", node=node)
-
- @utils.check_messages("raising-string", "old-raise-syntax")
- def visit_raise(self, node):
- """Visit a raise statement and check for raising
- strings or old-raise-syntax.
- """
-
- # Ignore empty raise.
- if node.exc is None:
- return
- expr = node.exc
- if self._check_raise_value(node, expr):
- return
- try:
- value = next(astroid.unpack_infer(expr))
- except astroid.InferenceError:
- return
- self._check_raise_value(node, value)
-
- def _check_raise_value(self, node, expr):
- if isinstance(expr, astroid.Const):
- value = expr.value
- if isinstance(value, str):
- self.add_message("raising-string", node=node)
- return True
- return None
-
-
-class Python3TokenChecker(checkers.BaseTokenChecker):
- __implements__ = interfaces.ITokenChecker
- name = "python3"
- enabled = False
-
- msgs = {
- "E1606": (
- "Use of long suffix",
- "long-suffix",
- 'Used when "l" or "L" is used to mark a long integer. '
- "This will not work in Python 3, since `int` and `long` "
- "types have merged.",
- {"maxversion": (3, 0)},
- ),
- "E1607": (
- "Use of the <> operator",
- "old-ne-operator",
- 'Used when the deprecated "<>" operator is used instead '
- 'of "!=". This is removed in Python 3.',
- {"maxversion": (3, 0), "old_names": [("W0331", "old-old-ne-operator")]},
- ),
- "E1608": (
- "Use of old octal literal",
- "old-octal-literal",
- "Used when encountering the old octal syntax, "
- "removed in Python 3. To use the new syntax, "
- "prepend 0o on the number.",
- {"maxversion": (3, 0)},
- ),
- "E1610": (
- "Non-ascii bytes literals not supported in 3.x",
- "non-ascii-bytes-literal",
- "Used when non-ascii bytes literals are found in a program. "
- "They are no longer supported in Python 3.",
- {"maxversion": (3, 0)},
- ),
- }
-
- def process_tokens(self, tokens):
- for idx, (tok_type, token, start, _, _) in enumerate(tokens):
- if tok_type == tokenize.NUMBER:
- if token.lower().endswith("l"):
- # This has a different semantic than lowercase-l-suffix.
- self.add_message("long-suffix", line=start[0])
- elif _is_old_octal(token):
- self.add_message("old-octal-literal", line=start[0])
- if tokens[idx][1] == "<>":
- self.add_message("old-ne-operator", line=tokens[idx][2][0])
- if tok_type == tokenize.STRING and token.startswith("b"):
- if any(elem for elem in token if ord(elem) > 127):
- self.add_message("non-ascii-bytes-literal", line=start[0])
-
-
-def register(linter):
- linter.register_checker(Python3Checker(linter))
- linter.register_checker(Python3TokenChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/raw_metrics.py b/venv/Lib/site-packages/pylint/checkers/raw_metrics.py
deleted file mode 100644
index 0564398..0000000
--- a/venv/Lib/site-packages/pylint/checkers/raw_metrics.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# Copyright (c) 2007, 2010, 2013, 2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2013 Google, Inc.
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2015 Mike Frysinger <vapier@gentoo.org>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
-
-# 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
-
-""" Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
- http://www.logilab.fr/ -- mailto:contact@logilab.fr
-
-Raw metrics checker
-"""
-
-import tokenize
-from typing import Any
-
-from pylint.checkers import BaseTokenChecker
-from pylint.exceptions import EmptyReportError
-from pylint.interfaces import ITokenChecker
-from pylint.reporters.ureports.nodes import Table
-
-
-def report_raw_stats(sect, stats, _):
- """calculate percentage of code / doc / comment / empty
- """
- total_lines = stats["total_lines"]
- if not total_lines:
- raise EmptyReportError()
- sect.description = "%s lines have been analyzed" % total_lines
- lines = ("type", "number", "%", "previous", "difference")
- for node_type in ("code", "docstring", "comment", "empty"):
- key = node_type + "_lines"
- total = stats[key]
- percent = float(total * 100) / total_lines
- lines += (node_type, str(total), "%.2f" % percent, "NC", "NC")
- sect.append(Table(children=lines, cols=5, rheaders=1))
-
-
-class RawMetricsChecker(BaseTokenChecker):
- """does not check anything but gives some raw metrics :
- * total number of lines
- * total number of code lines
- * total number of docstring lines
- * total number of comments lines
- * total number of empty lines
- """
-
- __implements__ = (ITokenChecker,)
-
- # configuration section name
- name = "metrics"
- # configuration options
- options = ()
- # messages
- msgs = {} # type: Any
- # reports
- reports = (("RP0701", "Raw metrics", report_raw_stats),)
-
- def __init__(self, linter):
- BaseTokenChecker.__init__(self, linter)
- self.stats = None
-
- def open(self):
- """init statistics"""
- self.stats = self.linter.add_stats(
- total_lines=0,
- code_lines=0,
- empty_lines=0,
- docstring_lines=0,
- comment_lines=0,
- )
-
- def process_tokens(self, tokens):
- """update stats"""
- i = 0
- tokens = list(tokens)
- while i < len(tokens):
- i, lines_number, line_type = get_type(tokens, i)
- self.stats["total_lines"] += lines_number
- self.stats[line_type] += lines_number
-
-
-JUNK = (tokenize.NL, tokenize.INDENT, tokenize.NEWLINE, tokenize.ENDMARKER)
-
-
-def get_type(tokens, start_index):
- """return the line type : docstring, comment, code, empty"""
- i = start_index
- tok_type = tokens[i][0]
- start = tokens[i][2]
- pos = start
- line_type = None
- while i < len(tokens) and tokens[i][2][0] == start[0]:
- tok_type = tokens[i][0]
- pos = tokens[i][3]
- if line_type is None:
- if tok_type == tokenize.STRING:
- line_type = "docstring_lines"
- elif tok_type == tokenize.COMMENT:
- line_type = "comment_lines"
- elif tok_type in JUNK:
- pass
- else:
- line_type = "code_lines"
- i += 1
- if line_type is None:
- line_type = "empty_lines"
- elif i < len(tokens) and tokens[i][0] == tokenize.NEWLINE:
- i += 1
- return i, pos[0] - start[0] + 1, line_type
-
-
-def register(linter):
- """ required method to auto register this checker """
- linter.register_checker(RawMetricsChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/refactoring.py b/venv/Lib/site-packages/pylint/checkers/refactoring.py
deleted file mode 100644
index 2831343..0000000
--- a/venv/Lib/site-packages/pylint/checkers/refactoring.py
+++ /dev/null
@@ -1,1510 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2016-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
-# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
-# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
-# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
-# Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com>
-# Copyright (c) 2017 Łukasz Sznuk <ls@rdprojekt.pl>
-# Copyright (c) 2017 Alex Hearn <alex.d.hearn@gmail.com>
-# Copyright (c) 2017 Antonio Ossa <aaossa@uc.cl>
-# Copyright (c) 2018 Konstantin Manna <Konstantin@Manna.uno>
-# Copyright (c) 2018 Konstantin <Github@pheanex.de>
-# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
-# Copyright (c) 2018 Matej Marušák <marusak.matej@gmail.com>
-# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
-# Copyright (c) 2018 Mr. Senko <atodorov@mrsenko.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
-
-"""Looks for code which can be refactored."""
-import builtins
-import collections
-import itertools
-import tokenize
-from functools import reduce
-
-import astroid
-from astroid import decorators
-
-from pylint import checkers, interfaces
-from pylint import utils as lint_utils
-from pylint.checkers import utils
-
-KNOWN_INFINITE_ITERATORS = {"itertools.count"}
-BUILTIN_EXIT_FUNCS = frozenset(("quit", "exit"))
-
-
-def _if_statement_is_always_returning(if_node, returning_node_class):
- for node in if_node.body:
- if isinstance(node, returning_node_class):
- return True
- return False
-
-
-def _is_len_call(node):
- """Checks if node is len(SOMETHING)."""
- return (
- isinstance(node, astroid.Call)
- and isinstance(node.func, astroid.Name)
- and node.func.name == "len"
- )
-
-
-def _is_constant_zero(node):
- return isinstance(node, astroid.Const) and node.value == 0
-
-
-def _node_is_test_condition(node):
- """ Checks if node is an if, while, assert or if expression statement."""
- return isinstance(node, (astroid.If, astroid.While, astroid.Assert, astroid.IfExp))
-
-
-def _is_trailing_comma(tokens, index):
- """Check if the given token is a trailing comma
-
- :param tokens: Sequence of modules tokens
- :type tokens: list[tokenize.TokenInfo]
- :param int index: Index of token under check in tokens
- :returns: True if the token is a comma which trails an expression
- :rtype: bool
- """
- token = tokens[index]
- if token.exact_type != tokenize.COMMA:
- return False
- # Must have remaining tokens on the same line such as NEWLINE
- left_tokens = itertools.islice(tokens, index + 1, None)
- same_line_remaining_tokens = list(
- itertools.takewhile(
- lambda other_token, _token=token: other_token.start[0] == _token.start[0],
- left_tokens,
- )
- )
- # Note: If the newline is tokenize.NEWLINE and not tokenize.NL
- # then the newline denotes the end of expression
- is_last_element = all(
- other_token.type in (tokenize.NEWLINE, tokenize.COMMENT)
- for other_token in same_line_remaining_tokens
- )
- if not same_line_remaining_tokens or not is_last_element:
- return False
-
- def get_curline_index_start():
- """Get the index denoting the start of the current line"""
- for subindex, token in enumerate(reversed(tokens[:index])):
- # See Lib/tokenize.py and Lib/token.py in cpython for more info
- if token.type in (tokenize.NEWLINE, tokenize.NL):
- return index - subindex
- return 0
-
- curline_start = get_curline_index_start()
- expected_tokens = {"return", "yield"}
- for prevtoken in tokens[curline_start:index]:
- if "=" in prevtoken.string or prevtoken.string in expected_tokens:
- return True
- return False
-
-
-class RefactoringChecker(checkers.BaseTokenChecker):
- """Looks for code which can be refactored
-
- This checker also mixes the astroid and the token approaches
- in order to create knowledge about whether an "else if" node
- is a true "else if" node, or an "elif" node.
- """
-
- __implements__ = (interfaces.ITokenChecker, interfaces.IAstroidChecker)
-
- name = "refactoring"
-
- msgs = {
- "R1701": (
- "Consider merging these isinstance calls to isinstance(%s, (%s))",
- "consider-merging-isinstance",
- "Used when multiple consecutive isinstance calls can be merged into one.",
- ),
- "R1706": (
- "Consider using ternary (%s)",
- "consider-using-ternary",
- "Used when one of known pre-python 2.5 ternary syntax is used.",
- ),
- "R1709": (
- "Boolean expression may be simplified to %s",
- "simplify-boolean-expression",
- "Emitted when redundant pre-python 2.5 ternary syntax is used.",
- ),
- "R1702": (
- "Too many nested blocks (%s/%s)",
- "too-many-nested-blocks",
- "Used when a function or a method has too many nested "
- "blocks. This makes the code less understandable and "
- "maintainable.",
- {"old_names": [("R0101", "old-too-many-nested-blocks")]},
- ),
- "R1703": (
- "The if statement can be replaced with %s",
- "simplifiable-if-statement",
- "Used when an if statement can be replaced with 'bool(test)'. ",
- {"old_names": [("R0102", "old-simplifiable-if-statement")]},
- ),
- "R1704": (
- "Redefining argument with the local name %r",
- "redefined-argument-from-local",
- "Used when a local name is redefining an argument, which might "
- "suggest a potential error. This is taken in account only for "
- "a handful of name binding operations, such as for iteration, "
- "with statement assignment and exception handler assignment.",
- ),
- "R1705": (
- 'Unnecessary "%s" after "return"',
- "no-else-return",
- "Used in order to highlight an unnecessary block of "
- "code following an if containing a return statement. "
- "As such, it will warn when it encounters an else "
- "following a chain of ifs, all of them containing a "
- "return statement.",
- ),
- "R1707": (
- "Disallow trailing comma tuple",
- "trailing-comma-tuple",
- "In Python, a tuple is actually created by the comma symbol, "
- "not by the parentheses. Unfortunately, one can actually create a "
- "tuple by misplacing a trailing comma, which can lead to potential "
- "weird bugs in your code. You should always use parentheses "
- "explicitly for creating a tuple.",
- ),
- "R1708": (
- "Do not raise StopIteration in generator, use return statement instead",
- "stop-iteration-return",
- "According to PEP479, the raise of StopIteration to end the loop of "
- "a generator may lead to hard to find bugs. This PEP specify that "
- "raise StopIteration has to be replaced by a simple return statement",
- ),
- "R1710": (
- "Either all return statements in a function should return an expression, "
- "or none of them should.",
- "inconsistent-return-statements",
- "According to PEP8, if any return statement returns an expression, "
- "any return statements where no value is returned should explicitly "
- "state this as return None, and an explicit return statement "
- "should be present at the end of the function (if reachable)",
- ),
- "R1711": (
- "Useless return at end of function or method",
- "useless-return",
- 'Emitted when a single "return" or "return None" statement is found '
- "at the end of function or method definition. This statement can safely be "
- "removed because Python will implicitly return None",
- ),
- "R1712": (
- "Consider using tuple unpacking for swapping variables",
- "consider-swap-variables",
- "You do not have to use a temporary variable in order to "
- 'swap variables. Using "tuple unpacking" to directly swap '
- "variables makes the intention more clear.",
- ),
- "R1713": (
- "Consider using str.join(sequence) for concatenating "
- "strings from an iterable",
- "consider-using-join",
- "Using str.join(sequence) is faster, uses less memory "
- "and increases readability compared to for-loop iteration.",
- ),
- "R1714": (
- 'Consider merging these comparisons with "in" to %r',
- "consider-using-in",
- "To check if a variable is equal to one of many values,"
- 'combine the values into a tuple and check if the variable is contained "in" it '
- "instead of checking for equality against each of the values."
- "This is faster and less verbose.",
- ),
- "R1715": (
- "Consider using dict.get for getting values from a dict "
- "if a key is present or a default if not",
- "consider-using-get",
- "Using the builtin dict.get for getting a value from a dictionary "
- "if a key is present or a default if not, is simpler and considered "
- "more idiomatic, although sometimes a bit slower",
- ),
- "R1716": (
- "Simplify chained comparison between the operands",
- "chained-comparison",
- "This message is emitted when pylint encounters boolean operation like"
- '"a < b and b < c", suggesting instead to refactor it to "a < b < c"',
- ),
- "R1717": (
- "Consider using a dictionary comprehension",
- "consider-using-dict-comprehension",
- "Emitted when we detect the creation of a dictionary "
- "using the dict() callable and a transient list. "
- "Although there is nothing syntactically wrong with this code, "
- "it is hard to read and can be simplified to a dict comprehension."
- "Also it is faster since you don't need to create another "
- "transient list",
- ),
- "R1718": (
- "Consider using a set comprehension",
- "consider-using-set-comprehension",
- "Although there is nothing syntactically wrong with this code, "
- "it is hard to read and can be simplified to a set comprehension."
- "Also it is faster since you don't need to create another "
- "transient list",
- ),
- "R1719": (
- "The if expression can be replaced with %s",
- "simplifiable-if-expression",
- "Used when an if expression can be replaced with 'bool(test)'. ",
- ),
- "R1720": (
- 'Unnecessary "%s" after "raise"',
- "no-else-raise",
- "Used in order to highlight an unnecessary block of "
- "code following an if containing a raise statement. "
- "As such, it will warn when it encounters an else "
- "following a chain of ifs, all of them containing a "
- "raise statement.",
- ),
- "R1721": (
- "Unnecessary use of a comprehension",
- "unnecessary-comprehension",
- "Instead of using an identitiy comprehension, "
- "consider using the list, dict or set constructor. "
- "It is faster and simpler.",
- ),
- "R1722": (
- "Consider using sys.exit()",
- "consider-using-sys-exit",
- "Instead of using exit() or quit(), consider using the sys.exit().",
- ),
- "R1723": (
- 'Unnecessary "%s" after "break"',
- "no-else-break",
- "Used in order to highlight an unnecessary block of "
- "code following an if containing a break statement. "
- "As such, it will warn when it encounters an else "
- "following a chain of ifs, all of them containing a "
- "break statement.",
- ),
- "R1724": (
- 'Unnecessary "%s" after "continue"',
- "no-else-continue",
- "Used in order to highlight an unnecessary block of "
- "code following an if containing a continue statement. "
- "As such, it will warn when it encounters an else "
- "following a chain of ifs, all of them containing a "
- "continue statement.",
- ),
- }
- options = (
- (
- "max-nested-blocks",
- {
- "default": 5,
- "type": "int",
- "metavar": "<int>",
- "help": "Maximum number of nested blocks for function / method body",
- },
- ),
- (
- "never-returning-functions",
- {
- "default": ("sys.exit",),
- "type": "csv",
- "help": "Complete name of functions that never returns. When checking "
- "for inconsistent-return-statements if a never returning function is "
- "called then it will be considered as an explicit return statement "
- "and no message will be printed.",
- },
- ),
- )
-
- priority = 0
-
- def __init__(self, linter=None):
- checkers.BaseTokenChecker.__init__(self, linter)
- self._return_nodes = {}
- self._init()
- self._never_returning_functions = None
-
- def _init(self):
- self._nested_blocks = []
- self._elifs = []
- self._nested_blocks_msg = None
- self._reported_swap_nodes = set()
-
- def open(self):
- # do this in open since config not fully initialized in __init__
- self._never_returning_functions = set(self.config.never_returning_functions)
-
- @decorators.cachedproperty
- def _dummy_rgx(self):
- return lint_utils.get_global_option(self, "dummy-variables-rgx", default=None)
-
- @staticmethod
- def _is_bool_const(node):
- return isinstance(node.value, astroid.Const) and isinstance(
- node.value.value, bool
- )
-
- def _is_actual_elif(self, node):
- """Check if the given node is an actual elif
-
- This is a problem we're having with the builtin ast module,
- which splits `elif` branches into a separate if statement.
- Unfortunately we need to know the exact type in certain
- cases.
- """
- if isinstance(node.parent, astroid.If):
- orelse = node.parent.orelse
- # current if node must directly follow an "else"
- if orelse and orelse == [node]:
- if (node.lineno, node.col_offset) in self._elifs:
- return True
- return False
-
- def _check_simplifiable_if(self, node):
- """Check if the given if node can be simplified.
-
- The if statement can be reduced to a boolean expression
- in some cases. For instance, if there are two branches
- and both of them return a boolean value that depends on
- the result of the statement's test, then this can be reduced
- to `bool(test)` without losing any functionality.
- """
-
- if self._is_actual_elif(node):
- # Not interested in if statements with multiple branches.
- return
- if len(node.orelse) != 1 or len(node.body) != 1:
- return
-
- # Check if both branches can be reduced.
- first_branch = node.body[0]
- else_branch = node.orelse[0]
- if isinstance(first_branch, astroid.Return):
- if not isinstance(else_branch, astroid.Return):
- return
- first_branch_is_bool = self._is_bool_const(first_branch)
- else_branch_is_bool = self._is_bool_const(else_branch)
- reduced_to = "'return bool(test)'"
- elif isinstance(first_branch, astroid.Assign):
- if not isinstance(else_branch, astroid.Assign):
- return
-
- # Check if we assign to the same value
- first_branch_targets = [
- target.name
- for target in first_branch.targets
- if isinstance(target, astroid.AssignName)
- ]
- else_branch_targets = [
- target.name
- for target in else_branch.targets
- if isinstance(target, astroid.AssignName)
- ]
- if not first_branch_targets or not else_branch_targets:
- return
- if sorted(first_branch_targets) != sorted(else_branch_targets):
- return
-
- first_branch_is_bool = self._is_bool_const(first_branch)
- else_branch_is_bool = self._is_bool_const(else_branch)
- reduced_to = "'var = bool(test)'"
- else:
- return
-
- if not first_branch_is_bool or not else_branch_is_bool:
- return
- if not first_branch.value.value:
- # This is a case that can't be easily simplified and
- # if it can be simplified, it will usually result in a
- # code that's harder to understand and comprehend.
- # Let's take for instance `arg and arg <= 3`. This could theoretically be
- # reduced to `not arg or arg > 3`, but the net result is that now the
- # condition is harder to understand, because it requires understanding of
- # an extra clause:
- # * first, there is the negation of truthness with `not arg`
- # * the second clause is `arg > 3`, which occurs when arg has a
- # a truth value, but it implies that `arg > 3` is equivalent
- # with `arg and arg > 3`, which means that the user must
- # think about this assumption when evaluating `arg > 3`.
- # The original form is easier to grasp.
- return
-
- self.add_message("simplifiable-if-statement", node=node, args=(reduced_to,))
-
- def process_tokens(self, tokens):
- # Process tokens and look for 'if' or 'elif'
- for index, token in enumerate(tokens):
- token_string = token[1]
- if token_string == "elif":
- # AST exists by the time process_tokens is called, so
- # it's safe to assume tokens[index+1]
- # exists. tokens[index+1][2] is the elif's position as
- # reported by CPython and PyPy,
- # tokens[index][2] is the actual position and also is
- # reported by IronPython.
- self._elifs.extend([tokens[index][2], tokens[index + 1][2]])
- elif _is_trailing_comma(tokens, index):
- if self.linter.is_message_enabled("trailing-comma-tuple"):
- self.add_message("trailing-comma-tuple", line=token.start[0])
-
- def leave_module(self, _):
- self._init()
-
- @utils.check_messages("too-many-nested-blocks")
- def visit_tryexcept(self, node):
- self._check_nested_blocks(node)
-
- visit_tryfinally = visit_tryexcept
- visit_while = visit_tryexcept
-
- def _check_redefined_argument_from_local(self, name_node):
- if self._dummy_rgx and self._dummy_rgx.match(name_node.name):
- return
- if not name_node.lineno:
- # Unknown position, maybe it is a manually built AST?
- return
-
- scope = name_node.scope()
- if not isinstance(scope, astroid.FunctionDef):
- return
-
- for defined_argument in scope.args.nodes_of_class(
- astroid.AssignName, skip_klass=(astroid.Lambda,)
- ):
- if defined_argument.name == name_node.name:
- self.add_message(
- "redefined-argument-from-local",
- node=name_node,
- args=(name_node.name,),
- )
-
- @utils.check_messages("redefined-argument-from-local", "too-many-nested-blocks")
- def visit_for(self, node):
- self._check_nested_blocks(node)
-
- for name in node.target.nodes_of_class(astroid.AssignName):
- self._check_redefined_argument_from_local(name)
-
- @utils.check_messages("redefined-argument-from-local")
- def visit_excepthandler(self, node):
- if node.name and isinstance(node.name, astroid.AssignName):
- self._check_redefined_argument_from_local(node.name)
-
- @utils.check_messages("redefined-argument-from-local")
- def visit_with(self, node):
- for _, names in node.items:
- if not names:
- continue
- for name in names.nodes_of_class(astroid.AssignName):
- self._check_redefined_argument_from_local(name)
-
- def _check_superfluous_else(self, node, msg_id, returning_node_class):
- if not node.orelse:
- # Not interested in if statements without else.
- return
-
- if self._is_actual_elif(node):
- # Not interested in elif nodes; only if
- return
-
- if _if_statement_is_always_returning(node, returning_node_class):
- orelse = node.orelse[0]
- followed_by_elif = (orelse.lineno, orelse.col_offset) in self._elifs
- self.add_message(
- msg_id, node=node, args="elif" if followed_by_elif else "else"
- )
-
- def _check_superfluous_else_return(self, node):
- return self._check_superfluous_else(
- node, msg_id="no-else-return", returning_node_class=astroid.Return
- )
-
- def _check_superfluous_else_raise(self, node):
- return self._check_superfluous_else(
- node, msg_id="no-else-raise", returning_node_class=astroid.Raise
- )
-
- def _check_superfluous_else_break(self, node):
- return self._check_superfluous_else(
- node, msg_id="no-else-break", returning_node_class=astroid.Break
- )
-
- def _check_superfluous_else_continue(self, node):
- return self._check_superfluous_else(
- node, msg_id="no-else-continue", returning_node_class=astroid.Continue
- )
-
- def _check_consider_get(self, node):
- def type_and_name_are_equal(node_a, node_b):
- for _type in [astroid.Name, astroid.AssignName]:
- if all(isinstance(_node, _type) for _node in [node_a, node_b]):
- return node_a.name == node_b.name
- if all(isinstance(_node, astroid.Const) for _node in [node_a, node_b]):
- return node_a.value == node_b.value
- return False
-
- if_block_ok = (
- isinstance(node.test, astroid.Compare)
- and len(node.body) == 1
- and isinstance(node.body[0], astroid.Assign)
- and isinstance(node.body[0].value, astroid.Subscript)
- and type_and_name_are_equal(node.body[0].value.value, node.test.ops[0][1])
- and isinstance(node.body[0].value.slice, astroid.Index)
- and type_and_name_are_equal(node.body[0].value.slice.value, node.test.left)
- and len(node.body[0].targets) == 1
- and isinstance(node.body[0].targets[0], astroid.AssignName)
- and isinstance(utils.safe_infer(node.test.ops[0][1]), astroid.Dict)
- )
-
- if if_block_ok and not node.orelse:
- self.add_message("consider-using-get", node=node)
- elif (
- if_block_ok
- and len(node.orelse) == 1
- and isinstance(node.orelse[0], astroid.Assign)
- and type_and_name_are_equal(
- node.orelse[0].targets[0], node.body[0].targets[0]
- )
- and len(node.orelse[0].targets) == 1
- ):
- self.add_message("consider-using-get", node=node)
-
- @utils.check_messages(
- "too-many-nested-blocks",
- "simplifiable-if-statement",
- "no-else-return",
- "no-else-raise",
- "no-else-break",
- "no-else-continue",
- "consider-using-get",
- )
- def visit_if(self, node):
- self._check_simplifiable_if(node)
- self._check_nested_blocks(node)
- self._check_superfluous_else_return(node)
- self._check_superfluous_else_raise(node)
- self._check_superfluous_else_break(node)
- self._check_superfluous_else_continue(node)
- self._check_consider_get(node)
-
- @utils.check_messages("simplifiable-if-expression")
- def visit_ifexp(self, node):
- self._check_simplifiable_ifexp(node)
-
- def _check_simplifiable_ifexp(self, node):
- if not isinstance(node.body, astroid.Const) or not isinstance(
- node.orelse, astroid.Const
- ):
- return
-
- if not isinstance(node.body.value, bool) or not isinstance(
- node.orelse.value, bool
- ):
- return
-
- if isinstance(node.test, astroid.Compare):
- test_reduced_to = "test"
- else:
- test_reduced_to = "bool(test)"
-
- if (node.body.value, node.orelse.value) == (True, False):
- reduced_to = "'{}'".format(test_reduced_to)
- elif (node.body.value, node.orelse.value) == (False, True):
- reduced_to = "'not test'"
- else:
- return
-
- self.add_message("simplifiable-if-expression", node=node, args=(reduced_to,))
-
- @utils.check_messages(
- "too-many-nested-blocks", "inconsistent-return-statements", "useless-return"
- )
- def leave_functiondef(self, node):
- # check left-over nested blocks stack
- self._emit_nested_blocks_message_if_needed(self._nested_blocks)
- # new scope = reinitialize the stack of nested blocks
- self._nested_blocks = []
- #  check consistent return statements
- self._check_consistent_returns(node)
- # check for single return or return None at the end
- self._check_return_at_the_end(node)
- self._return_nodes[node.name] = []
-
- @utils.check_messages("stop-iteration-return")
- def visit_raise(self, node):
- self._check_stop_iteration_inside_generator(node)
-
- def _check_stop_iteration_inside_generator(self, node):
- """Check if an exception of type StopIteration is raised inside a generator"""
- frame = node.frame()
- if not isinstance(frame, astroid.FunctionDef) or not frame.is_generator():
- return
- if utils.node_ignores_exception(node, StopIteration):
- return
- if not node.exc:
- return
- exc = utils.safe_infer(node.exc)
- if exc is None or exc is astroid.Uninferable:
- return
- if self._check_exception_inherit_from_stopiteration(exc):
- self.add_message("stop-iteration-return", node=node)
-
- @staticmethod
- def _check_exception_inherit_from_stopiteration(exc):
- """Return True if the exception node in argument inherit from StopIteration"""
- stopiteration_qname = "{}.StopIteration".format(utils.EXCEPTIONS_MODULE)
- return any(_class.qname() == stopiteration_qname for _class in exc.mro())
-
- def _check_consider_using_comprehension_constructor(self, node):
- if (
- isinstance(node.func, astroid.Name)
- and node.args
- and isinstance(node.args[0], astroid.ListComp)
- ):
- if node.func.name == "dict" and not isinstance(
- node.args[0].elt, astroid.Call
- ):
- message_name = "consider-using-dict-comprehension"
- self.add_message(message_name, node=node)
- elif node.func.name == "set":
- message_name = "consider-using-set-comprehension"
- self.add_message(message_name, node=node)
-
- @utils.check_messages(
- "stop-iteration-return",
- "consider-using-dict-comprehension",
- "consider-using-set-comprehension",
- "consider-using-sys-exit",
- )
- def visit_call(self, node):
- self._check_raising_stopiteration_in_generator_next_call(node)
- self._check_consider_using_comprehension_constructor(node)
- self._check_quit_exit_call(node)
-
- @staticmethod
- def _has_exit_in_scope(scope):
- exit_func = scope.locals.get("exit")
- return bool(
- exit_func and isinstance(exit_func[0], (astroid.ImportFrom, astroid.Import))
- )
-
- def _check_quit_exit_call(self, node):
-
- if isinstance(node.func, astroid.Name) and node.func.name in BUILTIN_EXIT_FUNCS:
- # If we have `exit` imported from `sys` in the current or global scope, exempt this instance.
- local_scope = node.scope()
- if self._has_exit_in_scope(local_scope) or self._has_exit_in_scope(
- node.root()
- ):
- return
- self.add_message("consider-using-sys-exit", node=node)
-
- def _check_raising_stopiteration_in_generator_next_call(self, node):
- """Check if a StopIteration exception is raised by the call to next function
-
- If the next value has a default value, then do not add message.
-
- :param node: Check to see if this Call node is a next function
- :type node: :class:`astroid.node_classes.Call`
- """
-
- def _looks_like_infinite_iterator(param):
- inferred = utils.safe_infer(param)
- if inferred:
- return inferred.qname() in KNOWN_INFINITE_ITERATORS
- return False
-
- if isinstance(node.func, astroid.Attribute):
- # A next() method, which is now what we want.
- return
-
- inferred = utils.safe_infer(node.func)
- if getattr(inferred, "name", "") == "next":
- frame = node.frame()
- # The next builtin can only have up to two
- # positional arguments and no keyword arguments
- has_sentinel_value = len(node.args) > 1
- if (
- isinstance(frame, astroid.FunctionDef)
- and frame.is_generator()
- and not has_sentinel_value
- and not utils.node_ignores_exception(node, StopIteration)
- and not _looks_like_infinite_iterator(node.args[0])
- ):
- self.add_message("stop-iteration-return", node=node)
-
- def _check_nested_blocks(self, node):
- """Update and check the number of nested blocks
- """
- # only check block levels inside functions or methods
- if not isinstance(node.scope(), astroid.FunctionDef):
- return
- # messages are triggered on leaving the nested block. Here we save the
- # stack in case the current node isn't nested in the previous one
- nested_blocks = self._nested_blocks[:]
- if node.parent == node.scope():
- self._nested_blocks = [node]
- else:
- # go through ancestors from the most nested to the less
- for ancestor_node in reversed(self._nested_blocks):
- if ancestor_node == node.parent:
- break
- self._nested_blocks.pop()
- # if the node is an elif, this should not be another nesting level
- if isinstance(node, astroid.If) and self._is_actual_elif(node):
- if self._nested_blocks:
- self._nested_blocks.pop()
- self._nested_blocks.append(node)
-
- # send message only once per group of nested blocks
- if len(nested_blocks) > len(self._nested_blocks):
- self._emit_nested_blocks_message_if_needed(nested_blocks)
-
- def _emit_nested_blocks_message_if_needed(self, nested_blocks):
- if len(nested_blocks) > self.config.max_nested_blocks:
- self.add_message(
- "too-many-nested-blocks",
- node=nested_blocks[0],
- args=(len(nested_blocks), self.config.max_nested_blocks),
- )
-
- @staticmethod
- def _duplicated_isinstance_types(node):
- """Get the duplicated types from the underlying isinstance calls.
-
- :param astroid.BoolOp node: Node which should contain a bunch of isinstance calls.
- :returns: Dictionary of the comparison objects from the isinstance calls,
- to duplicate values from consecutive calls.
- :rtype: dict
- """
- duplicated_objects = set()
- all_types = collections.defaultdict(set)
-
- for call in node.values:
- if not isinstance(call, astroid.Call) or len(call.args) != 2:
- continue
-
- inferred = utils.safe_infer(call.func)
- if not inferred or not utils.is_builtin_object(inferred):
- continue
-
- if inferred.name != "isinstance":
- continue
-
- isinstance_object = call.args[0].as_string()
- isinstance_types = call.args[1]
-
- if isinstance_object in all_types:
- duplicated_objects.add(isinstance_object)
-
- if isinstance(isinstance_types, astroid.Tuple):
- elems = [
- class_type.as_string() for class_type in isinstance_types.itered()
- ]
- else:
- elems = [isinstance_types.as_string()]
- all_types[isinstance_object].update(elems)
-
- # Remove all keys which not duplicated
- return {
- key: value for key, value in all_types.items() if key in duplicated_objects
- }
-
- def _check_consider_merging_isinstance(self, node):
- """Check isinstance calls which can be merged together."""
- if node.op != "or":
- return
-
- first_args = self._duplicated_isinstance_types(node)
- for duplicated_name, class_names in first_args.items():
- names = sorted(name for name in class_names)
- self.add_message(
- "consider-merging-isinstance",
- node=node,
- args=(duplicated_name, ", ".join(names)),
- )
-
- def _check_consider_using_in(self, node):
- allowed_ops = {"or": "==", "and": "!="}
-
- if node.op not in allowed_ops or len(node.values) < 2:
- return
-
- for value in node.values:
- if (
- not isinstance(value, astroid.Compare)
- or len(value.ops) != 1
- or value.ops[0][0] not in allowed_ops[node.op]
- ):
- return
- for comparable in value.left, value.ops[0][1]:
- if isinstance(comparable, astroid.Call):
- return
-
- # Gather variables and values from comparisons
- variables, values = [], []
- for value in node.values:
- variable_set = set()
- for comparable in value.left, value.ops[0][1]:
- if isinstance(comparable, astroid.Name):
- variable_set.add(comparable.as_string())
- values.append(comparable.as_string())
- variables.append(variable_set)
-
- # Look for (common-)variables that occur in all comparisons
- common_variables = reduce(lambda a, b: a.intersection(b), variables)
-
- if not common_variables:
- return
-
- # Gather information for the suggestion
- common_variable = sorted(list(common_variables))[0]
- comprehension = "in" if node.op == "or" else "not in"
- values = list(collections.OrderedDict.fromkeys(values))
- values.remove(common_variable)
- values_string = ", ".join(values) if len(values) != 1 else values[0] + ","
- suggestion = "%s %s (%s)" % (common_variable, comprehension, values_string)
-
- self.add_message("consider-using-in", node=node, args=(suggestion,))
-
- def _check_chained_comparison(self, node):
- """Check if there is any chained comparison in the expression.
-
- Add a refactoring message if a boolOp contains comparison like a < b and b < c,
- which can be chained as a < b < c.
-
- Care is taken to avoid simplifying a < b < c and b < d.
- """
- if node.op != "and" or len(node.values) < 2:
- return
-
- def _find_lower_upper_bounds(comparison_node, uses):
- left_operand = comparison_node.left
- for operator, right_operand in comparison_node.ops:
- for operand in (left_operand, right_operand):
- value = None
- if isinstance(operand, astroid.Name):
- value = operand.name
- elif isinstance(operand, astroid.Const):
- value = operand.value
-
- if value is None:
- continue
-
- if operator in ("<", "<="):
- if operand is left_operand:
- uses[value]["lower_bound"].add(comparison_node)
- elif operand is right_operand:
- uses[value]["upper_bound"].add(comparison_node)
- elif operator in (">", ">="):
- if operand is left_operand:
- uses[value]["upper_bound"].add(comparison_node)
- elif operand is right_operand:
- uses[value]["lower_bound"].add(comparison_node)
- left_operand = right_operand
-
- uses = collections.defaultdict(
- lambda: {"lower_bound": set(), "upper_bound": set()}
- )
- for comparison_node in node.values:
- if isinstance(comparison_node, astroid.Compare):
- _find_lower_upper_bounds(comparison_node, uses)
-
- for _, bounds in uses.items():
- num_shared = len(bounds["lower_bound"].intersection(bounds["upper_bound"]))
- num_lower_bounds = len(bounds["lower_bound"])
- num_upper_bounds = len(bounds["upper_bound"])
- if num_shared < num_lower_bounds and num_shared < num_upper_bounds:
- self.add_message("chained-comparison", node=node)
- break
-
- @utils.check_messages(
- "consider-merging-isinstance", "consider-using-in", "chained-comparison"
- )
- def visit_boolop(self, node):
- self._check_consider_merging_isinstance(node)
- self._check_consider_using_in(node)
- self._check_chained_comparison(node)
-
- @staticmethod
- def _is_simple_assignment(node):
- return (
- isinstance(node, astroid.Assign)
- and len(node.targets) == 1
- and isinstance(node.targets[0], astroid.node_classes.AssignName)
- and isinstance(node.value, astroid.node_classes.Name)
- )
-
- def _check_swap_variables(self, node):
- if not node.next_sibling() or not node.next_sibling().next_sibling():
- return
- assignments = [node, node.next_sibling(), node.next_sibling().next_sibling()]
- if not all(self._is_simple_assignment(node) for node in assignments):
- return
- if any(node in self._reported_swap_nodes for node in assignments):
- return
- left = [node.targets[0].name for node in assignments]
- right = [node.value.name for node in assignments]
- if left[0] == right[-1] and left[1:] == right[:-1]:
- self._reported_swap_nodes.update(assignments)
- message = "consider-swap-variables"
- self.add_message(message, node=node)
-
- @utils.check_messages(
- "simplify-boolean-expression",
- "consider-using-ternary",
- "consider-swap-variables",
- )
- def visit_assign(self, node):
- self._check_swap_variables(node)
- if self._is_and_or_ternary(node.value):
- cond, truth_value, false_value = self._and_or_ternary_arguments(node.value)
- else:
- return
-
- if all(
- isinstance(value, astroid.Compare) for value in (truth_value, false_value)
- ):
- return
-
- inferred_truth_value = utils.safe_infer(truth_value)
- if inferred_truth_value in (None, astroid.Uninferable):
- truth_boolean_value = True
- else:
- truth_boolean_value = truth_value.bool_value()
-
- if truth_boolean_value is False:
- message = "simplify-boolean-expression"
- suggestion = false_value.as_string()
- else:
- message = "consider-using-ternary"
- suggestion = "{truth} if {cond} else {false}".format(
- truth=truth_value.as_string(),
- cond=cond.as_string(),
- false=false_value.as_string(),
- )
- self.add_message(message, node=node, args=(suggestion,))
-
- visit_return = visit_assign
-
- def _check_consider_using_join(self, aug_assign):
- """
- We start with the augmented assignment and work our way upwards.
- Names of variables for nodes if match successful:
- result = '' # assign
- for number in ['1', '2', '3'] # for_loop
- result += number # aug_assign
- """
- for_loop = aug_assign.parent
- if not isinstance(for_loop, astroid.For) or len(for_loop.body) > 1:
- return
- assign = for_loop.previous_sibling()
- if not isinstance(assign, astroid.Assign):
- return
- result_assign_names = {
- target.name
- for target in assign.targets
- if isinstance(target, astroid.AssignName)
- }
-
- is_concat_loop = (
- aug_assign.op == "+="
- and isinstance(aug_assign.target, astroid.AssignName)
- and len(for_loop.body) == 1
- and aug_assign.target.name in result_assign_names
- and isinstance(assign.value, astroid.Const)
- and isinstance(assign.value.value, str)
- and isinstance(aug_assign.value, astroid.Name)
- and aug_assign.value.name == for_loop.target.name
- )
- if is_concat_loop:
- self.add_message("consider-using-join", node=aug_assign)
-
- @utils.check_messages("consider-using-join")
- def visit_augassign(self, node):
- self._check_consider_using_join(node)
-
- @utils.check_messages("unnecessary-comprehension")
- def visit_comprehension(self, node):
- self._check_unnecessary_comprehension(node)
-
- def _check_unnecessary_comprehension(self, node):
- if (
- isinstance(node.parent, astroid.GeneratorExp)
- or len(node.ifs) != 0
- or len(node.parent.generators) != 1
- or node.is_async
- ):
- return
-
- if (
- isinstance(node.parent, astroid.DictComp)
- and isinstance(node.parent.key, astroid.Name)
- and isinstance(node.parent.value, astroid.Name)
- and isinstance(node.target, astroid.Tuple)
- and all(isinstance(elt, astroid.AssignName) for elt in node.target.elts)
- ):
- expr_list = [node.parent.key.name, node.parent.value.name]
- target_list = [elt.name for elt in node.target.elts]
-
- elif isinstance(node.parent, (astroid.ListComp, astroid.SetComp)):
- expr = node.parent.elt
- if isinstance(expr, astroid.Name):
- expr_list = expr.name
- elif isinstance(expr, astroid.Tuple):
- if any(not isinstance(elt, astroid.Name) for elt in expr.elts):
- return
- expr_list = [elt.name for elt in expr.elts]
- else:
- expr_list = []
- target = node.parent.generators[0].target
- target_list = (
- target.name
- if isinstance(target, astroid.AssignName)
- else (
- [
- elt.name
- for elt in target.elts
- if isinstance(elt, astroid.AssignName)
- ]
- if isinstance(target, astroid.Tuple)
- else []
- )
- )
- else:
- return
- if expr_list == target_list != []:
- self.add_message("unnecessary-comprehension", node=node)
-
- @staticmethod
- def _is_and_or_ternary(node):
- """
- Returns true if node is 'condition and true_value or false_value' form.
-
- All of: condition, true_value and false_value should not be a complex boolean expression
- """
- return (
- isinstance(node, astroid.BoolOp)
- and node.op == "or"
- and len(node.values) == 2
- and isinstance(node.values[0], astroid.BoolOp)
- and not isinstance(node.values[1], astroid.BoolOp)
- and node.values[0].op == "and"
- and not isinstance(node.values[0].values[1], astroid.BoolOp)
- and len(node.values[0].values) == 2
- )
-
- @staticmethod
- def _and_or_ternary_arguments(node):
- false_value = node.values[1]
- condition, true_value = node.values[0].values
- return condition, true_value, false_value
-
- def visit_functiondef(self, node):
- self._return_nodes[node.name] = list(
- node.nodes_of_class(astroid.Return, skip_klass=astroid.FunctionDef)
- )
-
- def _check_consistent_returns(self, node):
- """Check that all return statements inside a function are consistent.
-
- Return statements are consistent if:
- - all returns are explicit and if there is no implicit return;
- - all returns are empty and if there is, possibly, an implicit return.
-
- Args:
- node (astroid.FunctionDef): the function holding the return statements.
-
- """
- # explicit return statements are those with a not None value
- explicit_returns = [
- _node for _node in self._return_nodes[node.name] if _node.value is not None
- ]
- if not explicit_returns:
- return
- if len(explicit_returns) == len(
- self._return_nodes[node.name]
- ) and self._is_node_return_ended(node):
- return
- self.add_message("inconsistent-return-statements", node=node)
-
- def _is_node_return_ended(self, node):
- """Check if the node ends with an explicit return statement.
-
- Args:
- node (astroid.NodeNG): node to be checked.
-
- Returns:
- bool: True if the node ends with an explicit statement, False otherwise.
-
- """
- #  Recursion base case
- if isinstance(node, astroid.Return):
- return True
- if isinstance(node, astroid.Call):
- try:
- funcdef_node = node.func.inferred()[0]
- if self._is_function_def_never_returning(funcdef_node):
- return True
- except astroid.InferenceError:
- pass
- # Avoid the check inside while loop as we don't know
- #  if they will be completed
- if isinstance(node, astroid.While):
- return True
- if isinstance(node, astroid.Raise):
- # a Raise statement doesn't need to end with a return statement
- # but if the exception raised is handled, then the handler has to
- # ends with a return statement
- if not node.exc:
- # Ignore bare raises
- return True
- if not utils.is_node_inside_try_except(node):
- # If the raise statement is not inside a try/except statement
- #  then the exception is raised and cannot be caught. No need
- #  to infer it.
- return True
- exc = utils.safe_infer(node.exc)
- if exc is None or exc is astroid.Uninferable:
- return False
- exc_name = exc.pytype().split(".")[-1]
- handlers = utils.get_exception_handlers(node, exc_name)
- handlers = list(handlers) if handlers is not None else []
- if handlers:
- # among all the handlers handling the exception at least one
- # must end with a return statement
- return any(
- self._is_node_return_ended(_handler) for _handler in handlers
- )
- # if no handlers handle the exception then it's ok
- return True
- if isinstance(node, astroid.If):
- # if statement is returning if there are exactly two return statements in its
- #  children : one for the body part, the other for the orelse part
- # Do not check if inner function definition are return ended.
- is_orelse_returning = any(
- self._is_node_return_ended(_ore)
- for _ore in node.orelse
- if not isinstance(_ore, astroid.FunctionDef)
- )
- is_if_returning = any(
- self._is_node_return_ended(_ifn)
- for _ifn in node.body
- if not isinstance(_ifn, astroid.FunctionDef)
- )
- return is_if_returning and is_orelse_returning
- #  recurses on the children of the node except for those which are except handler
- # because one cannot be sure that the handler will really be used
- return any(
- self._is_node_return_ended(_child)
- for _child in node.get_children()
- if not isinstance(_child, astroid.ExceptHandler)
- )
-
- def _is_function_def_never_returning(self, node):
- """Return True if the function never returns. False otherwise.
-
- Args:
- node (astroid.FunctionDef): function definition node to be analyzed.
-
- Returns:
- bool: True if the function never returns, False otherwise.
- """
- try:
- return node.qname() in self._never_returning_functions
- except TypeError:
- return False
-
- def _check_return_at_the_end(self, node):
- """Check for presence of a *single* return statement at the end of a
- function. "return" or "return None" are useless because None is the
- default return type if they are missing.
-
- NOTE: produces a message only if there is a single return statement
- in the function body. Otherwise _check_consistent_returns() is called!
- Per its implementation and PEP8 we can have a "return None" at the end
- of the function body if there are other return statements before that!
- """
- if len(self._return_nodes[node.name]) > 1:
- return
- if len(node.body) <= 1:
- return
-
- last = node.body[-1]
- if isinstance(last, astroid.Return):
- # e.g. "return"
- if last.value is None:
- self.add_message("useless-return", node=node)
- # return None"
- elif isinstance(last.value, astroid.Const) and (last.value.value is None):
- self.add_message("useless-return", node=node)
-
-
-class RecommandationChecker(checkers.BaseChecker):
- __implements__ = (interfaces.IAstroidChecker,)
- name = "refactoring"
- msgs = {
- "C0200": (
- "Consider using enumerate instead of iterating with range and len",
- "consider-using-enumerate",
- "Emitted when code that iterates with range and len is "
- "encountered. Such code can be simplified by using the "
- "enumerate builtin.",
- ),
- "C0201": (
- "Consider iterating the dictionary directly instead of calling .keys()",
- "consider-iterating-dictionary",
- "Emitted when the keys of a dictionary are iterated through the .keys() "
- "method. It is enough to just iterate through the dictionary itself, as "
- 'in "for key in dictionary".',
- ),
- }
-
- @staticmethod
- def _is_builtin(node, function):
- inferred = utils.safe_infer(node)
- if not inferred:
- return False
- return utils.is_builtin_object(inferred) and inferred.name == function
-
- @utils.check_messages("consider-iterating-dictionary")
- def visit_call(self, node):
- if not isinstance(node.func, astroid.Attribute):
- return
- if node.func.attrname != "keys":
- return
- if not isinstance(node.parent, (astroid.For, astroid.Comprehension)):
- return
-
- inferred = utils.safe_infer(node.func)
- if not isinstance(inferred, astroid.BoundMethod) or not isinstance(
- inferred.bound, astroid.Dict
- ):
- return
-
- if isinstance(node.parent, (astroid.For, astroid.Comprehension)):
- self.add_message("consider-iterating-dictionary", node=node)
-
- @utils.check_messages("consider-using-enumerate")
- def visit_for(self, node):
- """Emit a convention whenever range and len are used for indexing."""
- # Verify that we have a `range([start], len(...), [stop])` call and
- # that the object which is iterated is used as a subscript in the
- # body of the for.
-
- # Is it a proper range call?
- if not isinstance(node.iter, astroid.Call):
- return
- if not self._is_builtin(node.iter.func, "range"):
- return
- if len(node.iter.args) == 2 and not _is_constant_zero(node.iter.args[0]):
- return
- if len(node.iter.args) > 2:
- return
-
- # Is it a proper len call?
- if not isinstance(node.iter.args[-1], astroid.Call):
- return
- second_func = node.iter.args[-1].func
- if not self._is_builtin(second_func, "len"):
- return
- len_args = node.iter.args[-1].args
- if not len_args or len(len_args) != 1:
- return
- iterating_object = len_args[0]
- if not isinstance(iterating_object, astroid.Name):
- return
- # If we're defining __iter__ on self, enumerate won't work
- scope = node.scope()
- if iterating_object.name == "self" and scope.name == "__iter__":
- return
-
- # Verify that the body of the for loop uses a subscript
- # with the object that was iterated. This uses some heuristics
- # in order to make sure that the same object is used in the
- # for body.
- for child in node.body:
- for subscript in child.nodes_of_class(astroid.Subscript):
- if not isinstance(subscript.value, astroid.Name):
- continue
- if not isinstance(subscript.slice, astroid.Index):
- continue
- if not isinstance(subscript.slice.value, astroid.Name):
- continue
- if subscript.slice.value.name != node.target.name:
- continue
- if iterating_object.name != subscript.value.name:
- continue
- if subscript.value.scope() != node.scope():
- # Ignore this subscript if it's not in the same
- # scope. This means that in the body of the for
- # loop, another scope was created, where the same
- # name for the iterating object was used.
- continue
- self.add_message("consider-using-enumerate", node=node)
- return
-
-
-class NotChecker(checkers.BaseChecker):
- """checks for too many not in comparison expressions
-
- - "not not" should trigger a warning
- - "not" followed by a comparison should trigger a warning
- """
-
- __implements__ = (interfaces.IAstroidChecker,)
- msgs = {
- "C0113": (
- 'Consider changing "%s" to "%s"',
- "unneeded-not",
- "Used when a boolean expression contains an unneeded negation.",
- )
- }
- name = "refactoring"
- reverse_op = {
- "<": ">=",
- "<=": ">",
- ">": "<=",
- ">=": "<",
- "==": "!=",
- "!=": "==",
- "in": "not in",
- "is": "is not",
- }
- # sets are not ordered, so for example "not set(LEFT_VALS) <= set(RIGHT_VALS)" is
- # not equivalent to "set(LEFT_VALS) > set(RIGHT_VALS)"
- skipped_nodes = (astroid.Set,)
- # 'builtins' py3, '__builtin__' py2
- skipped_classnames = [
- "%s.%s" % (builtins.__name__, qname) for qname in ("set", "frozenset")
- ]
-
- @utils.check_messages("unneeded-not")
- def visit_unaryop(self, node):
- if node.op != "not":
- return
- operand = node.operand
-
- if isinstance(operand, astroid.UnaryOp) and operand.op == "not":
- self.add_message(
- "unneeded-not",
- node=node,
- args=(node.as_string(), operand.operand.as_string()),
- )
- elif isinstance(operand, astroid.Compare):
- left = operand.left
- # ignore multiple comparisons
- if len(operand.ops) > 1:
- return
- operator, right = operand.ops[0]
- if operator not in self.reverse_op:
- return
- # Ignore __ne__ as function of __eq__
- frame = node.frame()
- if frame.name == "__ne__" and operator == "==":
- return
- for _type in (utils.node_type(left), utils.node_type(right)):
- if not _type:
- return
- if isinstance(_type, self.skipped_nodes):
- return
- if (
- isinstance(_type, astroid.Instance)
- and _type.qname() in self.skipped_classnames
- ):
- return
- suggestion = "%s %s %s" % (
- left.as_string(),
- self.reverse_op[operator],
- right.as_string(),
- )
- self.add_message(
- "unneeded-not", node=node, args=(node.as_string(), suggestion)
- )
-
-
-class LenChecker(checkers.BaseChecker):
- """Checks for incorrect usage of len() inside conditions.
- Pep8 states:
- For sequences, (strings, lists, tuples), use the fact that empty sequences are false.
-
- Yes: if not seq:
- if seq:
-
- No: if len(seq):
- if not len(seq):
-
- Problems detected:
- * if len(sequence):
- * if not len(sequence):
- * elif len(sequence):
- * elif not len(sequence):
- * while len(sequence):
- * while not len(sequence):
- * assert len(sequence):
- * assert not len(sequence):
- """
-
- __implements__ = (interfaces.IAstroidChecker,)
-
- # configuration section name
- name = "refactoring"
- msgs = {
- "C1801": (
- "Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty",
- "len-as-condition",
- "Used when Pylint detects that len(sequence) is being used "
- "without explicit comparison inside a condition to determine if a sequence is empty. "
- "Instead of coercing the length to a boolean, either "
- "rely on the fact that empty sequences are false or "
- "compare the length against a scalar.",
- )
- }
-
- priority = -2
- options = ()
-
- @utils.check_messages("len-as-condition")
- def visit_call(self, node):
- # a len(S) call is used inside a test condition
- # could be if, while, assert or if expression statement
- # e.g. `if len(S):`
- if _is_len_call(node):
- # the len() call could also be nested together with other
- # boolean operations, e.g. `if z or len(x):`
- parent = node.parent
- while isinstance(parent, astroid.BoolOp):
- parent = parent.parent
-
- # we're finally out of any nested boolean operations so check if
- # this len() call is part of a test condition
- if not _node_is_test_condition(parent):
- return
- if not (node is parent.test or parent.test.parent_of(node)):
- return
- self.add_message("len-as-condition", node=node)
-
- @utils.check_messages("len-as-condition")
- def visit_unaryop(self, node):
- """`not len(S)` must become `not S` regardless if the parent block
- is a test condition or something else (boolean expression)
- e.g. `if not len(S):`"""
- if (
- isinstance(node, astroid.UnaryOp)
- and node.op == "not"
- and _is_len_call(node.operand)
- ):
- self.add_message("len-as-condition", node=node)
-
-
-def register(linter):
- """Required method to auto register this checker."""
- linter.register_checker(RefactoringChecker(linter))
- linter.register_checker(NotChecker(linter))
- linter.register_checker(RecommandationChecker(linter))
- linter.register_checker(LenChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/similar.py b/venv/Lib/site-packages/pylint/checkers/similar.py
deleted file mode 100644
index 019b55f..0000000
--- a/venv/Lib/site-packages/pylint/checkers/similar.py
+++ /dev/null
@@ -1,452 +0,0 @@
-# Copyright (c) 2006, 2008-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2012 Ry4an Brase <ry4an-hg@ry4an.org>
-# Copyright (c) 2012 Google, Inc.
-# Copyright (c) 2012 Anthony VEREZ <anthony.verez.external@cassidian.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 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2017 Anthony Sottile <asottile@umich.edu>
-# Copyright (c) 2017 Mikhail Fesenko <proggga@gmail.com>
-# Copyright (c) 2018 ssolanki <sushobhitsolanki@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
-
-# pylint: disable=redefined-builtin
-"""a similarities / code duplication command line tool and pylint checker
-"""
-
-import sys
-from collections import defaultdict
-from getopt import getopt
-from itertools import groupby
-
-import astroid
-
-from pylint.checkers import BaseChecker, table_lines_from_stats
-from pylint.interfaces import IRawChecker
-from pylint.reporters.ureports.nodes import Table
-from pylint.utils import decoding_stream
-
-
-class Similar:
- """finds copy-pasted lines of code in a project"""
-
- def __init__(
- self,
- min_lines=4,
- ignore_comments=False,
- ignore_docstrings=False,
- ignore_imports=False,
- ):
- self.min_lines = min_lines
- self.ignore_comments = ignore_comments
- self.ignore_docstrings = ignore_docstrings
- self.ignore_imports = ignore_imports
- self.linesets = []
-
- def append_stream(self, streamid, stream, encoding=None):
- """append a file to search for similarities"""
- if encoding is None:
- readlines = stream.readlines
- else:
- readlines = decoding_stream(stream, encoding).readlines
- try:
- self.linesets.append(
- LineSet(
- streamid,
- readlines(),
- self.ignore_comments,
- self.ignore_docstrings,
- self.ignore_imports,
- )
- )
- except UnicodeDecodeError:
- pass
-
- def run(self):
- """start looking for similarities and display results on stdout"""
- self._display_sims(self._compute_sims())
-
- def _compute_sims(self):
- """compute similarities in appended files"""
- no_duplicates = defaultdict(list)
- for num, lineset1, idx1, lineset2, idx2 in self._iter_sims():
- duplicate = no_duplicates[num]
- for couples in duplicate:
- if (lineset1, idx1) in couples or (lineset2, idx2) in couples:
- couples.add((lineset1, idx1))
- couples.add((lineset2, idx2))
- break
- else:
- duplicate.append({(lineset1, idx1), (lineset2, idx2)})
- sims = []
- for num, ensembles in no_duplicates.items():
- for couples in ensembles:
- sims.append((num, couples))
- sims.sort()
- sims.reverse()
- return sims
-
- def _display_sims(self, sims):
- """display computed similarities on stdout"""
- nb_lignes_dupliquees = 0
- for num, couples in sims:
- print()
- print(num, "similar lines in", len(couples), "files")
- couples = sorted(couples)
- lineset = idx = None
- for lineset, idx in couples:
- print("==%s:%s" % (lineset.name, idx))
- if lineset:
- for line in lineset._real_lines[idx : idx + num]:
- print(" ", line.rstrip())
- nb_lignes_dupliquees += num * (len(couples) - 1)
- nb_total_lignes = sum([len(lineset) for lineset in self.linesets])
- print(
- "TOTAL lines=%s duplicates=%s percent=%.2f"
- % (
- nb_total_lignes,
- nb_lignes_dupliquees,
- nb_lignes_dupliquees * 100.0 / nb_total_lignes,
- )
- )
-
- def _find_common(self, lineset1, lineset2):
- """find similarities in the two given linesets"""
- lines1 = lineset1.enumerate_stripped
- lines2 = lineset2.enumerate_stripped
- find = lineset2.find
- index1 = 0
- min_lines = self.min_lines
- while index1 < len(lineset1):
- skip = 1
- num = 0
- for index2 in find(lineset1[index1]):
- non_blank = 0
- for num, ((_, line1), (_, line2)) in enumerate(
- zip(lines1(index1), lines2(index2))
- ):
- if line1 != line2:
- if non_blank > min_lines:
- yield num, lineset1, index1, lineset2, index2
- skip = max(skip, num)
- break
- if line1:
- non_blank += 1
- else:
- # we may have reach the end
- num += 1
- if non_blank > min_lines:
- yield num, lineset1, index1, lineset2, index2
- skip = max(skip, num)
- index1 += skip
-
- def _iter_sims(self):
- """iterate on similarities among all files, by making a cartesian
- product
- """
- for idx, lineset in enumerate(self.linesets[:-1]):
- for lineset2 in self.linesets[idx + 1 :]:
- for sim in self._find_common(lineset, lineset2):
- yield sim
-
-
-def stripped_lines(lines, ignore_comments, ignore_docstrings, ignore_imports):
- """return lines with leading/trailing whitespace and any ignored code
- features removed
- """
- if ignore_imports:
- tree = astroid.parse("".join(lines))
- node_is_import_by_lineno = (
- (node.lineno, isinstance(node, (astroid.Import, astroid.ImportFrom)))
- for node in tree.body
- )
- line_begins_import = {
- lineno: all(is_import for _, is_import in node_is_import_group)
- for lineno, node_is_import_group in groupby(
- node_is_import_by_lineno, key=lambda x: x[0]
- )
- }
- current_line_is_import = False
-
- strippedlines = []
- docstring = None
- for lineno, line in enumerate(lines, start=1):
- line = line.strip()
- if ignore_docstrings:
- if not docstring and any(
- line.startswith(i) for i in ['"""', "'''", 'r"""', "r'''"]
- ):
- docstring = line[:3]
- line = line[3:]
- if docstring:
- if line.endswith(docstring):
- docstring = None
- line = ""
- if ignore_imports:
- current_line_is_import = line_begins_import.get(
- lineno, current_line_is_import
- )
- if current_line_is_import:
- line = ""
- if ignore_comments:
- line = line.split("#", 1)[0].strip()
- strippedlines.append(line)
- return strippedlines
-
-
-class LineSet:
- """Holds and indexes all the lines of a single source file"""
-
- def __init__(
- self,
- name,
- lines,
- ignore_comments=False,
- ignore_docstrings=False,
- ignore_imports=False,
- ):
- self.name = name
- self._real_lines = lines
- self._stripped_lines = stripped_lines(
- lines, ignore_comments, ignore_docstrings, ignore_imports
- )
- self._index = self._mk_index()
-
- def __str__(self):
- return "<Lineset for %s>" % self.name
-
- def __len__(self):
- return len(self._real_lines)
-
- def __getitem__(self, index):
- return self._stripped_lines[index]
-
- def __lt__(self, other):
- return self.name < other.name
-
- def __hash__(self):
- return id(self)
-
- def enumerate_stripped(self, start_at=0):
- """return an iterator on stripped lines, starting from a given index
- if specified, else 0
- """
- idx = start_at
- if start_at:
- lines = self._stripped_lines[start_at:]
- else:
- lines = self._stripped_lines
- for line in lines:
- # if line:
- yield idx, line
- idx += 1
-
- def find(self, stripped_line):
- """return positions of the given stripped line in this set"""
- return self._index.get(stripped_line, ())
-
- def _mk_index(self):
- """create the index for this set"""
- index = defaultdict(list)
- for line_no, line in enumerate(self._stripped_lines):
- if line:
- index[line].append(line_no)
- return index
-
-
-MSGS = {
- "R0801": (
- "Similar lines in %s files\n%s",
- "duplicate-code",
- "Indicates that a set of similar lines has been detected "
- "among multiple file. This usually means that the code should "
- "be refactored to avoid this duplication.",
- )
-}
-
-
-def report_similarities(sect, stats, old_stats):
- """make a layout with some stats about duplication"""
- lines = ["", "now", "previous", "difference"]
- lines += table_lines_from_stats(
- stats, old_stats, ("nb_duplicated_lines", "percent_duplicated_lines")
- )
- sect.append(Table(children=lines, cols=4, rheaders=1, cheaders=1))
-
-
-# wrapper to get a pylint checker from the similar class
-class SimilarChecker(BaseChecker, Similar):
- """checks for similarities and duplicated code. This computation may be
- memory / CPU intensive, so you should disable it if you experiment some
- problems.
- """
-
- __implements__ = (IRawChecker,)
- # configuration section name
- name = "similarities"
- # messages
- msgs = MSGS
- # configuration options
- # for available dict keys/values see the optik parser 'add_option' method
- options = (
- (
- "min-similarity-lines", # type: ignore
- {
- "default": 4,
- "type": "int",
- "metavar": "<int>",
- "help": "Minimum lines number of a similarity.",
- },
- ),
- (
- "ignore-comments",
- {
- "default": True,
- "type": "yn",
- "metavar": "<y or n>",
- "help": "Ignore comments when computing similarities.",
- },
- ),
- (
- "ignore-docstrings",
- {
- "default": True,
- "type": "yn",
- "metavar": "<y or n>",
- "help": "Ignore docstrings when computing similarities.",
- },
- ),
- (
- "ignore-imports",
- {
- "default": False,
- "type": "yn",
- "metavar": "<y or n>",
- "help": "Ignore imports when computing similarities.",
- },
- ),
- )
- # reports
- reports = (("RP0801", "Duplication", report_similarities),) # type: ignore
-
- def __init__(self, linter=None):
- BaseChecker.__init__(self, linter)
- Similar.__init__(
- self, min_lines=4, ignore_comments=True, ignore_docstrings=True
- )
- self.stats = None
-
- def set_option(self, optname, value, action=None, optdict=None):
- """method called to set an option (registered in the options list)
-
- overridden to report options setting to Similar
- """
- BaseChecker.set_option(self, optname, value, action, optdict)
- if optname == "min-similarity-lines":
- self.min_lines = self.config.min_similarity_lines
- elif optname == "ignore-comments":
- self.ignore_comments = self.config.ignore_comments
- elif optname == "ignore-docstrings":
- self.ignore_docstrings = self.config.ignore_docstrings
- elif optname == "ignore-imports":
- self.ignore_imports = self.config.ignore_imports
-
- def open(self):
- """init the checkers: reset linesets and statistics information"""
- self.linesets = []
- self.stats = self.linter.add_stats(
- nb_duplicated_lines=0, percent_duplicated_lines=0
- )
-
- def process_module(self, node):
- """process a module
-
- the module's content is accessible via the stream object
-
- stream must implement the readlines method
- """
- with node.stream() as stream:
- self.append_stream(self.linter.current_name, stream, node.file_encoding)
-
- def close(self):
- """compute and display similarities on closing (i.e. end of parsing)"""
- total = sum(len(lineset) for lineset in self.linesets)
- duplicated = 0
- stats = self.stats
- for num, couples in self._compute_sims():
- msg = []
- lineset = idx = None
- for lineset, idx in couples:
- msg.append("==%s:%s" % (lineset.name, idx))
- msg.sort()
-
- if lineset:
- for line in lineset._real_lines[idx : idx + num]:
- msg.append(line.rstrip())
-
- self.add_message("R0801", args=(len(couples), "\n".join(msg)))
- duplicated += num * (len(couples) - 1)
- stats["nb_duplicated_lines"] = duplicated
- stats["percent_duplicated_lines"] = total and duplicated * 100.0 / total
-
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(SimilarChecker(linter))
-
-
-def usage(status=0):
- """display command line usage information"""
- print("finds copy pasted blocks in a set of files")
- print()
- print(
- "Usage: symilar [-d|--duplicates min_duplicated_lines] \
-[-i|--ignore-comments] [--ignore-docstrings] [--ignore-imports] file1..."
- )
- sys.exit(status)
-
-
-def Run(argv=None):
- """standalone command line access point"""
- if argv is None:
- argv = sys.argv[1:]
-
- s_opts = "hdi"
- l_opts = (
- "help",
- "duplicates=",
- "ignore-comments",
- "ignore-imports",
- "ignore-docstrings",
- )
- min_lines = 4
- ignore_comments = False
- ignore_docstrings = False
- ignore_imports = False
- opts, args = getopt(argv, s_opts, l_opts)
- for opt, val in opts:
- if opt in ("-d", "--duplicates"):
- min_lines = int(val)
- elif opt in ("-h", "--help"):
- usage()
- elif opt in ("-i", "--ignore-comments"):
- ignore_comments = True
- elif opt in ("--ignore-docstrings",):
- ignore_docstrings = True
- elif opt in ("--ignore-imports",):
- ignore_imports = True
- if not args:
- usage(1)
- sim = Similar(min_lines, ignore_comments, ignore_docstrings, ignore_imports)
- for filename in args:
- with open(filename) as stream:
- sim.append_stream(filename, stream)
- sim.run()
- sys.exit(0)
-
-
-if __name__ == "__main__":
- Run()
diff --git a/venv/Lib/site-packages/pylint/checkers/spelling.py b/venv/Lib/site-packages/pylint/checkers/spelling.py
deleted file mode 100644
index b1a5334..0000000
--- a/venv/Lib/site-packages/pylint/checkers/spelling.py
+++ /dev/null
@@ -1,411 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2014-2017 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
-# Copyright (c) 2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2015 Pavel Roskin <proski@gnu.org>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016-2017 Pedro Algarvio <pedro@algarvio.me>
-# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
-# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2017 Mikhail Fesenko <proggga@gmail.com>
-# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
-# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
-# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
-
-# 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
-
-"""Checker for spelling errors in comments and docstrings.
-"""
-
-import os
-import re
-import tokenize
-
-from pylint.checkers import BaseTokenChecker
-from pylint.checkers.utils import check_messages
-from pylint.interfaces import IAstroidChecker, ITokenChecker
-
-try:
- import enchant
- from enchant.tokenize import ( # type: ignore
- get_tokenizer,
- Chunker,
- Filter,
- EmailFilter,
- URLFilter,
- WikiWordFilter,
- )
-except ImportError:
- enchant = None
- # pylint: disable=no-init
- class Filter: # type: ignore
- def _skip(self, word):
- raise NotImplementedError
-
- class Chunker: # type: ignore
- pass
-
-
-if enchant is not None:
- br = enchant.Broker()
- dicts = br.list_dicts()
- dict_choices = [""] + [d[0] for d in dicts]
- dicts = ["%s (%s)" % (d[0], d[1].name) for d in dicts]
- dicts = ", ".join(dicts)
- instr = ""
-else:
- dicts = "none"
- dict_choices = [""]
- instr = " To make it work, install the python-enchant package."
-
-
-class WordsWithDigigtsFilter(Filter):
- """Skips words with digits.
- """
-
- def _skip(self, word):
- for char in word:
- if char.isdigit():
- return True
- return False
-
-
-class WordsWithUnderscores(Filter):
- """Skips words with underscores.
-
- They are probably function parameter names.
- """
-
- def _skip(self, word):
- return "_" in word
-
-
-class CamelCasedWord(Filter):
- r"""Filter skipping over camelCasedWords.
- This filter skips any words matching the following regular expression:
-
- ^([a-z]\w+[A-Z]+\w+)
-
- That is, any words that are camelCasedWords.
- """
- _pattern = re.compile(r"^([a-z]+([\d]|[A-Z])(?:\w+)?)")
-
- def _skip(self, word):
- return bool(self._pattern.match(word))
-
-
-class SphinxDirectives(Filter):
- r"""Filter skipping over Sphinx Directives.
- This filter skips any words matching the following regular expression:
-
- ^:([a-z]+):`([^`]+)(`)?
-
- That is, for example, :class:`BaseQuery`
- """
- # The final ` in the pattern is optional because enchant strips it out
- _pattern = re.compile(r"^:([a-z]+):`([^`]+)(`)?")
-
- def _skip(self, word):
- return bool(self._pattern.match(word))
-
-
-class ForwardSlashChunkder(Chunker):
- """
- This chunker allows splitting words like 'before/after' into 'before' and 'after'
- """
-
- def next(self):
- while True:
- if not self._text:
- raise StopIteration()
- if "/" not in self._text:
- text = self._text
- self._offset = 0
- self._text = ""
- return (text, 0)
- pre_text, post_text = self._text.split("/", 1)
- self._text = post_text
- self._offset = 0
- if (
- not pre_text
- or not post_text
- or not pre_text[-1].isalpha()
- or not post_text[0].isalpha()
- ):
- self._text = ""
- self._offset = 0
- return (pre_text + "/" + post_text, 0)
- return (pre_text, 0)
-
- def _next(self):
- while True:
- if "/" not in self._text:
- return (self._text, 0)
- pre_text, post_text = self._text.split("/", 1)
- if not pre_text or not post_text:
- break
- if not pre_text[-1].isalpha() or not post_text[0].isalpha():
- raise StopIteration()
- self._text = pre_text + " " + post_text
- raise StopIteration()
-
-
-class SpellingChecker(BaseTokenChecker):
- """Check spelling in comments and docstrings"""
-
- __implements__ = (ITokenChecker, IAstroidChecker)
- name = "spelling"
- msgs = {
- "C0401": (
- "Wrong spelling of a word '%s' in a comment:\n%s\n"
- "%s\nDid you mean: '%s'?",
- "wrong-spelling-in-comment",
- "Used when a word in comment is not spelled correctly.",
- ),
- "C0402": (
- "Wrong spelling of a word '%s' in a docstring:\n%s\n"
- "%s\nDid you mean: '%s'?",
- "wrong-spelling-in-docstring",
- "Used when a word in docstring is not spelled correctly.",
- ),
- "C0403": (
- "Invalid characters %r in a docstring",
- "invalid-characters-in-docstring",
- "Used when a word in docstring cannot be checked by enchant.",
- ),
- }
- options = (
- (
- "spelling-dict",
- {
- "default": "",
- "type": "choice",
- "metavar": "<dict name>",
- "choices": dict_choices,
- "help": "Spelling dictionary name. "
- "Available dictionaries: %s.%s" % (dicts, instr),
- },
- ),
- (
- "spelling-ignore-words",
- {
- "default": "",
- "type": "string",
- "metavar": "<comma separated words>",
- "help": "List of comma separated words that " "should not be checked.",
- },
- ),
- (
- "spelling-private-dict-file",
- {
- "default": "",
- "type": "string",
- "metavar": "<path to file>",
- "help": "A path to a file that contains the private "
- "dictionary; one word per line.",
- },
- ),
- (
- "spelling-store-unknown-words",
- {
- "default": "n",
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": "Tells whether to store unknown words to the "
- "private dictionary (see the "
- "--spelling-private-dict-file option) instead of "
- "raising a message.",
- },
- ),
- (
- "max-spelling-suggestions",
- {
- "default": 4,
- "type": "int",
- "metavar": "N",
- "help": "Limits count of emitted suggestions for " "spelling mistakes.",
- },
- ),
- )
-
- def open(self):
- self.initialized = False
- self.private_dict_file = None
-
- if enchant is None:
- return
- dict_name = self.config.spelling_dict
- if not dict_name:
- return
-
- self.ignore_list = [
- w.strip() for w in self.config.spelling_ignore_words.split(",")
- ]
- # "param" appears in docstring in param description and
- # "pylint" appears in comments in pylint pragmas.
- self.ignore_list.extend(["param", "pylint"])
-
- # Expand tilde to allow e.g. spelling-private-dict-file = ~/.pylintdict
- if self.config.spelling_private_dict_file:
- self.config.spelling_private_dict_file = os.path.expanduser(
- self.config.spelling_private_dict_file
- )
-
- if self.config.spelling_private_dict_file:
- self.spelling_dict = enchant.DictWithPWL(
- dict_name, self.config.spelling_private_dict_file
- )
- self.private_dict_file = open(self.config.spelling_private_dict_file, "a")
- else:
- self.spelling_dict = enchant.Dict(dict_name)
-
- if self.config.spelling_store_unknown_words:
- self.unknown_words = set()
-
- self.tokenizer = get_tokenizer(
- dict_name,
- chunkers=[ForwardSlashChunkder],
- filters=[
- EmailFilter,
- URLFilter,
- WikiWordFilter,
- WordsWithDigigtsFilter,
- WordsWithUnderscores,
- CamelCasedWord,
- SphinxDirectives,
- ],
- )
- self.initialized = True
-
- def close(self):
- if self.private_dict_file:
- self.private_dict_file.close()
-
- def _check_spelling(self, msgid, line, line_num):
- original_line = line
- try:
- initial_space = re.search(r"^[^\S]\s*", line).regs[0][1]
- except (IndexError, AttributeError):
- initial_space = 0
- if line.strip().startswith("#"):
- line = line.strip()[1:]
- starts_with_comment = True
- else:
- starts_with_comment = False
- for word, word_start_at in self.tokenizer(line.strip()):
- word_start_at += initial_space
- lower_cased_word = word.casefold()
-
- # Skip words from ignore list.
- if word in self.ignore_list or lower_cased_word in self.ignore_list:
- continue
-
- # Strip starting u' from unicode literals and r' from raw strings.
- if word.startswith(("u'", 'u"', "r'", 'r"')) and len(word) > 2:
- word = word[2:]
- lower_cased_word = lower_cased_word[2:]
-
- # If it is a known word, then continue.
- try:
- if self.spelling_dict.check(lower_cased_word):
- # The lower cased version of word passed spell checking
- continue
-
- # If we reached this far, it means there was a spelling mistake.
- # Let's retry with the original work because 'unicode' is a
- # spelling mistake but 'Unicode' is not
- if self.spelling_dict.check(word):
- continue
- except enchant.errors.Error:
- self.add_message(
- "invalid-characters-in-docstring", line=line_num, args=(word,)
- )
- continue
-
- # Store word to private dict or raise a message.
- if self.config.spelling_store_unknown_words:
- if lower_cased_word not in self.unknown_words:
- self.private_dict_file.write("%s\n" % lower_cased_word)
- self.unknown_words.add(lower_cased_word)
- else:
- # Present up to N suggestions.
- suggestions = self.spelling_dict.suggest(word)
- del suggestions[self.config.max_spelling_suggestions :]
-
- line_segment = line[word_start_at:]
- match = re.search(r"(\W|^)(%s)(\W|$)" % word, line_segment)
- if match:
- # Start position of second group in regex.
- col = match.regs[2][0]
- else:
- col = line_segment.index(word)
-
- col += word_start_at
-
- if starts_with_comment:
- col += 1
- indicator = (" " * col) + ("^" * len(word))
-
- self.add_message(
- msgid,
- line=line_num,
- args=(
- word,
- original_line,
- indicator,
- "'{}'".format("' or '".join(suggestions)),
- ),
- )
-
- def process_tokens(self, tokens):
- if not self.initialized:
- return
-
- # Process tokens and look for comments.
- for (tok_type, token, (start_row, _), _, _) in tokens:
- if tok_type == tokenize.COMMENT:
- if start_row == 1 and token.startswith("#!/"):
- # Skip shebang lines
- continue
- if token.startswith("# pylint:"):
- # Skip pylint enable/disable comments
- continue
- self._check_spelling("wrong-spelling-in-comment", token, start_row)
-
- @check_messages("wrong-spelling-in-docstring")
- def visit_module(self, node):
- if not self.initialized:
- return
- self._check_docstring(node)
-
- @check_messages("wrong-spelling-in-docstring")
- def visit_classdef(self, node):
- if not self.initialized:
- return
- self._check_docstring(node)
-
- @check_messages("wrong-spelling-in-docstring")
- def visit_functiondef(self, node):
- if not self.initialized:
- return
- self._check_docstring(node)
-
- visit_asyncfunctiondef = visit_functiondef
-
- def _check_docstring(self, node):
- """check the node has any spelling errors"""
- docstring = node.doc
- if not docstring:
- return
-
- start_line = node.lineno + 1
-
- # Go through lines of docstring
- for idx, line in enumerate(docstring.splitlines()):
- self._check_spelling("wrong-spelling-in-docstring", line, start_line + idx)
-
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(SpellingChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/stdlib.py b/venv/Lib/site-packages/pylint/checkers/stdlib.py
deleted file mode 100644
index a945107..0000000
--- a/venv/Lib/site-packages/pylint/checkers/stdlib.py
+++ /dev/null
@@ -1,452 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2013-2014 Google, Inc.
-# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Cosmin Poieana <cmin@ropython.org>
-# Copyright (c) 2014 Vlad Temian <vladtemian@gmail.com>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Cezar <celnazli@bitdefender.com>
-# Copyright (c) 2015 Chris Rebert <code@rebertia.com>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Jared Garst <cultofjared@gmail.com>
-# Copyright (c) 2017 Renat Galimov <renat2017@gmail.com>
-# Copyright (c) 2017 Martin <MartinBasti@users.noreply.github.com>
-# Copyright (c) 2017 Christopher Zurcher <zurcher@users.noreply.github.com>
-# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2018 Banjamin Freeman <befreeman@users.noreply.github.com>
-# Copyright (c) 2018 Ioana Tagirta <ioana.tagirta@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
-
-"""Checkers for various standard library functions."""
-
-import sys
-
-import astroid
-from astroid.bases import Instance
-from astroid.node_classes import Const
-
-from pylint.checkers import BaseChecker, utils
-from pylint.interfaces import IAstroidChecker
-
-OPEN_FILES = {"open", "file"}
-UNITTEST_CASE = "unittest.case"
-THREADING_THREAD = "threading.Thread"
-COPY_COPY = "copy.copy"
-OS_ENVIRON = "os._Environ"
-ENV_GETTERS = {"os.getenv"}
-SUBPROCESS_POPEN = "subprocess.Popen"
-SUBPROCESS_RUN = "subprocess.run"
-OPEN_MODULE = "_io"
-
-
-def _check_mode_str(mode):
- # check type
- if not isinstance(mode, str):
- return False
- # check syntax
- modes = set(mode)
- _mode = "rwatb+Ux"
- creating = "x" in modes
- if modes - set(_mode) or len(mode) > len(modes):
- return False
- # check logic
- reading = "r" in modes
- writing = "w" in modes
- appending = "a" in modes
- text = "t" in modes
- binary = "b" in modes
- if "U" in modes:
- if writing or appending or creating:
- return False
- reading = True
- if text and binary:
- return False
- total = reading + writing + appending + creating
- if total > 1:
- return False
- if not (reading or writing or appending or creating):
- return False
- return True
-
-
-class StdlibChecker(BaseChecker):
- __implements__ = (IAstroidChecker,)
- name = "stdlib"
-
- msgs = {
- "W1501": (
- '"%s" is not a valid mode for open.',
- "bad-open-mode",
- "Python supports: r, w, a[, x] modes with b, +, "
- "and U (only with r) options. "
- "See http://docs.python.org/2/library/functions.html#open",
- ),
- "W1502": (
- "Using datetime.time in a boolean context.",
- "boolean-datetime",
- "Using datetime.time in a boolean context can hide "
- "subtle bugs when the time they represent matches "
- "midnight UTC. This behaviour was fixed in Python 3.5. "
- "See http://bugs.python.org/issue13936 for reference.",
- {"maxversion": (3, 5)},
- ),
- "W1503": (
- "Redundant use of %s with constant value %r",
- "redundant-unittest-assert",
- "The first argument of assertTrue and assertFalse is "
- "a condition. If a constant is passed as parameter, that "
- "condition will be always true. In this case a warning "
- "should be emitted.",
- ),
- "W1505": (
- "Using deprecated method %s()",
- "deprecated-method",
- "The method is marked as deprecated and will be removed in "
- "a future version of Python. Consider looking for an "
- "alternative in the documentation.",
- ),
- "W1506": (
- "threading.Thread needs the target function",
- "bad-thread-instantiation",
- "The warning is emitted when a threading.Thread class "
- "is instantiated without the target function being passed. "
- "By default, the first parameter is the group param, not the target param. ",
- ),
- "W1507": (
- "Using copy.copy(os.environ). Use os.environ.copy() instead. ",
- "shallow-copy-environ",
- "os.environ is not a dict object but proxy object, so "
- "shallow copy has still effects on original object. "
- "See https://bugs.python.org/issue15373 for reference. ",
- ),
- "E1507": (
- "%s does not support %s type argument",
- "invalid-envvar-value",
- "Env manipulation functions support only string type arguments. "
- "See https://docs.python.org/3/library/os.html#os.getenv. ",
- ),
- "W1508": (
- "%s default type is %s. Expected str or None.",
- "invalid-envvar-default",
- "Env manipulation functions return None or str values. "
- "Supplying anything different as a default may cause bugs. "
- "See https://docs.python.org/3/library/os.html#os.getenv. ",
- ),
- "W1509": (
- "Using preexec_fn keyword which may be unsafe in the presence "
- "of threads",
- "subprocess-popen-preexec-fn",
- "The preexec_fn parameter is not safe to use in the presence "
- "of threads in your application. The child process could "
- "deadlock before exec is called. If you must use it, keep it "
- "trivial! Minimize the number of libraries you call into."
- "https://docs.python.org/3/library/subprocess.html#popen-constructor",
- ),
- "W1510": (
- "Using subprocess.run without explicitly set `check` is not recommended.",
- "subprocess-run-check",
- "The check parameter should always be used with explicitly set "
- "`check` keyword to make clear what the error-handling behavior is."
- "https://docs.python.org/3/library/subprocess.html#subprocess.runs",
- ),
- }
-
- deprecated = {
- 0: {
- "cgi.parse_qs",
- "cgi.parse_qsl",
- "ctypes.c_buffer",
- "distutils.command.register.register.check_metadata",
- "distutils.command.sdist.sdist.check_metadata",
- "tkinter.Misc.tk_menuBar",
- "tkinter.Menu.tk_bindForTraversal",
- },
- 2: {
- (2, 6, 0): {
- "commands.getstatus",
- "os.popen2",
- "os.popen3",
- "os.popen4",
- "macostools.touched",
- },
- (2, 7, 0): {
- "unittest.case.TestCase.assertEquals",
- "unittest.case.TestCase.assertNotEquals",
- "unittest.case.TestCase.assertAlmostEquals",
- "unittest.case.TestCase.assertNotAlmostEquals",
- "unittest.case.TestCase.assert_",
- "xml.etree.ElementTree.Element.getchildren",
- "xml.etree.ElementTree.Element.getiterator",
- "xml.etree.ElementTree.XMLParser.getiterator",
- "xml.etree.ElementTree.XMLParser.doctype",
- },
- },
- 3: {
- (3, 0, 0): {
- "inspect.getargspec",
- "failUnlessEqual",
- "assertEquals",
- "failIfEqual",
- "assertNotEquals",
- "failUnlessAlmostEqual",
- "assertAlmostEquals",
- "failIfAlmostEqual",
- "assertNotAlmostEquals",
- "failUnless",
- "assert_",
- "failUnlessRaises",
- "failIf",
- "assertRaisesRegexp",
- "assertRegexpMatches",
- "assertNotRegexpMatches",
- },
- (3, 1, 0): {
- "base64.encodestring",
- "base64.decodestring",
- "ntpath.splitunc",
- },
- (3, 2, 0): {
- "cgi.escape",
- "configparser.RawConfigParser.readfp",
- "xml.etree.ElementTree.Element.getchildren",
- "xml.etree.ElementTree.Element.getiterator",
- "xml.etree.ElementTree.XMLParser.getiterator",
- "xml.etree.ElementTree.XMLParser.doctype",
- },
- (3, 3, 0): {
- "inspect.getmoduleinfo",
- "logging.warn",
- "logging.Logger.warn",
- "logging.LoggerAdapter.warn",
- "nntplib._NNTPBase.xpath",
- "platform.popen",
- },
- (3, 4, 0): {
- "importlib.find_loader",
- "plistlib.readPlist",
- "plistlib.writePlist",
- "plistlib.readPlistFromBytes",
- "plistlib.writePlistToBytes",
- },
- (3, 4, 4): {"asyncio.tasks.async"},
- (3, 5, 0): {
- "fractions.gcd",
- "inspect.getargvalues",
- "inspect.formatargspec",
- "inspect.formatargvalues",
- "inspect.getcallargs",
- "platform.linux_distribution",
- "platform.dist",
- },
- (3, 6, 0): {"importlib._bootstrap_external.FileLoader.load_module"},
- },
- }
-
- def _check_bad_thread_instantiation(self, node):
- if not node.kwargs and not node.keywords and len(node.args) <= 1:
- self.add_message("bad-thread-instantiation", node=node)
-
- def _check_for_preexec_fn_in_popen(self, node):
- if node.keywords:
- for keyword in node.keywords:
- if keyword.arg == "preexec_fn":
- self.add_message("subprocess-popen-preexec-fn", node=node)
-
- def _check_for_check_kw_in_run(self, node):
- kwargs = {keyword.arg for keyword in (node.keywords or ())}
- if "check" not in kwargs:
- self.add_message("subprocess-run-check", node=node)
-
- def _check_shallow_copy_environ(self, node):
- arg = utils.get_argument_from_call(node, position=0)
- for inferred in arg.inferred():
- if inferred.qname() == OS_ENVIRON:
- self.add_message("shallow-copy-environ", node=node)
- break
-
- @utils.check_messages(
- "bad-open-mode",
- "redundant-unittest-assert",
- "deprecated-method",
- "bad-thread-instantiation",
- "shallow-copy-environ",
- "invalid-envvar-value",
- "invalid-envvar-default",
- "subprocess-popen-preexec-fn",
- "subprocess-run-check",
- )
- def visit_call(self, node):
- """Visit a Call node."""
- try:
- for inferred in node.func.infer():
- if inferred is astroid.Uninferable:
- continue
- if inferred.root().name == OPEN_MODULE:
- if getattr(node.func, "name", None) in OPEN_FILES:
- self._check_open_mode(node)
- elif inferred.root().name == UNITTEST_CASE:
- self._check_redundant_assert(node, inferred)
- elif isinstance(inferred, astroid.ClassDef):
- if inferred.qname() == THREADING_THREAD:
- self._check_bad_thread_instantiation(node)
- elif inferred.qname() == SUBPROCESS_POPEN:
- self._check_for_preexec_fn_in_popen(node)
- elif isinstance(inferred, astroid.FunctionDef):
- name = inferred.qname()
- if name == COPY_COPY:
- self._check_shallow_copy_environ(node)
- elif name in ENV_GETTERS:
- self._check_env_function(node, inferred)
- elif name == SUBPROCESS_RUN:
- self._check_for_check_kw_in_run(node)
- self._check_deprecated_method(node, inferred)
- except astroid.InferenceError:
- return
-
- @utils.check_messages("boolean-datetime")
- def visit_unaryop(self, node):
- if node.op == "not":
- self._check_datetime(node.operand)
-
- @utils.check_messages("boolean-datetime")
- def visit_if(self, node):
- self._check_datetime(node.test)
-
- @utils.check_messages("boolean-datetime")
- def visit_ifexp(self, node):
- self._check_datetime(node.test)
-
- @utils.check_messages("boolean-datetime")
- def visit_boolop(self, node):
- for value in node.values:
- self._check_datetime(value)
-
- def _check_deprecated_method(self, node, inferred):
- py_vers = sys.version_info[0]
-
- if isinstance(node.func, astroid.Attribute):
- func_name = node.func.attrname
- elif isinstance(node.func, astroid.Name):
- func_name = node.func.name
- else:
- # Not interested in other nodes.
- return
-
- # Reject nodes which aren't of interest to us.
- acceptable_nodes = (
- astroid.BoundMethod,
- astroid.UnboundMethod,
- astroid.FunctionDef,
- )
- if not isinstance(inferred, acceptable_nodes):
- return
-
- qname = inferred.qname()
- if any(name in self.deprecated[0] for name in (qname, func_name)):
- self.add_message("deprecated-method", node=node, args=(func_name,))
- else:
- for since_vers, func_list in self.deprecated[py_vers].items():
- if since_vers <= sys.version_info and any(
- name in func_list for name in (qname, func_name)
- ):
- self.add_message("deprecated-method", node=node, args=(func_name,))
- break
-
- def _check_redundant_assert(self, node, infer):
- if (
- isinstance(infer, astroid.BoundMethod)
- and node.args
- and isinstance(node.args[0], astroid.Const)
- and infer.name in ["assertTrue", "assertFalse"]
- ):
- self.add_message(
- "redundant-unittest-assert",
- args=(infer.name, node.args[0].value),
- node=node,
- )
-
- def _check_datetime(self, node):
- """ Check that a datetime was inferred.
- If so, emit boolean-datetime warning.
- """
- try:
- inferred = next(node.infer())
- except astroid.InferenceError:
- return
- if isinstance(inferred, Instance) and inferred.qname() == "datetime.time":
- self.add_message("boolean-datetime", node=node)
-
- def _check_open_mode(self, node):
- """Check that the mode argument of an open or file call is valid."""
- try:
- mode_arg = utils.get_argument_from_call(node, position=1, keyword="mode")
- except utils.NoSuchArgumentError:
- return
- if mode_arg:
- mode_arg = utils.safe_infer(mode_arg)
- if isinstance(mode_arg, astroid.Const) and not _check_mode_str(
- mode_arg.value
- ):
- self.add_message("bad-open-mode", node=node, args=mode_arg.value)
-
- def _check_env_function(self, node, infer):
- env_name_kwarg = "key"
- env_value_kwarg = "default"
- if node.keywords:
- kwargs = {keyword.arg: keyword.value for keyword in node.keywords}
- else:
- kwargs = None
- if node.args:
- env_name_arg = node.args[0]
- elif kwargs and env_name_kwarg in kwargs:
- env_name_arg = kwargs[env_name_kwarg]
- else:
- env_name_arg = None
-
- if env_name_arg:
- self._check_invalid_envvar_value(
- node=node,
- message="invalid-envvar-value",
- call_arg=utils.safe_infer(env_name_arg),
- infer=infer,
- allow_none=False,
- )
-
- if len(node.args) == 2:
- env_value_arg = node.args[1]
- elif kwargs and env_value_kwarg in kwargs:
- env_value_arg = kwargs[env_value_kwarg]
- else:
- env_value_arg = None
-
- if env_value_arg:
- self._check_invalid_envvar_value(
- node=node,
- infer=infer,
- message="invalid-envvar-default",
- call_arg=utils.safe_infer(env_value_arg),
- allow_none=True,
- )
-
- def _check_invalid_envvar_value(self, node, infer, message, call_arg, allow_none):
- if call_arg in (astroid.Uninferable, None):
- return
-
- name = infer.qname()
- if isinstance(call_arg, Const):
- emit = False
- if call_arg.value is None:
- emit = not allow_none
- elif not isinstance(call_arg.value, str):
- emit = True
- if emit:
- self.add_message(message, node=node, args=(name, call_arg.pytype()))
- else:
- self.add_message(message, node=node, args=(name, call_arg.pytype()))
-
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(StdlibChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/strings.py b/venv/Lib/site-packages/pylint/checkers/strings.py
deleted file mode 100644
index 9470f46..0000000
--- a/venv/Lib/site-packages/pylint/checkers/strings.py
+++ /dev/null
@@ -1,755 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2009 Charles Hebert <charles.hebert@logilab.fr>
-# Copyright (c) 2010-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
-# Copyright (c) 2012-2014 Google, Inc.
-# Copyright (c) 2013-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 Rene Zhang <rz99@cornell.edu>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016, 2018 Jakub Wilk <jwilk@jwilk.net>
-# Copyright (c) 2016 Peter Dawyndt <Peter.Dawyndt@UGent.be>
-# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
-# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
-# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
-
-
-# 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
-
-"""Checker for string formatting operations.
-"""
-
-import builtins
-import numbers
-import tokenize
-from collections import Counter
-
-import astroid
-from astroid.arguments import CallSite
-from astroid.node_classes import Const
-
-from pylint.checkers import BaseChecker, BaseTokenChecker, utils
-from pylint.checkers.utils import check_messages
-from pylint.interfaces import IAstroidChecker, IRawChecker, ITokenChecker
-
-_AST_NODE_STR_TYPES = ("__builtin__.unicode", "__builtin__.str", "builtins.str")
-
-MSGS = {
- "E1300": (
- "Unsupported format character %r (%#02x) at index %d",
- "bad-format-character",
- "Used when an unsupported format character is used in a format string.",
- ),
- "E1301": (
- "Format string ends in middle of conversion specifier",
- "truncated-format-string",
- "Used when a format string terminates before the end of a "
- "conversion specifier.",
- ),
- "E1302": (
- "Mixing named and unnamed conversion specifiers in format string",
- "mixed-format-string",
- "Used when a format string contains both named (e.g. '%(foo)d') "
- "and unnamed (e.g. '%d') conversion specifiers. This is also "
- "used when a named conversion specifier contains * for the "
- "minimum field width and/or precision.",
- ),
- "E1303": (
- "Expected mapping for format string, not %s",
- "format-needs-mapping",
- "Used when a format string that uses named conversion specifiers "
- "is used with an argument that is not a mapping.",
- ),
- "W1300": (
- "Format string dictionary key should be a string, not %s",
- "bad-format-string-key",
- "Used when a format string that uses named conversion specifiers "
- "is used with a dictionary whose keys are not all strings.",
- ),
- "W1301": (
- "Unused key %r in format string dictionary",
- "unused-format-string-key",
- "Used when a format string that uses named conversion specifiers "
- "is used with a dictionary that contains keys not required by the "
- "format string.",
- ),
- "E1304": (
- "Missing key %r in format string dictionary",
- "missing-format-string-key",
- "Used when a format string that uses named conversion specifiers "
- "is used with a dictionary that doesn't contain all the keys "
- "required by the format string.",
- ),
- "E1305": (
- "Too many arguments for format string",
- "too-many-format-args",
- "Used when a format string that uses unnamed conversion "
- "specifiers is given too many arguments.",
- ),
- "E1306": (
- "Not enough arguments for format string",
- "too-few-format-args",
- "Used when a format string that uses unnamed conversion "
- "specifiers is given too few arguments",
- ),
- "E1307": (
- "Argument %r does not match format type %r",
- "bad-string-format-type",
- "Used when a type required by format string "
- "is not suitable for actual argument type",
- ),
- "E1310": (
- "Suspicious argument in %s.%s call",
- "bad-str-strip-call",
- "The argument to a str.{l,r,}strip call contains a duplicate character, ",
- ),
- "W1302": (
- "Invalid format string",
- "bad-format-string",
- "Used when a PEP 3101 format string is invalid.",
- ),
- "W1303": (
- "Missing keyword argument %r for format string",
- "missing-format-argument-key",
- "Used when a PEP 3101 format string that uses named fields "
- "doesn't receive one or more required keywords.",
- ),
- "W1304": (
- "Unused format argument %r",
- "unused-format-string-argument",
- "Used when a PEP 3101 format string that uses named "
- "fields is used with an argument that "
- "is not required by the format string.",
- ),
- "W1305": (
- "Format string contains both automatic field numbering "
- "and manual field specification",
- "format-combined-specification",
- "Used when a PEP 3101 format string contains both automatic "
- "field numbering (e.g. '{}') and manual field "
- "specification (e.g. '{0}').",
- ),
- "W1306": (
- "Missing format attribute %r in format specifier %r",
- "missing-format-attribute",
- "Used when a PEP 3101 format string uses an "
- "attribute specifier ({0.length}), but the argument "
- "passed for formatting doesn't have that attribute.",
- ),
- "W1307": (
- "Using invalid lookup key %r in format specifier %r",
- "invalid-format-index",
- "Used when a PEP 3101 format string uses a lookup specifier "
- "({a[1]}), but the argument passed for formatting "
- "doesn't contain or doesn't have that key as an attribute.",
- ),
- "W1308": (
- "Duplicate string formatting argument %r, consider passing as named argument",
- "duplicate-string-formatting-argument",
- "Used when we detect that a string formatting is "
- "repeating an argument instead of using named string arguments",
- ),
-}
-
-OTHER_NODES = (
- astroid.Const,
- astroid.List,
- astroid.Lambda,
- astroid.FunctionDef,
- astroid.ListComp,
- astroid.SetComp,
- astroid.GeneratorExp,
-)
-
-BUILTINS_STR = builtins.__name__ + ".str"
-BUILTINS_FLOAT = builtins.__name__ + ".float"
-BUILTINS_INT = builtins.__name__ + ".int"
-
-
-def get_access_path(key, parts):
- """ Given a list of format specifiers, returns
- the final access path (e.g. a.b.c[0][1]).
- """
- path = []
- for is_attribute, specifier in parts:
- if is_attribute:
- path.append(".{}".format(specifier))
- else:
- path.append("[{!r}]".format(specifier))
- return str(key) + "".join(path)
-
-
-def arg_matches_format_type(arg_type, format_type):
- if format_type in "sr":
- # All types can be printed with %s and %r
- return True
- if isinstance(arg_type, astroid.Instance):
- arg_type = arg_type.pytype()
- if arg_type == BUILTINS_STR:
- return format_type == "c"
- if arg_type == BUILTINS_FLOAT:
- return format_type in "deEfFgGn%"
- if arg_type == BUILTINS_INT:
- # Integers allow all types
- return True
- return False
- return True
-
-
-class StringFormatChecker(BaseChecker):
- """Checks string formatting operations to ensure that the format string
- is valid and the arguments match the format string.
- """
-
- __implements__ = (IAstroidChecker,)
- name = "string"
- msgs = MSGS
-
- # pylint: disable=too-many-branches
- @check_messages(*MSGS)
- def visit_binop(self, node):
- if node.op != "%":
- return
- left = node.left
- args = node.right
-
- if not (isinstance(left, astroid.Const) and isinstance(left.value, str)):
- return
- format_string = left.value
- try:
- required_keys, required_num_args, required_key_types, required_arg_types = utils.parse_format_string(
- format_string
- )
- except utils.UnsupportedFormatCharacter as exc:
- formatted = format_string[exc.index]
- self.add_message(
- "bad-format-character",
- node=node,
- args=(formatted, ord(formatted), exc.index),
- )
- return
- except utils.IncompleteFormatString:
- self.add_message("truncated-format-string", node=node)
- return
- if required_keys and required_num_args:
- # The format string uses both named and unnamed format
- # specifiers.
- self.add_message("mixed-format-string", node=node)
- elif required_keys:
- # The format string uses only named format specifiers.
- # Check that the RHS of the % operator is a mapping object
- # that contains precisely the set of keys required by the
- # format string.
- if isinstance(args, astroid.Dict):
- keys = set()
- unknown_keys = False
- for k, _ in args.items:
- if isinstance(k, astroid.Const):
- key = k.value
- if isinstance(key, str):
- keys.add(key)
- else:
- self.add_message(
- "bad-format-string-key", node=node, args=key
- )
- else:
- # One of the keys was something other than a
- # constant. Since we can't tell what it is,
- # suppress checks for missing keys in the
- # dictionary.
- unknown_keys = True
- if not unknown_keys:
- for key in required_keys:
- if key not in keys:
- self.add_message(
- "missing-format-string-key", node=node, args=key
- )
- for key in keys:
- if key not in required_keys:
- self.add_message(
- "unused-format-string-key", node=node, args=key
- )
- for key, arg in args.items:
- if not isinstance(key, astroid.Const):
- continue
- format_type = required_key_types.get(key.value, None)
- arg_type = utils.safe_infer(arg)
- if (
- format_type is not None
- and arg_type not in (None, astroid.Uninferable)
- and not arg_matches_format_type(arg_type, format_type)
- ):
- self.add_message(
- "bad-string-format-type",
- node=node,
- args=(arg_type.pytype(), format_type),
- )
- elif isinstance(args, (OTHER_NODES, astroid.Tuple)):
- type_name = type(args).__name__
- self.add_message("format-needs-mapping", node=node, args=type_name)
- # else:
- # The RHS of the format specifier is a name or
- # expression. It may be a mapping object, so
- # there's nothing we can check.
- else:
- # The format string uses only unnamed format specifiers.
- # Check that the number of arguments passed to the RHS of
- # the % operator matches the number required by the format
- # string.
- args_elts = ()
- if isinstance(args, astroid.Tuple):
- rhs_tuple = utils.safe_infer(args)
- num_args = None
- if hasattr(rhs_tuple, "elts"):
- args_elts = rhs_tuple.elts
- num_args = len(args_elts)
- elif isinstance(args, (OTHER_NODES, (astroid.Dict, astroid.DictComp))):
- args_elts = [args]
- num_args = 1
- else:
- # The RHS of the format specifier is a name or
- # expression. It could be a tuple of unknown size, so
- # there's nothing we can check.
- num_args = None
- if num_args is not None:
- if num_args > required_num_args:
- self.add_message("too-many-format-args", node=node)
- elif num_args < required_num_args:
- self.add_message("too-few-format-args", node=node)
- for arg, format_type in zip(args_elts, required_arg_types):
- if not arg:
- continue
- arg_type = utils.safe_infer(arg)
- if arg_type not in (
- None,
- astroid.Uninferable,
- ) and not arg_matches_format_type(arg_type, format_type):
- self.add_message(
- "bad-string-format-type",
- node=node,
- args=(arg_type.pytype(), format_type),
- )
-
- @check_messages(*MSGS)
- def visit_call(self, node):
- func = utils.safe_infer(node.func)
- if (
- isinstance(func, astroid.BoundMethod)
- and isinstance(func.bound, astroid.Instance)
- and func.bound.name in ("str", "unicode", "bytes")
- ):
- if func.name in ("strip", "lstrip", "rstrip") and node.args:
- arg = utils.safe_infer(node.args[0])
- if not isinstance(arg, astroid.Const) or not isinstance(arg.value, str):
- return
- if len(arg.value) != len(set(arg.value)):
- self.add_message(
- "bad-str-strip-call",
- node=node,
- args=(func.bound.name, func.name),
- )
- elif func.name == "format":
- self._check_new_format(node, func)
-
- def _detect_vacuous_formatting(self, node, positional_arguments):
- counter = Counter(
- arg.name for arg in positional_arguments if isinstance(arg, astroid.Name)
- )
- for name, count in counter.items():
- if count == 1:
- continue
- self.add_message(
- "duplicate-string-formatting-argument", node=node, args=(name,)
- )
-
- def _check_new_format(self, node, func):
- """Check the new string formatting. """
- # Skip ormat nodes which don't have an explicit string on the
- # left side of the format operation.
- # We do this because our inference engine can't properly handle
- # redefinitions of the original string.
- # Note that there may not be any left side at all, if the format method
- # has been assigned to another variable. See issue 351. For example:
- #
- # fmt = 'some string {}'.format
- # fmt('arg')
- if isinstance(node.func, astroid.Attribute) and not isinstance(
- node.func.expr, astroid.Const
- ):
- return
- if node.starargs or node.kwargs:
- return
- try:
- strnode = next(func.bound.infer())
- except astroid.InferenceError:
- return
- if not (isinstance(strnode, astroid.Const) and isinstance(strnode.value, str)):
- return
- try:
- call_site = CallSite.from_call(node)
- except astroid.InferenceError:
- return
-
- try:
- fields, num_args, manual_pos = utils.parse_format_method_string(
- strnode.value
- )
- except utils.IncompleteFormatString:
- self.add_message("bad-format-string", node=node)
- return
-
- positional_arguments = call_site.positional_arguments
- named_arguments = call_site.keyword_arguments
- named_fields = {field[0] for field in fields if isinstance(field[0], str)}
- if num_args and manual_pos:
- self.add_message("format-combined-specification", node=node)
- return
-
- check_args = False
- # Consider "{[0]} {[1]}" as num_args.
- num_args += sum(1 for field in named_fields if field == "")
- if named_fields:
- for field in named_fields:
- if field and field not in named_arguments:
- self.add_message(
- "missing-format-argument-key", node=node, args=(field,)
- )
- for field in named_arguments:
- if field not in named_fields:
- self.add_message(
- "unused-format-string-argument", node=node, args=(field,)
- )
- # num_args can be 0 if manual_pos is not.
- num_args = num_args or manual_pos
- if positional_arguments or num_args:
- empty = any(True for field in named_fields if field == "")
- if named_arguments or empty:
- # Verify the required number of positional arguments
- # only if the .format got at least one keyword argument.
- # This means that the format strings accepts both
- # positional and named fields and we should warn
- # when one of the them is missing or is extra.
- check_args = True
- else:
- check_args = True
- if check_args:
- # num_args can be 0 if manual_pos is not.
- num_args = num_args or manual_pos
- if len(positional_arguments) > num_args:
- self.add_message("too-many-format-args", node=node)
- elif len(positional_arguments) < num_args:
- self.add_message("too-few-format-args", node=node)
-
- self._detect_vacuous_formatting(node, positional_arguments)
- self._check_new_format_specifiers(node, fields, named_arguments)
-
- def _check_new_format_specifiers(self, node, fields, named):
- """
- Check attribute and index access in the format
- string ("{0.a}" and "{0[a]}").
- """
- for key, specifiers in fields:
- # Obtain the argument. If it can't be obtained
- # or inferred, skip this check.
- if key == "":
- # {[0]} will have an unnamed argument, defaulting
- # to 0. It will not be present in `named`, so use the value
- # 0 for it.
- key = 0
- if isinstance(key, numbers.Number):
- try:
- argname = utils.get_argument_from_call(node, key)
- except utils.NoSuchArgumentError:
- continue
- else:
- if key not in named:
- continue
- argname = named[key]
- if argname in (astroid.Uninferable, None):
- continue
- try:
- argument = utils.safe_infer(argname)
- except astroid.InferenceError:
- continue
- if not specifiers or not argument:
- # No need to check this key if it doesn't
- # use attribute / item access
- continue
- if argument.parent and isinstance(argument.parent, astroid.Arguments):
- # Ignore any object coming from an argument,
- # because we can't infer its value properly.
- continue
- previous = argument
- parsed = []
- for is_attribute, specifier in specifiers:
- if previous is astroid.Uninferable:
- break
- parsed.append((is_attribute, specifier))
- if is_attribute:
- try:
- previous = previous.getattr(specifier)[0]
- except astroid.NotFoundError:
- if (
- hasattr(previous, "has_dynamic_getattr")
- and previous.has_dynamic_getattr()
- ):
- # Don't warn if the object has a custom __getattr__
- break
- path = get_access_path(key, parsed)
- self.add_message(
- "missing-format-attribute",
- args=(specifier, path),
- node=node,
- )
- break
- else:
- warn_error = False
- if hasattr(previous, "getitem"):
- try:
- previous = previous.getitem(astroid.Const(specifier))
- except (
- astroid.AstroidIndexError,
- astroid.AstroidTypeError,
- astroid.AttributeInferenceError,
- ):
- warn_error = True
- except astroid.InferenceError:
- break
- if previous is astroid.Uninferable:
- break
- else:
- try:
- # Lookup __getitem__ in the current node,
- # but skip further checks, because we can't
- # retrieve the looked object
- previous.getattr("__getitem__")
- break
- except astroid.NotFoundError:
- warn_error = True
- if warn_error:
- path = get_access_path(key, parsed)
- self.add_message(
- "invalid-format-index", args=(specifier, path), node=node
- )
- break
-
- try:
- previous = next(previous.infer())
- except astroid.InferenceError:
- # can't check further if we can't infer it
- break
-
-
-class StringConstantChecker(BaseTokenChecker):
- """Check string literals"""
-
- __implements__ = (IAstroidChecker, ITokenChecker, IRawChecker)
- name = "string"
- msgs = {
- "W1401": (
- "Anomalous backslash in string: '%s'. "
- "String constant might be missing an r prefix.",
- "anomalous-backslash-in-string",
- "Used when a backslash is in a literal string but not as an escape.",
- ),
- "W1402": (
- "Anomalous Unicode escape in byte string: '%s'. "
- "String constant might be missing an r or u prefix.",
- "anomalous-unicode-escape-in-string",
- "Used when an escape like \\u is encountered in a byte "
- "string where it has no effect.",
- ),
- "W1403": (
- "Implicit string concatenation found in %s",
- "implicit-str-concat-in-sequence",
- "String literals are implicitly concatenated in a "
- "literal iterable definition : "
- "maybe a comma is missing ?",
- ),
- }
- options = (
- (
- "check-str-concat-over-line-jumps",
- {
- "default": False,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": "This flag controls whether the "
- "implicit-str-concat-in-sequence should generate a warning "
- "on implicit string concatenation in sequences defined over "
- "several lines.",
- },
- ),
- )
-
- # Characters that have a special meaning after a backslash in either
- # Unicode or byte strings.
- ESCAPE_CHARACTERS = "abfnrtvx\n\r\t\\'\"01234567"
-
- # Characters that have a special meaning after a backslash but only in
- # Unicode strings.
- UNICODE_ESCAPE_CHARACTERS = "uUN"
-
- def __init__(self, *args, **kwargs):
- super(StringConstantChecker, self).__init__(*args, **kwargs)
- self.string_tokens = {} # token position -> (token value, next token)
-
- def process_module(self, module):
- self._unicode_literals = "unicode_literals" in module.future_imports
-
- def process_tokens(self, tokens):
- encoding = "ascii"
- for i, (tok_type, token, start, _, line) in enumerate(tokens):
- if tok_type == tokenize.ENCODING:
- # this is always the first token processed
- encoding = token
- elif tok_type == tokenize.STRING:
- # 'token' is the whole un-parsed token; we can look at the start
- # of it to see whether it's a raw or unicode string etc.
- self.process_string_token(token, start[0])
- # We figure the next token, ignoring comments & newlines:
- j = i + 1
- while j < len(tokens) and tokens[j].type in (
- tokenize.NEWLINE,
- tokenize.NL,
- tokenize.COMMENT,
- ):
- j += 1
- next_token = tokens[j] if j < len(tokens) else None
- if encoding != "ascii":
- # We convert `tokenize` character count into a byte count,
- # to match with astroid `.col_offset`
- start = (start[0], len(line[: start[1]].encode(encoding)))
- self.string_tokens[start] = (str_eval(token), next_token)
-
- @check_messages(*(msgs.keys()))
- def visit_list(self, node):
- self.check_for_concatenated_strings(node, "list")
-
- @check_messages(*(msgs.keys()))
- def visit_set(self, node):
- self.check_for_concatenated_strings(node, "set")
-
- @check_messages(*(msgs.keys()))
- def visit_tuple(self, node):
- self.check_for_concatenated_strings(node, "tuple")
-
- def check_for_concatenated_strings(self, iterable_node, iterable_type):
- for elt in iterable_node.elts:
- if isinstance(elt, Const) and elt.pytype() in _AST_NODE_STR_TYPES:
- if elt.col_offset < 0:
- # This can happen in case of escaped newlines
- continue
- if (elt.lineno, elt.col_offset) not in self.string_tokens:
- # This may happen with Latin1 encoding
- # cf. https://github.com/PyCQA/pylint/issues/2610
- continue
- matching_token, next_token = self.string_tokens[
- (elt.lineno, elt.col_offset)
- ]
- # We detect string concatenation: the AST Const is the
- # combination of 2 string tokens
- if matching_token != elt.value and next_token is not None:
- if next_token.type == tokenize.STRING and (
- next_token.start[0] == elt.lineno
- or self.config.check_str_concat_over_line_jumps
- ):
- self.add_message(
- "implicit-str-concat-in-sequence",
- line=elt.lineno,
- args=(iterable_type,),
- )
-
- def process_string_token(self, token, start_row):
- quote_char = None
- index = None
- for index, char in enumerate(token):
- if char in "'\"":
- quote_char = char
- break
- if quote_char is None:
- return
-
- prefix = token[:index].lower() # markers like u, b, r.
- after_prefix = token[index:]
- if after_prefix[:3] == after_prefix[-3:] == 3 * quote_char:
- string_body = after_prefix[3:-3]
- else:
- string_body = after_prefix[1:-1] # Chop off quotes
- # No special checks on raw strings at the moment.
- if "r" not in prefix:
- self.process_non_raw_string_token(prefix, string_body, start_row)
-
- def process_non_raw_string_token(self, prefix, string_body, start_row):
- """check for bad escapes in a non-raw string.
-
- prefix: lowercase string of eg 'ur' string prefix markers.
- string_body: the un-parsed body of the string, not including the quote
- marks.
- start_row: integer line number in the source.
- """
- # Walk through the string; if we see a backslash then escape the next
- # character, and skip over it. If we see a non-escaped character,
- # alert, and continue.
- #
- # Accept a backslash when it escapes a backslash, or a quote, or
- # end-of-line, or one of the letters that introduce a special escape
- # sequence <http://docs.python.org/reference/lexical_analysis.html>
- #
- index = 0
- while True:
- index = string_body.find("\\", index)
- if index == -1:
- break
- # There must be a next character; having a backslash at the end
- # of the string would be a SyntaxError.
- next_char = string_body[index + 1]
- match = string_body[index : index + 2]
- if next_char in self.UNICODE_ESCAPE_CHARACTERS:
- if "u" in prefix:
- pass
- elif "b" not in prefix:
- pass # unicode by default
- else:
- self.add_message(
- "anomalous-unicode-escape-in-string",
- line=start_row,
- args=(match,),
- col_offset=index,
- )
- elif next_char not in self.ESCAPE_CHARACTERS:
- self.add_message(
- "anomalous-backslash-in-string",
- line=start_row,
- args=(match,),
- col_offset=index,
- )
- # Whether it was a valid escape or not, backslash followed by
- # another character can always be consumed whole: the second
- # character can never be the start of a new backslash escape.
- index += 2
-
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(StringFormatChecker(linter))
- linter.register_checker(StringConstantChecker(linter))
-
-
-def str_eval(token):
- """
- Mostly replicate `ast.literal_eval(token)` manually to avoid any performance hit.
- This supports f-strings, contrary to `ast.literal_eval`.
- We have to support all string literal notations:
- https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
- """
- if token[0:2].lower() in ("fr", "rf"):
- token = token[2:]
- elif token[0].lower() in ("r", "u", "f"):
- token = token[1:]
- if token[0:3] in ('"""', "'''"):
- return token[3:-3]
- return token[1:-1]
diff --git a/venv/Lib/site-packages/pylint/checkers/typecheck.py b/venv/Lib/site-packages/pylint/checkers/typecheck.py
deleted file mode 100644
index a288f49..0000000
--- a/venv/Lib/site-packages/pylint/checkers/typecheck.py
+++ /dev/null
@@ -1,1770 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2009 James Lingard <jchl@aristanetworks.com>
-# Copyright (c) 2012-2014 Google, Inc.
-# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 David Shea <dshea@redhat.com>
-# Copyright (c) 2014 Steven Myint <hg@stevenmyint.com>
-# Copyright (c) 2014 Holger Peters <email@holger-peters.de>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Anentropic <ego@anentropic.com>
-# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
-# Copyright (c) 2015 Rene Zhang <rz99@cornell.edu>
-# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
-# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2016 Jürgen Hermann <jh@web.de>
-# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
-# Copyright (c) 2016 Filipe Brandenburger <filbranden@google.com>
-# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2017 Derek Gustafson <degustaf@gmail.com>
-# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
-# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
-# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
-# Copyright (c) 2018 Ben Green <benhgreen@icloud.com>
-# Copyright (c) 2018 Konstantin <Github@pheanex.de>
-# Copyright (c) 2018 Justin Li <justinnhli@users.noreply.github.com>
-# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@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
-
-"""try to find more bugs in the code using astroid inference capabilities
-"""
-
-import builtins
-import fnmatch
-import heapq
-import itertools
-import operator
-import re
-import shlex
-import sys
-import types
-from collections import deque
-from collections.abc import Sequence
-from functools import singledispatch
-
-import astroid
-import astroid.arguments
-import astroid.context
-import astroid.nodes
-from astroid import bases, decorators, exceptions, modutils, objects
-from astroid.interpreter import dunder_lookup
-
-from pylint.checkers import BaseChecker
-from pylint.checkers.utils import (
- check_messages,
- decorated_with,
- decorated_with_property,
- has_known_bases,
- is_builtin_object,
- is_comprehension,
- is_inside_abstract_class,
- is_iterable,
- is_mapping,
- is_overload_stub,
- is_super,
- node_ignores_exception,
- safe_infer,
- supports_delitem,
- supports_getitem,
- supports_membership_test,
- supports_setitem,
-)
-from pylint.interfaces import INFERENCE, IAstroidChecker
-from pylint.utils import get_global_option
-
-BUILTINS = builtins.__name__
-STR_FORMAT = {"%s.str.format" % BUILTINS}
-ASYNCIO_COROUTINE = "asyncio.coroutines.coroutine"
-
-
-def _unflatten(iterable):
- for index, elem in enumerate(iterable):
- if isinstance(elem, Sequence) and not isinstance(elem, str):
- for single_elem in _unflatten(elem):
- yield single_elem
- elif elem and not index:
- # We're interested only in the first element.
- yield elem
-
-
-def _flatten_container(iterable):
- # Flatten nested containers into a single iterable
- for item in iterable:
- if isinstance(item, (list, tuple, types.GeneratorType)):
- yield from _flatten_container(item)
- else:
- yield item
-
-
-def _is_owner_ignored(owner, attrname, ignored_classes, ignored_modules):
- """Check if the given owner should be ignored
-
- This will verify if the owner's module is in *ignored_modules*
- or the owner's module fully qualified name is in *ignored_modules*
- or if the *ignored_modules* contains a pattern which catches
- the fully qualified name of the module.
-
- Also, similar checks are done for the owner itself, if its name
- matches any name from the *ignored_classes* or if its qualified
- name can be found in *ignored_classes*.
- """
- ignored_modules = set(ignored_modules)
- module_name = owner.root().name
- module_qname = owner.root().qname()
-
- for ignore in ignored_modules:
- # Try to match the module name / fully qualified name directly
- if module_qname in ignored_modules or module_name in ignored_modules:
- return True
-
- # Try to see if the ignores pattern match against the module name.
- if fnmatch.fnmatch(module_qname, ignore):
- return True
-
- # Otherwise we might have a root module name being ignored,
- # and the qualified owner has more levels of depth.
- parts = deque(module_name.split("."))
- current_module = ""
-
- while parts:
- part = parts.popleft()
- if not current_module:
- current_module = part
- else:
- current_module += ".{}".format(part)
- if current_module in ignored_modules:
- return True
-
- # Match against ignored classes.
- ignored_classes = set(ignored_classes)
- if hasattr(owner, "qname"):
- qname = owner.qname()
- else:
- qname = ""
- return any(ignore in (attrname, qname) for ignore in ignored_classes)
-
-
-@singledispatch
-def _node_names(node):
- if not hasattr(node, "locals"):
- return []
- return node.locals.keys()
-
-
-@_node_names.register(astroid.ClassDef)
-@_node_names.register(astroid.Instance)
-def _(node):
- values = itertools.chain(node.instance_attrs.keys(), node.locals.keys())
-
- try:
- mro = node.mro()[1:]
- except (NotImplementedError, TypeError):
- mro = node.ancestors()
-
- other_values = [value for cls in mro for value in _node_names(cls)]
- return itertools.chain(values, other_values)
-
-
-def _string_distance(seq1, seq2):
- seq2_length = len(seq2)
-
- row = list(range(1, seq2_length + 1)) + [0]
- for seq1_index, seq1_char in enumerate(seq1):
- last_row = row
- row = [0] * seq2_length + [seq1_index + 1]
-
- for seq2_index, seq2_char in enumerate(seq2):
- row[seq2_index] = min(
- last_row[seq2_index] + 1,
- row[seq2_index - 1] + 1,
- last_row[seq2_index - 1] + (seq1_char != seq2_char),
- )
-
- return row[seq2_length - 1]
-
-
-def _similar_names(owner, attrname, distance_threshold, max_choices):
- """Given an owner and a name, try to find similar names
-
- The similar names are searched given a distance metric and only
- a given number of choices will be returned.
- """
- possible_names = []
- names = _node_names(owner)
-
- for name in names:
- if name == attrname:
- continue
-
- distance = _string_distance(attrname, name)
- if distance <= distance_threshold:
- possible_names.append((name, distance))
-
- # Now get back the values with a minimum, up to the given
- # limit or choices.
- picked = [
- name
- for (name, _) in heapq.nsmallest(
- max_choices, possible_names, key=operator.itemgetter(1)
- )
- ]
- return sorted(picked)
-
-
-def _missing_member_hint(owner, attrname, distance_threshold, max_choices):
- names = _similar_names(owner, attrname, distance_threshold, max_choices)
- if not names:
- # No similar name.
- return ""
-
- names = list(map(repr, names))
- if len(names) == 1:
- names = ", ".join(names)
- else:
- names = "one of {} or {}".format(", ".join(names[:-1]), names[-1])
-
- return "; maybe {}?".format(names)
-
-
-MSGS = {
- "E1101": (
- "%s %r has no %r member%s",
- "no-member",
- "Used when a variable is accessed for an unexistent member.",
- {"old_names": [("E1103", "maybe-no-member")]},
- ),
- "I1101": (
- "%s %r has no %r member%s, but source is unavailable. Consider "
- "adding this module to extension-pkg-whitelist if you want "
- "to perform analysis based on run-time introspection of living objects.",
- "c-extension-no-member",
- "Used when a variable is accessed for non-existent member of C "
- "extension. Due to unavailability of source static analysis is impossible, "
- "but it may be performed by introspecting living objects in run-time.",
- ),
- "E1102": (
- "%s is not callable",
- "not-callable",
- "Used when an object being called has been inferred to a non "
- "callable object.",
- ),
- "E1111": (
- "Assigning result of a function call, where the function has no return",
- "assignment-from-no-return",
- "Used when an assignment is done on a function call but the "
- "inferred function doesn't return anything.",
- ),
- "E1120": (
- "No value for argument %s in %s call",
- "no-value-for-parameter",
- "Used when a function call passes too few arguments.",
- ),
- "E1121": (
- "Too many positional arguments for %s call",
- "too-many-function-args",
- "Used when a function call passes too many positional arguments.",
- ),
- "E1123": (
- "Unexpected keyword argument %r in %s call",
- "unexpected-keyword-arg",
- "Used when a function call passes a keyword argument that "
- "doesn't correspond to one of the function's parameter names.",
- ),
- "E1124": (
- "Argument %r passed by position and keyword in %s call",
- "redundant-keyword-arg",
- "Used when a function call would result in assigning multiple "
- "values to a function parameter, one value from a positional "
- "argument and one from a keyword argument.",
- ),
- "E1125": (
- "Missing mandatory keyword argument %r in %s call",
- "missing-kwoa",
- (
- "Used when a function call does not pass a mandatory"
- " keyword-only argument."
- ),
- ),
- "E1126": (
- "Sequence index is not an int, slice, or instance with __index__",
- "invalid-sequence-index",
- "Used when a sequence type is indexed with an invalid type. "
- "Valid types are ints, slices, and objects with an __index__ "
- "method.",
- ),
- "E1127": (
- "Slice index is not an int, None, or instance with __index__",
- "invalid-slice-index",
- "Used when a slice index is not an integer, None, or an object "
- "with an __index__ method.",
- ),
- "E1128": (
- "Assigning result of a function call, where the function returns None",
- "assignment-from-none",
- "Used when an assignment is done on a function call but the "
- "inferred function returns nothing but None.",
- {"old_names": [("W1111", "old-assignment-from-none")]},
- ),
- "E1129": (
- "Context manager '%s' doesn't implement __enter__ and __exit__.",
- "not-context-manager",
- "Used when an instance in a with statement doesn't implement "
- "the context manager protocol(__enter__/__exit__).",
- ),
- "E1130": (
- "%s",
- "invalid-unary-operand-type",
- "Emitted when a unary operand is used on an object which does not "
- "support this type of operation.",
- ),
- "E1131": (
- "%s",
- "unsupported-binary-operation",
- "Emitted when a binary arithmetic operation between two "
- "operands is not supported.",
- ),
- "E1132": (
- "Got multiple values for keyword argument %r in function call",
- "repeated-keyword",
- "Emitted when a function call got multiple values for a keyword.",
- ),
- "E1135": (
- "Value '%s' doesn't support membership test",
- "unsupported-membership-test",
- "Emitted when an instance in membership test expression doesn't "
- "implement membership protocol (__contains__/__iter__/__getitem__).",
- ),
- "E1136": (
- "Value '%s' is unsubscriptable",
- "unsubscriptable-object",
- "Emitted when a subscripted value doesn't support subscription "
- "(i.e. doesn't define __getitem__ method or __class_getitem__ for a class).",
- ),
- "E1137": (
- "%r does not support item assignment",
- "unsupported-assignment-operation",
- "Emitted when an object does not support item assignment "
- "(i.e. doesn't define __setitem__ method).",
- ),
- "E1138": (
- "%r does not support item deletion",
- "unsupported-delete-operation",
- "Emitted when an object does not support item deletion "
- "(i.e. doesn't define __delitem__ method).",
- ),
- "E1139": (
- "Invalid metaclass %r used",
- "invalid-metaclass",
- "Emitted whenever we can detect that a class is using, "
- "as a metaclass, something which might be invalid for using as "
- "a metaclass.",
- ),
- "E1140": (
- "Dict key is unhashable",
- "unhashable-dict-key",
- "Emitted when a dict key is not hashable "
- "(i.e. doesn't define __hash__ method).",
- ),
- "E1141": (
- "Unpacking a dictionary in iteration without calling .items()",
- "dict-iter-missing-items",
- "Emitted when trying to iterate through a dict without calling .items()",
- ),
- "W1113": (
- "Keyword argument before variable positional arguments list "
- "in the definition of %s function",
- "keyword-arg-before-vararg",
- "When defining a keyword argument before variable positional arguments, one can "
- "end up in having multiple values passed for the aforementioned parameter in "
- "case the method is called with keyword arguments.",
- ),
- "W1114": (
- "Positional arguments appear to be out of order",
- "arguments-out-of-order",
- "Emitted when the caller's argument names fully match the parameter "
- "names in the function signature but do not have the same order.",
- ),
-}
-
-# builtin sequence types in Python 2 and 3.
-SEQUENCE_TYPES = {
- "str",
- "unicode",
- "list",
- "tuple",
- "bytearray",
- "xrange",
- "range",
- "bytes",
- "memoryview",
-}
-
-
-def _emit_no_member(node, owner, owner_name, ignored_mixins=True, ignored_none=True):
- """Try to see if no-member should be emitted for the given owner.
-
- The following cases are ignored:
-
- * the owner is a function and it has decorators.
- * the owner is an instance and it has __getattr__, __getattribute__ implemented
- * the module is explicitly ignored from no-member checks
- * the owner is a class and the name can be found in its metaclass.
- * The access node is protected by an except handler, which handles
- AttributeError, Exception or bare except.
- """
- # pylint: disable=too-many-return-statements
- if node_ignores_exception(node, AttributeError):
- return False
- if ignored_none and isinstance(owner, astroid.Const) and owner.value is None:
- return False
- if is_super(owner) or getattr(owner, "type", None) == "metaclass":
- return False
- if owner_name and ignored_mixins and owner_name[-5:].lower() == "mixin":
- return False
- if isinstance(owner, astroid.FunctionDef) and owner.decorators:
- return False
- if isinstance(owner, (astroid.Instance, astroid.ClassDef)):
- if owner.has_dynamic_getattr():
- # Issue #2565: Don't ignore enums, as they have a `__getattr__` but it's not
- # invoked at this point.
- try:
- metaclass = owner.metaclass()
- except exceptions.MroError:
- return False
- if metaclass:
- return metaclass.qname() == "enum.EnumMeta"
- return False
- if not has_known_bases(owner):
- return False
-
- # Exclude typed annotations, since these might actually exist
- # at some point during the runtime of the program.
- attribute = owner.locals.get(node.attrname, [None])[0]
- if (
- attribute
- and isinstance(attribute, astroid.AssignName)
- and isinstance(attribute.parent, astroid.AnnAssign)
- ):
- return False
- if isinstance(owner, objects.Super):
- # Verify if we are dealing with an invalid Super object.
- # If it is invalid, then there's no point in checking that
- # it has the required attribute. Also, don't fail if the
- # MRO is invalid.
- try:
- owner.super_mro()
- except (exceptions.MroError, exceptions.SuperError):
- return False
- if not all(map(has_known_bases, owner.type.mro())):
- return False
- if isinstance(owner, astroid.Module):
- try:
- owner.getattr("__getattr__")
- return False
- except astroid.NotFoundError:
- pass
- if owner_name and node.attrname.startswith("_" + owner_name):
- # Test if an attribute has been mangled ('private' attribute)
- unmangled_name = node.attrname.split("_" + owner_name)[-1]
- try:
- if owner.getattr(unmangled_name, context=None) is not None:
- return False
- except astroid.NotFoundError:
- return True
- return True
-
-
-def _determine_callable(callable_obj):
- # Ordering is important, since BoundMethod is a subclass of UnboundMethod,
- # and Function inherits Lambda.
- parameters = 0
- if hasattr(callable_obj, "implicit_parameters"):
- parameters = callable_obj.implicit_parameters()
- if isinstance(callable_obj, astroid.BoundMethod):
- # Bound methods have an extra implicit 'self' argument.
- return callable_obj, parameters, callable_obj.type
- if isinstance(callable_obj, astroid.UnboundMethod):
- return callable_obj, parameters, "unbound method"
- if isinstance(callable_obj, astroid.FunctionDef):
- return callable_obj, parameters, callable_obj.type
- if isinstance(callable_obj, astroid.Lambda):
- return callable_obj, parameters, "lambda"
- if isinstance(callable_obj, astroid.ClassDef):
- # Class instantiation, lookup __new__ instead.
- # If we only find object.__new__, we can safely check __init__
- # instead. If __new__ belongs to builtins, then we look
- # again for __init__ in the locals, since we won't have
- # argument information for the builtin __new__ function.
- try:
- # Use the last definition of __new__.
- new = callable_obj.local_attr("__new__")[-1]
- except exceptions.NotFoundError:
- new = None
-
- from_object = new and new.parent.scope().name == "object"
- from_builtins = new and new.root().name in sys.builtin_module_names
-
- if not new or from_object or from_builtins:
- try:
- # Use the last definition of __init__.
- callable_obj = callable_obj.local_attr("__init__")[-1]
- except exceptions.NotFoundError:
- # do nothing, covered by no-init.
- raise ValueError
- else:
- callable_obj = new
-
- if not isinstance(callable_obj, astroid.FunctionDef):
- raise ValueError
- # both have an extra implicit 'cls'/'self' argument.
- return callable_obj, parameters, "constructor"
-
- raise ValueError
-
-
-def _has_parent_of_type(node, node_type, statement):
- """Check if the given node has a parent of the given type."""
- parent = node.parent
- while not isinstance(parent, node_type) and statement.parent_of(parent):
- parent = parent.parent
- return isinstance(parent, node_type)
-
-
-def _no_context_variadic_keywords(node, scope):
- statement = node.statement()
- variadics = ()
-
- if isinstance(scope, astroid.Lambda) and not isinstance(scope, astroid.FunctionDef):
- variadics = list(node.keywords or []) + node.kwargs
- else:
- if isinstance(statement, (astroid.Return, astroid.Expr)) and isinstance(
- statement.value, astroid.Call
- ):
- call = statement.value
- variadics = list(call.keywords or []) + call.kwargs
-
- return _no_context_variadic(node, scope.args.kwarg, astroid.Keyword, variadics)
-
-
-def _no_context_variadic_positional(node, scope):
- variadics = ()
- if isinstance(scope, astroid.Lambda) and not isinstance(scope, astroid.FunctionDef):
- variadics = node.starargs + node.kwargs
- else:
- statement = node.statement()
- if isinstance(statement, (astroid.Expr, astroid.Return)) and isinstance(
- statement.value, astroid.Call
- ):
- call = statement.value
- variadics = call.starargs + call.kwargs
-
- return _no_context_variadic(node, scope.args.vararg, astroid.Starred, variadics)
-
-
-def _no_context_variadic(node, variadic_name, variadic_type, variadics):
- """Verify if the given call node has variadic nodes without context
-
- This is a workaround for handling cases of nested call functions
- which don't have the specific call context at hand.
- Variadic arguments (variable positional arguments and variable
- keyword arguments) are inferred, inherently wrong, by astroid
- as a Tuple, respectively a Dict with empty elements.
- This can lead pylint to believe that a function call receives
- too few arguments.
- """
- scope = node.scope()
- is_in_lambda_scope = not isinstance(scope, astroid.FunctionDef) and isinstance(
- scope, astroid.Lambda
- )
- statement = node.statement()
- for name in statement.nodes_of_class(astroid.Name):
- if name.name != variadic_name:
- continue
-
- inferred = safe_infer(name)
- if isinstance(inferred, (astroid.List, astroid.Tuple)):
- length = len(inferred.elts)
- elif isinstance(inferred, astroid.Dict):
- length = len(inferred.items)
- else:
- continue
-
- if is_in_lambda_scope and isinstance(inferred.parent, astroid.Arguments):
- # The statement of the variadic will be the assignment itself,
- # so we need to go the lambda instead
- inferred_statement = inferred.parent.parent
- else:
- inferred_statement = inferred.statement()
-
- if not length and isinstance(inferred_statement, astroid.Lambda):
- is_in_starred_context = _has_parent_of_type(node, variadic_type, statement)
- used_as_starred_argument = any(
- variadic.value == name or variadic.value.parent_of(name)
- for variadic in variadics
- )
- if is_in_starred_context or used_as_starred_argument:
- return True
- return False
-
-
-def _is_invalid_metaclass(metaclass):
- try:
- mro = metaclass.mro()
- except NotImplementedError:
- # Cannot have a metaclass which is not a newstyle class.
- return True
- else:
- if not any(is_builtin_object(cls) and cls.name == "type" for cls in mro):
- return True
- return False
-
-
-def _infer_from_metaclass_constructor(cls, func):
- """Try to infer what the given *func* constructor is building
-
- :param astroid.FunctionDef func:
- A metaclass constructor. Metaclass definitions can be
- functions, which should accept three arguments, the name of
- the class, the bases of the class and the attributes.
- The function could return anything, but usually it should
- be a proper metaclass.
- :param astroid.ClassDef cls:
- The class for which the *func* parameter should generate
- a metaclass.
- :returns:
- The class generated by the function or None,
- if we couldn't infer it.
- :rtype: astroid.ClassDef
- """
- context = astroid.context.InferenceContext()
-
- class_bases = astroid.List()
- class_bases.postinit(elts=cls.bases)
-
- attrs = astroid.Dict()
- local_names = [(name, values[-1]) for name, values in cls.locals.items()]
- attrs.postinit(local_names)
-
- builder_args = astroid.Tuple()
- builder_args.postinit([cls.name, class_bases, attrs])
-
- context.callcontext = astroid.context.CallContext(builder_args)
- try:
- inferred = next(func.infer_call_result(func, context), None)
- except astroid.InferenceError:
- return None
- return inferred or None
-
-
-def _is_c_extension(module_node):
- return (
- not modutils.is_standard_module(module_node.name)
- and not module_node.fully_defined()
- )
-
-
-class TypeChecker(BaseChecker):
- """try to find bugs in the code using type inference
- """
-
- __implements__ = (IAstroidChecker,)
-
- # configuration section name
- name = "typecheck"
- # messages
- msgs = MSGS
- priority = -1
- # configuration options
- options = (
- (
- "ignore-on-opaque-inference",
- {
- "default": True,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": "This flag controls whether pylint should warn about "
- "no-member and similar checks whenever an opaque object "
- "is returned when inferring. The inference can return "
- "multiple potential results while evaluating a Python object, "
- "but some branches might not be evaluated, which results in "
- "partial inference. In that case, it might be useful to still emit "
- "no-member and other checks for the rest of the inferred objects.",
- },
- ),
- (
- "ignore-mixin-members",
- {
- "default": True,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": 'Tells whether missing members accessed in mixin \
-class should be ignored. A mixin class is detected if its name ends with \
-"mixin" (case insensitive).',
- },
- ),
- (
- "ignore-none",
- {
- "default": True,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": "Tells whether to warn about missing members when the owner "
- "of the attribute is inferred to be None.",
- },
- ),
- (
- "ignored-modules",
- {
- "default": (),
- "type": "csv",
- "metavar": "<module names>",
- "help": "List of module names for which member attributes "
- "should not be checked (useful for modules/projects "
- "where namespaces are manipulated during runtime and "
- "thus existing member attributes cannot be "
- "deduced by static analysis). It supports qualified "
- "module names, as well as Unix pattern matching.",
- },
- ),
- # the defaults here are *stdlib* names that (almost) always
- # lead to false positives, since their idiomatic use is
- # 'too dynamic' for pylint to grok.
- (
- "ignored-classes",
- {
- "default": ("optparse.Values", "thread._local", "_thread._local"),
- "type": "csv",
- "metavar": "<members names>",
- "help": "List of class names for which member attributes "
- "should not be checked (useful for classes with "
- "dynamically set attributes). This supports "
- "the use of qualified names.",
- },
- ),
- (
- "generated-members",
- {
- "default": (),
- "type": "string",
- "metavar": "<members names>",
- "help": "List of members which are set dynamically and \
-missed by pylint inference system, and so shouldn't trigger E1101 when \
-accessed. Python regular expressions are accepted.",
- },
- ),
- (
- "contextmanager-decorators",
- {
- "default": ["contextlib.contextmanager"],
- "type": "csv",
- "metavar": "<decorator names>",
- "help": "List of decorators that produce context managers, "
- "such as contextlib.contextmanager. Add to this list "
- "to register other decorators that produce valid "
- "context managers.",
- },
- ),
- (
- "missing-member-hint-distance",
- {
- "default": 1,
- "type": "int",
- "metavar": "<member hint edit distance>",
- "help": "The minimum edit distance a name should have in order "
- "to be considered a similar match for a missing member name.",
- },
- ),
- (
- "missing-member-max-choices",
- {
- "default": 1,
- "type": "int",
- "metavar": "<member hint max choices>",
- "help": "The total number of similar names that should be taken in "
- "consideration when showing a hint for a missing member.",
- },
- ),
- (
- "missing-member-hint",
- {
- "default": True,
- "type": "yn",
- "metavar": "<missing member hint>",
- "help": "Show a hint with possible names when a member name was not "
- "found. The aspect of finding the hint is based on edit distance.",
- },
- ),
- (
- "signature-mutators",
- {
- "default": [],
- "type": "csv",
- "metavar": "<decorator names>",
- "help": "List of decorators that change the signature of "
- "a decorated function.",
- },
- ),
- )
-
- @decorators.cachedproperty
- def _suggestion_mode(self):
- return get_global_option(self, "suggestion-mode", default=True)
-
- def open(self):
- # do this in open since config not fully initialized in __init__
- # generated_members may contain regular expressions
- # (surrounded by quote `"` and followed by a comma `,`)
- # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' =>
- # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}')
- if isinstance(self.config.generated_members, str):
- gen = shlex.shlex(self.config.generated_members)
- gen.whitespace += ","
- gen.wordchars += r"[]-+\.*?()|"
- self.config.generated_members = tuple(tok.strip('"') for tok in gen)
-
- @check_messages("keyword-arg-before-vararg")
- def visit_functiondef(self, node):
- # check for keyword arg before varargs
- if node.args.vararg and node.args.defaults:
- self.add_message("keyword-arg-before-vararg", node=node, args=(node.name))
-
- visit_asyncfunctiondef = visit_functiondef
-
- @check_messages("invalid-metaclass")
- def visit_classdef(self, node):
- def _metaclass_name(metaclass):
- if isinstance(metaclass, (astroid.ClassDef, astroid.FunctionDef)):
- return metaclass.name
- return metaclass.as_string()
-
- metaclass = node.declared_metaclass()
- if not metaclass:
- return
-
- if isinstance(metaclass, astroid.FunctionDef):
- # Try to infer the result.
- metaclass = _infer_from_metaclass_constructor(node, metaclass)
- if not metaclass:
- # Don't do anything if we cannot infer the result.
- return
-
- if isinstance(metaclass, astroid.ClassDef):
- if _is_invalid_metaclass(metaclass):
- self.add_message(
- "invalid-metaclass", node=node, args=(_metaclass_name(metaclass),)
- )
- else:
- self.add_message(
- "invalid-metaclass", node=node, args=(_metaclass_name(metaclass),)
- )
-
- def visit_assignattr(self, node):
- if isinstance(node.assign_type(), astroid.AugAssign):
- self.visit_attribute(node)
-
- def visit_delattr(self, node):
- self.visit_attribute(node)
-
- @check_messages("no-member", "c-extension-no-member")
- def visit_attribute(self, node):
- """check that the accessed attribute exists
-
- to avoid too much false positives for now, we'll consider the code as
- correct if a single of the inferred nodes has the accessed attribute.
-
- function/method, super call and metaclasses are ignored
- """
- for pattern in self.config.generated_members:
- # attribute is marked as generated, stop here
- if re.match(pattern, node.attrname):
- return
- if re.match(pattern, node.as_string()):
- return
-
- try:
- inferred = list(node.expr.infer())
- except exceptions.InferenceError:
- return
-
- # list of (node, nodename) which are missing the attribute
- missingattr = set()
-
- non_opaque_inference_results = [
- owner
- for owner in inferred
- if owner is not astroid.Uninferable
- and not isinstance(owner, astroid.nodes.Unknown)
- ]
- if (
- len(non_opaque_inference_results) != len(inferred)
- and self.config.ignore_on_opaque_inference
- ):
- # There is an ambiguity in the inference. Since we can't
- # make sure that we won't emit a false positive, we just stop
- # whenever the inference returns an opaque inference object.
- return
- for owner in non_opaque_inference_results:
- name = getattr(owner, "name", None)
- if _is_owner_ignored(
- owner, name, self.config.ignored_classes, self.config.ignored_modules
- ):
- continue
-
- try:
- if not [
- n
- for n in owner.getattr(node.attrname)
- if not isinstance(n.statement(), astroid.AugAssign)
- ]:
- missingattr.add((owner, name))
- continue
- except AttributeError:
- continue
- except exceptions.NotFoundError:
- # This can't be moved before the actual .getattr call,
- # because there can be more values inferred and we are
- # stopping after the first one which has the attribute in question.
- # The problem is that if the first one has the attribute,
- # but we continue to the next values which doesn't have the
- # attribute, then we'll have a false positive.
- # So call this only after the call has been made.
- if not _emit_no_member(
- node,
- owner,
- name,
- ignored_mixins=self.config.ignore_mixin_members,
- ignored_none=self.config.ignore_none,
- ):
- continue
- missingattr.add((owner, name))
- continue
- # stop on the first found
- break
- else:
- # we have not found any node with the attributes, display the
- # message for inferred nodes
- done = set()
- for owner, name in missingattr:
- if isinstance(owner, astroid.Instance):
- actual = owner._proxied
- else:
- actual = owner
- if actual in done:
- continue
- done.add(actual)
-
- msg, hint = self._get_nomember_msgid_hint(node, owner)
- self.add_message(
- msg,
- node=node,
- args=(owner.display_type(), name, node.attrname, hint),
- confidence=INFERENCE,
- )
-
- def _get_nomember_msgid_hint(self, node, owner):
- suggestions_are_possible = self._suggestion_mode and isinstance(
- owner, astroid.Module
- )
- if suggestions_are_possible and _is_c_extension(owner):
- msg = "c-extension-no-member"
- hint = ""
- else:
- msg = "no-member"
- if self.config.missing_member_hint:
- hint = _missing_member_hint(
- owner,
- node.attrname,
- self.config.missing_member_hint_distance,
- self.config.missing_member_max_choices,
- )
- else:
- hint = ""
- return msg, hint
-
- @check_messages("assignment-from-no-return", "assignment-from-none")
- def visit_assign(self, node):
- """check that if assigning to a function call, the function is
- possibly returning something valuable
- """
- if not isinstance(node.value, astroid.Call):
- return
-
- function_node = safe_infer(node.value.func)
- funcs = (astroid.FunctionDef, astroid.UnboundMethod, astroid.BoundMethod)
- if not isinstance(function_node, funcs):
- return
-
- # Unwrap to get the actual function object
- if isinstance(function_node, astroid.BoundMethod) and isinstance(
- function_node._proxied, astroid.UnboundMethod
- ):
- function_node = function_node._proxied._proxied
-
- # Make sure that it's a valid function that we can analyze.
- # Ordered from less expensive to more expensive checks.
- # pylint: disable=too-many-boolean-expressions
- if (
- not function_node.is_function
- or isinstance(function_node, astroid.AsyncFunctionDef)
- or function_node.decorators
- or function_node.is_generator()
- or function_node.is_abstract(pass_is_abstract=False)
- or not function_node.root().fully_defined()
- ):
- return
-
- returns = list(
- function_node.nodes_of_class(astroid.Return, skip_klass=astroid.FunctionDef)
- )
- if not returns:
- self.add_message("assignment-from-no-return", node=node)
- else:
- for rnode in returns:
- if not (
- isinstance(rnode.value, astroid.Const)
- and rnode.value.value is None
- or rnode.value is None
- ):
- break
- else:
- self.add_message("assignment-from-none", node=node)
-
- def _check_uninferable_call(self, node):
- """
- Check that the given uninferable Call node does not
- call an actual function.
- """
- if not isinstance(node.func, astroid.Attribute):
- return
-
- # Look for properties. First, obtain
- # the lhs of the Attribute node and search the attribute
- # there. If that attribute is a property or a subclass of properties,
- # then most likely it's not callable.
-
- expr = node.func.expr
- klass = safe_infer(expr)
- if (
- klass is None
- or klass is astroid.Uninferable
- or not isinstance(klass, astroid.Instance)
- ):
- return
-
- try:
- attrs = klass._proxied.getattr(node.func.attrname)
- except exceptions.NotFoundError:
- return
-
- for attr in attrs:
- if attr is astroid.Uninferable:
- continue
- if not isinstance(attr, astroid.FunctionDef):
- continue
-
- # Decorated, see if it is decorated with a property.
- # Also, check the returns and see if they are callable.
- if decorated_with_property(attr):
-
- try:
- all_returns_are_callable = all(
- return_node.callable() or return_node is astroid.Uninferable
- for return_node in attr.infer_call_result(node)
- )
- except astroid.InferenceError:
- continue
-
- if not all_returns_are_callable:
- self.add_message(
- "not-callable", node=node, args=node.func.as_string()
- )
- break
-
- def _check_argument_order(self, node, call_site, called, called_param_names):
- """Match the supplied argument names against the function parameters.
- Warn if some argument names are not in the same order as they are in
- the function signature.
- """
- # Check for called function being an object instance function
- # If so, ignore the initial 'self' argument in the signature
- try:
- is_classdef = isinstance(called.parent, astroid.scoped_nodes.ClassDef)
- if is_classdef and called_param_names[0] == "self":
- called_param_names = called_param_names[1:]
- except IndexError:
- return
-
- try:
- # extract argument names, if they have names
- calling_parg_names = [p.name for p in call_site.positional_arguments]
-
- # Additionally get names of keyword arguments to use in a full match
- # against parameters
- calling_kwarg_names = [
- arg.name for arg in call_site.keyword_arguments.values()
- ]
- except AttributeError:
- # the type of arg does not provide a `.name`. In this case we
- # stop checking for out-of-order arguments because it is only relevant
- # for named variables.
- return
-
- # Don't check for ordering if there is an unmatched arg or param
- arg_set = set(calling_parg_names) | set(calling_kwarg_names)
- param_set = set(called_param_names)
- if arg_set != param_set:
- return
-
- # Warn based on the equality of argument ordering
- if calling_parg_names != called_param_names[: len(calling_parg_names)]:
- self.add_message("arguments-out-of-order", node=node, args=())
-
- # pylint: disable=too-many-branches,too-many-locals
- @check_messages(*(list(MSGS.keys())))
- def visit_call(self, node):
- """check that called functions/methods are inferred to callable objects,
- and that the arguments passed to the function match the parameters in
- the inferred function's definition
- """
- called = safe_infer(node.func)
- # only function, generator and object defining __call__ are allowed
- # Ignore instances of descriptors since astroid cannot properly handle them
- # yet
- if called and not called.callable():
- if isinstance(called, astroid.Instance) and (
- not has_known_bases(called)
- or (
- called.parent is not None
- and isinstance(called.scope(), astroid.ClassDef)
- and "__get__" in called.locals
- )
- ):
- # Don't emit if we can't make sure this object is callable.
- pass
- else:
- self.add_message("not-callable", node=node, args=node.func.as_string())
-
- self._check_uninferable_call(node)
- try:
- called, implicit_args, callable_name = _determine_callable(called)
- except ValueError:
- # Any error occurred during determining the function type, most of
- # those errors are handled by different warnings.
- return
-
- if called.args.args is None:
- # Built-in functions have no argument information.
- return
-
- if len(called.argnames()) != len(set(called.argnames())):
- # Duplicate parameter name (see duplicate-argument). We can't really
- # make sense of the function call in this case, so just return.
- return
-
- # Build the set of keyword arguments, checking for duplicate keywords,
- # and count the positional arguments.
- call_site = astroid.arguments.CallSite.from_call(node)
-
- # Warn about duplicated keyword arguments, such as `f=24, **{'f': 24}`
- for keyword in call_site.duplicated_keywords:
- self.add_message("repeated-keyword", node=node, args=(keyword,))
-
- if call_site.has_invalid_arguments() or call_site.has_invalid_keywords():
- # Can't make sense of this.
- return
-
- # Has the function signature changed in ways we cannot reliably detect?
- if hasattr(called, "decorators") and decorated_with(
- called, self.config.signature_mutators
- ):
- return
-
- num_positional_args = len(call_site.positional_arguments)
- keyword_args = list(call_site.keyword_arguments.keys())
- overload_function = is_overload_stub(called)
-
- # Determine if we don't have a context for our call and we use variadics.
- node_scope = node.scope()
- if isinstance(node_scope, (astroid.Lambda, astroid.FunctionDef)):
- has_no_context_positional_variadic = _no_context_variadic_positional(
- node, node_scope
- )
- has_no_context_keywords_variadic = _no_context_variadic_keywords(
- node, node_scope
- )
- else:
- has_no_context_positional_variadic = (
- has_no_context_keywords_variadic
- ) = False
-
- # These are coming from the functools.partial implementation in astroid
- already_filled_positionals = getattr(called, "filled_positionals", 0)
- already_filled_keywords = getattr(called, "filled_keywords", {})
-
- keyword_args += list(already_filled_keywords)
- num_positional_args += implicit_args + already_filled_positionals
-
- # Analyze the list of formal parameters.
- args = list(itertools.chain(called.args.posonlyargs or (), called.args.args))
- num_mandatory_parameters = len(args) - len(called.args.defaults)
- parameters = []
- parameter_name_to_index = {}
- for i, arg in enumerate(args):
- if isinstance(arg, astroid.Tuple):
- name = None
- # Don't store any parameter names within the tuple, since those
- # are not assignable from keyword arguments.
- else:
- assert isinstance(arg, astroid.AssignName)
- # This occurs with:
- # def f( (a), (b) ): pass
- name = arg.name
- parameter_name_to_index[name] = i
- if i >= num_mandatory_parameters:
- defval = called.args.defaults[i - num_mandatory_parameters]
- else:
- defval = None
- parameters.append([(name, defval), False])
-
- kwparams = {}
- for i, arg in enumerate(called.args.kwonlyargs):
- if isinstance(arg, astroid.Keyword):
- name = arg.arg
- else:
- assert isinstance(arg, astroid.AssignName)
- name = arg.name
- kwparams[name] = [called.args.kw_defaults[i], False]
-
- self._check_argument_order(
- node, call_site, called, [p[0][0] for p in parameters]
- )
-
- # 1. Match the positional arguments.
- for i in range(num_positional_args):
- if i < len(parameters):
- parameters[i][1] = True
- elif called.args.vararg is not None:
- # The remaining positional arguments get assigned to the *args
- # parameter.
- break
- else:
- if not overload_function:
- # Too many positional arguments.
- self.add_message(
- "too-many-function-args", node=node, args=(callable_name,)
- )
- break
-
- # 2. Match the keyword arguments.
- for keyword in keyword_args:
- if keyword in parameter_name_to_index:
- i = parameter_name_to_index[keyword]
- if parameters[i][1]:
- # Duplicate definition of function parameter.
-
- # Might be too hardcoded, but this can actually
- # happen when using str.format and `self` is passed
- # by keyword argument, as in `.format(self=self)`.
- # It's perfectly valid to so, so we're just skipping
- # it if that's the case.
- if not (keyword == "self" and called.qname() in STR_FORMAT):
- self.add_message(
- "redundant-keyword-arg",
- node=node,
- args=(keyword, callable_name),
- )
- else:
- parameters[i][1] = True
- elif keyword in kwparams:
- if kwparams[keyword][1]:
- # Duplicate definition of function parameter.
- self.add_message(
- "redundant-keyword-arg",
- node=node,
- args=(keyword, callable_name),
- )
- else:
- kwparams[keyword][1] = True
- elif called.args.kwarg is not None:
- # The keyword argument gets assigned to the **kwargs parameter.
- pass
- elif not overload_function:
- # Unexpected keyword argument.
- self.add_message(
- "unexpected-keyword-arg", node=node, args=(keyword, callable_name)
- )
-
- # 3. Match the **kwargs, if any.
- if node.kwargs:
- for i, [(name, defval), assigned] in enumerate(parameters):
- # Assume that *kwargs provides values for all remaining
- # unassigned named parameters.
- if name is not None:
- parameters[i][1] = True
- else:
- # **kwargs can't assign to tuples.
- pass
-
- # Check that any parameters without a default have been assigned
- # values.
- for [(name, defval), assigned] in parameters:
- if (defval is None) and not assigned:
- if name is None:
- display_name = "<tuple>"
- else:
- display_name = repr(name)
- if not has_no_context_positional_variadic and not overload_function:
- self.add_message(
- "no-value-for-parameter",
- node=node,
- args=(display_name, callable_name),
- )
-
- for name in kwparams:
- defval, assigned = kwparams[name]
- if defval is None and not assigned and not has_no_context_keywords_variadic:
- self.add_message("missing-kwoa", node=node, args=(name, callable_name))
-
- @check_messages("invalid-sequence-index")
- def visit_extslice(self, node):
- # Check extended slice objects as if they were used as a sequence
- # index to check if the object being sliced can support them
- return self.visit_index(node)
-
- @check_messages("invalid-sequence-index")
- def visit_index(self, node):
- if not node.parent or not hasattr(node.parent, "value"):
- return None
- # Look for index operations where the parent is a sequence type.
- # If the types can be determined, only allow indices to be int,
- # slice or instances with __index__.
- parent_type = safe_infer(node.parent.value)
- if not isinstance(
- parent_type, (astroid.ClassDef, astroid.Instance)
- ) or not has_known_bases(parent_type):
- return None
-
- # Determine what method on the parent this index will use
- # The parent of this node will be a Subscript, and the parent of that
- # node determines if the Subscript is a get, set, or delete operation.
- if node.parent.ctx is astroid.Store:
- methodname = "__setitem__"
- elif node.parent.ctx is astroid.Del:
- methodname = "__delitem__"
- else:
- methodname = "__getitem__"
-
- # Check if this instance's __getitem__, __setitem__, or __delitem__, as
- # appropriate to the statement, is implemented in a builtin sequence
- # type. This way we catch subclasses of sequence types but skip classes
- # that override __getitem__ and which may allow non-integer indices.
- try:
- methods = dunder_lookup.lookup(parent_type, methodname)
- if methods is astroid.Uninferable:
- return None
- itemmethod = methods[0]
- except (
- exceptions.NotFoundError,
- exceptions.AttributeInferenceError,
- IndexError,
- ):
- return None
-
- if (
- not isinstance(itemmethod, astroid.FunctionDef)
- or itemmethod.root().name != BUILTINS
- or not itemmethod.parent
- or itemmethod.parent.name not in SEQUENCE_TYPES
- ):
- return None
-
- # For ExtSlice objects coming from visit_extslice, no further
- # inference is necessary, since if we got this far the ExtSlice
- # is an error.
- if isinstance(node, astroid.ExtSlice):
- index_type = node
- else:
- index_type = safe_infer(node)
- if index_type is None or index_type is astroid.Uninferable:
- return None
- # Constants must be of type int
- if isinstance(index_type, astroid.Const):
- if isinstance(index_type.value, int):
- return None
- # Instance values must be int, slice, or have an __index__ method
- elif isinstance(index_type, astroid.Instance):
- if index_type.pytype() in (BUILTINS + ".int", BUILTINS + ".slice"):
- return None
- try:
- index_type.getattr("__index__")
- return None
- except exceptions.NotFoundError:
- pass
- elif isinstance(index_type, astroid.Slice):
- # Delegate to visit_slice. A slice can be present
- # here after inferring the index node, which could
- # be a `slice(...)` call for instance.
- return self.visit_slice(index_type)
-
- # Anything else is an error
- self.add_message("invalid-sequence-index", node=node)
- return None
-
- @check_messages("invalid-slice-index")
- def visit_slice(self, node):
- # Check the type of each part of the slice
- invalid_slices = 0
- for index in (node.lower, node.upper, node.step):
- if index is None:
- continue
-
- index_type = safe_infer(index)
- if index_type is None or index_type is astroid.Uninferable:
- continue
-
- # Constants must of type int or None
- if isinstance(index_type, astroid.Const):
- if isinstance(index_type.value, (int, type(None))):
- continue
- # Instance values must be of type int, None or an object
- # with __index__
- elif isinstance(index_type, astroid.Instance):
- if index_type.pytype() in (BUILTINS + ".int", BUILTINS + ".NoneType"):
- continue
-
- try:
- index_type.getattr("__index__")
- return
- except exceptions.NotFoundError:
- pass
- invalid_slices += 1
-
- if not invalid_slices:
- return
-
- # Anything else is an error, unless the object that is indexed
- # is a custom object, which knows how to handle this kind of slices
- parent = node.parent
- if isinstance(parent, astroid.ExtSlice):
- parent = parent.parent
- if isinstance(parent, astroid.Subscript):
- inferred = safe_infer(parent.value)
- if inferred is None or inferred is astroid.Uninferable:
- # Don't know what this is
- return
- known_objects = (
- astroid.List,
- astroid.Dict,
- astroid.Tuple,
- astroid.objects.FrozenSet,
- astroid.Set,
- )
- if not isinstance(inferred, known_objects):
- # Might be an instance that knows how to handle this slice object
- return
- for _ in range(invalid_slices):
- self.add_message("invalid-slice-index", node=node)
-
- @check_messages("not-context-manager")
- def visit_with(self, node):
- for ctx_mgr, _ in node.items:
- context = astroid.context.InferenceContext()
- inferred = safe_infer(ctx_mgr, context=context)
- if inferred is None or inferred is astroid.Uninferable:
- continue
-
- if isinstance(inferred, bases.Generator):
- # Check if we are dealing with a function decorated
- # with contextlib.contextmanager.
- if decorated_with(
- inferred.parent, self.config.contextmanager_decorators
- ):
- continue
- # If the parent of the generator is not the context manager itself,
- # that means that it could have been returned from another
- # function which was the real context manager.
- # The following approach is more of a hack rather than a real
- # solution: walk all the inferred statements for the
- # given *ctx_mgr* and if you find one function scope
- # which is decorated, consider it to be the real
- # manager and give up, otherwise emit not-context-manager.
- # See the test file for not_context_manager for a couple
- # of self explaining tests.
-
- # Retrieve node from all previusly visited nodes in the the inference history
- context_path_names = filter(None, _unflatten(context.path))
- inferred_paths = _flatten_container(
- safe_infer(path) for path in context_path_names
- )
- for inferred_path in inferred_paths:
- if not inferred_path:
- continue
- scope = inferred_path.scope()
- if not isinstance(scope, astroid.FunctionDef):
- continue
- if decorated_with(scope, self.config.contextmanager_decorators):
- break
- else:
- self.add_message(
- "not-context-manager", node=node, args=(inferred.name,)
- )
- else:
- try:
- inferred.getattr("__enter__")
- inferred.getattr("__exit__")
- except exceptions.NotFoundError:
- if isinstance(inferred, astroid.Instance):
- # If we do not know the bases of this class,
- # just skip it.
- if not has_known_bases(inferred):
- continue
- # Just ignore mixin classes.
- if self.config.ignore_mixin_members:
- if inferred.name[-5:].lower() == "mixin":
- continue
-
- self.add_message(
- "not-context-manager", node=node, args=(inferred.name,)
- )
-
- @check_messages("invalid-unary-operand-type")
- def visit_unaryop(self, node):
- """Detect TypeErrors for unary operands."""
-
- for error in node.type_errors():
- # Let the error customize its output.
- self.add_message("invalid-unary-operand-type", args=str(error), node=node)
-
- @check_messages("unsupported-binary-operation")
- def _visit_binop(self, node):
- """Detect TypeErrors for binary arithmetic operands."""
- self._check_binop_errors(node)
-
- @check_messages("unsupported-binary-operation")
- def _visit_augassign(self, node):
- """Detect TypeErrors for augmented binary arithmetic operands."""
- self._check_binop_errors(node)
-
- def _check_binop_errors(self, node):
- for error in node.type_errors():
- # Let the error customize its output.
- if any(
- isinstance(obj, astroid.ClassDef) and not has_known_bases(obj)
- for obj in (error.left_type, error.right_type)
- ):
- continue
- self.add_message("unsupported-binary-operation", args=str(error), node=node)
-
- def _check_membership_test(self, node):
- if is_inside_abstract_class(node):
- return
- if is_comprehension(node):
- return
- inferred = safe_infer(node)
- if inferred is None or inferred is astroid.Uninferable:
- return
- if not supports_membership_test(inferred):
- self.add_message(
- "unsupported-membership-test", args=node.as_string(), node=node
- )
-
- @check_messages("unsupported-membership-test")
- def visit_compare(self, node):
- if len(node.ops) != 1:
- return
-
- op, right = node.ops[0]
- if op in ["in", "not in"]:
- self._check_membership_test(right)
-
- @check_messages(
- "unsubscriptable-object",
- "unsupported-assignment-operation",
- "unsupported-delete-operation",
- "unhashable-dict-key",
- )
- def visit_subscript(self, node):
- supported_protocol = None
- if isinstance(node.value, (astroid.ListComp, astroid.DictComp)):
- return
-
- if isinstance(node.value, astroid.Dict):
- # Assert dict key is hashable
- inferred = safe_infer(node.slice.value)
- if inferred not in (None, astroid.Uninferable):
- try:
- hash_fn = next(inferred.igetattr("__hash__"))
- except astroid.InferenceError:
- pass
- else:
- if getattr(hash_fn, "value", True) is None:
- self.add_message("unhashable-dict-key", node=node.value)
-
- if node.ctx == astroid.Load:
- supported_protocol = supports_getitem
- msg = "unsubscriptable-object"
- elif node.ctx == astroid.Store:
- supported_protocol = supports_setitem
- msg = "unsupported-assignment-operation"
- elif node.ctx == astroid.Del:
- supported_protocol = supports_delitem
- msg = "unsupported-delete-operation"
-
- if isinstance(node.value, astroid.SetComp):
- self.add_message(msg, args=node.value.as_string(), node=node.value)
- return
-
- if is_inside_abstract_class(node):
- return
-
- inferred = safe_infer(node.value)
- if inferred is None or inferred is astroid.Uninferable:
- return
-
- if not supported_protocol(inferred):
- self.add_message(msg, args=node.value.as_string(), node=node.value)
-
- @check_messages("dict-items-missing-iter")
- def visit_for(self, node):
- if not isinstance(node.target, astroid.node_classes.Tuple):
- # target is not a tuple
- return
- if not len(node.target.elts) == 2:
- # target is not a tuple of two elements
- return
-
- iterable = node.iter
- if not isinstance(iterable, astroid.node_classes.Name):
- # it's not a bare variable
- return
-
- inferred = safe_infer(iterable)
- if not inferred:
- return
- if not isinstance(inferred, astroid.node_classes.Dict):
- # the iterable is not a dict
- return
-
- self.add_message("dict-iter-missing-items", node=node)
-
-
-class IterableChecker(BaseChecker):
- """
- Checks for non-iterables used in an iterable context.
- Contexts include:
- - for-statement
- - starargs in function call
- - `yield from`-statement
- - list, dict and set comprehensions
- - generator expressions
- Also checks for non-mappings in function call kwargs.
- """
-
- __implements__ = (IAstroidChecker,)
- name = "typecheck"
-
- msgs = {
- "E1133": (
- "Non-iterable value %s is used in an iterating context",
- "not-an-iterable",
- "Used when a non-iterable value is used in place where "
- "iterable is expected",
- ),
- "E1134": (
- "Non-mapping value %s is used in a mapping context",
- "not-a-mapping",
- "Used when a non-mapping value is used in place where "
- "mapping is expected",
- ),
- }
-
- @staticmethod
- def _is_asyncio_coroutine(node):
- if not isinstance(node, astroid.Call):
- return False
-
- inferred_func = safe_infer(node.func)
- if not isinstance(inferred_func, astroid.FunctionDef):
- return False
- if not inferred_func.decorators:
- return False
- for decorator in inferred_func.decorators.nodes:
- inferred_decorator = safe_infer(decorator)
- if not isinstance(inferred_decorator, astroid.FunctionDef):
- continue
- if inferred_decorator.qname() != ASYNCIO_COROUTINE:
- continue
- return True
- return False
-
- def _check_iterable(self, node, check_async=False):
- if is_inside_abstract_class(node) or is_comprehension(node):
- return
- inferred = safe_infer(node)
- if not inferred:
- return
- if not is_iterable(inferred, check_async=check_async):
- self.add_message("not-an-iterable", args=node.as_string(), node=node)
-
- def _check_mapping(self, node):
- if is_inside_abstract_class(node):
- return
- if isinstance(node, astroid.DictComp):
- return
- inferred = safe_infer(node)
- if inferred is None or inferred is astroid.Uninferable:
- return
- if not is_mapping(inferred):
- self.add_message("not-a-mapping", args=node.as_string(), node=node)
-
- @check_messages("not-an-iterable")
- def visit_for(self, node):
- self._check_iterable(node.iter)
-
- @check_messages("not-an-iterable")
- def visit_asyncfor(self, node):
- self._check_iterable(node.iter, check_async=True)
-
- @check_messages("not-an-iterable")
- def visit_yieldfrom(self, node):
- if self._is_asyncio_coroutine(node.value):
- return
- self._check_iterable(node.value)
-
- @check_messages("not-an-iterable", "not-a-mapping")
- def visit_call(self, node):
- for stararg in node.starargs:
- self._check_iterable(stararg.value)
- for kwarg in node.kwargs:
- self._check_mapping(kwarg.value)
-
- @check_messages("not-an-iterable")
- def visit_listcomp(self, node):
- for gen in node.generators:
- self._check_iterable(gen.iter, check_async=gen.is_async)
-
- @check_messages("not-an-iterable")
- def visit_dictcomp(self, node):
- for gen in node.generators:
- self._check_iterable(gen.iter, check_async=gen.is_async)
-
- @check_messages("not-an-iterable")
- def visit_setcomp(self, node):
- for gen in node.generators:
- self._check_iterable(gen.iter, check_async=gen.is_async)
-
- @check_messages("not-an-iterable")
- def visit_generatorexp(self, node):
- for gen in node.generators:
- self._check_iterable(gen.iter, check_async=gen.is_async)
-
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(TypeChecker(linter))
- linter.register_checker(IterableChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/utils.py b/venv/Lib/site-packages/pylint/checkers/utils.py
deleted file mode 100644
index 2a6820a..0000000
--- a/venv/Lib/site-packages/pylint/checkers/utils.py
+++ /dev/null
@@ -1,1253 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006-2007, 2009-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com>
-# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
-# Copyright (c) 2012-2014 Google, Inc.
-# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
-# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Ricardo Gemignani <ricardo.gemignani@gmail.com>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
-# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
-# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016, 2018 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2016-2017 Moises Lopez <moylop260@vauxoo.com>
-# Copyright (c) 2016 Brian C. Lane <bcl@redhat.com>
-# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017 ttenhoeve-aa <ttenhoeve@appannie.com>
-# Copyright (c) 2018 Bryce Guinta <bryce.guinta@protonmail.com>
-# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
-# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
-# Copyright (c) 2018 Brian Shaginaw <brian.shaginaw@warbyparker.com>
-# Copyright (c) 2018 Caio Carrara <ccarrara@redhat.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
-
-"""some functions that may be useful for various checkers
-"""
-import builtins
-import itertools
-import numbers
-import re
-import string
-from functools import lru_cache, partial
-from typing import Callable, Dict, Iterable, List, Match, Optional, Set, Tuple, Union
-
-import astroid
-from astroid import bases as _bases
-from astroid import helpers, scoped_nodes
-from astroid.exceptions import _NonDeducibleTypeHierarchy
-
-import _string # pylint: disable=wrong-import-position, wrong-import-order
-
-BUILTINS_NAME = builtins.__name__
-COMP_NODE_TYPES = (
- astroid.ListComp,
- astroid.SetComp,
- astroid.DictComp,
- astroid.GeneratorExp,
-)
-EXCEPTIONS_MODULE = "builtins"
-ABC_METHODS = {
- "abc.abstractproperty",
- "abc.abstractmethod",
- "abc.abstractclassmethod",
- "abc.abstractstaticmethod",
-}
-TYPING_PROTOCOLS = frozenset({"typing.Protocol", "typing_extensions.Protocol"})
-ITER_METHOD = "__iter__"
-AITER_METHOD = "__aiter__"
-NEXT_METHOD = "__next__"
-GETITEM_METHOD = "__getitem__"
-CLASS_GETITEM_METHOD = "__class_getitem__"
-SETITEM_METHOD = "__setitem__"
-DELITEM_METHOD = "__delitem__"
-CONTAINS_METHOD = "__contains__"
-KEYS_METHOD = "keys"
-
-# Dictionary which maps the number of expected parameters a
-# special method can have to a set of special methods.
-# The following keys are used to denote the parameters restrictions:
-#
-# * None: variable number of parameters
-# * number: exactly that number of parameters
-# * tuple: this are the odd ones. Basically it means that the function
-# can work with any number of arguments from that tuple,
-# although it's best to implement it in order to accept
-# all of them.
-_SPECIAL_METHODS_PARAMS = {
- None: ("__new__", "__init__", "__call__"),
- 0: (
- "__del__",
- "__repr__",
- "__str__",
- "__bytes__",
- "__hash__",
- "__bool__",
- "__dir__",
- "__len__",
- "__length_hint__",
- "__iter__",
- "__reversed__",
- "__neg__",
- "__pos__",
- "__abs__",
- "__invert__",
- "__complex__",
- "__int__",
- "__float__",
- "__neg__",
- "__pos__",
- "__abs__",
- "__complex__",
- "__int__",
- "__float__",
- "__index__",
- "__enter__",
- "__aenter__",
- "__getnewargs_ex__",
- "__getnewargs__",
- "__getstate__",
- "__reduce__",
- "__copy__",
- "__unicode__",
- "__nonzero__",
- "__await__",
- "__aiter__",
- "__anext__",
- "__fspath__",
- ),
- 1: (
- "__format__",
- "__lt__",
- "__le__",
- "__eq__",
- "__ne__",
- "__gt__",
- "__ge__",
- "__getattr__",
- "__getattribute__",
- "__delattr__",
- "__delete__",
- "__instancecheck__",
- "__subclasscheck__",
- "__getitem__",
- "__missing__",
- "__delitem__",
- "__contains__",
- "__add__",
- "__sub__",
- "__mul__",
- "__truediv__",
- "__floordiv__",
- "__rfloordiv__",
- "__mod__",
- "__divmod__",
- "__lshift__",
- "__rshift__",
- "__and__",
- "__xor__",
- "__or__",
- "__radd__",
- "__rsub__",
- "__rmul__",
- "__rtruediv__",
- "__rmod__",
- "__rdivmod__",
- "__rpow__",
- "__rlshift__",
- "__rrshift__",
- "__rand__",
- "__rxor__",
- "__ror__",
- "__iadd__",
- "__isub__",
- "__imul__",
- "__itruediv__",
- "__ifloordiv__",
- "__imod__",
- "__ilshift__",
- "__irshift__",
- "__iand__",
- "__ixor__",
- "__ior__",
- "__ipow__",
- "__setstate__",
- "__reduce_ex__",
- "__deepcopy__",
- "__cmp__",
- "__matmul__",
- "__rmatmul__",
- "__div__",
- ),
- 2: ("__setattr__", "__get__", "__set__", "__setitem__", "__set_name__"),
- 3: ("__exit__", "__aexit__"),
- (0, 1): ("__round__",),
-}
-
-SPECIAL_METHODS_PARAMS = {
- name: params
- for params, methods in _SPECIAL_METHODS_PARAMS.items()
- for name in methods # type: ignore
-}
-PYMETHODS = set(SPECIAL_METHODS_PARAMS)
-
-
-class NoSuchArgumentError(Exception):
- pass
-
-
-def is_inside_except(node):
- """Returns true if node is inside the name of an except handler."""
- current = node
- while current and not isinstance(current.parent, astroid.ExceptHandler):
- current = current.parent
-
- return current and current is current.parent.name
-
-
-def is_inside_lambda(node: astroid.node_classes.NodeNG) -> bool:
- """Return true if given node is inside lambda"""
- parent = node.parent
- while parent is not None:
- if isinstance(parent, astroid.Lambda):
- return True
- parent = parent.parent
- return False
-
-
-def get_all_elements(
- node: astroid.node_classes.NodeNG
-) -> Iterable[astroid.node_classes.NodeNG]:
- """Recursively returns all atoms in nested lists and tuples."""
- if isinstance(node, (astroid.Tuple, astroid.List)):
- for child in node.elts:
- yield from get_all_elements(child)
- else:
- yield node
-
-
-def clobber_in_except(
- node: astroid.node_classes.NodeNG
-) -> Tuple[bool, Optional[Tuple[str, str]]]:
- """Checks if an assignment node in an except handler clobbers an existing
- variable.
-
- Returns (True, args for W0623) if assignment clobbers an existing variable,
- (False, None) otherwise.
- """
- if isinstance(node, astroid.AssignAttr):
- return True, (node.attrname, "object %r" % (node.expr.as_string(),))
- if isinstance(node, astroid.AssignName):
- name = node.name
- if is_builtin(name):
- return True, (name, "builtins")
-
- stmts = node.lookup(name)[1]
- if stmts and not isinstance(
- stmts[0].assign_type(),
- (astroid.Assign, astroid.AugAssign, astroid.ExceptHandler),
- ):
- return True, (name, "outer scope (line %s)" % stmts[0].fromlineno)
- return False, None
-
-
-def is_super(node: astroid.node_classes.NodeNG) -> bool:
- """return True if the node is referencing the "super" builtin function
- """
- if getattr(node, "name", None) == "super" and node.root().name == BUILTINS_NAME:
- return True
- return False
-
-
-def is_error(node: astroid.node_classes.NodeNG) -> bool:
- """return true if the function does nothing but raising an exception"""
- raises = False
- returns = False
- for child_node in node.nodes_of_class((astroid.Raise, astroid.Return)):
- if isinstance(child_node, astroid.Raise):
- raises = True
- if isinstance(child_node, astroid.Return):
- returns = True
- return raises and not returns
-
-
-builtins = builtins.__dict__.copy() # type: ignore
-SPECIAL_BUILTINS = ("__builtins__",) # '__path__', '__file__')
-
-
-def is_builtin_object(node: astroid.node_classes.NodeNG) -> bool:
- """Returns True if the given node is an object from the __builtin__ module."""
- return node and node.root().name == BUILTINS_NAME
-
-
-def is_builtin(name: str) -> bool:
- """return true if <name> could be considered as a builtin defined by python
- """
- return name in builtins or name in SPECIAL_BUILTINS # type: ignore
-
-
-def is_defined_in_scope(
- var_node: astroid.node_classes.NodeNG,
- varname: str,
- scope: astroid.node_classes.NodeNG,
-) -> bool:
- if isinstance(scope, astroid.If):
- for node in scope.body:
- if (
- isinstance(node, astroid.Assign)
- and any(
- isinstance(target, astroid.AssignName) and target.name == varname
- for target in node.targets
- )
- ) or (isinstance(node, astroid.Nonlocal) and varname in node.names):
- return True
- elif isinstance(scope, (COMP_NODE_TYPES, astroid.For)):
- for ass_node in scope.nodes_of_class(astroid.AssignName):
- if ass_node.name == varname:
- return True
- elif isinstance(scope, astroid.With):
- for expr, ids in scope.items:
- if expr.parent_of(var_node):
- break
- if ids and isinstance(ids, astroid.AssignName) and ids.name == varname:
- return True
- elif isinstance(scope, (astroid.Lambda, astroid.FunctionDef)):
- if scope.args.is_argument(varname):
- # If the name is found inside a default value
- # of a function, then let the search continue
- # in the parent's tree.
- if scope.args.parent_of(var_node):
- try:
- scope.args.default_value(varname)
- scope = scope.parent
- is_defined_in_scope(var_node, varname, scope)
- except astroid.NoDefault:
- pass
- return True
- if getattr(scope, "name", None) == varname:
- return True
- elif isinstance(scope, astroid.ExceptHandler):
- if isinstance(scope.name, astroid.AssignName):
- ass_node = scope.name
- if ass_node.name == varname:
- return True
- return False
-
-
-def is_defined_before(var_node: astroid.node_classes.NodeNG) -> bool:
- """return True if the variable node is defined by a parent node (list,
- set, dict, or generator comprehension, lambda) or in a previous sibling
- node on the same line (statement_defining ; statement_using)
- """
- varname = var_node.name
- _node = var_node.parent
- while _node:
- if is_defined_in_scope(var_node, varname, _node):
- return True
- _node = _node.parent
- # possibly multiple statements on the same line using semi colon separator
- stmt = var_node.statement()
- _node = stmt.previous_sibling()
- lineno = stmt.fromlineno
- while _node and _node.fromlineno == lineno:
- for assign_node in _node.nodes_of_class(astroid.AssignName):
- if assign_node.name == varname:
- return True
- for imp_node in _node.nodes_of_class((astroid.ImportFrom, astroid.Import)):
- if varname in [name[1] or name[0] for name in imp_node.names]:
- return True
- _node = _node.previous_sibling()
- return False
-
-
-def is_default_argument(node: astroid.node_classes.NodeNG) -> bool:
- """return true if the given Name node is used in function or lambda
- default argument's value
- """
- parent = node.scope()
- if isinstance(parent, (astroid.FunctionDef, astroid.Lambda)):
- for default_node in parent.args.defaults:
- for default_name_node in default_node.nodes_of_class(astroid.Name):
- if default_name_node is node:
- return True
- return False
-
-
-def is_func_decorator(node: astroid.node_classes.NodeNG) -> bool:
- """return true if the name is used in function decorator"""
- parent = node.parent
- while parent is not None:
- if isinstance(parent, astroid.Decorators):
- return True
- if parent.is_statement or isinstance(
- parent,
- (astroid.Lambda, scoped_nodes.ComprehensionScope, scoped_nodes.ListComp),
- ):
- break
- parent = parent.parent
- return False
-
-
-def is_ancestor_name(
- frame: astroid.node_classes.NodeNG, node: astroid.node_classes.NodeNG
-) -> bool:
- """return True if `frame` is an astroid.Class node with `node` in the
- subtree of its bases attribute
- """
- try:
- bases = frame.bases
- except AttributeError:
- return False
- for base in bases:
- if node in base.nodes_of_class(astroid.Name):
- return True
- return False
-
-
-def assign_parent(node: astroid.node_classes.NodeNG) -> astroid.node_classes.NodeNG:
- """return the higher parent which is not an AssignName, Tuple or List node
- """
- while node and isinstance(node, (astroid.AssignName, astroid.Tuple, astroid.List)):
- node = node.parent
- return node
-
-
-def overrides_a_method(class_node: astroid.node_classes.NodeNG, name: str) -> bool:
- """return True if <name> is a method overridden from an ancestor"""
- for ancestor in class_node.ancestors():
- if name in ancestor and isinstance(ancestor[name], astroid.FunctionDef):
- return True
- return False
-
-
-def check_messages(*messages: str) -> Callable:
- """decorator to store messages that are handled by a checker method"""
-
- def store_messages(func):
- func.checks_msgs = messages
- return func
-
- return store_messages
-
-
-class IncompleteFormatString(Exception):
- """A format string ended in the middle of a format specifier."""
-
-
-class UnsupportedFormatCharacter(Exception):
- """A format character in a format string is not one of the supported
- format characters."""
-
- def __init__(self, index):
- Exception.__init__(self, index)
- self.index = index
-
-
-def parse_format_string(
- format_string: str
-) -> Tuple[Set[str], int, Dict[str, str], List[str]]:
- """Parses a format string, returning a tuple of (keys, num_args), where keys
- is the set of mapping keys in the format string, and num_args is the number
- of arguments required by the format string. Raises
- IncompleteFormatString or UnsupportedFormatCharacter if a
- parse error occurs."""
- keys = set()
- key_types = dict()
- pos_types = []
- num_args = 0
-
- def next_char(i):
- i += 1
- if i == len(format_string):
- raise IncompleteFormatString
- return (i, format_string[i])
-
- i = 0
- while i < len(format_string):
- char = format_string[i]
- if char == "%":
- i, char = next_char(i)
- # Parse the mapping key (optional).
- key = None
- if char == "(":
- depth = 1
- i, char = next_char(i)
- key_start = i
- while depth != 0:
- if char == "(":
- depth += 1
- elif char == ")":
- depth -= 1
- i, char = next_char(i)
- key_end = i - 1
- key = format_string[key_start:key_end]
-
- # Parse the conversion flags (optional).
- while char in "#0- +":
- i, char = next_char(i)
- # Parse the minimum field width (optional).
- if char == "*":
- num_args += 1
- i, char = next_char(i)
- else:
- while char in string.digits:
- i, char = next_char(i)
- # Parse the precision (optional).
- if char == ".":
- i, char = next_char(i)
- if char == "*":
- num_args += 1
- i, char = next_char(i)
- else:
- while char in string.digits:
- i, char = next_char(i)
- # Parse the length modifier (optional).
- if char in "hlL":
- i, char = next_char(i)
- # Parse the conversion type (mandatory).
- flags = "diouxXeEfFgGcrs%a"
- if char not in flags:
- raise UnsupportedFormatCharacter(i)
- if key:
- keys.add(key)
- key_types[key] = char
- elif char != "%":
- num_args += 1
- pos_types.append(char)
- i += 1
- return keys, num_args, key_types, pos_types
-
-
-def split_format_field_names(format_string) -> Tuple[str, Iterable[Tuple[bool, str]]]:
- try:
- return _string.formatter_field_name_split(format_string)
- except ValueError:
- raise IncompleteFormatString()
-
-
-def collect_string_fields(format_string) -> Iterable[Optional[str]]:
- """ Given a format string, return an iterator
- of all the valid format fields. It handles nested fields
- as well.
- """
- formatter = string.Formatter()
- try:
- parseiterator = formatter.parse(format_string)
- for result in parseiterator:
- if all(item is None for item in result[1:]):
- # not a replacement format
- continue
- name = result[1]
- nested = result[2]
- yield name
- if nested:
- for field in collect_string_fields(nested):
- yield field
- except ValueError as exc:
- # Probably the format string is invalid.
- if exc.args[0].startswith("cannot switch from manual"):
- # On Jython, parsing a string with both manual
- # and automatic positions will fail with a ValueError,
- # while on CPython it will simply return the fields,
- # the validation being done in the interpreter (?).
- # We're just returning two mixed fields in order
- # to trigger the format-combined-specification check.
- yield ""
- yield "1"
- return
- raise IncompleteFormatString(format_string)
-
-
-def parse_format_method_string(
- format_string: str
-) -> Tuple[List[Tuple[str, List[Tuple[bool, str]]]], int, int]:
- """
- Parses a PEP 3101 format string, returning a tuple of
- (keyword_arguments, implicit_pos_args_cnt, explicit_pos_args),
- where keyword_arguments is the set of mapping keys in the format string, implicit_pos_args_cnt
- is the number of arguments required by the format string and
- explicit_pos_args is the number of arguments passed with the position.
- """
- keyword_arguments = []
- implicit_pos_args_cnt = 0
- explicit_pos_args = set()
- for name in collect_string_fields(format_string):
- if name and str(name).isdigit():
- explicit_pos_args.add(str(name))
- elif name:
- keyname, fielditerator = split_format_field_names(name)
- if isinstance(keyname, numbers.Number):
- # In Python 2 it will return long which will lead
- # to different output between 2 and 3
- explicit_pos_args.add(str(keyname))
- keyname = int(keyname)
- try:
- keyword_arguments.append((keyname, list(fielditerator)))
- except ValueError:
- raise IncompleteFormatString()
- else:
- implicit_pos_args_cnt += 1
- return keyword_arguments, implicit_pos_args_cnt, len(explicit_pos_args)
-
-
-def is_attr_protected(attrname: str) -> bool:
- """return True if attribute name is protected (start with _ and some other
- details), False otherwise.
- """
- return (
- attrname[0] == "_"
- and attrname != "_"
- and not (attrname.startswith("__") and attrname.endswith("__"))
- )
-
-
-def node_frame_class(node: astroid.node_classes.NodeNG) -> Optional[astroid.ClassDef]:
- """Return the class that is wrapping the given node
-
- The function returns a class for a method node (or a staticmethod or a
- classmethod), otherwise it returns `None`.
- """
- klass = node.frame()
-
- while klass is not None and not isinstance(klass, astroid.ClassDef):
- if klass.parent is None:
- klass = None
- else:
- klass = klass.parent.frame()
-
- return klass
-
-
-def is_attr_private(attrname: str) -> Optional[Match[str]]:
- """Check that attribute name is private (at least two leading underscores,
- at most one trailing underscore)
- """
- regex = re.compile("^_{2,}.*[^_]+_?$")
- return regex.match(attrname)
-
-
-def get_argument_from_call(
- call_node: astroid.Call, position: int = None, keyword: str = None
-) -> astroid.Name:
- """Returns the specified argument from a function call.
-
- :param astroid.Call call_node: Node representing a function call to check.
- :param int position: position of the argument.
- :param str keyword: the keyword of the argument.
-
- :returns: The node representing the argument, None if the argument is not found.
- :rtype: astroid.Name
- :raises ValueError: if both position and keyword are None.
- :raises NoSuchArgumentError: if no argument at the provided position or with
- the provided keyword.
- """
- if position is None and keyword is None:
- raise ValueError("Must specify at least one of: position or keyword.")
- if position is not None:
- try:
- return call_node.args[position]
- except IndexError:
- pass
- if keyword and call_node.keywords:
- for arg in call_node.keywords:
- if arg.arg == keyword:
- return arg.value
-
- raise NoSuchArgumentError
-
-
-def inherit_from_std_ex(node: astroid.node_classes.NodeNG) -> bool:
- """
- Return true if the given class node is subclass of
- exceptions.Exception.
- """
- ancestors = node.ancestors() if hasattr(node, "ancestors") else []
- for ancestor in itertools.chain([node], ancestors):
- if (
- ancestor.name in ("Exception", "BaseException")
- and ancestor.root().name == EXCEPTIONS_MODULE
- ):
- return True
- return False
-
-
-def error_of_type(handler: astroid.ExceptHandler, error_type) -> bool:
- """
- Check if the given exception handler catches
- the given error_type.
-
- The *handler* parameter is a node, representing an ExceptHandler node.
- The *error_type* can be an exception, such as AttributeError,
- the name of an exception, or it can be a tuple of errors.
- The function will return True if the handler catches any of the
- given errors.
- """
-
- def stringify_error(error):
- if not isinstance(error, str):
- return error.__name__
- return error
-
- if not isinstance(error_type, tuple):
- error_type = (error_type,) # type: ignore
- expected_errors = {stringify_error(error) for error in error_type} # type: ignore
- if not handler.type:
- return True
- return handler.catch(expected_errors)
-
-
-def decorated_with_property(node: astroid.FunctionDef) -> bool:
- """Detect if the given function node is decorated with a property. """
- if not node.decorators:
- return False
- for decorator in node.decorators.nodes:
- try:
- if _is_property_decorator(decorator):
- return True
- except astroid.InferenceError:
- pass
- return False
-
-
-def _is_property_kind(node, *kinds):
- if not isinstance(node, (astroid.UnboundMethod, astroid.FunctionDef)):
- return False
- if node.decorators:
- for decorator in node.decorators.nodes:
- if isinstance(decorator, astroid.Attribute) and decorator.attrname in kinds:
- return True
- return False
-
-
-def is_property_setter(node: astroid.FunctionDef) -> bool:
- """Check if the given node is a property setter"""
- return _is_property_kind(node, "setter")
-
-
-def is_property_setter_or_deleter(node: astroid.FunctionDef) -> bool:
- """Check if the given node is either a property setter or a deleter"""
- return _is_property_kind(node, "setter", "deleter")
-
-
-def _is_property_decorator(decorator: astroid.Name) -> bool:
- for inferred in decorator.infer():
- if isinstance(inferred, astroid.ClassDef):
- if inferred.root().name == BUILTINS_NAME and inferred.name == "property":
- return True
- for ancestor in inferred.ancestors():
- if (
- ancestor.name == "property"
- and ancestor.root().name == BUILTINS_NAME
- ):
- return True
- return False
-
-
-def decorated_with(
- func: Union[astroid.FunctionDef, astroid.BoundMethod, astroid.UnboundMethod],
- qnames: Iterable[str],
-) -> bool:
- """Determine if the `func` node has a decorator with the qualified name `qname`."""
- decorators = func.decorators.nodes if func.decorators else []
- for decorator_node in decorators:
- if isinstance(decorator_node, astroid.Call):
- # We only want to infer the function name
- decorator_node = decorator_node.func
- try:
- if any(
- i is not None and i.qname() in qnames or i.name in qnames
- for i in decorator_node.infer()
- ):
- return True
- except astroid.InferenceError:
- continue
- return False
-
-
-@lru_cache(maxsize=1024)
-def unimplemented_abstract_methods(
- node: astroid.node_classes.NodeNG, is_abstract_cb: astroid.FunctionDef = None
-) -> Dict[str, astroid.node_classes.NodeNG]:
- """
- Get the unimplemented abstract methods for the given *node*.
-
- A method can be considered abstract if the callback *is_abstract_cb*
- returns a ``True`` value. The check defaults to verifying that
- a method is decorated with abstract methods.
- The function will work only for new-style classes. For old-style
- classes, it will simply return an empty dictionary.
- For the rest of them, it will return a dictionary of abstract method
- names and their inferred objects.
- """
- if is_abstract_cb is None:
- is_abstract_cb = partial(decorated_with, qnames=ABC_METHODS)
- visited = {} # type: Dict[str, astroid.node_classes.NodeNG]
- try:
- mro = reversed(node.mro())
- except NotImplementedError:
- # Old style class, it will not have a mro.
- return {}
- except astroid.ResolveError:
- # Probably inconsistent hierarchy, don'try
- # to figure this out here.
- return {}
- for ancestor in mro:
- for obj in ancestor.values():
- inferred = obj
- if isinstance(obj, astroid.AssignName):
- inferred = safe_infer(obj)
- if not inferred:
- # Might be an abstract function,
- # but since we don't have enough information
- # in order to take this decision, we're taking
- # the *safe* decision instead.
- if obj.name in visited:
- del visited[obj.name]
- continue
- if not isinstance(inferred, astroid.FunctionDef):
- if obj.name in visited:
- del visited[obj.name]
- if isinstance(inferred, astroid.FunctionDef):
- # It's critical to use the original name,
- # since after inferring, an object can be something
- # else than expected, as in the case of the
- # following assignment.
- #
- # class A:
- # def keys(self): pass
- # __iter__ = keys
- abstract = is_abstract_cb(inferred)
- if abstract:
- visited[obj.name] = inferred
- elif not abstract and obj.name in visited:
- del visited[obj.name]
- return visited
-
-
-def find_try_except_wrapper_node(
- node: astroid.node_classes.NodeNG
-) -> Union[astroid.ExceptHandler, astroid.TryExcept]:
- """Return the ExceptHandler or the TryExcept node in which the node is."""
- current = node
- ignores = (astroid.ExceptHandler, astroid.TryExcept)
- while current and not isinstance(current.parent, ignores):
- current = current.parent
-
- if current and isinstance(current.parent, ignores):
- return current.parent
- return None
-
-
-def is_from_fallback_block(node: astroid.node_classes.NodeNG) -> bool:
- """Check if the given node is from a fallback import block."""
- context = find_try_except_wrapper_node(node)
- if not context:
- return False
-
- if isinstance(context, astroid.ExceptHandler):
- other_body = context.parent.body
- handlers = context.parent.handlers
- else:
- other_body = itertools.chain.from_iterable(
- handler.body for handler in context.handlers
- )
- handlers = context.handlers
-
- has_fallback_imports = any(
- isinstance(import_node, (astroid.ImportFrom, astroid.Import))
- for import_node in other_body
- )
- ignores_import_error = _except_handlers_ignores_exception(handlers, ImportError)
- return ignores_import_error or has_fallback_imports
-
-
-def _except_handlers_ignores_exception(
- handlers: astroid.ExceptHandler, exception
-) -> bool:
- func = partial(error_of_type, error_type=(exception,))
- return any(map(func, handlers))
-
-
-def get_exception_handlers(
- node: astroid.node_classes.NodeNG, exception=Exception
-) -> Optional[List[astroid.ExceptHandler]]:
- """Return the collections of handlers handling the exception in arguments.
-
- Args:
- node (astroid.NodeNG): A node that is potentially wrapped in a try except.
- exception (builtin.Exception or str): exception or name of the exception.
-
- Returns:
- list: the collection of handlers that are handling the exception or None.
-
- """
- context = find_try_except_wrapper_node(node)
- if isinstance(context, astroid.TryExcept):
- return [
- handler for handler in context.handlers if error_of_type(handler, exception)
- ]
- return []
-
-
-def is_node_inside_try_except(node: astroid.Raise) -> bool:
- """Check if the node is directly under a Try/Except statement.
- (but not under an ExceptHandler!)
-
- Args:
- node (astroid.Raise): the node raising the exception.
-
- Returns:
- bool: True if the node is inside a try/except statement, False otherwise.
- """
- context = find_try_except_wrapper_node(node)
- return isinstance(context, astroid.TryExcept)
-
-
-def node_ignores_exception(
- node: astroid.node_classes.NodeNG, exception=Exception
-) -> bool:
- """Check if the node is in a TryExcept which handles the given exception.
-
- If the exception is not given, the function is going to look for bare
- excepts.
- """
- managing_handlers = get_exception_handlers(node, exception)
- if not managing_handlers:
- return False
- return any(managing_handlers)
-
-
-def class_is_abstract(node: astroid.ClassDef) -> bool:
- """return true if the given class node should be considered as an abstract
- class
- """
- for method in node.methods():
- if method.parent.frame() is node:
- if method.is_abstract(pass_is_abstract=False):
- return True
- return False
-
-
-def _supports_protocol_method(value: astroid.node_classes.NodeNG, attr: str) -> bool:
- try:
- attributes = value.getattr(attr)
- except astroid.NotFoundError:
- return False
-
- first = attributes[0]
- if isinstance(first, astroid.AssignName):
- if isinstance(first.parent.value, astroid.Const):
- return False
- return True
-
-
-def is_comprehension(node: astroid.node_classes.NodeNG) -> bool:
- comprehensions = (
- astroid.ListComp,
- astroid.SetComp,
- astroid.DictComp,
- astroid.GeneratorExp,
- )
- return isinstance(node, comprehensions)
-
-
-def _supports_mapping_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(
- value, GETITEM_METHOD
- ) and _supports_protocol_method(value, KEYS_METHOD)
-
-
-def _supports_membership_test_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, CONTAINS_METHOD)
-
-
-def _supports_iteration_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, ITER_METHOD) or _supports_protocol_method(
- value, GETITEM_METHOD
- )
-
-
-def _supports_async_iteration_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, AITER_METHOD)
-
-
-def _supports_getitem_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, GETITEM_METHOD)
-
-
-def _supports_setitem_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, SETITEM_METHOD)
-
-
-def _supports_delitem_protocol(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol_method(value, DELITEM_METHOD)
-
-
-def _is_abstract_class_name(name: str) -> bool:
- lname = name.lower()
- is_mixin = lname.endswith("mixin")
- is_abstract = lname.startswith("abstract")
- is_base = lname.startswith("base") or lname.endswith("base")
- return is_mixin or is_abstract or is_base
-
-
-def is_inside_abstract_class(node: astroid.node_classes.NodeNG) -> bool:
- while node is not None:
- if isinstance(node, astroid.ClassDef):
- if class_is_abstract(node):
- return True
- name = getattr(node, "name", None)
- if name is not None and _is_abstract_class_name(name):
- return True
- node = node.parent
- return False
-
-
-def _supports_protocol(
- value: astroid.node_classes.NodeNG, protocol_callback: astroid.FunctionDef
-) -> bool:
- if isinstance(value, astroid.ClassDef):
- if not has_known_bases(value):
- return True
- # classobj can only be iterable if it has an iterable metaclass
- meta = value.metaclass()
- if meta is not None:
- if protocol_callback(meta):
- return True
- if isinstance(value, astroid.BaseInstance):
- if not has_known_bases(value):
- return True
- if value.has_dynamic_getattr():
- return True
- if protocol_callback(value):
- return True
-
- if (
- isinstance(value, _bases.Proxy)
- and isinstance(value._proxied, astroid.BaseInstance)
- and has_known_bases(value._proxied)
- ):
- value = value._proxied
- return protocol_callback(value)
-
- return False
-
-
-def is_iterable(value: astroid.node_classes.NodeNG, check_async: bool = False) -> bool:
- if check_async:
- protocol_check = _supports_async_iteration_protocol
- else:
- protocol_check = _supports_iteration_protocol
- return _supports_protocol(value, protocol_check)
-
-
-def is_mapping(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol(value, _supports_mapping_protocol)
-
-
-def supports_membership_test(value: astroid.node_classes.NodeNG) -> bool:
- supported = _supports_protocol(value, _supports_membership_test_protocol)
- return supported or is_iterable(value)
-
-
-def supports_getitem(value: astroid.node_classes.NodeNG) -> bool:
- if isinstance(value, astroid.ClassDef):
- if _supports_protocol_method(value, CLASS_GETITEM_METHOD):
- return True
- return _supports_protocol(value, _supports_getitem_protocol)
-
-
-def supports_setitem(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol(value, _supports_setitem_protocol)
-
-
-def supports_delitem(value: astroid.node_classes.NodeNG) -> bool:
- return _supports_protocol(value, _supports_delitem_protocol)
-
-
-@lru_cache(maxsize=1024)
-def safe_infer(
- node: astroid.node_classes.NodeNG, context=None
-) -> Optional[astroid.node_classes.NodeNG]:
- """Return the inferred value for the given node.
-
- Return None if inference failed or if there is some ambiguity (more than
- one node has been inferred).
- """
- try:
- inferit = node.infer(context=context)
- value = next(inferit)
- except astroid.InferenceError:
- return None
- try:
- next(inferit)
- return None # None if there is ambiguity on the inferred node
- except astroid.InferenceError:
- return None # there is some kind of ambiguity
- except StopIteration:
- return value
-
-
-def has_known_bases(klass: astroid.ClassDef, context=None) -> bool:
- """Return true if all base classes of a class could be inferred."""
- try:
- return klass._all_bases_known
- except AttributeError:
- pass
- for base in klass.bases:
- result = safe_infer(base, context=context)
- if (
- not isinstance(result, astroid.ClassDef)
- or result is klass
- or not has_known_bases(result, context=context)
- ):
- klass._all_bases_known = False
- return False
- klass._all_bases_known = True
- return True
-
-
-def is_none(node: astroid.node_classes.NodeNG) -> bool:
- return (
- node is None
- or (isinstance(node, astroid.Const) and node.value is None)
- or (isinstance(node, astroid.Name) and node.name == "None")
- )
-
-
-def node_type(node: astroid.node_classes.NodeNG) -> Optional[type]:
- """Return the inferred type for `node`
-
- If there is more than one possible type, or if inferred type is Uninferable or None,
- return None
- """
- # check there is only one possible type for the assign node. Else we
- # don't handle it for now
- types = set()
- try:
- for var_type in node.infer():
- if var_type == astroid.Uninferable or is_none(var_type):
- continue
- types.add(var_type)
- if len(types) > 1:
- return None
- except astroid.InferenceError:
- return None
- return types.pop() if types else None
-
-
-def is_registered_in_singledispatch_function(node: astroid.FunctionDef) -> bool:
- """Check if the given function node is a singledispatch function."""
-
- singledispatch_qnames = (
- "functools.singledispatch",
- "singledispatch.singledispatch",
- )
-
- if not isinstance(node, astroid.FunctionDef):
- return False
-
- decorators = node.decorators.nodes if node.decorators else []
- for decorator in decorators:
- # func.register are function calls
- if not isinstance(decorator, astroid.Call):
- continue
-
- func = decorator.func
- if not isinstance(func, astroid.Attribute) or func.attrname != "register":
- continue
-
- try:
- func_def = next(func.expr.infer())
- except astroid.InferenceError:
- continue
-
- if isinstance(func_def, astroid.FunctionDef):
- # pylint: disable=redundant-keyword-arg; some flow inference goes wrong here
- return decorated_with(func_def, singledispatch_qnames)
-
- return False
-
-
-def get_node_last_lineno(node: astroid.node_classes.NodeNG) -> int:
- """
- Get the last lineno of the given node. For a simple statement this will just be node.lineno,
- but for a node that has child statements (e.g. a method) this will be the lineno of the last
- child statement recursively.
- """
- # 'finalbody' is always the last clause in a try statement, if present
- if getattr(node, "finalbody", False):
- return get_node_last_lineno(node.finalbody[-1])
- # For if, while, and for statements 'orelse' is always the last clause.
- # For try statements 'orelse' is the last in the absence of a 'finalbody'
- if getattr(node, "orelse", False):
- return get_node_last_lineno(node.orelse[-1])
- # try statements have the 'handlers' last if there is no 'orelse' or 'finalbody'
- if getattr(node, "handlers", False):
- return get_node_last_lineno(node.handlers[-1])
- # All compound statements have a 'body'
- if getattr(node, "body", False):
- return get_node_last_lineno(node.body[-1])
- # Not a compound statement
- return node.lineno
-
-
-def is_postponed_evaluation_enabled(node: astroid.node_classes.NodeNG) -> bool:
- """Check if the postponed evaluation of annotations is enabled"""
- name = "annotations"
- module = node.root()
- stmt = module.locals.get(name)
- return (
- stmt
- and isinstance(stmt[0], astroid.ImportFrom)
- and stmt[0].modname == "__future__"
- )
-
-
-def is_subclass_of(child: astroid.ClassDef, parent: astroid.ClassDef) -> bool:
- """
- Check if first node is a subclass of second node.
- :param child: Node to check for subclass.
- :param parent: Node to check for superclass.
- :returns: True if child is derived from parent. False otherwise.
- """
- if not all(isinstance(node, astroid.ClassDef) for node in (child, parent)):
- return False
-
- for ancestor in child.ancestors():
- try:
- if helpers.is_subtype(ancestor, parent):
- return True
- except _NonDeducibleTypeHierarchy:
- continue
- return False
-
-
-@lru_cache(maxsize=1024)
-def is_overload_stub(node: astroid.node_classes.NodeNG) -> bool:
- """Check if a node if is a function stub decorated with typing.overload.
-
- :param node: Node to check.
- :returns: True if node is an overload function stub. False otherwise.
- """
- decorators = getattr(node, "decorators", None)
- return bool(decorators and decorated_with(node, ["typing.overload", "overload"]))
-
-
-def is_protocol_class(cls: astroid.node_classes.NodeNG) -> bool:
- """Check if the given node represents a protocol class
-
- :param cls: The node to check
- :returns: True if the node is a typing protocol class, false otherwise.
- """
- if not isinstance(cls, astroid.ClassDef):
- return False
-
- # Use .ancestors() since not all protocol classes can have
- # their mro deduced.
- return any(parent.qname() in TYPING_PROTOCOLS for parent in cls.ancestors())
diff --git a/venv/Lib/site-packages/pylint/checkers/variables.py b/venv/Lib/site-packages/pylint/checkers/variables.py
deleted file mode 100644
index e13f9b5..0000000
--- a/venv/Lib/site-packages/pylint/checkers/variables.py
+++ /dev/null
@@ -1,1987 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com>
-# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
-# Copyright (c) 2011-2014, 2017 Google, Inc.
-# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
-# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
-# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
-# Copyright (c) 2014 Brett Cannon <brett@python.org>
-# Copyright (c) 2014 Ricardo Gemignani <ricardo.gemignani@gmail.com>
-# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
-# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
-# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro>
-# Copyright (c) 2015 Simu Toni <simutoni@gmail.com>
-# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
-# Copyright (c) 2016, 2018 Ashley Whetter <ashley@awhetter.co.uk>
-# Copyright (c) 2016, 2018 Jakub Wilk <jwilk@jwilk.net>
-# Copyright (c) 2016-2017 Derek Gustafson <degustaf@gmail.com>
-# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
-# Copyright (c) 2016 Grant Welch <gwelch925+github@gmail.com>
-# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
-# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017 Dan Garrette <dhgarrette@gmail.com>
-# Copyright (c) 2018 Bryce Guinta <bryce.guinta@protonmail.com>
-# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
-# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
-# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
-# Copyright (c) 2018 Marianna Polatoglou <mpolatoglou@bloomberg.net>
-# Copyright (c) 2018 mar-chi-pan <mar.polatoglou@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
-
-"""variables checkers for Python code
-"""
-import collections
-import copy
-import itertools
-import os
-import re
-from functools import lru_cache
-
-import astroid
-from astroid import decorators, modutils, objects
-from astroid.context import InferenceContext
-
-from pylint.checkers import BaseChecker, utils
-from pylint.checkers.utils import is_postponed_evaluation_enabled
-from pylint.interfaces import HIGH, INFERENCE, INFERENCE_FAILURE, IAstroidChecker
-from pylint.utils import get_global_option
-
-SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
-FUTURE = "__future__"
-# regexp for ignored argument name
-IGNORED_ARGUMENT_NAMES = re.compile("_.*|^ignored_|^unused_")
-# In Python 3.7 abc has a Python implementation which is preferred
-# by astroid. Unfortunately this also messes up our explicit checks
-# for `abc`
-METACLASS_NAME_TRANSFORMS = {"_py_abc": "abc"}
-TYPING_TYPE_CHECKS_GUARDS = frozenset({"typing.TYPE_CHECKING", "TYPE_CHECKING"})
-BUILTIN_RANGE = "builtins.range"
-TYPING_MODULE = "typing"
-TYPING_NAMES = frozenset(
- {
- "Any",
- "Callable",
- "ClassVar",
- "Generic",
- "Optional",
- "Tuple",
- "Type",
- "TypeVar",
- "Union",
- "AbstractSet",
- "ByteString",
- "Container",
- "ContextManager",
- "Hashable",
- "ItemsView",
- "Iterable",
- "Iterator",
- "KeysView",
- "Mapping",
- "MappingView",
- "MutableMapping",
- "MutableSequence",
- "MutableSet",
- "Sequence",
- "Sized",
- "ValuesView",
- "Awaitable",
- "AsyncIterator",
- "AsyncIterable",
- "Coroutine",
- "Collection",
- "AsyncGenerator",
- "AsyncContextManager",
- "Reversible",
- "SupportsAbs",
- "SupportsBytes",
- "SupportsComplex",
- "SupportsFloat",
- "SupportsInt",
- "SupportsRound",
- "Counter",
- "Deque",
- "Dict",
- "DefaultDict",
- "List",
- "Set",
- "FrozenSet",
- "NamedTuple",
- "Generator",
- "AnyStr",
- "Text",
- "Pattern",
- }
-)
-
-
-def _is_from_future_import(stmt, name):
- """Check if the name is a future import from another module."""
- try:
- module = stmt.do_import_module(stmt.modname)
- except astroid.AstroidBuildingException:
- return None
-
- for local_node in module.locals.get(name, []):
- if isinstance(local_node, astroid.ImportFrom) and local_node.modname == FUTURE:
- return True
- return None
-
-
-def in_for_else_branch(parent, stmt):
- """Returns True if stmt in inside the else branch for a parent For stmt."""
- return isinstance(parent, astroid.For) and any(
- else_stmt.parent_of(stmt) or else_stmt == stmt for else_stmt in parent.orelse
- )
-
-
-@lru_cache(maxsize=1000)
-def overridden_method(klass, name):
- """get overridden method if any"""
- try:
- parent = next(klass.local_attr_ancestors(name))
- except (StopIteration, KeyError):
- return None
- try:
- meth_node = parent[name]
- except KeyError:
- # We have found an ancestor defining <name> but it's not in the local
- # dictionary. This may happen with astroid built from living objects.
- return None
- if isinstance(meth_node, astroid.FunctionDef):
- return meth_node
- return None
-
-
-def _get_unpacking_extra_info(node, inferred):
- """return extra information to add to the message for unpacking-non-sequence
- and unbalanced-tuple-unpacking errors
- """
- more = ""
- inferred_module = inferred.root().name
- if node.root().name == inferred_module:
- if node.lineno == inferred.lineno:
- more = " %s" % inferred.as_string()
- elif inferred.lineno:
- more = " defined at line %s" % inferred.lineno
- elif inferred.lineno:
- more = " defined at line %s of %s" % (inferred.lineno, inferred_module)
- return more
-
-
-def _detect_global_scope(node, frame, defframe):
- """ Detect that the given frames shares a global
- scope.
-
- Two frames shares a global scope when neither
- of them are hidden under a function scope, as well
- as any of parent scope of them, until the root scope.
- In this case, depending from something defined later on
- will not work, because it is still undefined.
-
- Example:
- class A:
- # B has the same global scope as `C`, leading to a NameError.
- class B(C): ...
- class C: ...
-
- """
- def_scope = scope = None
- if frame and frame.parent:
- scope = frame.parent.scope()
- if defframe and defframe.parent:
- def_scope = defframe.parent.scope()
- if isinstance(frame, astroid.FunctionDef):
- # If the parent of the current node is a
- # function, then it can be under its scope
- # (defined in, which doesn't concern us) or
- # the `->` part of annotations. The same goes
- # for annotations of function arguments, they'll have
- # their parent the Arguments node.
- if not isinstance(node.parent, (astroid.FunctionDef, astroid.Arguments)):
- return False
- elif any(
- not isinstance(f, (astroid.ClassDef, astroid.Module)) for f in (frame, defframe)
- ):
- # Not interested in other frames, since they are already
- # not in a global scope.
- return False
-
- break_scopes = []
- for current_scope in (scope, def_scope):
- # Look for parent scopes. If there is anything different
- # than a module or a class scope, then they frames don't
- # share a global scope.
- parent_scope = current_scope
- while parent_scope:
- if not isinstance(parent_scope, (astroid.ClassDef, astroid.Module)):
- break_scopes.append(parent_scope)
- break
- if parent_scope.parent:
- parent_scope = parent_scope.parent.scope()
- else:
- break
- if break_scopes and len(set(break_scopes)) != 1:
- # Store different scopes than expected.
- # If the stored scopes are, in fact, the very same, then it means
- # that the two frames (frame and defframe) shares the same scope,
- # and we could apply our lineno analysis over them.
- # For instance, this works when they are inside a function, the node
- # that uses a definition and the definition itself.
- return False
- # At this point, we are certain that frame and defframe shares a scope
- # and the definition of the first depends on the second.
- return frame.lineno < defframe.lineno
-
-
-def _infer_name_module(node, name):
- context = InferenceContext()
- context.lookupname = name
- return node.infer(context, asname=False)
-
-
-def _fix_dot_imports(not_consumed):
- """ Try to fix imports with multiple dots, by returning a dictionary
- with the import names expanded. The function unflattens root imports,
- like 'xml' (when we have both 'xml.etree' and 'xml.sax'), to 'xml.etree'
- and 'xml.sax' respectively.
- """
- names = {}
- for name, stmts in not_consumed.items():
- if any(
- isinstance(stmt, astroid.AssignName)
- and isinstance(stmt.assign_type(), astroid.AugAssign)
- for stmt in stmts
- ):
- continue
- for stmt in stmts:
- if not isinstance(stmt, (astroid.ImportFrom, astroid.Import)):
- continue
- for imports in stmt.names:
- second_name = None
- import_module_name = imports[0]
- if import_module_name == "*":
- # In case of wildcard imports,
- # pick the name from inside the imported module.
- second_name = name
- else:
- name_matches_dotted_import = False
- if (
- import_module_name.startswith(name)
- and import_module_name.find(".") > -1
- ):
- name_matches_dotted_import = True
-
- if name_matches_dotted_import or name in imports:
- # Most likely something like 'xml.etree',
- # which will appear in the .locals as 'xml'.
- # Only pick the name if it wasn't consumed.
- second_name = import_module_name
- if second_name and second_name not in names:
- names[second_name] = stmt
- return sorted(names.items(), key=lambda a: a[1].fromlineno)
-
-
-def _find_frame_imports(name, frame):
- """
- Detect imports in the frame, with the required
- *name*. Such imports can be considered assignments.
- Returns True if an import for the given name was found.
- """
- imports = frame.nodes_of_class((astroid.Import, astroid.ImportFrom))
- for import_node in imports:
- for import_name, import_alias in import_node.names:
- # If the import uses an alias, check only that.
- # Otherwise, check only the import name.
- if import_alias:
- if import_alias == name:
- return True
- elif import_name and import_name == name:
- return True
- return None
-
-
-def _import_name_is_global(stmt, global_names):
- for import_name, import_alias in stmt.names:
- # If the import uses an alias, check only that.
- # Otherwise, check only the import name.
- if import_alias:
- if import_alias in global_names:
- return True
- elif import_name in global_names:
- return True
- return False
-
-
-def _flattened_scope_names(iterator):
- values = (set(stmt.names) for stmt in iterator)
- return set(itertools.chain.from_iterable(values))
-
-
-def _assigned_locally(name_node):
- """
- Checks if name_node has corresponding assign statement in same scope
- """
- assign_stmts = name_node.scope().nodes_of_class(astroid.AssignName)
- return any(a.name == name_node.name for a in assign_stmts)
-
-
-def _is_type_checking_import(node):
- parent = node.parent
- if not isinstance(parent, astroid.If):
- return False
- test = parent.test
- return test.as_string() in TYPING_TYPE_CHECKS_GUARDS
-
-
-def _has_locals_call_after_node(stmt, scope):
- skip_nodes = (
- astroid.FunctionDef,
- astroid.ClassDef,
- astroid.Import,
- astroid.ImportFrom,
- )
- for call in scope.nodes_of_class(astroid.Call, skip_klass=skip_nodes):
- inferred = utils.safe_infer(call.func)
- if (
- utils.is_builtin_object(inferred)
- and getattr(inferred, "name", None) == "locals"
- ):
- if stmt.lineno < call.lineno:
- return True
- return False
-
-
-MSGS = {
- "E0601": (
- "Using variable %r before assignment",
- "used-before-assignment",
- "Used when a local variable is accessed before its assignment.",
- ),
- "E0602": (
- "Undefined variable %r",
- "undefined-variable",
- "Used when an undefined variable is accessed.",
- ),
- "E0603": (
- "Undefined variable name %r in __all__",
- "undefined-all-variable",
- "Used when an undefined variable name is referenced in __all__.",
- ),
- "E0604": (
- "Invalid object %r in __all__, must contain only strings",
- "invalid-all-object",
- "Used when an invalid (non-string) object occurs in __all__.",
- ),
- "E0611": (
- "No name %r in module %r",
- "no-name-in-module",
- "Used when a name cannot be found in a module.",
- ),
- "W0601": (
- "Global variable %r undefined at the module level",
- "global-variable-undefined",
- 'Used when a variable is defined through the "global" statement '
- "but the variable is not defined in the module scope.",
- ),
- "W0602": (
- "Using global for %r but no assignment is done",
- "global-variable-not-assigned",
- 'Used when a variable is defined through the "global" statement '
- "but no assignment to this variable is done.",
- ),
- "W0603": (
- "Using the global statement", # W0121
- "global-statement",
- 'Used when you use the "global" statement to update a global '
- "variable. Pylint just try to discourage this "
- "usage. That doesn't mean you cannot use it !",
- ),
- "W0604": (
- "Using the global statement at the module level", # W0103
- "global-at-module-level",
- 'Used when you use the "global" statement at the module level '
- "since it has no effect",
- ),
- "W0611": (
- "Unused %s",
- "unused-import",
- "Used when an imported module or variable is not used.",
- ),
- "W0612": (
- "Unused variable %r",
- "unused-variable",
- "Used when a variable is defined but not used.",
- ),
- "W0613": (
- "Unused argument %r",
- "unused-argument",
- "Used when a function or method argument is not used.",
- ),
- "W0614": (
- "Unused import %s from wildcard import",
- "unused-wildcard-import",
- "Used when an imported module or variable is not used from a "
- "`'from X import *'` style import.",
- ),
- "W0621": (
- "Redefining name %r from outer scope (line %s)",
- "redefined-outer-name",
- "Used when a variable's name hides a name defined in the outer scope.",
- ),
- "W0622": (
- "Redefining built-in %r",
- "redefined-builtin",
- "Used when a variable or function override a built-in.",
- ),
- "W0623": (
- "Redefining name %r from %s in exception handler",
- "redefine-in-handler",
- "Used when an exception handler assigns the exception to an existing name",
- ),
- "W0631": (
- "Using possibly undefined loop variable %r",
- "undefined-loop-variable",
- "Used when a loop variable (i.e. defined by a for loop or "
- "a list comprehension or a generator expression) is used outside "
- "the loop.",
- ),
- "W0632": (
- "Possible unbalanced tuple unpacking with "
- "sequence%s: "
- "left side has %d label(s), right side has %d value(s)",
- "unbalanced-tuple-unpacking",
- "Used when there is an unbalanced tuple unpacking in assignment",
- {"old_names": [("E0632", "old-unbalanced-tuple-unpacking")]},
- ),
- "E0633": (
- "Attempting to unpack a non-sequence%s",
- "unpacking-non-sequence",
- "Used when something which is not "
- "a sequence is used in an unpack assignment",
- {"old_names": [("W0633", "old-unpacking-non-sequence")]},
- ),
- "W0640": (
- "Cell variable %s defined in loop",
- "cell-var-from-loop",
- "A variable used in a closure is defined in a loop. "
- "This will result in all closures using the same value for "
- "the closed-over variable.",
- ),
- "W0641": (
- "Possibly unused variable %r",
- "possibly-unused-variable",
- "Used when a variable is defined but might not be used. "
- "The possibility comes from the fact that locals() might be used, "
- "which could consume or not the said variable",
- ),
- "W0642": (
- "Invalid assignment to %s in method",
- "self-cls-assignment",
- "Invalid assignment to self or cls in instance or class method "
- "respectively.",
- ),
-}
-
-
-ScopeConsumer = collections.namedtuple(
- "ScopeConsumer", "to_consume consumed scope_type"
-)
-
-
-class NamesConsumer:
- """
- A simple class to handle consumed, to consume and scope type info of node locals
- """
-
- def __init__(self, node, scope_type):
- self._atomic = ScopeConsumer(copy.copy(node.locals), {}, scope_type)
-
- def __repr__(self):
- msg = "\nto_consume : {:s}\n".format(
- ", ".join(
- [
- "{}->{}".format(key, val)
- for key, val in self._atomic.to_consume.items()
- ]
- )
- )
- msg += "consumed : {:s}\n".format(
- ", ".join(
- [
- "{}->{}".format(key, val)
- for key, val in self._atomic.consumed.items()
- ]
- )
- )
- msg += "scope_type : {:s}\n".format(self._atomic.scope_type)
- return msg
-
- def __iter__(self):
- return iter(self._atomic)
-
- @property
- def to_consume(self):
- return self._atomic.to_consume
-
- @property
- def consumed(self):
- return self._atomic.consumed
-
- @property
- def scope_type(self):
- return self._atomic.scope_type
-
- def mark_as_consumed(self, name, new_node):
- """
- Mark the name as consumed and delete it from
- the to_consume dictionary
- """
- self.consumed[name] = new_node
- del self.to_consume[name]
-
- def get_next_to_consume(self, node):
- # mark the name as consumed if it's defined in this scope
- name = node.name
- parent_node = node.parent
- found_node = self.to_consume.get(name)
- if (
- found_node
- and isinstance(parent_node, astroid.Assign)
- and parent_node == found_node[0].parent
- ):
- lhs = found_node[0].parent.targets[0]
- if lhs.name == name: # this name is defined in this very statement
- found_node = None
- return found_node
-
-
-# pylint: disable=too-many-public-methods
-class VariablesChecker(BaseChecker):
- """checks for
- * unused variables / imports
- * undefined variables
- * redefinition of variable from builtins or from an outer scope
- * use of variable before assignment
- * __all__ consistency
- * self/cls assignment
- """
-
- __implements__ = IAstroidChecker
-
- name = "variables"
- msgs = MSGS
- priority = -1
- options = (
- (
- "init-import",
- {
- "default": 0,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": "Tells whether we should check for unused import in "
- "__init__ files.",
- },
- ),
- (
- "dummy-variables-rgx",
- {
- "default": "_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_",
- "type": "regexp",
- "metavar": "<regexp>",
- "help": "A regular expression matching the name of dummy "
- "variables (i.e. expected to not be used).",
- },
- ),
- (
- "additional-builtins",
- {
- "default": (),
- "type": "csv",
- "metavar": "<comma separated list>",
- "help": "List of additional names supposed to be defined in "
- "builtins. Remember that you should avoid defining new builtins "
- "when possible.",
- },
- ),
- (
- "callbacks",
- {
- "default": ("cb_", "_cb"),
- "type": "csv",
- "metavar": "<callbacks>",
- "help": "List of strings which can identify a callback "
- "function by name. A callback name must start or "
- "end with one of those strings.",
- },
- ),
- (
- "redefining-builtins-modules",
- {
- "default": (
- "six.moves",
- "past.builtins",
- "future.builtins",
- "builtins",
- "io",
- ),
- "type": "csv",
- "metavar": "<comma separated list>",
- "help": "List of qualified module names which can have objects "
- "that can redefine builtins.",
- },
- ),
- (
- "ignored-argument-names",
- {
- "default": IGNORED_ARGUMENT_NAMES,
- "type": "regexp",
- "metavar": "<regexp>",
- "help": "Argument names that match this expression will be "
- "ignored. Default to name with leading underscore.",
- },
- ),
- (
- "allow-global-unused-variables",
- {
- "default": True,
- "type": "yn",
- "metavar": "<y_or_n>",
- "help": "Tells whether unused global variables should be treated as a violation.",
- },
- ),
- )
-
- def __init__(self, linter=None):
- BaseChecker.__init__(self, linter)
- self._to_consume = (
- None
- ) # list of tuples: (to_consume:dict, consumed:dict, scope_type:str)
- self._checking_mod_attr = None
- self._loop_variables = []
- self._type_annotation_names = []
- self._postponed_evaluation_enabled = False
-
- @utils.check_messages("redefined-outer-name")
- def visit_for(self, node):
- assigned_to = [
- var.name for var in node.target.nodes_of_class(astroid.AssignName)
- ]
-
- # Only check variables that are used
- dummy_rgx = self.config.dummy_variables_rgx
- assigned_to = [var for var in assigned_to if not dummy_rgx.match(var)]
-
- for variable in assigned_to:
- for outer_for, outer_variables in self._loop_variables:
- if variable in outer_variables and not in_for_else_branch(
- outer_for, node
- ):
- self.add_message(
- "redefined-outer-name",
- args=(variable, outer_for.fromlineno),
- node=node,
- )
- break
-
- self._loop_variables.append((node, assigned_to))
-
- @utils.check_messages("redefined-outer-name")
- def leave_for(self, node):
- self._loop_variables.pop()
- self._store_type_annotation_names(node)
-
- def visit_module(self, node):
- """visit module : update consumption analysis variable
- checks globals doesn't overrides builtins
- """
- self._to_consume = [NamesConsumer(node, "module")]
- self._postponed_evaluation_enabled = is_postponed_evaluation_enabled(node)
-
- for name, stmts in node.locals.items():
- if utils.is_builtin(name) and not utils.is_inside_except(stmts[0]):
- if self._should_ignore_redefined_builtin(stmts[0]) or name == "__doc__":
- continue
- self.add_message("redefined-builtin", args=name, node=stmts[0])
-
- @utils.check_messages(
- "unused-import",
- "unused-wildcard-import",
- "redefined-builtin",
- "undefined-all-variable",
- "invalid-all-object",
- "unused-variable",
- )
- def leave_module(self, node):
- """leave module: check globals
- """
- assert len(self._to_consume) == 1
-
- self._check_metaclasses(node)
- not_consumed = self._to_consume.pop().to_consume
- # attempt to check for __all__ if defined
- if "__all__" in node.locals:
- self._check_all(node, not_consumed)
-
- # check for unused globals
- self._check_globals(not_consumed)
-
- # don't check unused imports in __init__ files
- if not self.config.init_import and node.package:
- return
-
- self._check_imports(not_consumed)
-
- def visit_classdef(self, node):
- """visit class: update consumption analysis variable
- """
- self._to_consume.append(NamesConsumer(node, "class"))
-
- def leave_classdef(self, _):
- """leave class: update consumption analysis variable
- """
- # do not check for not used locals here (no sense)
- self._to_consume.pop()
-
- def visit_lambda(self, node):
- """visit lambda: update consumption analysis variable
- """
- self._to_consume.append(NamesConsumer(node, "lambda"))
-
- def leave_lambda(self, _):
- """leave lambda: update consumption analysis variable
- """
- # do not check for not used locals here
- self._to_consume.pop()
-
- def visit_generatorexp(self, node):
- """visit genexpr: update consumption analysis variable
- """
- self._to_consume.append(NamesConsumer(node, "comprehension"))
-
- def leave_generatorexp(self, _):
- """leave genexpr: update consumption analysis variable
- """
- # do not check for not used locals here
- self._to_consume.pop()
-
- def visit_dictcomp(self, node):
- """visit dictcomp: update consumption analysis variable
- """
- self._to_consume.append(NamesConsumer(node, "comprehension"))
-
- def leave_dictcomp(self, _):
- """leave dictcomp: update consumption analysis variable
- """
- # do not check for not used locals here
- self._to_consume.pop()
-
- def visit_setcomp(self, node):
- """visit setcomp: update consumption analysis variable
- """
- self._to_consume.append(NamesConsumer(node, "comprehension"))
-
- def leave_setcomp(self, _):
- """leave setcomp: update consumption analysis variable
- """
- # do not check for not used locals here
- self._to_consume.pop()
-
- def visit_functiondef(self, node):
- """visit function: update consumption analysis variable and check locals
- """
- self._to_consume.append(NamesConsumer(node, "function"))
- if not (
- self.linter.is_message_enabled("redefined-outer-name")
- or self.linter.is_message_enabled("redefined-builtin")
- ):
- return
- globs = node.root().globals
- for name, stmt in node.items():
- if utils.is_inside_except(stmt):
- continue
- if name in globs and not isinstance(stmt, astroid.Global):
- definition = globs[name][0]
- if (
- isinstance(definition, astroid.ImportFrom)
- and definition.modname == FUTURE
- ):
- # It is a __future__ directive, not a symbol.
- continue
-
- # Do not take in account redefined names for the purpose
- # of type checking.:
- if any(
- isinstance(definition.parent, astroid.If)
- and definition.parent.test.as_string() in TYPING_TYPE_CHECKS_GUARDS
- for definition in globs[name]
- ):
- continue
-
- line = definition.fromlineno
- if not self._is_name_ignored(stmt, name):
- self.add_message(
- "redefined-outer-name", args=(name, line), node=stmt
- )
-
- elif utils.is_builtin(name) and not self._should_ignore_redefined_builtin(
- stmt
- ):
- # do not print Redefining builtin for additional builtins
- self.add_message("redefined-builtin", args=name, node=stmt)
-
- def leave_functiondef(self, node):
- """leave function: check function's locals are consumed"""
- self._check_metaclasses(node)
-
- if node.type_comment_returns:
- self._store_type_annotation_node(node.type_comment_returns)
- if node.type_comment_args:
- for argument_annotation in node.type_comment_args:
- self._store_type_annotation_node(argument_annotation)
-
- not_consumed = self._to_consume.pop().to_consume
- if not (
- self.linter.is_message_enabled("unused-variable")
- or self.linter.is_message_enabled("possibly-unused-variable")
- or self.linter.is_message_enabled("unused-argument")
- ):
- return
-
- # Don't check arguments of function which are only raising an exception.
- if utils.is_error(node):
- return
-
- # Don't check arguments of abstract methods or within an interface.
- is_method = node.is_method()
- if is_method and node.is_abstract():
- return
-
- global_names = _flattened_scope_names(node.nodes_of_class(astroid.Global))
- nonlocal_names = _flattened_scope_names(node.nodes_of_class(astroid.Nonlocal))
- for name, stmts in not_consumed.items():
- self._check_is_unused(name, node, stmts[0], global_names, nonlocal_names)
-
- visit_asyncfunctiondef = visit_functiondef
- leave_asyncfunctiondef = leave_functiondef
-
- @utils.check_messages(
- "global-variable-undefined",
- "global-variable-not-assigned",
- "global-statement",
- "global-at-module-level",
- "redefined-builtin",
- )
- def visit_global(self, node):
- """check names imported exists in the global scope"""
- frame = node.frame()
- if isinstance(frame, astroid.Module):
- self.add_message("global-at-module-level", node=node)
- return
-
- module = frame.root()
- default_message = True
- locals_ = node.scope().locals
- for name in node.names:
- try:
- assign_nodes = module.getattr(name)
- except astroid.NotFoundError:
- # unassigned global, skip
- assign_nodes = []
-
- not_defined_locally_by_import = not any(
- isinstance(local, astroid.node_classes.Import)
- for local in locals_.get(name, ())
- )
- if not assign_nodes and not_defined_locally_by_import:
- self.add_message("global-variable-not-assigned", args=name, node=node)
- default_message = False
- continue
-
- for anode in assign_nodes:
- if (
- isinstance(anode, astroid.AssignName)
- and anode.name in module.special_attributes
- ):
- self.add_message("redefined-builtin", args=name, node=node)
- break
- if anode.frame() is module:
- # module level assignment
- break
- else:
- if not_defined_locally_by_import:
- # global undefined at the module scope
- self.add_message("global-variable-undefined", args=name, node=node)
- default_message = False
-
- if default_message:
- self.add_message("global-statement", node=node)
-
- def visit_assignname(self, node):
- if isinstance(node.assign_type(), astroid.AugAssign):
- self.visit_name(node)
-
- def visit_delname(self, node):
- self.visit_name(node)
-
- @utils.check_messages(*MSGS)
- def visit_name(self, node):
- """check that a name is defined if the current scope and doesn't
- redefine a built-in
- """
- stmt = node.statement()
- if stmt.fromlineno is None:
- # name node from an astroid built from live code, skip
- assert not stmt.root().file.endswith(".py")
- return
-
- name = node.name
- frame = stmt.scope()
- # if the name node is used as a function default argument's value or as
- # a decorator, then start from the parent frame of the function instead
- # of the function frame - and thus open an inner class scope
- if (
- utils.is_default_argument(node)
- or utils.is_func_decorator(node)
- or utils.is_ancestor_name(frame, node)
- ):
- start_index = len(self._to_consume) - 2
- else:
- start_index = len(self._to_consume) - 1
- # iterates through parent scopes, from the inner to the outer
- base_scope_type = self._to_consume[start_index].scope_type
- # pylint: disable=too-many-nested-blocks; refactoring this block is a pain.
- for i in range(start_index, -1, -1):
- current_consumer = self._to_consume[i]
- # if the current scope is a class scope but it's not the inner
- # scope, ignore it. This prevents to access this scope instead of
- # the globals one in function members when there are some common
- # names. The only exception is when the starting scope is a
- # comprehension and its direct outer scope is a class
- if (
- current_consumer.scope_type == "class"
- and i != start_index
- and not (base_scope_type == "comprehension" and i == start_index - 1)
- ):
- if self._ignore_class_scope(node):
- continue
-
- # the name has already been consumed, only check it's not a loop
- # variable used outside the loop
- # avoid the case where there are homonyms inside function scope and
- #  comprehension current scope (avoid bug #1731)
- if name in current_consumer.consumed and not (
- current_consumer.scope_type == "comprehension"
- and self._has_homonym_in_upper_function_scope(node, i)
- ):
- defnode = utils.assign_parent(current_consumer.consumed[name][0])
- self._check_late_binding_closure(node, defnode)
- self._loopvar_name(node, name)
- break
-
- found_node = current_consumer.get_next_to_consume(node)
- if found_node is None:
- continue
-
- # checks for use before assignment
- defnode = utils.assign_parent(current_consumer.to_consume[name][0])
-
- if defnode is not None:
- self._check_late_binding_closure(node, defnode)
- defstmt = defnode.statement()
- defframe = defstmt.frame()
- # The class reuses itself in the class scope.
- recursive_klass = (
- frame is defframe
- and defframe.parent_of(node)
- and isinstance(defframe, astroid.ClassDef)
- and node.name == defframe.name
- )
-
- if (
- recursive_klass
- and utils.is_inside_lambda(node)
- and (
- not utils.is_default_argument(node)
- or node.scope().parent.scope() is not defframe
- )
- ):
- # Self-referential class references are fine in lambda's --
- # As long as they are not part of the default argument directly
- # under the scope of the parent self-referring class.
- # Example of valid default argument:
- # class MyName3:
- # myattr = 1
- # mylambda3 = lambda: lambda a=MyName3: a
- # Example of invalid default argument:
- # class MyName4:
- # myattr = 1
- # mylambda4 = lambda a=MyName4: lambda: a
-
- # If the above conditional is True,
- # there is no possibility of undefined-variable
- # Also do not consume class name
- # (since consuming blocks subsequent checks)
- # -- quit
- break
-
- maybee0601, annotation_return, use_outer_definition = self._is_variable_violation(
- node,
- name,
- defnode,
- stmt,
- defstmt,
- frame,
- defframe,
- base_scope_type,
- recursive_klass,
- )
-
- if use_outer_definition:
- continue
-
- if (
- maybee0601
- and not utils.is_defined_before(node)
- and not astroid.are_exclusive(stmt, defstmt, ("NameError",))
- ):
-
- # Used and defined in the same place, e.g `x += 1` and `del x`
- defined_by_stmt = defstmt is stmt and isinstance(
- node, (astroid.DelName, astroid.AssignName)
- )
- if (
- recursive_klass
- or defined_by_stmt
- or annotation_return
- or isinstance(defstmt, astroid.Delete)
- ):
- if not utils.node_ignores_exception(node, NameError):
-
- # Handle postponed evaluation of annotations
- if not (
- self._postponed_evaluation_enabled
- and isinstance(
- stmt,
- (
- astroid.AnnAssign,
- astroid.FunctionDef,
- astroid.Arguments,
- ),
- )
- and name in node.root().locals
- ):
- self.add_message(
- "undefined-variable", args=name, node=node
- )
- elif base_scope_type != "lambda":
- # E0601 may *not* occurs in lambda scope.
-
- # Handle postponed evaluation of annotations
- if not (
- self._postponed_evaluation_enabled
- and isinstance(
- stmt, (astroid.AnnAssign, astroid.FunctionDef)
- )
- ):
- self.add_message(
- "used-before-assignment", args=name, node=node
- )
- elif base_scope_type == "lambda":
- # E0601 can occur in class-level scope in lambdas, as in
- # the following example:
- # class A:
- # x = lambda attr: f + attr
- # f = 42
- if isinstance(frame, astroid.ClassDef) and name in frame.locals:
- if isinstance(node.parent, astroid.Arguments):
- if stmt.fromlineno <= defstmt.fromlineno:
- # Doing the following is fine:
- # class A:
- # x = 42
- # y = lambda attr=x: attr
- self.add_message(
- "used-before-assignment", args=name, node=node
- )
- else:
- self.add_message(
- "undefined-variable", args=name, node=node
- )
- elif current_consumer.scope_type == "lambda":
- self.add_message("undefined-variable", node=node, args=name)
-
- current_consumer.mark_as_consumed(name, found_node)
- # check it's not a loop variable used outside the loop
- self._loopvar_name(node, name)
- break
- else:
- # we have not found the name, if it isn't a builtin, that's an
- # undefined name !
- if not (
- name in astroid.Module.scope_attrs
- or utils.is_builtin(name)
- or name in self.config.additional_builtins
- ):
- if not utils.node_ignores_exception(node, NameError):
- self.add_message("undefined-variable", args=name, node=node)
-
- @utils.check_messages("no-name-in-module")
- def visit_import(self, node):
- """check modules attribute accesses"""
- if not self._analyse_fallback_blocks and utils.is_from_fallback_block(node):
- # No need to verify this, since ImportError is already
- # handled by the client code.
- return
-
- for name, _ in node.names:
- parts = name.split(".")
- try:
- module = next(_infer_name_module(node, parts[0]))
- except astroid.ResolveError:
- continue
- self._check_module_attrs(node, module, parts[1:])
-
- @utils.check_messages("no-name-in-module")
- def visit_importfrom(self, node):
- """check modules attribute accesses"""
- if not self._analyse_fallback_blocks and utils.is_from_fallback_block(node):
- # No need to verify this, since ImportError is already
- # handled by the client code.
- return
-
- name_parts = node.modname.split(".")
- try:
- module = node.do_import_module(name_parts[0])
- except astroid.AstroidBuildingException:
- return
- module = self._check_module_attrs(node, module, name_parts[1:])
- if not module:
- return
- for name, _ in node.names:
- if name == "*":
- continue
- self._check_module_attrs(node, module, name.split("."))
-
- @utils.check_messages(
- "unbalanced-tuple-unpacking", "unpacking-non-sequence", "self-cls-assignment"
- )
- def visit_assign(self, node):
- """Check unbalanced tuple unpacking for assignments
- and unpacking non-sequences as well as in case self/cls
- get assigned.
- """
- self._check_self_cls_assign(node)
- if not isinstance(node.targets[0], (astroid.Tuple, astroid.List)):
- return
-
- targets = node.targets[0].itered()
- try:
- inferred = utils.safe_infer(node.value)
- if inferred is not None:
- self._check_unpacking(inferred, node, targets)
- except astroid.InferenceError:
- return
-
- # listcomp have now also their scope
- def visit_listcomp(self, node):
- """visit dictcomp: update consumption analysis variable
- """
- self._to_consume.append(NamesConsumer(node, "comprehension"))
-
- def leave_listcomp(self, _):
- """leave dictcomp: update consumption analysis variable
- """
- # do not check for not used locals here
- self._to_consume.pop()
-
- def leave_assign(self, node):
- self._store_type_annotation_names(node)
-
- def leave_with(self, node):
- self._store_type_annotation_names(node)
-
- def visit_arguments(self, node):
- for annotation in node.type_comment_args:
- self._store_type_annotation_node(annotation)
-
- # Relying on other checker's options, which might not have been initialized yet.
- @decorators.cachedproperty
- def _analyse_fallback_blocks(self):
- return get_global_option(self, "analyse-fallback-blocks", default=False)
-
- @decorators.cachedproperty
- def _ignored_modules(self):
- return get_global_option(self, "ignored-modules", default=[])
-
- @decorators.cachedproperty
- def _allow_global_unused_variables(self):
- return get_global_option(self, "allow-global-unused-variables", default=True)
-
- @staticmethod
- def _defined_in_function_definition(node, frame):
- in_annotation_or_default = False
- if isinstance(frame, astroid.FunctionDef) and node.statement() is frame:
- in_annotation_or_default = (
- node in frame.args.annotations
- or node in frame.args.kwonlyargs_annotations
- or node is frame.args.varargannotation
- or node is frame.args.kwargannotation
- ) or frame.args.parent_of(node)
- return in_annotation_or_default
-
- @staticmethod
- def _is_variable_violation(
- node,
- name,
- defnode,
- stmt,
- defstmt,
- frame,
- defframe,
- base_scope_type,
- recursive_klass,
- ):
- # pylint: disable=too-many-nested-blocks
- # node: Node to check for violation
- # name: name of node to check violation for
- # frame: Scope of statement of node
- # base_scope_type: local scope type
- maybee0601 = True
- annotation_return = False
- use_outer_definition = False
- if frame is not defframe:
- maybee0601 = _detect_global_scope(node, frame, defframe)
- elif defframe.parent is None:
- # we are at the module level, check the name is not
- # defined in builtins
- if name in defframe.scope_attrs or astroid.builtin_lookup(name)[1]:
- maybee0601 = False
- else:
- # we are in a local scope, check the name is not
- # defined in global or builtin scope
- # skip this lookup if name is assigned later in function scope/lambda
- # Note: the node.frame() is not the same as the `frame` argument which is
- # equivalent to frame.statement().scope()
- forbid_lookup = (
- isinstance(frame, astroid.FunctionDef)
- or isinstance(node.frame(), astroid.Lambda)
- ) and _assigned_locally(node)
- if not forbid_lookup and defframe.root().lookup(name)[1]:
- maybee0601 = False
- use_outer_definition = stmt == defstmt and not isinstance(
- defnode, astroid.node_classes.Comprehension
- )
- else:
- # check if we have a nonlocal
- if name in defframe.locals:
- maybee0601 = not any(
- isinstance(child, astroid.Nonlocal) and name in child.names
- for child in defframe.get_children()
- )
-
- if (
- base_scope_type == "lambda"
- and isinstance(frame, astroid.ClassDef)
- and name in frame.locals
- ):
-
- # This rule verifies that if the definition node of the
- # checked name is an Arguments node and if the name
- # is used a default value in the arguments defaults
- # and the actual definition of the variable label
- # is happening before the Arguments definition.
- #
- # bar = None
- # foo = lambda bar=bar: bar
- #
- # In this case, maybee0601 should be False, otherwise
- # it should be True.
- maybee0601 = not (
- isinstance(defnode, astroid.Arguments)
- and node in defnode.defaults
- and frame.locals[name][0].fromlineno < defstmt.fromlineno
- )
- elif isinstance(defframe, astroid.ClassDef) and isinstance(
- frame, astroid.FunctionDef
- ):
- # Special rule for function return annotations,
- # which uses the same name as the class where
- # the function lives.
- if node is frame.returns and defframe.parent_of(frame.returns):
- maybee0601 = annotation_return = True
-
- if (
- maybee0601
- and defframe.name in defframe.locals
- and defframe.locals[name][0].lineno < frame.lineno
- ):
- # Detect class assignments with the same
- # name as the class. In this case, no warning
- # should be raised.
- maybee0601 = False
- if isinstance(node.parent, astroid.Arguments):
- maybee0601 = stmt.fromlineno <= defstmt.fromlineno
- elif recursive_klass:
- maybee0601 = True
- else:
- maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno
- if maybee0601 and stmt.fromlineno == defstmt.fromlineno:
- if (
- isinstance(defframe, astroid.FunctionDef)
- and frame is defframe
- and defframe.parent_of(node)
- and stmt is not defstmt
- ):
- # Single statement function, with the statement on the
- # same line as the function definition
- maybee0601 = False
-
- # Look for type checking definitions inside a type checking guard.
- if isinstance(defstmt, (astroid.Import, astroid.ImportFrom)):
- defstmt_parent = defstmt.parent
-
- if (
- isinstance(defstmt_parent, astroid.If)
- and defstmt_parent.test.as_string() in TYPING_TYPE_CHECKS_GUARDS
- ):
- # Exempt those definitions that are used inside the type checking
- # guard or that are defined in both type checking guard branches.
- used_in_branch = defstmt_parent.parent_of(node)
- defined_in_or_else = False
-
- for definition in defstmt_parent.orelse:
- if isinstance(definition, astroid.Assign):
- defined_in_or_else = any(
- target.name == name for target in definition.targets
- )
- if defined_in_or_else:
- break
-
- if not used_in_branch and not defined_in_or_else:
- maybee0601 = True
-
- return maybee0601, annotation_return, use_outer_definition
-
- def _ignore_class_scope(self, node):
- """
- Return True if the node is in a local class scope, as an assignment.
-
- :param node: Node considered
- :type node: astroid.Node
- :return: True if the node is in a local class scope, as an assignment. False otherwise.
- :rtype: bool
- """
- # Detect if we are in a local class scope, as an assignment.
- # For example, the following is fair game.
- #
- # class A:
- # b = 1
- # c = lambda b=b: b * b
- #
- # class B:
- # tp = 1
- # def func(self, arg: tp):
- # ...
- # class C:
- # tp = 2
- # def func(self, arg=tp):
- # ...
-
- name = node.name
- frame = node.statement().scope()
- in_annotation_or_default = self._defined_in_function_definition(node, frame)
- if in_annotation_or_default:
- frame_locals = frame.parent.scope().locals
- else:
- frame_locals = frame.locals
- return not (
- (isinstance(frame, astroid.ClassDef) or in_annotation_or_default)
- and name in frame_locals
- )
-
- def _loopvar_name(self, node, name):
- # filter variables according to node's scope
- if not self.linter.is_message_enabled("undefined-loop-variable"):
- return
- astmts = [stmt for stmt in node.lookup(name)[1] if hasattr(stmt, "assign_type")]
- # If this variable usage exists inside a function definition
- # that exists in the same loop,
- # the usage is safe because the function will not be defined either if
- # the variable is not defined.
- scope = node.scope()
- if isinstance(scope, astroid.FunctionDef) and any(
- asmt.statement().parent_of(scope) for asmt in astmts
- ):
- return
-
- # filter variables according their respective scope test is_statement
- # and parent to avoid #74747. This is not a total fix, which would
- # introduce a mechanism similar to special attribute lookup in
- # modules. Also, in order to get correct inference in this case, the
- # scope lookup rules would need to be changed to return the initial
- # assignment (which does not exist in code per se) as well as any later
- # modifications.
- if (
- not astmts
- or (astmts[0].is_statement or astmts[0].parent)
- and astmts[0].statement().parent_of(node)
- ):
- _astmts = []
- else:
- _astmts = astmts[:1]
- for i, stmt in enumerate(astmts[1:]):
- if astmts[i].statement().parent_of(stmt) and not in_for_else_branch(
- astmts[i].statement(), stmt
- ):
- continue
- _astmts.append(stmt)
- astmts = _astmts
- if len(astmts) != 1:
- return
-
- assign = astmts[0].assign_type()
- if not (
- isinstance(
- assign, (astroid.For, astroid.Comprehension, astroid.GeneratorExp)
- )
- and assign.statement() is not node.statement()
- ):
- return
-
- # For functions we can do more by inferring the length of the itered object
- if not isinstance(assign, astroid.For):
- self.add_message("undefined-loop-variable", args=name, node=node)
- return
-
- try:
- inferred = next(assign.iter.infer())
- except astroid.InferenceError:
- self.add_message("undefined-loop-variable", args=name, node=node)
- else:
- if (
- isinstance(inferred, astroid.Instance)
- and inferred.qname() == BUILTIN_RANGE
- ):
- # Consider range() objects safe, even if they might not yield any results.
- return
-
- # Consider sequences.
- sequences = (
- astroid.List,
- astroid.Tuple,
- astroid.Dict,
- astroid.Set,
- objects.FrozenSet,
- )
- if not isinstance(inferred, sequences):
- self.add_message("undefined-loop-variable", args=name, node=node)
- return
-
- elements = getattr(inferred, "elts", getattr(inferred, "items", []))
- if not elements:
- self.add_message("undefined-loop-variable", args=name, node=node)
-
- def _check_is_unused(self, name, node, stmt, global_names, nonlocal_names):
- # pylint: disable=too-many-branches
- # Ignore some special names specified by user configuration.
- if self._is_name_ignored(stmt, name):
- return
- # Ignore names that were added dynamically to the Function scope
- if (
- isinstance(node, astroid.FunctionDef)
- and name == "__class__"
- and len(node.locals["__class__"]) == 1
- and isinstance(node.locals["__class__"][0], astroid.ClassDef)
- ):
- return
-
- # Ignore names imported by the global statement.
- if isinstance(stmt, (astroid.Global, astroid.Import, astroid.ImportFrom)):
- # Detect imports, assigned to global statements.
- if global_names and _import_name_is_global(stmt, global_names):
- return
-
- argnames = list(
- itertools.chain(node.argnames(), [arg.name for arg in node.args.kwonlyargs])
- )
- # Care about functions with unknown argument (builtins)
- if name in argnames:
- self._check_unused_arguments(name, node, stmt, argnames)
- else:
- if stmt.parent and isinstance(
- stmt.parent, (astroid.Assign, astroid.AnnAssign)
- ):
- if name in nonlocal_names:
- return
-
- qname = asname = None
- if isinstance(stmt, (astroid.Import, astroid.ImportFrom)):
- # Need the complete name, which we don't have in .locals.
- if len(stmt.names) > 1:
- import_names = next(
- (names for names in stmt.names if name in names), None
- )
- else:
- import_names = stmt.names[0]
- if import_names:
- qname, asname = import_names
- name = asname or qname
-
- if _has_locals_call_after_node(stmt, node.scope()):
- message_name = "possibly-unused-variable"
- else:
- if isinstance(stmt, astroid.Import):
- if asname is not None:
- msg = "%s imported as %s" % (qname, asname)
- else:
- msg = "import %s" % name
- self.add_message("unused-import", args=msg, node=stmt)
- return
- if isinstance(stmt, astroid.ImportFrom):
- if asname is not None:
- msg = "%s imported from %s as %s" % (
- qname,
- stmt.modname,
- asname,
- )
- else:
- msg = "%s imported from %s" % (name, stmt.modname)
- self.add_message("unused-import", args=msg, node=stmt)
- return
- message_name = "unused-variable"
-
- # Don't check function stubs created only for type information
- if utils.is_overload_stub(node):
- return
-
- self.add_message(message_name, args=name, node=stmt)
-
- def _is_name_ignored(self, stmt, name):
- authorized_rgx = self.config.dummy_variables_rgx
- if (
- isinstance(stmt, astroid.AssignName)
- and isinstance(stmt.parent, astroid.Arguments)
- or isinstance(stmt, astroid.Arguments)
- ):
- regex = self.config.ignored_argument_names
- else:
- regex = authorized_rgx
- return regex and regex.match(name)
-
- def _check_unused_arguments(self, name, node, stmt, argnames):
- is_method = node.is_method()
- klass = node.parent.frame()
- if is_method and isinstance(klass, astroid.ClassDef):
- confidence = (
- INFERENCE if utils.has_known_bases(klass) else INFERENCE_FAILURE
- )
- else:
- confidence = HIGH
-
- if is_method:
- # Don't warn for the first argument of a (non static) method
- if node.type != "staticmethod" and name == argnames[0]:
- return
- # Don't warn for argument of an overridden method
- overridden = overridden_method(klass, node.name)
- if overridden is not None and name in overridden.argnames():
- return
- if node.name in utils.PYMETHODS and node.name not in (
- "__init__",
- "__new__",
- ):
- return
- # Don't check callback arguments
- if any(
- node.name.startswith(cb) or node.name.endswith(cb)
- for cb in self.config.callbacks
- ):
- return
- # Don't check arguments of singledispatch.register function.
- if utils.is_registered_in_singledispatch_function(node):
- return
-
- # Don't check function stubs created only for type information
- if utils.is_overload_stub(node):
- return
-
- # Don't check protocol classes
- if utils.is_protocol_class(klass):
- return
-
- self.add_message("unused-argument", args=name, node=stmt, confidence=confidence)
-
- def _check_late_binding_closure(self, node, assignment_node):
- def _is_direct_lambda_call():
- return (
- isinstance(node_scope.parent, astroid.Call)
- and node_scope.parent.func is node_scope
- )
-
- node_scope = node.scope()
- if not isinstance(node_scope, (astroid.Lambda, astroid.FunctionDef)):
- return
- if isinstance(node.parent, astroid.Arguments):
- return
-
- if isinstance(assignment_node, astroid.Comprehension):
- if assignment_node.parent.parent_of(node.scope()):
- self.add_message("cell-var-from-loop", node=node, args=node.name)
- else:
- assign_scope = assignment_node.scope()
- maybe_for = assignment_node
- while not isinstance(maybe_for, astroid.For):
- if maybe_for is assign_scope:
- break
- maybe_for = maybe_for.parent
- else:
- if (
- maybe_for.parent_of(node_scope)
- and not _is_direct_lambda_call()
- and not isinstance(node_scope.statement(), astroid.Return)
- ):
- self.add_message("cell-var-from-loop", node=node, args=node.name)
-
- def _should_ignore_redefined_builtin(self, stmt):
- if not isinstance(stmt, astroid.ImportFrom):
- return False
- return stmt.modname in self.config.redefining_builtins_modules
-
- def _has_homonym_in_upper_function_scope(self, node, index):
- """
- Return True if there is a node with the same name in the to_consume dict of an upper scope
- and if that scope is a function
-
- :param node: node to check for
- :type node: astroid.Node
- :param index: index of the current consumer inside self._to_consume
- :type index: int
- :return: True if there is a node with the same name in the to_consume dict of an upper scope
- and if that scope is a function
- :rtype: bool
- """
- for _consumer in self._to_consume[index - 1 :: -1]:
- if _consumer.scope_type == "function" and node.name in _consumer.to_consume:
- return True
- return False
-
- def _store_type_annotation_node(self, type_annotation):
- """Given a type annotation, store all the name nodes it refers to"""
- if isinstance(type_annotation, astroid.Name):
- self._type_annotation_names.append(type_annotation.name)
- return
-
- if not isinstance(type_annotation, astroid.Subscript):
- return
-
- if (
- isinstance(type_annotation.value, astroid.Attribute)
- and isinstance(type_annotation.value.expr, astroid.Name)
- and type_annotation.value.expr.name == TYPING_MODULE
- ):
- self._type_annotation_names.append(TYPING_MODULE)
- return
-
- self._type_annotation_names.extend(
- annotation.name
- for annotation in type_annotation.nodes_of_class(astroid.Name)
- )
-
- def _store_type_annotation_names(self, node):
- type_annotation = node.type_annotation
- if not type_annotation:
- return
- self._store_type_annotation_node(node.type_annotation)
-
- def _check_self_cls_assign(self, node):
- """Check that self/cls don't get assigned"""
- assign_names = {
- target.name
- for target in node.targets
- if isinstance(target, astroid.AssignName)
- }
- scope = node.scope()
- nonlocals_with_same_name = any(
- child
- for child in scope.body
- if isinstance(child, astroid.Nonlocal) and assign_names & set(child.names)
- )
- if nonlocals_with_same_name:
- scope = node.scope().parent.scope()
-
- if not (
- isinstance(scope, astroid.scoped_nodes.FunctionDef)
- and scope.is_method()
- and "builtins.staticmethod" not in scope.decoratornames()
- ):
- return
- argument_names = scope.argnames()
- if not argument_names:
- return
- self_cls_name = argument_names[0]
- target_assign_names = (
- target.name
- for target in node.targets
- if isinstance(target, astroid.node_classes.AssignName)
- )
- if self_cls_name in target_assign_names:
- self.add_message("self-cls-assignment", node=node, args=(self_cls_name))
-
- def _check_unpacking(self, inferred, node, targets):
- """ Check for unbalanced tuple unpacking
- and unpacking non sequences.
- """
- if utils.is_inside_abstract_class(node):
- return
- if utils.is_comprehension(node):
- return
- if inferred is astroid.Uninferable:
- return
- if (
- isinstance(inferred.parent, astroid.Arguments)
- and isinstance(node.value, astroid.Name)
- and node.value.name == inferred.parent.vararg
- ):
- # Variable-length argument, we can't determine the length.
- return
- if isinstance(inferred, (astroid.Tuple, astroid.List)):
- # attempt to check unpacking is properly balanced
- values = inferred.itered()
- if len(targets) != len(values):
- # Check if we have starred nodes.
- if any(isinstance(target, astroid.Starred) for target in targets):
- return
- self.add_message(
- "unbalanced-tuple-unpacking",
- node=node,
- args=(
- _get_unpacking_extra_info(node, inferred),
- len(targets),
- len(values),
- ),
- )
- # attempt to check unpacking may be possible (ie RHS is iterable)
- else:
- if not utils.is_iterable(inferred):
- self.add_message(
- "unpacking-non-sequence",
- node=node,
- args=(_get_unpacking_extra_info(node, inferred),),
- )
-
- def _check_module_attrs(self, node, module, module_names):
- """check that module_names (list of string) are accessible through the
- given module
- if the latest access name corresponds to a module, return it
- """
- assert isinstance(module, astroid.Module), module
- while module_names:
- name = module_names.pop(0)
- if name == "__dict__":
- module = None
- break
- try:
- module = next(module.getattr(name)[0].infer())
- if module is astroid.Uninferable:
- return None
- except astroid.NotFoundError:
- if module.name in self._ignored_modules:
- return None
- self.add_message(
- "no-name-in-module", args=(name, module.name), node=node
- )
- return None
- except astroid.InferenceError:
- return None
- if module_names:
- modname = module.name if module else "__dict__"
- self.add_message(
- "no-name-in-module", node=node, args=(".".join(module_names), modname)
- )
- return None
- if isinstance(module, astroid.Module):
- return module
- return None
-
- def _check_all(self, node, not_consumed):
- assigned = next(node.igetattr("__all__"))
- if assigned is astroid.Uninferable:
- return
-
- for elt in getattr(assigned, "elts", ()):
- try:
- elt_name = next(elt.infer())
- except astroid.InferenceError:
- continue
- if elt_name is astroid.Uninferable:
- continue
- if not elt_name.parent:
- continue
-
- if not isinstance(elt_name, astroid.Const) or not isinstance(
- elt_name.value, str
- ):
- self.add_message("invalid-all-object", args=elt.as_string(), node=elt)
- continue
-
- elt_name = elt_name.value
- # If elt is in not_consumed, remove it from not_consumed
- if elt_name in not_consumed:
- del not_consumed[elt_name]
- continue
-
- if elt_name not in node.locals:
- if not node.package:
- self.add_message(
- "undefined-all-variable", args=(elt_name,), node=elt
- )
- else:
- basename = os.path.splitext(node.file)[0]
- if os.path.basename(basename) == "__init__":
- name = node.name + "." + elt_name
- try:
- modutils.file_from_modpath(name.split("."))
- except ImportError:
- self.add_message(
- "undefined-all-variable", args=(elt_name,), node=elt
- )
- except SyntaxError:
- # don't yield a syntax-error warning,
- # because it will be later yielded
- # when the file will be checked
- pass
-
- def _check_globals(self, not_consumed):
- if self._allow_global_unused_variables:
- return
- for name, nodes in not_consumed.items():
- for node in nodes:
- self.add_message("unused-variable", args=(name,), node=node)
-
- def _check_imports(self, not_consumed):
- local_names = _fix_dot_imports(not_consumed)
- checked = set()
- for name, stmt in local_names:
- for imports in stmt.names:
- real_name = imported_name = imports[0]
- if imported_name == "*":
- real_name = name
- as_name = imports[1]
- if real_name in checked:
- continue
- if name not in (real_name, as_name):
- continue
- checked.add(real_name)
-
- if isinstance(stmt, astroid.Import) or (
- isinstance(stmt, astroid.ImportFrom) and not stmt.modname
- ):
- if isinstance(stmt, astroid.ImportFrom) and SPECIAL_OBJ.search(
- imported_name
- ):
- # Filter special objects (__doc__, __all__) etc.,
- # because they can be imported for exporting.
- continue
-
- if imported_name in self._type_annotation_names:
- # Most likely a typing import if it wasn't used so far.
- continue
-
- if as_name == "_":
- continue
- if as_name is None:
- msg = "import %s" % imported_name
- else:
- msg = "%s imported as %s" % (imported_name, as_name)
- if not _is_type_checking_import(stmt):
- self.add_message("unused-import", args=msg, node=stmt)
- elif isinstance(stmt, astroid.ImportFrom) and stmt.modname != FUTURE:
- if SPECIAL_OBJ.search(imported_name):
- # Filter special objects (__doc__, __all__) etc.,
- # because they can be imported for exporting.
- continue
-
- if _is_from_future_import(stmt, name):
- # Check if the name is in fact loaded from a
- # __future__ import in another module.
- continue
-
- if imported_name in self._type_annotation_names:
- # Most likely a typing import if it wasn't used so far.
- continue
-
- if imported_name == "*":
- self.add_message("unused-wildcard-import", args=name, node=stmt)
- else:
- if as_name is None:
- msg = "%s imported from %s" % (imported_name, stmt.modname)
- else:
- fields = (imported_name, stmt.modname, as_name)
- msg = "%s imported from %s as %s" % fields
- if not _is_type_checking_import(stmt):
- self.add_message("unused-import", args=msg, node=stmt)
- del self._to_consume
-
- def _check_metaclasses(self, node):
- """ Update consumption analysis for metaclasses. """
- consumed = [] # [(scope_locals, consumed_key)]
-
- for child_node in node.get_children():
- if isinstance(child_node, astroid.ClassDef):
- consumed.extend(self._check_classdef_metaclasses(child_node, node))
-
- # Pop the consumed items, in order to avoid having
- # unused-import and unused-variable false positives
- for scope_locals, name in consumed:
- scope_locals.pop(name, None)
-
- def _check_classdef_metaclasses(self, klass, parent_node):
- if not klass._metaclass:
- # Skip if this class doesn't use explicitly a metaclass, but inherits it from ancestors
- return []
-
- consumed = [] # [(scope_locals, consumed_key)]
- metaclass = klass.metaclass()
-
- name = None
- if isinstance(klass._metaclass, astroid.Name):
- name = klass._metaclass.name
- elif metaclass:
- name = metaclass.root().name
-
- found = None
- name = METACLASS_NAME_TRANSFORMS.get(name, name)
- if name:
- # check enclosing scopes starting from most local
- for scope_locals, _, _ in self._to_consume[::-1]:
- found = scope_locals.get(name)
- if found:
- consumed.append((scope_locals, name))
- break
-
- if found is None and not metaclass:
- name = None
- if isinstance(klass._metaclass, astroid.Name):
- name = klass._metaclass.name
- elif isinstance(klass._metaclass, astroid.Attribute):
- name = klass._metaclass.as_string()
-
- if name is not None:
- if not (
- name in astroid.Module.scope_attrs
- or utils.is_builtin(name)
- or name in self.config.additional_builtins
- or name in parent_node.locals
- ):
- self.add_message("undefined-variable", node=klass, args=(name,))
-
- return consumed
-
-
-def register(linter):
- """required method to auto register this checker"""
- linter.register_checker(VariablesChecker(linter))